-
Notifications
You must be signed in to change notification settings - Fork 283
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #959 from Bedrock-OSS/jsontomd
Table Component
- Loading branch information
Showing
23 changed files
with
738 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
<script setup lang="ts"> | ||
import { computed, ref, watch } from "vue"; | ||
import TableHeader from "./TableHeader.vue"; | ||
import TableCell from "./TableCell.vue"; | ||
import sortTableRows, { TableSorting } from "../../data/tables/sortTableRows"; | ||
import { data as tables } from "../../data/tables/tables.data"; | ||
import displayError from "../../utils/displayError"; | ||
import useData from "../../composables/data"; | ||
const { page } = useData(); | ||
const props = defineProps<{ | ||
data: string; | ||
}>(); | ||
const table = computed(() => { | ||
let path = "public"; | ||
if (props.data[0] !== "/") { | ||
path += "/assets/tables/" + page.value.relativePath.replace(/\.md$/, "/"); | ||
} | ||
path += props.data; | ||
const table = tables[path]; | ||
if (!table) { | ||
displayError(new TypeError(`No table with the path "${path}" exists.`)); | ||
} | ||
return table; | ||
}); | ||
const sorting = ref<TableSorting | null>(null); | ||
const sortedRows = ref(table.value.rows); | ||
function toggleSorting(column: string) { | ||
if (sorting.value?.column === column) { | ||
if (sorting.value.order === "ascending") { | ||
sorting.value = { column, order: "descending" }; | ||
return; | ||
} | ||
sorting.value = null; | ||
return; | ||
} | ||
sorting.value = { column, order: "ascending" }; | ||
} | ||
function sortRows() { | ||
if (sorting.value === null) { | ||
sortedRows.value = table.value.rows; | ||
return; | ||
} | ||
const rows = table.value.rows.map((row, index) => ({ | ||
...row, | ||
__initial_index__: index, // Persist index to prevent unnecessary Vue rerenders | ||
})); | ||
sortTableRows(sorting.value, rows); | ||
sortedRows.value = rows; | ||
} | ||
watch(sorting, sortRows); | ||
</script> | ||
|
||
<template> | ||
<table> | ||
<thead> | ||
<tr> | ||
<TableHeader | ||
v-for="(column, columnId) in table.columns" | ||
:key="columnId" | ||
:column-id="columnId as string" | ||
:column | ||
:sorting | ||
@toggle-sorting="toggleSorting(columnId as string)" | ||
/> | ||
</tr> | ||
</thead> | ||
|
||
<tbody> | ||
<tr v-for="(row, index) in sortedRows" :key="(row.__initial_index__ as number) ?? index"> | ||
<TableCell | ||
v-for="(column, columnId) in table.columns" | ||
:key="columnId" | ||
:column | ||
:value="row[columnId] ?? column.default" | ||
/> | ||
</tr> | ||
</tbody> | ||
</table> | ||
</template> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
<script setup lang="ts"> | ||
import { computed, onBeforeUnmount, onMounted, ref } from "vue"; | ||
import { TableValue, TableColumn } from "../../types"; | ||
const props = defineProps<{ | ||
column: TableColumn; | ||
value: TableValue; | ||
}>(); | ||
const shortListLength = 10; | ||
const showMore = ref(false); | ||
const content = ref<HTMLElement | null>(null); | ||
const isOverflowing = ref(false); | ||
const canShowMore = computed(() => { | ||
if (Array.isArray(props.value)) return props.value.length > shortListLength; | ||
if (typeof props.value === "string") return isOverflowing.value; | ||
return false; | ||
}); | ||
const checkOverflow = () => { | ||
if (content.value === null) return; | ||
isOverflowing.value = content.value.scrollHeight > content.value.clientHeight; | ||
}; | ||
onMounted(() => { | ||
checkOverflow(); | ||
window.addEventListener("resize", checkOverflow); | ||
}); | ||
onBeforeUnmount(() => { | ||
window.removeEventListener("resize", checkOverflow); | ||
}); | ||
</script> | ||
|
||
<template> | ||
<td :style="{ textAlign: column.textAlign }"> | ||
<ul v-if="Array.isArray(value)" class="content"> | ||
<li | ||
v-for="(item, itemIndex) in showMore ? value : value.slice(0, shortListLength)" | ||
:key="itemIndex" | ||
v-html="item" | ||
/> | ||
</ul> | ||
|
||
<div | ||
v-else-if="typeof value === 'string'" | ||
ref="content" | ||
class="content" | ||
:data-show-more="showMore" | ||
v-html="value" | ||
/> | ||
|
||
<div v-else-if="typeof value === 'boolean'" class="content">{{ value ? "✔️" : "❌" }}</div> | ||
|
||
<div v-else class="content">{{ value }}</div> | ||
|
||
<template v-if="canShowMore"> | ||
<button | ||
v-if="showMore" | ||
type="button" | ||
class="show-less-button" | ||
@click="() => (showMore = false)" | ||
> | ||
Show Less | ||
</button> | ||
<button v-else type="button" class="show-more-button" @click="() => (showMore = true)"> | ||
…Show More | ||
</button> | ||
</template> | ||
</td> | ||
</template> | ||
|
||
<style lang="scss" scoped> | ||
.content[data-show-more="false"] { | ||
max-height: 5lh; | ||
overflow: hidden; | ||
} | ||
td { | ||
position: relative; | ||
} | ||
.show-more-button, | ||
.show-less-button { | ||
color: var(--accent-color); | ||
cursor: pointer; | ||
&:hover { | ||
text-decoration: underline; | ||
} | ||
} | ||
.show-more-button { | ||
position: absolute; | ||
right: 1em; | ||
bottom: 0.5em; | ||
background-color: var(--cell-bg-color); | ||
&::before { | ||
content: ""; | ||
position: absolute; | ||
left: -1.5em; | ||
width: 1.5em; | ||
height: 1lh; | ||
background: linear-gradient(90deg, transparent, var(--cell-bg-color) 80%); | ||
} | ||
} | ||
</style> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
<script setup lang="ts"> | ||
import { computed } from "vue"; | ||
import { TableColumn } from "../../types"; | ||
import { TableSorting } from "../../data/tables/sortTableRows"; | ||
import SortIcon from "../icons/SortIcon.vue"; | ||
const props = defineProps<{ | ||
columnId: string; | ||
column: TableColumn; | ||
sorting: TableSorting | null; | ||
}>(); | ||
const emit = defineEmits(["toggleSorting"]); | ||
const sortOrder = computed(() => { | ||
if (props.sorting?.column === props.columnId) return props.sorting.order; | ||
return undefined; | ||
}); | ||
const sortButtonTitle = computed(() => { | ||
if (sortOrder.value === "ascending") return "Sort Descending"; | ||
if (sortOrder.value === "descending") return "Sort Initial"; | ||
return "Sort Ascending"; | ||
}); | ||
</script> | ||
|
||
<template> | ||
<th :key="columnId" :style="{ textAlign: column.textAlign }"> | ||
<div> | ||
<span v-html="column.name" /> | ||
<button | ||
v-if="column.sortable" | ||
class="sort-button" | ||
type="button" | ||
:title="sortButtonTitle" | ||
@click="emit('toggleSorting')" | ||
> | ||
<SortIcon :order="sortOrder" /> | ||
</button> | ||
</div> | ||
</th> | ||
</template> | ||
|
||
<style lang="scss" scoped> | ||
th > div { | ||
display: grid; | ||
grid-template-columns: 1fr max-content; | ||
align-items: center; | ||
} | ||
.sort-button { | ||
cursor: pointer; | ||
border-radius: 6px; | ||
border: var(--border); | ||
background-color: var(--light-bg-color); | ||
transition: background-color 0.1s; | ||
padding: 6px; | ||
margin-left: 1em; | ||
margin-right: -0.5em; | ||
&:hover { | ||
background-color: var(--bg-color); | ||
} | ||
} | ||
</style> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
<script setup lang="ts"> | ||
defineProps<{ | ||
order?: "ascending" | "descending"; | ||
}>(); | ||
</script> | ||
|
||
<template> | ||
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24"> | ||
<g :data-active="order === 'ascending'"> | ||
<path d="m 2.3,7.5 5,-4.5 5,4.5" /> | ||
<path d="M 7.3,3 V 19" /> | ||
</g> | ||
<g :data-active="order === 'descending'"> | ||
<path d="M 16.7,5 V 21" /> | ||
<path d="m 11.7,16.5 5,4.5 5,-4.5" /> | ||
</g> | ||
</svg> | ||
</template> | ||
|
||
<style lang="scss" scoped> | ||
svg { | ||
display: block; | ||
} | ||
path { | ||
fill: none; | ||
stroke: currentColor; | ||
stroke-linecap: round; | ||
stroke-linejoin: round; | ||
stroke-width: 3; | ||
} | ||
[data-active="false"] path { | ||
filter: brightness(70%); | ||
stroke-width: 2.5; | ||
} | ||
</style> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import { SiteConfig } from "vitepress"; | ||
|
||
const config: SiteConfig = globalThis.VITEPRESS_CONFIG; | ||
|
||
export default config; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import { createMarkdownRenderer } from "vitepress"; | ||
import config from "./config"; | ||
|
||
export default await createMarkdownRenderer( | ||
config.srcDir, | ||
config.markdown, | ||
config.site.base, | ||
config.logger | ||
); |
Oops, something went wrong.