@joewinke/jatui 0.37.0 → 0.38.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/artifact-blocks/AttachmentChip.svelte +66 -0
- package/dist/artifact-blocks/AttachmentChip.svelte.d.ts +7 -0
- package/dist/artifact-blocks/BlockComments.svelte +309 -0
- package/dist/artifact-blocks/BlockComments.svelte.d.ts +33 -0
- package/dist/artifact-blocks/BlockRenderer.svelte +674 -0
- package/dist/artifact-blocks/BlockRenderer.svelte.d.ts +55 -0
- package/dist/artifact-blocks/BlockVerdict.svelte +151 -0
- package/dist/artifact-blocks/BlockVerdict.svelte.d.ts +13 -0
- package/dist/artifact-blocks/BlocksArtifactView.svelte +1616 -0
- package/dist/artifact-blocks/BlocksArtifactView.svelte.d.ts +113 -0
- package/dist/artifact-blocks/CONTRACT.md +225 -0
- package/dist/artifact-blocks/CommentPill.svelte +49 -0
- package/dist/artifact-blocks/CommentPill.svelte.d.ts +16 -0
- package/dist/artifact-blocks/FoldedRounds.svelte +95 -0
- package/dist/artifact-blocks/FoldedRounds.svelte.d.ts +9 -0
- package/dist/artifact-blocks/Glyph.svelte +54 -0
- package/dist/artifact-blocks/Glyph.svelte.d.ts +10 -0
- package/dist/artifact-blocks/HistoryScrubber.svelte +500 -0
- package/dist/artifact-blocks/HistoryScrubber.svelte.d.ts +15 -0
- package/dist/artifact-blocks/Markdown.svelte +186 -0
- package/dist/artifact-blocks/Markdown.svelte.d.ts +30 -0
- package/dist/artifact-blocks/NoteComposer.svelte +31 -0
- package/dist/artifact-blocks/NoteComposer.svelte.d.ts +8 -0
- package/dist/artifact-blocks/PlainComposer.svelte +200 -0
- package/dist/artifact-blocks/PlainComposer.svelte.d.ts +15 -0
- package/dist/artifact-blocks/SectionTools.svelte +102 -0
- package/dist/artifact-blocks/SectionTools.svelte.d.ts +19 -0
- package/dist/artifact-blocks/adapter.d.ts +13 -0
- package/dist/artifact-blocks/adapter.js +31 -0
- package/dist/artifact-blocks/autoSend.d.ts +49 -0
- package/dist/artifact-blocks/autoSend.js +103 -0
- package/dist/artifact-blocks/blocks/actions/ActionsBlock.svelte +557 -0
- package/dist/artifact-blocks/blocks/actions/ActionsBlock.svelte.d.ts +48 -0
- package/dist/artifact-blocks/blocks/actions/block.d.ts +8 -0
- package/dist/artifact-blocks/blocks/actions/block.js +74 -0
- package/dist/artifact-blocks/blocks/actions/requests.d.ts +62 -0
- package/dist/artifact-blocks/blocks/actions/requests.js +26 -0
- package/dist/artifact-blocks/blocks/actions/schema.d.ts +168 -0
- package/dist/artifact-blocks/blocks/actions/schema.js +253 -0
- package/dist/artifact-blocks/blocks/callout/CalloutBlock.svelte +779 -0
- package/dist/artifact-blocks/blocks/callout/CalloutBlock.svelte.d.ts +40 -0
- package/dist/artifact-blocks/blocks/callout/block.d.ts +7 -0
- package/dist/artifact-blocks/blocks/callout/block.js +39 -0
- package/dist/artifact-blocks/blocks/callout/schema.d.ts +72 -0
- package/dist/artifact-blocks/blocks/callout/schema.js +115 -0
- package/dist/artifact-blocks/blocks/chart/ChartBlock.svelte +875 -0
- package/dist/artifact-blocks/blocks/chart/ChartBlock.svelte.d.ts +59 -0
- package/dist/artifact-blocks/blocks/chart/block.d.ts +9 -0
- package/dist/artifact-blocks/blocks/chart/block.js +70 -0
- package/dist/artifact-blocks/blocks/chart/schema.d.ts +171 -0
- package/dist/artifact-blocks/blocks/chart/schema.js +498 -0
- package/dist/artifact-blocks/blocks/chart/thumb.d.ts +2 -0
- package/dist/artifact-blocks/blocks/chart/thumb.js +201 -0
- package/dist/artifact-blocks/blocks/checklist/ChecklistBlock.svelte +1048 -0
- package/dist/artifact-blocks/blocks/checklist/ChecklistBlock.svelte.d.ts +16 -0
- package/dist/artifact-blocks/blocks/checklist/Icon.svelte +105 -0
- package/dist/artifact-blocks/blocks/checklist/Icon.svelte.d.ts +17 -0
- package/dist/artifact-blocks/blocks/checklist/block.d.ts +10 -0
- package/dist/artifact-blocks/blocks/checklist/block.js +63 -0
- package/dist/artifact-blocks/blocks/checklist/itemReview.d.ts +16 -0
- package/dist/artifact-blocks/blocks/checklist/itemReview.js +63 -0
- package/dist/artifact-blocks/blocks/checklist/labels.d.ts +62 -0
- package/dist/artifact-blocks/blocks/checklist/labels.js +96 -0
- package/dist/artifact-blocks/blocks/checklist/retest.d.ts +43 -0
- package/dist/artifact-blocks/blocks/checklist/retest.js +68 -0
- package/dist/artifact-blocks/blocks/checklist/schema.d.ts +84 -0
- package/dist/artifact-blocks/blocks/checklist/schema.js +91 -0
- package/dist/artifact-blocks/blocks/code/CodeBlock.svelte +128 -0
- package/dist/artifact-blocks/blocks/code/CodeBlock.svelte.d.ts +12 -0
- package/dist/artifact-blocks/blocks/code/block.d.ts +8 -0
- package/dist/artifact-blocks/blocks/code/block.js +31 -0
- package/dist/artifact-blocks/blocks/code/highlight.d.ts +34 -0
- package/dist/artifact-blocks/blocks/code/highlight.js +280 -0
- package/dist/artifact-blocks/blocks/code/schema.d.ts +32 -0
- package/dist/artifact-blocks/blocks/code/schema.js +39 -0
- package/dist/artifact-blocks/blocks/collage/CollageBlock.svelte +290 -0
- package/dist/artifact-blocks/blocks/collage/CollageBlock.svelte.d.ts +29 -0
- package/dist/artifact-blocks/blocks/collage/block.d.ts +9 -0
- package/dist/artifact-blocks/blocks/collage/block.js +29 -0
- package/dist/artifact-blocks/blocks/collage/schema.d.ts +52 -0
- package/dist/artifact-blocks/blocks/collage/schema.js +86 -0
- package/dist/artifact-blocks/blocks/compare/CompareBlock.svelte +484 -0
- package/dist/artifact-blocks/blocks/compare/CompareBlock.svelte.d.ts +25 -0
- package/dist/artifact-blocks/blocks/compare/block.d.ts +9 -0
- package/dist/artifact-blocks/blocks/compare/block.js +32 -0
- package/dist/artifact-blocks/blocks/compare/schema.d.ts +74 -0
- package/dist/artifact-blocks/blocks/compare/schema.js +148 -0
- package/dist/artifact-blocks/blocks/diagram/DiagramBlock.svelte +392 -0
- package/dist/artifact-blocks/blocks/diagram/DiagramBlock.svelte.d.ts +30 -0
- package/dist/artifact-blocks/blocks/diagram/block.d.ts +9 -0
- package/dist/artifact-blocks/blocks/diagram/block.js +50 -0
- package/dist/artifact-blocks/blocks/diagram/constants.d.ts +48 -0
- package/dist/artifact-blocks/blocks/diagram/constants.js +53 -0
- package/dist/artifact-blocks/blocks/diagram/schema.d.ts +161 -0
- package/dist/artifact-blocks/blocks/diagram/schema.js +621 -0
- package/dist/artifact-blocks/blocks/diagram/thumb.d.ts +2 -0
- package/dist/artifact-blocks/blocks/diagram/thumb.js +157 -0
- package/dist/artifact-blocks/blocks/diagram/types.d.ts +74 -0
- package/dist/artifact-blocks/blocks/diagram/types.js +1 -0
- package/dist/artifact-blocks/blocks/diff/DiffBlock.svelte +81 -0
- package/dist/artifact-blocks/blocks/diff/DiffBlock.svelte.d.ts +10 -0
- package/dist/artifact-blocks/blocks/diff/block.d.ts +7 -0
- package/dist/artifact-blocks/blocks/diff/block.js +36 -0
- package/dist/artifact-blocks/blocks/diff/schema.d.ts +18 -0
- package/dist/artifact-blocks/blocks/diff/schema.js +35 -0
- package/dist/artifact-blocks/blocks/diff-review/DiffReviewBlock.svelte +1055 -0
- package/dist/artifact-blocks/blocks/diff-review/DiffReviewBlock.svelte.d.ts +5 -0
- package/dist/artifact-blocks/blocks/diff-review/block.d.ts +15 -0
- package/dist/artifact-blocks/blocks/diff-review/block.js +64 -0
- package/dist/artifact-blocks/blocks/diff-review/decisions.d.ts +57 -0
- package/dist/artifact-blocks/blocks/diff-review/decisions.js +80 -0
- package/dist/artifact-blocks/blocks/diff-review/schema.d.ts +13 -0
- package/dist/artifact-blocks/blocks/diff-review/schema.js +84 -0
- package/dist/artifact-blocks/blocks/diff-review/types.d.ts +35 -0
- package/dist/artifact-blocks/blocks/diff-review/types.js +1 -0
- package/dist/artifact-blocks/blocks/explainer/ExplainerBlock.svelte +145 -0
- package/dist/artifact-blocks/blocks/explainer/ExplainerBlock.svelte.d.ts +5 -0
- package/dist/artifact-blocks/blocks/explainer/block.d.ts +8 -0
- package/dist/artifact-blocks/blocks/explainer/block.js +21 -0
- package/dist/artifact-blocks/blocks/explainer/schema.d.ts +26 -0
- package/dist/artifact-blocks/blocks/explainer/schema.js +69 -0
- package/dist/artifact-blocks/blocks/group-content.d.ts +10 -0
- package/dist/artifact-blocks/blocks/group-content.js +16 -0
- package/dist/artifact-blocks/blocks/group-data.d.ts +10 -0
- package/dist/artifact-blocks/blocks/group-data.js +7 -0
- package/dist/artifact-blocks/blocks/group-review.d.ts +10 -0
- package/dist/artifact-blocks/blocks/group-review.js +12 -0
- package/dist/artifact-blocks/blocks/group-visual.d.ts +17 -0
- package/dist/artifact-blocks/blocks/group-visual.js +14 -0
- package/dist/artifact-blocks/blocks/map/MapBlock.svelte +413 -0
- package/dist/artifact-blocks/blocks/map/MapBlock.svelte.d.ts +53 -0
- package/dist/artifact-blocks/blocks/map/block.d.ts +9 -0
- package/dist/artifact-blocks/blocks/map/block.js +62 -0
- package/dist/artifact-blocks/blocks/map/schema.d.ts +123 -0
- package/dist/artifact-blocks/blocks/map/schema.js +113 -0
- package/dist/artifact-blocks/blocks/map/thumb.d.ts +2 -0
- package/dist/artifact-blocks/blocks/map/thumb.js +69 -0
- package/dist/artifact-blocks/blocks/markdown/MarkdownBlock.svelte +33 -0
- package/dist/artifact-blocks/blocks/markdown/MarkdownBlock.svelte.d.ts +15 -0
- package/dist/artifact-blocks/blocks/markdown/block.d.ts +7 -0
- package/dist/artifact-blocks/blocks/markdown/block.js +28 -0
- package/dist/artifact-blocks/blocks/markdown/render.d.ts +17 -0
- package/dist/artifact-blocks/blocks/markdown/render.js +115 -0
- package/dist/artifact-blocks/blocks/markdown/schema.d.ts +10 -0
- package/dist/artifact-blocks/blocks/markdown/schema.js +23 -0
- package/dist/artifact-blocks/blocks/media-picker/MediaPickerBlock.svelte +2112 -0
- package/dist/artifact-blocks/blocks/media-picker/MediaPickerBlock.svelte.d.ts +5 -0
- package/dist/artifact-blocks/blocks/media-picker/block.d.ts +24 -0
- package/dist/artifact-blocks/blocks/media-picker/block.js +137 -0
- package/dist/artifact-blocks/blocks/media-picker/choices.d.ts +28 -0
- package/dist/artifact-blocks/blocks/media-picker/choices.js +35 -0
- package/dist/artifact-blocks/blocks/media-picker/decisions.d.ts +51 -0
- package/dist/artifact-blocks/blocks/media-picker/decisions.js +75 -0
- package/dist/artifact-blocks/blocks/media-picker/schema.d.ts +7 -0
- package/dist/artifact-blocks/blocks/media-picker/schema.js +199 -0
- package/dist/artifact-blocks/blocks/media-picker/types.d.ts +92 -0
- package/dist/artifact-blocks/blocks/media-picker/types.js +1 -0
- package/dist/artifact-blocks/blocks/media-picker/versions.d.ts +51 -0
- package/dist/artifact-blocks/blocks/media-picker/versions.js +118 -0
- package/dist/artifact-blocks/blocks/mockup/MockupBlock.svelte +213 -0
- package/dist/artifact-blocks/blocks/mockup/MockupBlock.svelte.d.ts +5 -0
- package/dist/artifact-blocks/blocks/mockup/block.d.ts +26 -0
- package/dist/artifact-blocks/blocks/mockup/block.js +52 -0
- package/dist/artifact-blocks/blocks/mockup/doc.d.ts +24 -0
- package/dist/artifact-blocks/blocks/mockup/doc.js +44 -0
- package/dist/artifact-blocks/blocks/mockup/sandbox.d.ts +21 -0
- package/dist/artifact-blocks/blocks/mockup/sandbox.js +27 -0
- package/dist/artifact-blocks/blocks/mockup/schema.d.ts +7 -0
- package/dist/artifact-blocks/blocks/mockup/schema.js +90 -0
- package/dist/artifact-blocks/blocks/mockup/types.d.ts +36 -0
- package/dist/artifact-blocks/blocks/mockup/types.js +17 -0
- package/dist/artifact-blocks/blocks/program/ProgramBlock.svelte +359 -0
- package/dist/artifact-blocks/blocks/program/ProgramBlock.svelte.d.ts +32 -0
- package/dist/artifact-blocks/blocks/program/block.d.ts +35 -0
- package/dist/artifact-blocks/blocks/program/block.js +68 -0
- package/dist/artifact-blocks/blocks/program/schema.d.ts +132 -0
- package/dist/artifact-blocks/blocks/program/schema.js +268 -0
- package/dist/artifact-blocks/blocks/stats/StatsBlock.svelte +315 -0
- package/dist/artifact-blocks/blocks/stats/StatsBlock.svelte.d.ts +24 -0
- package/dist/artifact-blocks/blocks/stats/block.d.ts +7 -0
- package/dist/artifact-blocks/blocks/stats/block.js +49 -0
- package/dist/artifact-blocks/blocks/stats/schema.d.ts +82 -0
- package/dist/artifact-blocks/blocks/stats/schema.js +43 -0
- package/dist/artifact-blocks/blocks/stats/thumb.d.ts +2 -0
- package/dist/artifact-blocks/blocks/stats/thumb.js +102 -0
- package/dist/artifact-blocks/blocks/table/TableBlock.svelte +490 -0
- package/dist/artifact-blocks/blocks/table/TableBlock.svelte.d.ts +28 -0
- package/dist/artifact-blocks/blocks/table/block.d.ts +9 -0
- package/dist/artifact-blocks/blocks/table/block.js +100 -0
- package/dist/artifact-blocks/blocks/table/schema.d.ts +143 -0
- package/dist/artifact-blocks/blocks/table/schema.js +278 -0
- package/dist/artifact-blocks/blocks/timeline/TimelineBlock.svelte +347 -0
- package/dist/artifact-blocks/blocks/timeline/TimelineBlock.svelte.d.ts +42 -0
- package/dist/artifact-blocks/blocks/timeline/block.d.ts +9 -0
- package/dist/artifact-blocks/blocks/timeline/block.js +96 -0
- package/dist/artifact-blocks/blocks/timeline/constants.d.ts +26 -0
- package/dist/artifact-blocks/blocks/timeline/constants.js +27 -0
- package/dist/artifact-blocks/blocks/timeline/schema.d.ts +102 -0
- package/dist/artifact-blocks/blocks/timeline/schema.js +405 -0
- package/dist/artifact-blocks/blocks/timeline/thumb.d.ts +2 -0
- package/dist/artifact-blocks/blocks/timeline/thumb.js +117 -0
- package/dist/artifact-blocks/blocks/timeline/types.d.ts +66 -0
- package/dist/artifact-blocks/blocks/timeline/types.js +1 -0
- package/dist/artifact-blocks/blocks/transcript/TranscriptBlock.svelte +169 -0
- package/dist/artifact-blocks/blocks/transcript/TranscriptBlock.svelte.d.ts +26 -0
- package/dist/artifact-blocks/blocks/transcript/block.d.ts +9 -0
- package/dist/artifact-blocks/blocks/transcript/block.js +86 -0
- package/dist/artifact-blocks/blocks/transcript/schema.d.ts +114 -0
- package/dist/artifact-blocks/blocks/transcript/schema.js +126 -0
- package/dist/artifact-blocks/builtins.d.ts +40 -0
- package/dist/artifact-blocks/builtins.js +22 -0
- package/dist/artifact-blocks/contractBlocks.d.ts +92 -0
- package/dist/artifact-blocks/contractBlocks.js +279 -0
- package/dist/artifact-blocks/history.d.ts +140 -0
- package/dist/artifact-blocks/history.js +363 -0
- package/dist/artifact-blocks/index.d.ts +40 -0
- package/dist/artifact-blocks/index.js +46 -0
- package/dist/artifact-blocks/listen/context.d.ts +30 -0
- package/dist/artifact-blocks/listen/context.js +30 -0
- package/dist/artifact-blocks/liveData.d.ts +78 -0
- package/dist/artifact-blocks/liveData.js +137 -0
- package/dist/artifact-blocks/liveDataPoller.d.ts +41 -0
- package/dist/artifact-blocks/liveDataPoller.js +140 -0
- package/dist/artifact-blocks/openNav.d.ts +46 -0
- package/dist/artifact-blocks/openNav.js +106 -0
- package/dist/artifact-blocks/pageReviewKeys.d.ts +17 -0
- package/dist/artifact-blocks/pageReviewKeys.js +115 -0
- package/dist/artifact-blocks/params.d.ts +98 -0
- package/dist/artifact-blocks/params.js +159 -0
- package/dist/artifact-blocks/registry.d.ts +27 -0
- package/dist/artifact-blocks/registry.js +83 -0
- package/dist/artifact-blocks/review/BlockIcon.svelte +99 -0
- package/dist/artifact-blocks/review/BlockIcon.svelte.d.ts +17 -0
- package/dist/artifact-blocks/review/BulkRejectForm.svelte +94 -0
- package/dist/artifact-blocks/review/BulkRejectForm.svelte.d.ts +11 -0
- package/dist/artifact-blocks/review/BulkUndoBar.svelte +40 -0
- package/dist/artifact-blocks/review/BulkUndoBar.svelte.d.ts +9 -0
- package/dist/artifact-blocks/review/ItemNote.svelte +193 -0
- package/dist/artifact-blocks/review/ItemNote.svelte.d.ts +33 -0
- package/dist/artifact-blocks/review/NoteToggle.svelte +52 -0
- package/dist/artifact-blocks/review/NoteToggle.svelte.d.ts +12 -0
- package/dist/artifact-blocks/review/TeamChip.svelte +71 -0
- package/dist/artifact-blocks/review/TeamChip.svelte.d.ts +8 -0
- package/dist/artifact-blocks/review/attachments.d.ts +10 -0
- package/dist/artifact-blocks/review/attachments.js +37 -0
- package/dist/artifact-blocks/review/bulk.d.ts +52 -0
- package/dist/artifact-blocks/review/bulk.js +61 -0
- package/dist/artifact-blocks/review/itemReview.d.ts +33 -0
- package/dist/artifact-blocks/review/itemReview.js +112 -0
- package/dist/artifact-blocks/review/roundLines.d.ts +14 -0
- package/dist/artifact-blocks/review/roundLines.js +20 -0
- package/dist/artifact-blocks/review/rounds.d.ts +86 -0
- package/dist/artifact-blocks/review/rounds.js +302 -0
- package/dist/artifact-blocks/review/saver.d.ts +23 -0
- package/dist/artifact-blocks/review/saver.js +48 -0
- package/dist/artifact-blocks/review/team.d.ts +31 -0
- package/dist/artifact-blocks/review/team.js +42 -0
- package/dist/artifact-blocks/review/visualOrder.d.ts +29 -0
- package/dist/artifact-blocks/review/visualOrder.js +66 -0
- package/dist/artifact-blocks/review.d.ts +180 -0
- package/dist/artifact-blocks/review.js +716 -0
- package/dist/artifact-blocks/thumb-kit.d.ts +80 -0
- package/dist/artifact-blocks/thumb-kit.js +149 -0
- package/dist/artifact-blocks/types.d.ts +513 -0
- package/dist/artifact-blocks/types.js +1 -0
- package/package.json +11 -2
|
@@ -0,0 +1,490 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* TableBlock — jatui DataTable renderer (block type "table").
|
|
4
|
+
*
|
|
5
|
+
* Typed columns (text/number/percent/currency/date/badge/link), sort,
|
|
6
|
+
* filter box, pagination (50/page), sticky header, CSV download and
|
|
7
|
+
* conditional good/warn/bad tone per cell.
|
|
8
|
+
*
|
|
9
|
+
* Numeric columns (number/percent/currency) are right-aligned with tabular
|
|
10
|
+
* numerals, header included. Their tone is a small coloured dot beside a
|
|
11
|
+
* PLAIN number (campaign-blocks Gate A, J3): a column of solid red/amber/
|
|
12
|
+
* green chips was the loudest thing on the page. The tone is also spelled
|
|
13
|
+
* out for screen readers and in the dot's title. Badge-type columns keep
|
|
14
|
+
* SOLID chips for toned values (DESIGN.md: solid is the only chip variant
|
|
15
|
+
* that clears AA on every tone) and the quiet ghost chip otherwise.
|
|
16
|
+
*
|
|
17
|
+
* Column widths (mos-8cbma): `resolveColumnLayout` sorts each column into
|
|
18
|
+
* shrink (badge/number/date/link and short text: content width, never
|
|
19
|
+
* wraps) or flex (long text: wraps, shares the remaining width by weight,
|
|
20
|
+
* floored at a min width). The percentages ride CSS vars onto the header
|
|
21
|
+
* cells (`th:nth-child`) because DataTable takes only a class per header.
|
|
22
|
+
* More than 4 columns breaks out of the reading column (`wide`); when the
|
|
23
|
+
* table still overflows, the scroller shows an edge fade + a hint.
|
|
24
|
+
*
|
|
25
|
+
* Params are parsed through the schema here (defaults + corrupt-params →
|
|
26
|
+
* empty state), same contract as ChartBlock.
|
|
27
|
+
*/
|
|
28
|
+
import { DataTable, type ColumnDef, type SortingState } from "../../../components/table/index.js"
|
|
29
|
+
import type { CellContext } from "@tanstack/table-core"
|
|
30
|
+
import type { Snippet } from "svelte"
|
|
31
|
+
import type { BlockProps } from "../../types.js"
|
|
32
|
+
import {
|
|
33
|
+
tableSchema,
|
|
34
|
+
toneForCell,
|
|
35
|
+
TONE_CLASS,
|
|
36
|
+
formatCell,
|
|
37
|
+
sortValue,
|
|
38
|
+
filterRows,
|
|
39
|
+
toCsv,
|
|
40
|
+
downloadCsv,
|
|
41
|
+
isSafeUrl,
|
|
42
|
+
resolveColumnLayout,
|
|
43
|
+
isWide,
|
|
44
|
+
NUMERIC_TYPES,
|
|
45
|
+
type ColumnLayout,
|
|
46
|
+
type ColumnSpec,
|
|
47
|
+
type TableParams,
|
|
48
|
+
type CellValue,
|
|
49
|
+
} from "./schema.js"
|
|
50
|
+
import SectionTools from "../../SectionTools.svelte"
|
|
51
|
+
|
|
52
|
+
let { params: rawParams, ctx }: BlockProps<TableParams> = $props()
|
|
53
|
+
|
|
54
|
+
/** Parsed params (defaults filled); corrupt params → empty state. */
|
|
55
|
+
const EMPTY_PARAMS: TableParams = {
|
|
56
|
+
columns: [],
|
|
57
|
+
rows: [],
|
|
58
|
+
pageSize: 50,
|
|
59
|
+
filterable: true,
|
|
60
|
+
maxHeightPx: 420,
|
|
61
|
+
currency: "USD",
|
|
62
|
+
csv: true,
|
|
63
|
+
}
|
|
64
|
+
const parsed = $derived(tableSchema.safeParse(rawParams))
|
|
65
|
+
const params = $derived(parsed.success ? parsed.data : EMPTY_PARAMS)
|
|
66
|
+
|
|
67
|
+
type Row = Record<string, CellValue>
|
|
68
|
+
|
|
69
|
+
const layout = $derived(resolveColumnLayout(params.columns, params.rows))
|
|
70
|
+
const layoutByKey = $derived(
|
|
71
|
+
new Map(params.columns.map((c, i) => [c.key, layout[i]])),
|
|
72
|
+
)
|
|
73
|
+
const wide = $derived(isWide(params))
|
|
74
|
+
/** Per-column header width as CSS vars (consumed by th:nth-child rules). */
|
|
75
|
+
const widthVars = $derived(
|
|
76
|
+
layout
|
|
77
|
+
.map(
|
|
78
|
+
(l: ColumnLayout, i) =>
|
|
79
|
+
`--tbw-${i + 1}: ${l.mode === "flex" ? `${l.pct}%` : "1px"}`,
|
|
80
|
+
)
|
|
81
|
+
.join("; "),
|
|
82
|
+
)
|
|
83
|
+
function flexMin(key: string): string | undefined {
|
|
84
|
+
const l = layoutByKey.get(key)
|
|
85
|
+
return l?.mode === "flex" ? `${l.minRem}rem` : undefined
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Horizontal overflow is the last resort; when it happens, say so.
|
|
89
|
+
let figureEl = $state<HTMLElement>()
|
|
90
|
+
let overflowX = $state(false)
|
|
91
|
+
let atEnd = $state(false)
|
|
92
|
+
$effect(() => {
|
|
93
|
+
void params.columns.length
|
|
94
|
+
const sc = figureEl?.querySelector<HTMLElement>(
|
|
95
|
+
".dt-scroller, .overflow-x-auto",
|
|
96
|
+
)
|
|
97
|
+
if (!sc) return
|
|
98
|
+
const measure = () => {
|
|
99
|
+
overflowX = sc.scrollWidth > sc.clientWidth + 1
|
|
100
|
+
atEnd = sc.scrollLeft + sc.clientWidth >= sc.scrollWidth - 1
|
|
101
|
+
}
|
|
102
|
+
measure()
|
|
103
|
+
sc.addEventListener("scroll", measure, { passive: true })
|
|
104
|
+
const ro =
|
|
105
|
+
typeof ResizeObserver === "function" ? new ResizeObserver(measure) : null
|
|
106
|
+
ro?.observe(sc)
|
|
107
|
+
const tbl = sc.querySelector("table")
|
|
108
|
+
if (tbl) ro?.observe(tbl)
|
|
109
|
+
return () => {
|
|
110
|
+
sc.removeEventListener("scroll", measure)
|
|
111
|
+
ro?.disconnect()
|
|
112
|
+
}
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
let q = $state("")
|
|
116
|
+
let sorting = $state<SortingState>([])
|
|
117
|
+
|
|
118
|
+
const filtered = $derived.by(() => {
|
|
119
|
+
if (!q.trim()) return params.rows
|
|
120
|
+
return filterRows(params.rows, params.columns, q, params.currency)
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
// CSV exports what the viewer sees: filtered, in the current sort, display
|
|
124
|
+
// formatting (the table is the source of truth for the reader).
|
|
125
|
+
const csvRows = $derived.by(() => {
|
|
126
|
+
const sorted = applySort(filtered)
|
|
127
|
+
return sorted.map((r) =>
|
|
128
|
+
params.columns.map((c) =>
|
|
129
|
+
formatCell(r[c.key] ?? null, c, params.currency),
|
|
130
|
+
),
|
|
131
|
+
)
|
|
132
|
+
})
|
|
133
|
+
function applySort(rows: Row[]): Row[] {
|
|
134
|
+
if (!sorting.length) return rows
|
|
135
|
+
const [{ id, desc }] = sorting
|
|
136
|
+
const col = params.columns.find((c) => c.key === id)
|
|
137
|
+
if (!col) return rows
|
|
138
|
+
const sorted = [...rows].sort((a, b) => {
|
|
139
|
+
const av = sortValue(a[col.key] ?? null, col.type)
|
|
140
|
+
const bv = sortValue(b[col.key] ?? null, col.type)
|
|
141
|
+
if (av < bv) return -1
|
|
142
|
+
if (av > bv) return 1
|
|
143
|
+
return 0
|
|
144
|
+
})
|
|
145
|
+
return desc ? sorted.reverse() : sorted
|
|
146
|
+
}
|
|
147
|
+
function exportCsv() {
|
|
148
|
+
const name = (params.title ?? "data")
|
|
149
|
+
.toLowerCase()
|
|
150
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
151
|
+
.replace(/^-+|-+$/g, "")
|
|
152
|
+
.slice(0, 60)
|
|
153
|
+
downloadCsv(
|
|
154
|
+
`${name || "data"}.csv`,
|
|
155
|
+
toCsv(
|
|
156
|
+
params.columns.map((c) => c.label),
|
|
157
|
+
csvRows,
|
|
158
|
+
),
|
|
159
|
+
)
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/** Dot colour + spoken word per tone (numeric cells). */
|
|
163
|
+
const TONE_DOT = {
|
|
164
|
+
good: "bg-success",
|
|
165
|
+
warn: "bg-warning",
|
|
166
|
+
bad: "bg-error",
|
|
167
|
+
} as const
|
|
168
|
+
const TONE_WORD = { good: "good", warn: "watch", bad: "bad" } as const
|
|
169
|
+
|
|
170
|
+
function isNumeric(id: string): boolean {
|
|
171
|
+
const c = params.columns.find((col) => col.key === id)
|
|
172
|
+
return !!c && NUMERIC_TYPES.includes(c.type)
|
|
173
|
+
}
|
|
174
|
+
/** Shrink-column headers never wrap (the label sets the floor width);
|
|
175
|
+
* flex-column headers may wrap rather than widen a text column; numeric
|
|
176
|
+
* headers align with their right-aligned values. */
|
|
177
|
+
function headerProps(id: string) {
|
|
178
|
+
const flex = layoutByKey.get(id)?.mode === "flex"
|
|
179
|
+
return {
|
|
180
|
+
class: `${flex ? "" : "whitespace-nowrap"}${isNumeric(id) ? " text-right" : ""}`,
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// ── Column defs for the DataTable ────────────────────────────────────────
|
|
185
|
+
function metaOf(c: ColumnSpec): { col: ColumnSpec } {
|
|
186
|
+
return { col: c }
|
|
187
|
+
}
|
|
188
|
+
type CellCtx = CellContext<Row, unknown>
|
|
189
|
+
function colOf(context: CellCtx): ColumnSpec {
|
|
190
|
+
return (context.column.columnDef.meta as { col: ColumnSpec }).col
|
|
191
|
+
}
|
|
192
|
+
function cellFor(type: ColumnSpec["type"]): Snippet<[CellCtx]> {
|
|
193
|
+
switch (type) {
|
|
194
|
+
case "number":
|
|
195
|
+
case "percent":
|
|
196
|
+
case "currency":
|
|
197
|
+
return numCell
|
|
198
|
+
case "date":
|
|
199
|
+
return dateCell
|
|
200
|
+
case "badge":
|
|
201
|
+
return badgeCell
|
|
202
|
+
case "link":
|
|
203
|
+
return linkCell
|
|
204
|
+
default:
|
|
205
|
+
return textCell
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const columns = $derived(
|
|
210
|
+
params.columns.map((c) => {
|
|
211
|
+
const col = c
|
|
212
|
+
const def: ColumnDef<Row, unknown> = {
|
|
213
|
+
id: col.key,
|
|
214
|
+
header: col.label,
|
|
215
|
+
accessorFn: (r) => sortValue(r[col.key] ?? null, col.type),
|
|
216
|
+
meta: metaOf(col),
|
|
217
|
+
cell: () => cellFor(col.type),
|
|
218
|
+
}
|
|
219
|
+
return def
|
|
220
|
+
}),
|
|
221
|
+
)
|
|
222
|
+
</script>
|
|
223
|
+
|
|
224
|
+
{#snippet textCell(context: CellCtx)}
|
|
225
|
+
{@const col = colOf(context)}
|
|
226
|
+
{@const v = context.row.original[col.key] ?? null}
|
|
227
|
+
{@const min = flexMin(col.key)}
|
|
228
|
+
<span
|
|
229
|
+
class="{v === null || v === '' ? 'text-base-content/50' : ''} {params
|
|
230
|
+
.columns[0]?.key === col.key
|
|
231
|
+
? 'font-medium'
|
|
232
|
+
: ''} {min ? 'block break-words' : 'whitespace-nowrap'}"
|
|
233
|
+
style:min-width={min}
|
|
234
|
+
>
|
|
235
|
+
{formatCell(v, col, params.currency)}
|
|
236
|
+
</span>
|
|
237
|
+
{/snippet}
|
|
238
|
+
|
|
239
|
+
{#snippet numCell(context: CellCtx)}
|
|
240
|
+
{@const col = colOf(context)}
|
|
241
|
+
{@const v = context.row.original[col.key] ?? null}
|
|
242
|
+
{@const tone = toneForCell(col, v)}
|
|
243
|
+
{@const label = formatCell(v, col, params.currency)}
|
|
244
|
+
<span
|
|
245
|
+
class="flex items-center justify-end gap-1.5 whitespace-nowrap tabular-nums"
|
|
246
|
+
data-tone={tone ?? undefined}
|
|
247
|
+
>
|
|
248
|
+
{#if tone}
|
|
249
|
+
<span
|
|
250
|
+
class="size-2 shrink-0 rounded-full {TONE_DOT[tone]}"
|
|
251
|
+
title={TONE_WORD[tone]}
|
|
252
|
+
aria-hidden="true"
|
|
253
|
+
></span>
|
|
254
|
+
{/if}
|
|
255
|
+
<span class={v === null ? "text-base-content/50" : ""}>{label}</span>
|
|
256
|
+
{#if tone}<span class="sr-only">({TONE_WORD[tone]})</span>{/if}
|
|
257
|
+
</span>
|
|
258
|
+
{/snippet}
|
|
259
|
+
|
|
260
|
+
{#snippet dateCell(context: CellCtx)}
|
|
261
|
+
{@const col = colOf(context)}
|
|
262
|
+
{@const v = context.row.original[col.key] ?? null}
|
|
263
|
+
<span class="whitespace-nowrap tabular-nums">
|
|
264
|
+
{formatCell(v, col, params.currency)}
|
|
265
|
+
</span>
|
|
266
|
+
{/snippet}
|
|
267
|
+
|
|
268
|
+
{#snippet badgeCell(context: CellCtx)}
|
|
269
|
+
{@const col = colOf(context)}
|
|
270
|
+
{@const v = context.row.original[col.key] ?? null}
|
|
271
|
+
{@const tone = toneForCell(col, v)}
|
|
272
|
+
{#if v === null || v === ""}
|
|
273
|
+
<span class="text-base-content/50">n/a</span>
|
|
274
|
+
{:else if tone}
|
|
275
|
+
<span class="badge badge-sm whitespace-nowrap {TONE_CLASS[tone]}"
|
|
276
|
+
>{formatCell(v, col, params.currency)}</span
|
|
277
|
+
>
|
|
278
|
+
{:else}
|
|
279
|
+
<span class="badge badge-sm badge-ghost whitespace-nowrap"
|
|
280
|
+
>{formatCell(v, col, params.currency)}</span
|
|
281
|
+
>
|
|
282
|
+
{/if}
|
|
283
|
+
{/snippet}
|
|
284
|
+
|
|
285
|
+
{#snippet linkCell(context: CellCtx)}
|
|
286
|
+
{@const col = colOf(context)}
|
|
287
|
+
{@const row = context.row.original}
|
|
288
|
+
{@const raw = col.hrefKey ? row[col.hrefKey] : row[col.key]}
|
|
289
|
+
{@const url = typeof raw === "string" && isSafeUrl(raw) ? raw : null}
|
|
290
|
+
{#if url && /^https:\/\//i.test(url)}
|
|
291
|
+
<a
|
|
292
|
+
class="link link-hover font-medium whitespace-nowrap"
|
|
293
|
+
href={url}
|
|
294
|
+
target="_blank"
|
|
295
|
+
rel="noopener noreferrer"
|
|
296
|
+
>{formatCell(row[col.key] ?? null, col, params.currency)}<span
|
|
297
|
+
class="sr-only"
|
|
298
|
+
>
|
|
299
|
+
(opens in a new tab)</span
|
|
300
|
+
></a
|
|
301
|
+
>
|
|
302
|
+
{:else if url}
|
|
303
|
+
<a class="link link-hover font-medium whitespace-nowrap" href={url}
|
|
304
|
+
>{formatCell(row[col.key] ?? null, col, params.currency)}</a
|
|
305
|
+
>
|
|
306
|
+
{:else}
|
|
307
|
+
<span class="text-base-content/50"
|
|
308
|
+
>{formatCell(row[col.key] ?? null, col, params.currency)}</span
|
|
309
|
+
>
|
|
310
|
+
{/if}
|
|
311
|
+
{/snippet}
|
|
312
|
+
|
|
313
|
+
<figure
|
|
314
|
+
bind:this={figureEl}
|
|
315
|
+
class="tb-root @container w-full"
|
|
316
|
+
class:tb-wide={wide}
|
|
317
|
+
data-block-type="table"
|
|
318
|
+
data-wide={wide ? "" : undefined}
|
|
319
|
+
style={widthVars}
|
|
320
|
+
>
|
|
321
|
+
<figcaption class="mb-2 flex flex-wrap items-center gap-x-3 gap-y-2">
|
|
322
|
+
<div class="flex min-w-0 flex-1 items-center gap-2 @md:flex-none">
|
|
323
|
+
{#if params.title}
|
|
324
|
+
<h2 class="text-sm font-semibold text-base-content">{params.title}</h2>
|
|
325
|
+
{/if}
|
|
326
|
+
<span class="text-xs tabular-nums text-base-content/70">
|
|
327
|
+
{filtered.length} row{filtered.length === 1 ? "" : "s"}
|
|
328
|
+
</span>
|
|
329
|
+
{#if params.title}
|
|
330
|
+
<SectionTools {ctx} inline />
|
|
331
|
+
{/if}
|
|
332
|
+
</div>
|
|
333
|
+
<div class="flex w-full items-center gap-2 @md:ml-auto @md:w-auto">
|
|
334
|
+
{#if params.filterable}
|
|
335
|
+
<input
|
|
336
|
+
type="search"
|
|
337
|
+
class="input input-sm input-bordered min-w-0 flex-1 @md:w-56 @md:flex-none"
|
|
338
|
+
aria-label="Filter rows"
|
|
339
|
+
placeholder="Filter rows"
|
|
340
|
+
bind:value={q}
|
|
341
|
+
/>
|
|
342
|
+
{/if}
|
|
343
|
+
{#if params.csv}
|
|
344
|
+
<button
|
|
345
|
+
type="button"
|
|
346
|
+
class="btn btn-ghost btn-sm shrink-0 gap-1.5"
|
|
347
|
+
onclick={exportCsv}
|
|
348
|
+
>
|
|
349
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.25" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 15V3" /><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" /><path d="m7 10 5 5 5-5" /></svg>
|
|
350
|
+
Download CSV
|
|
351
|
+
</button>
|
|
352
|
+
{/if}
|
|
353
|
+
</div>
|
|
354
|
+
</figcaption>
|
|
355
|
+
|
|
356
|
+
{#if params.columns.length === 0 || params.rows.length === 0}
|
|
357
|
+
<p
|
|
358
|
+
class="rounded-box border border-dashed border-base-content/15 px-4 py-6 text-center text-sm text-base-content/70"
|
|
359
|
+
>
|
|
360
|
+
No data yet.
|
|
361
|
+
</p>
|
|
362
|
+
{:else}
|
|
363
|
+
<div class="relative">
|
|
364
|
+
<DataTable
|
|
365
|
+
data={filtered}
|
|
366
|
+
{columns}
|
|
367
|
+
sortable
|
|
368
|
+
bind:sorting
|
|
369
|
+
paginated
|
|
370
|
+
pageSize={params.pageSize}
|
|
371
|
+
stickyHeader
|
|
372
|
+
maxHeight={`${params.maxHeightPx}px`}
|
|
373
|
+
variant="plain"
|
|
374
|
+
{headerProps}
|
|
375
|
+
>
|
|
376
|
+
{#snippet empty()}
|
|
377
|
+
<div class="p-4 text-sm text-base-content/70">
|
|
378
|
+
No rows match "{q.trim()}".
|
|
379
|
+
<button
|
|
380
|
+
type="button"
|
|
381
|
+
class="link link-hover ml-1"
|
|
382
|
+
onclick={() => (q = "")}>Clear the filter</button
|
|
383
|
+
>
|
|
384
|
+
</div>
|
|
385
|
+
{/snippet}
|
|
386
|
+
</DataTable>
|
|
387
|
+
{#if overflowX && !atEnd}
|
|
388
|
+
<div
|
|
389
|
+
class="pointer-events-none absolute inset-y-0 right-0 w-8 bg-gradient-to-l from-base-100 to-transparent"
|
|
390
|
+
aria-hidden="true"
|
|
391
|
+
></div>
|
|
392
|
+
{/if}
|
|
393
|
+
</div>
|
|
394
|
+
{#if overflowX}
|
|
395
|
+
<p class="mt-1 text-xs text-base-content/70" data-scroll-hint>
|
|
396
|
+
Scroll sideways for more columns
|
|
397
|
+
<span aria-hidden="true">→</span>
|
|
398
|
+
</p>
|
|
399
|
+
{/if}
|
|
400
|
+
{/if}
|
|
401
|
+
</figure>
|
|
402
|
+
|
|
403
|
+
<style>
|
|
404
|
+
/* Wide layout: break out of the artifact page's reading column, centred
|
|
405
|
+
on it, capped at 80rem and kept 1.5rem inside the page view
|
|
406
|
+
(--artifact-page-w is that view's measured width, not the viewport's,
|
|
407
|
+
so an admin sidebar host stays correct). Only that page opts in;
|
|
408
|
+
pinned/embedded hosts keep the block in its column. Never narrower than
|
|
409
|
+
the column it sits in. */
|
|
410
|
+
:global([data-artifact-page]) .tb-wide {
|
|
411
|
+
--tb-w: max(100%, min(var(--artifact-page-w, 100%) - 3rem, 80rem));
|
|
412
|
+
width: var(--tb-w);
|
|
413
|
+
margin-inline: calc((100% - var(--tb-w)) / 2);
|
|
414
|
+
}
|
|
415
|
+
/* Column widths from resolveColumnLayout (shrink = 1px + nowrap content,
|
|
416
|
+
flex = its share in %). DataTable only takes a class per header, so the
|
|
417
|
+
values arrive as --tbw-N vars on the figure. MAX_COLUMNS = 24. */
|
|
418
|
+
.tb-root :global(thead th:nth-child(1)) {
|
|
419
|
+
width: var(--tbw-1);
|
|
420
|
+
}
|
|
421
|
+
.tb-root :global(thead th:nth-child(2)) {
|
|
422
|
+
width: var(--tbw-2);
|
|
423
|
+
}
|
|
424
|
+
.tb-root :global(thead th:nth-child(3)) {
|
|
425
|
+
width: var(--tbw-3);
|
|
426
|
+
}
|
|
427
|
+
.tb-root :global(thead th:nth-child(4)) {
|
|
428
|
+
width: var(--tbw-4);
|
|
429
|
+
}
|
|
430
|
+
.tb-root :global(thead th:nth-child(5)) {
|
|
431
|
+
width: var(--tbw-5);
|
|
432
|
+
}
|
|
433
|
+
.tb-root :global(thead th:nth-child(6)) {
|
|
434
|
+
width: var(--tbw-6);
|
|
435
|
+
}
|
|
436
|
+
.tb-root :global(thead th:nth-child(7)) {
|
|
437
|
+
width: var(--tbw-7);
|
|
438
|
+
}
|
|
439
|
+
.tb-root :global(thead th:nth-child(8)) {
|
|
440
|
+
width: var(--tbw-8);
|
|
441
|
+
}
|
|
442
|
+
.tb-root :global(thead th:nth-child(9)) {
|
|
443
|
+
width: var(--tbw-9);
|
|
444
|
+
}
|
|
445
|
+
.tb-root :global(thead th:nth-child(10)) {
|
|
446
|
+
width: var(--tbw-10);
|
|
447
|
+
}
|
|
448
|
+
.tb-root :global(thead th:nth-child(11)) {
|
|
449
|
+
width: var(--tbw-11);
|
|
450
|
+
}
|
|
451
|
+
.tb-root :global(thead th:nth-child(12)) {
|
|
452
|
+
width: var(--tbw-12);
|
|
453
|
+
}
|
|
454
|
+
.tb-root :global(thead th:nth-child(13)) {
|
|
455
|
+
width: var(--tbw-13);
|
|
456
|
+
}
|
|
457
|
+
.tb-root :global(thead th:nth-child(14)) {
|
|
458
|
+
width: var(--tbw-14);
|
|
459
|
+
}
|
|
460
|
+
.tb-root :global(thead th:nth-child(15)) {
|
|
461
|
+
width: var(--tbw-15);
|
|
462
|
+
}
|
|
463
|
+
.tb-root :global(thead th:nth-child(16)) {
|
|
464
|
+
width: var(--tbw-16);
|
|
465
|
+
}
|
|
466
|
+
.tb-root :global(thead th:nth-child(17)) {
|
|
467
|
+
width: var(--tbw-17);
|
|
468
|
+
}
|
|
469
|
+
.tb-root :global(thead th:nth-child(18)) {
|
|
470
|
+
width: var(--tbw-18);
|
|
471
|
+
}
|
|
472
|
+
.tb-root :global(thead th:nth-child(19)) {
|
|
473
|
+
width: var(--tbw-19);
|
|
474
|
+
}
|
|
475
|
+
.tb-root :global(thead th:nth-child(20)) {
|
|
476
|
+
width: var(--tbw-20);
|
|
477
|
+
}
|
|
478
|
+
.tb-root :global(thead th:nth-child(21)) {
|
|
479
|
+
width: var(--tbw-21);
|
|
480
|
+
}
|
|
481
|
+
.tb-root :global(thead th:nth-child(22)) {
|
|
482
|
+
width: var(--tbw-22);
|
|
483
|
+
}
|
|
484
|
+
.tb-root :global(thead th:nth-child(23)) {
|
|
485
|
+
width: var(--tbw-23);
|
|
486
|
+
}
|
|
487
|
+
.tb-root :global(thead th:nth-child(24)) {
|
|
488
|
+
width: var(--tbw-24);
|
|
489
|
+
}
|
|
490
|
+
</style>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { BlockProps } from "../../types.js";
|
|
2
|
+
declare const TableBlock: import("svelte").Component<BlockProps<{
|
|
3
|
+
columns: {
|
|
4
|
+
key: string;
|
|
5
|
+
label: string;
|
|
6
|
+
type: "number" | "date" | "link" | "text" | "badge" | "currency" | "percent";
|
|
7
|
+
direction: "highIsGood" | "lowIsGood";
|
|
8
|
+
good?: number | undefined;
|
|
9
|
+
warn?: number | undefined;
|
|
10
|
+
tones?: {
|
|
11
|
+
value: string;
|
|
12
|
+
tone: "warn" | "good" | "bad";
|
|
13
|
+
}[] | undefined;
|
|
14
|
+
hrefKey?: string | undefined;
|
|
15
|
+
width?: number | "auto" | "md" | "lg" | "xs" | "sm" | undefined;
|
|
16
|
+
}[];
|
|
17
|
+
rows: Record<string, string | number | boolean | null>[];
|
|
18
|
+
pageSize: number;
|
|
19
|
+
filterable: boolean;
|
|
20
|
+
maxHeightPx: number;
|
|
21
|
+
currency: string;
|
|
22
|
+
csv: boolean;
|
|
23
|
+
title?: string | undefined;
|
|
24
|
+
wide?: boolean | undefined;
|
|
25
|
+
dataKey?: string | undefined;
|
|
26
|
+
}>, {}, "">;
|
|
27
|
+
type TableBlock = ReturnType<typeof TableBlock>;
|
|
28
|
+
export default TableBlock;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Table block definition (block type "table").
|
|
3
|
+
* Contract: src/lib/artifact-blocks/CONTRACT.md — registry.ts discovers this
|
|
4
|
+
* folder; nothing else registers a block.
|
|
5
|
+
*/
|
|
6
|
+
import type { ArtifactBlockDef } from "../../types.js";
|
|
7
|
+
import { type TableParams } from "./schema.js";
|
|
8
|
+
declare const def: ArtifactBlockDef<TableParams>;
|
|
9
|
+
export default def;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { isWide, tableSchema } from "./schema.js";
|
|
2
|
+
import TableBlock from "./TableBlock.svelte";
|
|
3
|
+
const def = {
|
|
4
|
+
type: "table",
|
|
5
|
+
title: "Table",
|
|
6
|
+
description: "Sortable, filterable, paginated data table (jatui DataTable) for records " +
|
|
7
|
+
"and detail — the companion to the chart block (a chart for shape, a table " +
|
|
8
|
+
"for exact values; don't pair them to show the SAME series twice). columns: " +
|
|
9
|
+
"{key, label, type} where type is text | number | percent | currency | date | " +
|
|
10
|
+
"badge | link — number/percent/currency align right and sort numerically, date " +
|
|
11
|
+
"accepts ISO dates ('2026-01-15', or epoch ms), badge renders a chip (optionally " +
|
|
12
|
+
"toned via tones: [{value, tone}]), link renders an anchor from the cell value " +
|
|
13
|
+
"or the row's hrefKey field (https:// or root-relative ONLY — anything else " +
|
|
14
|
+
"renders as plain text). Conditional tone: give a numeric column good/warn " +
|
|
15
|
+
"thresholds plus direction (highIsGood default) and cells past them render as " +
|
|
16
|
+
"solid good/warn/bad chips — e.g. {good: 90, warn: 70} on highIsGood, or " +
|
|
17
|
+
"{good: 2, warn: 5, direction: 'lowIsGood'} for latency. Rows are objects keyed " +
|
|
18
|
+
"by column key; caps 5000 rows × 24 columns, page size max 200 (default 50). " +
|
|
19
|
+
"Sorting, the filter box (substring over display values), sticky header and the " +
|
|
20
|
+
"CSV download (exports the filtered rows in the current sort) are built in. " +
|
|
21
|
+
"percent values are 0-100 (81.5 renders 81.5%), not 0-1 fractions. " +
|
|
22
|
+
"Column widths: badge/number/date/link columns and short text (<= 28 chars) " +
|
|
23
|
+
"shrink to their content and never wrap; long text columns wrap and share the " +
|
|
24
|
+
"remaining width by length. Override per column with width: 'xs' | 'sm' | 'md' | " +
|
|
25
|
+
"'lg' (wrapping text, growing share) | 'auto' (shrink to content) | a number " +
|
|
26
|
+
"(fr weight 0.5-12). A table with more than 4 columns widens past the page's " +
|
|
27
|
+
"reading column (set wide: false to keep it in the column, true to force it). " +
|
|
28
|
+
"Tip: put the identifying column first and keep labels short — the header sticks " +
|
|
29
|
+
"while 50 rows scroll.",
|
|
30
|
+
schema: tableSchema,
|
|
31
|
+
component: TableBlock,
|
|
32
|
+
// mos-8fvt9: the section's Listen + comment sit on the heading row.
|
|
33
|
+
hostsTools: (p) => Boolean(p.title),
|
|
34
|
+
// The wide breakout (isWide — `wide` param, else >4 columns) is ALSO the
|
|
35
|
+
// renderer's cue to break out the per-block comment trigger above this
|
|
36
|
+
// block, so the pill aligns with the table's actual right edge (mos-51g8l).
|
|
37
|
+
wide: isWide,
|
|
38
|
+
example: {
|
|
39
|
+
title: "Invoice aging",
|
|
40
|
+
currency: "USD",
|
|
41
|
+
columns: [
|
|
42
|
+
{ key: "invoice", label: "Invoice", type: "link", hrefKey: "url" },
|
|
43
|
+
{ key: "customer", label: "Customer", type: "text" },
|
|
44
|
+
{ key: "amount", label: "Amount", type: "currency" },
|
|
45
|
+
{ key: "due", label: "Due", type: "date" },
|
|
46
|
+
{
|
|
47
|
+
key: "days",
|
|
48
|
+
label: "Days overdue",
|
|
49
|
+
type: "number",
|
|
50
|
+
direction: "lowIsGood",
|
|
51
|
+
good: 5,
|
|
52
|
+
warn: 30,
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
key: "status",
|
|
56
|
+
label: "Status",
|
|
57
|
+
type: "badge",
|
|
58
|
+
tones: [
|
|
59
|
+
{ value: "overdue", tone: "bad" },
|
|
60
|
+
{ value: "current", tone: "good" },
|
|
61
|
+
],
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
rows: [
|
|
65
|
+
{
|
|
66
|
+
invoice: "INV-1042",
|
|
67
|
+
customer: "Northwind Tractors",
|
|
68
|
+
amount: 12450.5,
|
|
69
|
+
due: "2026-09-14",
|
|
70
|
+
days: 42,
|
|
71
|
+
status: "overdue",
|
|
72
|
+
url: "/admin/billing/invoices/INV-1042",
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
invoice: "INV-1043",
|
|
76
|
+
customer: "Blue Harbor Cafe",
|
|
77
|
+
amount: 830,
|
|
78
|
+
due: "2026-10-02",
|
|
79
|
+
days: 6,
|
|
80
|
+
status: "current",
|
|
81
|
+
url: "/admin/billing/invoices/INV-1043",
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
invoice: "INV-1044",
|
|
85
|
+
customer: "Ridgeline Dental",
|
|
86
|
+
amount: 2210,
|
|
87
|
+
due: "2026-10-11",
|
|
88
|
+
days: 0,
|
|
89
|
+
status: "sent",
|
|
90
|
+
url: "/admin/billing/invoices/INV-1044",
|
|
91
|
+
},
|
|
92
|
+
],
|
|
93
|
+
pageSize: 50,
|
|
94
|
+
filterable: true,
|
|
95
|
+
maxHeightPx: 420,
|
|
96
|
+
csv: true,
|
|
97
|
+
},
|
|
98
|
+
dataParam: "dataKey",
|
|
99
|
+
};
|
|
100
|
+
export default def;
|