Skip to content

Commit

Permalink
Added validation and markdown support for tables
Browse files Browse the repository at this point in the history
  • Loading branch information
QuazChick committed Feb 7, 2025
1 parent 5bcb8db commit c1c9510
Show file tree
Hide file tree
Showing 13 changed files with 231 additions and 39 deletions.
9 changes: 4 additions & 5 deletions docs/.vitepress/theme/components/content/Table.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import { computed } from "vue";
import TableCell from "./TableCell.vue";
import { data as tables } from "../../data/tables.data";
import { data as tables } from "../../tables/tables.data";
import useData from "../../composables/data";
const { page } = useData();
Expand Down Expand Up @@ -33,10 +33,9 @@ const table = computed(() => tables[tablePath.value]);
<th
v-for="(column, columnId) in table.columns"
:key="columnId"
:style="{ textAlign: column.text_align }"
>
{{ column.name }}
</th>
:style="{ textAlign: column.textAlign }"
v-html="column.name"
/>
</tr>
</thead>

Expand Down
73 changes: 52 additions & 21 deletions docs/.vitepress/theme/components/content/TableCell.vue
Original file line number Diff line number Diff line change
@@ -1,44 +1,64 @@
<script setup lang="ts">
import { computed, ref } from "vue";
import { TableCell, TableColumn } from "../../types";
import { computed, onBeforeUnmount, onMounted, ref } from "vue";
import { TableValue, TableColumn } from "../../types";
const props = defineProps<{
column: TableColumn;
value: TableCell;
value: TableValue;
}>();
const shortListLength = 10;
const shortTextLength = 500;
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 props.value.length > shortTextLength;
if (typeof props.value === "string") return isOverflowing.value;
return false;
});
const showMore = ref(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.text_align }">
<ul v-if="Array.isArray(value)">
<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"
>
{{ item }}
</li>
v-html="item"
/>
</ul>

<span v-else-if="typeof value === 'string'">{{
showMore ? value : value.substring(0, shortTextLength)
}}</span>
<div
v-else-if="typeof value === 'string'"
ref="content"
class="content"
:data-show-more="showMore"
v-html="value"
/>

<span v-else-if="typeof value === 'boolean'">{{ value ? "✔️" : "❌" }}</span>
<div v-else-if="typeof value === 'boolean'" class="content">{{ value ? "✔️" : "❌" }}</div>

<span v-else>{{ value }}</span>
<div v-else class="content">{{ value }}</div>

<template v-if="canShowMore">
<button
Expand All @@ -57,10 +77,17 @@ const showMore = ref(false);
</template>

<style lang="scss" scoped>
.content[data-show-more="false"] {
max-height: 5lh;
overflow: hidden;
}
td {
position: relative;
}
.show-more-button,
.show-less-button {
margin-left: 0.5em;
color: var(--accent-color);
cursor: pointer;
Expand All @@ -70,17 +97,21 @@ const showMore = ref(false);
}
.show-more-button {
position: relative;
margin-left: 0.5em;
position: absolute;
right: 1em;
bottom: 0.5em;
background-color: var(--cell-bg-color);
&::before {
content: "";
position: absolute;
left: -2em;
left: -1.5em;
width: 1.5em;
height: 1lh;
background: linear-gradient(90deg, transparent, var(--cell-bg-color));
background: linear-gradient(90deg, transparent, var(--cell-bg-color) 80%);
}
}
</style>
1 change: 1 addition & 0 deletions docs/.vitepress/theme/styles/reset.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
*::after {
box-sizing: border-box;
word-wrap: break-word;
line-height: inherit;
font-family: inherit;
font-size: inherit;
background: none;
Expand Down
10 changes: 10 additions & 0 deletions docs/.vitepress/theme/tables/markdown.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { createMarkdownRenderer } from "vitepress";

const config = globalThis.VITEPRESS_CONFIG;

export default await createMarkdownRenderer(
config.srcDir,
config.markdown,
config.site.base,
config.logger
);
44 changes: 44 additions & 0 deletions docs/.vitepress/theme/tables/parseTable.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import isObject from "../utils/isObject";
import { Table } from "../types";

import parseTableColumn from "./parseTableColumn";
import parseTableRow from "./parseTableRow";

export default function parseTable(data: unknown) {
const table: Table = {
columns: {},
rows: [],
};

if (!isObject(data)) {
throw new TypeError("Tables must be an object.");
}

// Columns
if (!("columns" in data)) {
throw new TypeError('Tables must include a "columns" field.');
} else if (!isObject(data.columns)) {
throw new TypeError('Table "columns" field must be an object.');
} else {
for (const columnId in data.columns) {
const column = data.columns[columnId];

table.columns[columnId] = parseTableColumn(columnId, column);
}
}

// Rows
if (!("rows" in data)) {
throw new TypeError('Tables must include a "rows" field.');
} else if (!Array.isArray(data.rows)) {
throw new TypeError('Table "rows" field must be an array.');
} else {
for (let index = 0; index < data.rows.length; index++) {
const row = data.rows[index];

table.rows.push(parseTableRow(index, row, table.columns));
}
}

return table;
}
43 changes: 43 additions & 0 deletions docs/.vitepress/theme/tables/parseTableColumn.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import isObject from "../utils/isObject";
import { TableColumn } from "../types";

import markdown from "./markdown";
import parseTableValue from "./parseTableValue";

export default function parseTableColumn(id: string, data: unknown) {
const column: Partial<TableColumn> = {};

if (!isObject(data)) {
throw new TypeError(`Table column "${id}" must be an object.`);
}

// Column Name
if (!("name" in data)) {
throw new TypeError(`Table column "${id}" must include a "name" field.`);
} else if (typeof data.name !== "string") {
throw new TypeError(`The "name" field of column "${id}" must be a string.`);
}

column.name = markdown.renderInline(data.name);

// Default Value
if ("default" in data) {
column.default = parseTableValue(data.default);
}

// Text Align
if ("text_align" in data) {
if (
typeof data.text_align !== "string" ||
(data.text_align !== "left" && data.text_align !== "center" && data.text_align !== "right")
) {
throw new TypeError(
`The "text_align" field of column "${id}" must be "left", "center" or "right".`
);
}

column.textAlign = data.text_align;
}

return column as TableColumn;
}
24 changes: 24 additions & 0 deletions docs/.vitepress/theme/tables/parseTableRow.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import isObject from "../utils/isObject";
import { Table, TableRow } from "../types";

import parseTableValue from "./parseTableValue";

export default function parseTableRow(index: number, data: unknown, columns: Table["columns"]) {
const row: TableRow = {};

if (!isObject(data)) {
throw new TypeError(`Table row ${index} must be an object.`);
}

for (const columnId in data) {
if (!(columnId in columns)) {
throw new TypeError(
`Table row ${index} contains a value for the non-existent column "${columnId}".`
);
}

row[columnId] = parseTableValue(data[columnId]);
}

return row;
}
33 changes: 33 additions & 0 deletions docs/.vitepress/theme/tables/parseTableValue.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { TableValue } from "../types";

import markdown from "./markdown";

export default function parseTableValue(value: unknown) {
if (typeof value === "boolean") {
return value;
}

if (typeof value === "string") {
return markdown.renderInline(value);
}

if (typeof value === "number") {
return value;
}

if (Array.isArray(value)) {
const list: TableValue = [];

for (const item of value) {
if (typeof item !== "string") {
throw new TypeError(`Table list items must be strings.`);
}

list.push(markdown.renderInline(item));
}

return list;
}

throw new TypeError("Table value must be a boolean, number, string or array of strings.");
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { defineLoader } from "vitepress";
import { Table } from "../types";
import { readFileSync } from "fs";
import { Table } from "../types";

import parseTable from "./parseTable";

export interface Tables {
[path: string]: Table;
Expand All @@ -15,7 +17,9 @@ export default defineLoader({
const data = {};

for (const path of watchedFiles) {
data[path] = JSON.parse(readFileSync(path, "utf-8"));
const table = JSON.parse(readFileSync(path, "utf-8"));

data[path] = parseTable(table);
}

return data;
Expand Down
8 changes: 4 additions & 4 deletions docs/.vitepress/theme/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,12 @@ export interface Table {

export interface TableColumn {
name: string;
text_align: "left" | "center" | "right";
default: TableCell;
default?: TableValue;
textAlign?: "left" | "center" | "right";
}

export interface TableRow {
[column: string]: TableCell;
[column: string]: TableValue;
}

export type TableCell = (boolean | number | string) | (string | number)[];
export type TableValue = boolean | number | string | string[];
3 changes: 3 additions & 0 deletions docs/.vitepress/theme/utils/isObject.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function isObject(value: unknown): value is object {
return typeof value === "object" && value !== null && !Array.isArray(value);
}
4 changes: 2 additions & 2 deletions docs/public/assets/tables/test/my-table.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"rows": [
{
"number_column": 1,
"my_other_column": "Aute incididunt proident incididunt ullamco eiusmod ea dolor ea Lorem commodo. Veniam nulla cillum officia incididunt deserunt. Dolor consectetur consectetur veniam consectetur Lorem incididunt esse non. Mollit et do et ad. Commodo nostrud culpa Lorem reprehenderit sunt quis cupidatat id id tempor ullamco. Aliqua ipsum amet ea nisi culpa aliqua dolor aute duis nisi laborum incididunt incididunt Lorem. Anim irure fugiat et qui magna magna ullamco ullamco sint exercitation in commodo aliquip. Ad irure laborum deserunt irure qui proident elit. Incididunt minim magna occaecat proident ut cillum sunt culpa aliquip voluptate reprehenderit dolore. Est veniam sunt labore nulla. Quis pariatur id adipisicing sit magna elit commodo duis cillum voluptate esse nulla aliquip occaecat. Ea deserunt consequat tempor reprehenderit eu officia qui mollit. Officia exercitation nostrud do ipsum fugiat fugiat commodo commodo ipsum exercitation ex Lorem incididunt culpa.",
"my_other_column": "Aute **incididunt proident** incididunt ~~ullamco~~ eiusmod ea dolor ea Lorem commodo. *Veniam nulla cillum officia incididunt deserunt. Dolor consectetur consectetur veniam consectetur Lorem incididunt esse non.* Mollit et do et ad. Commodo nostrud culpa Lorem reprehenderit sunt quis cupidatat id id tempor ullamco. Aliqua ipsum amet ea nisi culpa aliqua dolor aute duis nisi laborum incididunt incididunt Lorem. Anim irure fugiat et qui magna magna ullamco ullamco sint exercitation in commodo aliquip. Ad irure laborum deserunt irure qui proident elit. Incididunt minim magna occaecat proident ut cillum sunt culpa aliquip voluptate reprehenderit dolore. Est veniam sunt labore nulla. Quis pariatur id adipisicing sit magna elit commodo duis cillum voluptate esse nulla aliquip occaecat. Ea deserunt consequat tempor reprehenderit eu officia qui mollit. Officia exercitation nostrud do ipsum fugiat fugiat commodo commodo ipsum exercitation ex Lorem incididunt culpa.",
"boolean_column": true
},
{
Expand All @@ -27,7 +27,7 @@
"number_column": 3,
"boolean_column": false,
"my_other_column": [
"Proident amet id ut exercitation.",
"Proident amet *id* ut exercitation.",
"Do esse exercitation eiusmod Lorem incididunt exercitation esse fugiat Lorem et.",
"Magna eu ex excepteur qui est consectetur nostrud dolor ea est."
]
Expand Down
Loading

0 comments on commit c1c9510

Please sign in to comment.