@warkypublic/svelix 0.1.44 → 0.1.47

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.
Files changed (109) hide show
  1. package/README.md +93 -33
  2. package/dist/components/BetterMenu/BetterMenu.svelte +21 -37
  3. package/dist/components/BetterMenu/BetterMenuAsyncButton.svelte +21 -31
  4. package/dist/components/BetterMenu/BetterMenuPreview.svelte +27 -29
  5. package/dist/components/BetterMenu/MenuRenderer.svelte +40 -51
  6. package/dist/components/Boxer/Boxer.svelte +488 -602
  7. package/dist/components/Boxer/BoxerTarget.svelte +6 -46
  8. package/dist/components/Boxer/BoxerTarget.svelte.d.ts +1 -0
  9. package/dist/components/Boxer/types.d.ts +0 -1
  10. package/dist/components/Button.svelte +19 -43
  11. package/dist/components/ButtonPreview.svelte +2 -11
  12. package/dist/components/CardGrid/CardGrid.svelte +1141 -0
  13. package/dist/components/CardGrid/CardGrid.svelte.d.ts +61 -0
  14. package/dist/components/CardGrid/CardGridFilterPanel.svelte +320 -0
  15. package/dist/components/CardGrid/CardGridFilterPanel.svelte.d.ts +11 -0
  16. package/dist/components/CardGrid/DefaultCard.svelte +287 -0
  17. package/dist/components/CardGrid/DefaultCard.svelte.d.ts +10 -0
  18. package/dist/components/CardGrid/ImageCardStory.svelte +308 -0
  19. package/dist/components/CardGrid/ImageCardStory.svelte.d.ts +18 -0
  20. package/dist/components/CardGrid/cardGrid.d.ts +33 -0
  21. package/dist/components/CardGrid/cardGrid.js +21 -0
  22. package/dist/components/CardGrid/index.d.ts +4 -0
  23. package/dist/components/CardGrid/index.js +4 -0
  24. package/dist/components/ContentEditor/CollaboraInsertTextDemo.svelte +5 -10
  25. package/dist/components/ContentEditor/ContentEditor.svelte +24 -50
  26. package/dist/components/ContentEditor/subcomponents/AudioPlayer.svelte +17 -17
  27. package/dist/components/ContentEditor/subcomponents/CollaboraEditor.svelte +244 -309
  28. package/dist/components/ContentEditor/subcomponents/EmailViewer.svelte +2 -3
  29. package/dist/components/ContentEditor/subcomponents/ImageViewer.svelte +14 -15
  30. package/dist/components/ContentEditor/subcomponents/MarkdownViewer.svelte +74 -90
  31. package/dist/components/ContentEditor/subcomponents/MonacoEditor.svelte +75 -103
  32. package/dist/components/ContentEditor/subcomponents/Office365Editor.svelte +234 -297
  33. package/dist/components/ContentEditor/subcomponents/PdfViewer.svelte +14 -20
  34. package/dist/components/ContentEditor/subcomponents/TextEditor.svelte +122 -150
  35. package/dist/components/ContentEditor/subcomponents/UnknownFile.svelte +105 -126
  36. package/dist/components/ContentEditor/subcomponents/VideoPlayer.svelte +17 -17
  37. package/dist/components/ContentEditor/subcomponents/ZipViewer.svelte +2 -3
  38. package/dist/components/ErrorBoundary/ErrorBoundary.svelte +41 -58
  39. package/dist/components/ErrorBoundary/ErrorBoundaryPreview.svelte +8 -16
  40. package/dist/components/Former/Former.svelte +306 -424
  41. package/dist/components/Former/FormerButtonArea.svelte +17 -68
  42. package/dist/components/Former/FormerDrawer.svelte +26 -78
  43. package/dist/components/Former/FormerDrawerPreview.svelte +99 -89
  44. package/dist/components/Former/FormerModal.svelte +24 -70
  45. package/dist/components/Former/FormerModalPreview.svelte +99 -89
  46. package/dist/components/Former/FormerPreview.svelte +76 -78
  47. package/dist/components/Former/FormerRestApiPreview.svelte +55 -48
  48. package/dist/components/FormerControllers/ButtonCtrl.svelte +13 -44
  49. package/dist/components/FormerControllers/DateTimeCtrl/DateTimeCtrl.svelte +280 -445
  50. package/dist/components/FormerControllers/DateTimeCtrl/DateTimeCtrlCalendar.svelte +95 -136
  51. package/dist/components/FormerControllers/DateTimeCtrl/DateTimeCtrlPickerPanel.svelte +3 -62
  52. package/dist/components/FormerControllers/DateTimeCtrl/DateTimeCtrlTimeFields.svelte +143 -217
  53. package/dist/components/FormerControllers/IconButtonCtrl.svelte +13 -42
  54. package/dist/components/FormerControllers/InlineWrapper.svelte +12 -41
  55. package/dist/components/FormerControllers/InlineWrapperPreview.svelte +4 -26
  56. package/dist/components/FormerControllers/NativeSelectCtrl.svelte +3 -29
  57. package/dist/components/FormerControllers/NumberInputCtrl.svelte +9 -35
  58. package/dist/components/FormerControllers/PasswordInputCtrl.svelte +4 -23
  59. package/dist/components/FormerControllers/SwitchCtrl.svelte +3 -20
  60. package/dist/components/FormerControllers/TextAreaCtrl.svelte +3 -24
  61. package/dist/components/FormerControllers/TextInputCtrl.svelte +3 -24
  62. package/dist/components/GlobalStateStore/GlobalStateStoreProvider.svelte +73 -120
  63. package/dist/components/Gridler/components/Gridler.svelte +338 -442
  64. package/dist/components/Gridler/components/GridlerCanvas.svelte +720 -1115
  65. package/dist/components/Gridler/components/GridlerEditor.svelte +2 -15
  66. package/dist/components/Gridler/components/GridlerFull.svelte +520 -841
  67. package/dist/components/Gridler/components/GridlerFull.svelte.d.ts +1 -1
  68. package/dist/components/Gridler/components/GridlerFullWithFormerPreview.svelte +194 -147
  69. package/dist/components/Gridler/components/GridlerSearch.svelte +7 -16
  70. package/dist/components/Gridler/components/GridlerSearchToggle.svelte +7 -19
  71. package/dist/components/Gridler/types.d.ts +1 -1
  72. package/dist/components/Icons/IconAdd.svelte +1 -3
  73. package/dist/components/Icons/IconAlertCircle.svelte +1 -3
  74. package/dist/components/Icons/IconAlertTriangle.svelte +1 -3
  75. package/dist/components/Icons/IconCamera.svelte +1 -3
  76. package/dist/components/Icons/IconClose.svelte +1 -3
  77. package/dist/components/Icons/IconEdit.svelte +1 -3
  78. package/dist/components/Icons/IconFilter.svelte +1 -3
  79. package/dist/components/Icons/IconSearch.svelte +1 -3
  80. package/dist/components/Icons/IconSort.svelte +1 -3
  81. package/dist/components/Icons/IconTrash.svelte +1 -3
  82. package/dist/components/Portal/Portal.svelte +14 -38
  83. package/dist/components/Screenshot/Screenshot.svelte +7 -13
  84. package/dist/components/Svark/Svark.svelte +680 -852
  85. package/dist/components/Svark/SvarkContextMenu.svelte +44 -63
  86. package/dist/components/Svark/SvarkInfiniteLoader.svelte +2 -6
  87. package/dist/components/Svark/SvarkPager.svelte +2 -28
  88. package/dist/components/Svark/SvarkSelectionDemo.svelte +4 -8
  89. package/dist/components/Svark/SvarkTopBar.svelte +9 -74
  90. package/dist/components/SvarkGrid/SvarkGrid.svelte +877 -1123
  91. package/dist/components/SvarkGrid/components/SvarkColumnFilterForm.svelte +45 -58
  92. package/dist/components/SvarkGrid/components/SvarkContextMenu.svelte +44 -63
  93. package/dist/components/SvarkGrid/components/SvarkHeaderFilterCell.svelte +41 -75
  94. package/dist/components/SvarkGrid/components/SvarkInfiniteLoader.svelte +2 -6
  95. package/dist/components/SvarkGrid/components/SvarkPager.svelte +2 -28
  96. package/dist/components/SvarkGrid/components/SvarkSearchPopover.svelte +8 -16
  97. package/dist/components/SvarkGrid/components/SvarkTopBar.svelte +6 -62
  98. package/dist/components/SvarkGrid/internal/SvarkGridView.svelte +345 -507
  99. package/dist/components/VTree/VTree.svelte +349 -431
  100. package/dist/components/VTree/VTreeContextMenu.svelte +21 -33
  101. package/dist/components/VTree/VTreeEventsDemo.svelte +48 -58
  102. package/dist/components/VTree/VTreeRow.svelte +13 -65
  103. package/dist/components/VTree/VTreeSearch.svelte +6 -13
  104. package/dist/components/VTree/VTreeVirtualViewport.svelte +37 -106
  105. package/dist/components/index.d.ts +1 -0
  106. package/dist/components/index.js +1 -0
  107. package/llm/COMPONENT_GUIDE.md +240 -0
  108. package/llm/plans/card_grid.plan.md +305 -0
  109. package/package.json +48 -46
@@ -0,0 +1,61 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { CardGridColumn, CardSortOption, CardGridContextMenuItem } from './cardGrid.js';
3
+ import type { GridColumnSortOrder, GridColumnFilters } from '../Types/generic_grid.js';
4
+ interface Props {
5
+ columns: CardGridColumn[];
6
+ displayColumns?: string[];
7
+ cardMinWidth?: number;
8
+ cardGap?: number;
9
+ /**
10
+ * Maximum number of columns shown on the front face.
11
+ * When `resolvedColumns.length` exceeds this, a flip button appears and the
12
+ * remaining columns are shown on the card's back face.
13
+ */
14
+ cardMaxFrontColumns?: number;
15
+ /** Front-face snippet: (record, index, selected, focused) */
16
+ card?: Snippet<[Record<string, unknown>, number, boolean, boolean]>;
17
+ /** Back-face snippet for flip mode: (record, index, selected, focused) */
18
+ cardBack?: Snippet<[Record<string, unknown>, number, boolean, boolean]>;
19
+ empty?: Snippet;
20
+ skeleton?: Snippet;
21
+ dataSource?: 'resolvespec' | 'headerspec' | 'local';
22
+ dataSourceOptions?: {
23
+ url?: string;
24
+ authToken?: string;
25
+ schema?: string;
26
+ entity?: string;
27
+ uniqueID?: string;
28
+ hotfields?: string[];
29
+ extraOptions?: Record<string, unknown>;
30
+ };
31
+ data?: Record<string, unknown>[] | (() => Promise<Record<string, unknown>[]>);
32
+ pageSize?: number;
33
+ uniqueID?: string;
34
+ searchValue?: string;
35
+ onSearchValueChange?: (v: string) => void;
36
+ serverSideSearch?: boolean;
37
+ searchColumns?: string[];
38
+ searchPlaceholder?: string;
39
+ sortOrder?: GridColumnSortOrder;
40
+ onSortOrderChange?: (order: GridColumnSortOrder) => void;
41
+ sortOptions?: CardSortOption[];
42
+ filters?: GridColumnFilters;
43
+ onFilterChange?: (filters: GridColumnFilters) => void;
44
+ selectedItems?: Record<string, unknown>[];
45
+ onSelectedItemsChange?: (items: Record<string, unknown>[]) => void;
46
+ multiSelect?: boolean;
47
+ onCardClick?: (record: Record<string, unknown>, index: number) => void;
48
+ onCardDblClick?: (record: Record<string, unknown>, index: number) => void;
49
+ onCardContextMenu?: (record: Record<string, unknown>, index: number, x: number, y: number) => void;
50
+ menuItems?: CardGridContextMenuItem[];
51
+ total?: number;
52
+ loading?: boolean;
53
+ onLoadError?: (error: string) => void;
54
+ class?: string;
55
+ toolbarEnd?: Snippet;
56
+ }
57
+ declare const CardGrid: import("svelte").Component<Props, {
58
+ refresh: () => void;
59
+ }, "loading" | "total">;
60
+ type CardGrid = ReturnType<typeof CardGrid>;
61
+ export default CardGrid;
@@ -0,0 +1,320 @@
1
+ <script lang="ts">const { columns, filters, onApply, onCancel } = $props();
2
+ const ALL_OPERATORS = [
3
+ {
4
+ value: "contains",
5
+ label: "Contains"
6
+ },
7
+ {
8
+ value: "equals",
9
+ label: "Equals"
10
+ },
11
+ {
12
+ value: "notEquals",
13
+ label: "Not equals"
14
+ },
15
+ {
16
+ value: "startsWith",
17
+ label: "Starts with"
18
+ },
19
+ {
20
+ value: "endsWith",
21
+ label: "Ends with"
22
+ },
23
+ {
24
+ value: "greaterThan",
25
+ label: ">"
26
+ },
27
+ {
28
+ value: "greaterThanOrEqual",
29
+ label: ">="
30
+ },
31
+ {
32
+ value: "lessThan",
33
+ label: "<"
34
+ },
35
+ {
36
+ value: "lessThanOrEqual",
37
+ label: "<="
38
+ },
39
+ {
40
+ value: "between",
41
+ label: "Between"
42
+ },
43
+ {
44
+ value: "notBetween",
45
+ label: "Not between"
46
+ },
47
+ {
48
+ value: "in",
49
+ label: "In (comma-sep)"
50
+ },
51
+ {
52
+ value: "notIn",
53
+ label: "Not in"
54
+ },
55
+ {
56
+ value: "isNull",
57
+ label: "Is empty"
58
+ },
59
+ {
60
+ value: "isNotNull",
61
+ label: "Is not empty"
62
+ }
63
+ ];
64
+ const filterableColumns = $derived(columns.filter((c) => !c.disableFilter));
65
+ function filtersToRows(f) {
66
+ return Object.entries(f).map(([colId, entry]) => ({
67
+ colId,
68
+ op: entry.op ?? "contains",
69
+ value: entry.value ?? "",
70
+ value2: entry.value2 ?? ""
71
+ }));
72
+ }
73
+ let rows = $state([]);
74
+ $effect(() => {
75
+ rows = filtersToRows(filters);
76
+ });
77
+ function addRow() {
78
+ const firstCol = filterableColumns[0];
79
+ if (!firstCol) return;
80
+ rows = [...rows, {
81
+ colId: firstCol.id,
82
+ op: "contains",
83
+ value: "",
84
+ value2: ""
85
+ }];
86
+ }
87
+ function removeRow(index) {
88
+ rows = rows.filter((_, i) => i !== index);
89
+ }
90
+ function apply() {
91
+ const next = {};
92
+ for (const row of rows) {
93
+ if (!row.colId) continue;
94
+ const needsValue = row.op !== "isNull" && row.op !== "isNotNull";
95
+ if (needsValue && !row.value) continue;
96
+ const col = columns.find((c) => c.id === row.colId);
97
+ const entry = {
98
+ value: row.value,
99
+ op: row.op,
100
+ ...col?.dataKey ? { dataKey: col.dataKey } : {}
101
+ };
102
+ if ((row.op === "between" || row.op === "notBetween") && row.value2) {
103
+ entry.value2 = row.value2;
104
+ }
105
+ next[row.colId] = entry;
106
+ }
107
+ onApply(next);
108
+ }
109
+ function clear() {
110
+ onApply({});
111
+ }
112
+ const needsValue = (op) => op !== "isNull" && op !== "isNotNull";
113
+ const needsValue2 = (op) => op === "between" || op === "notBetween";
114
+ export {};
115
+ </script>
116
+
117
+ <div class="fp-panel">
118
+ <div class="fp-header">
119
+ <span class="fp-title">Filters</span>
120
+ <button class="fp-add-btn" onclick={addRow} type="button">+ Add filter</button>
121
+ </div>
122
+
123
+ {#if rows.length === 0}
124
+ <p class="fp-empty">No filters applied.</p>
125
+ {/if}
126
+
127
+ {#each rows as row, i (i)}
128
+ <div class="fp-row">
129
+ <select
130
+ class="fp-select"
131
+ bind:value={row.colId}
132
+ aria-label="Column"
133
+ >
134
+ {#each filterableColumns as col (col.id)}
135
+ <option value={col.id}>{col.title}</option>
136
+ {/each}
137
+ </select>
138
+
139
+ <select
140
+ class="fp-select fp-select-op"
141
+ bind:value={row.op}
142
+ aria-label="Operator"
143
+ >
144
+ {#each ALL_OPERATORS as op (op.value)}
145
+ <option value={op.value}>{op.label}</option>
146
+ {/each}
147
+ </select>
148
+
149
+ {#if needsValue(row.op)}
150
+ <input
151
+ class="fp-input"
152
+ type="text"
153
+ bind:value={row.value}
154
+ placeholder="Value"
155
+ aria-label="Filter value"
156
+ />
157
+ {/if}
158
+
159
+ {#if needsValue2(row.op)}
160
+ <input
161
+ class="fp-input"
162
+ type="text"
163
+ bind:value={row.value2}
164
+ placeholder="To"
165
+ aria-label="Filter value 2"
166
+ />
167
+ {/if}
168
+
169
+ <button
170
+ class="fp-remove-btn"
171
+ onclick={() => removeRow(i)}
172
+ type="button"
173
+ aria-label="Remove filter"
174
+ >✕</button>
175
+ </div>
176
+ {/each}
177
+
178
+ <div class="fp-actions">
179
+ <button class="fp-btn fp-btn-clear" onclick={clear} type="button">Clear all</button>
180
+ <div class="fp-actions-right">
181
+ <button class="fp-btn" onclick={onCancel} type="button">Cancel</button>
182
+ <button class="fp-btn fp-btn-apply" onclick={apply} type="button">Apply</button>
183
+ </div>
184
+ </div>
185
+ </div>
186
+
187
+ <style>
188
+ .fp-panel {
189
+ background: var(--cg-surface-raised, var(--color-surface-50, #fff));
190
+ border: 1px solid var(--cg-border, var(--color-surface-200, #e2e8f0));
191
+ border-radius: 10px;
192
+ box-shadow: 0 8px 24px rgba(0,0,0,.12), 0 2px 6px rgba(0,0,0,.07);
193
+ min-width: 460px;
194
+ padding: 14px;
195
+ display: flex;
196
+ flex-direction: column;
197
+ gap: 8px;
198
+ color: var(--cg-text, var(--color-surface-900, #0f172a));
199
+ }
200
+
201
+ :global(.dark) .fp-panel {
202
+ box-shadow: 0 8px 24px rgba(0,0,0,.4), 0 2px 6px rgba(0,0,0,.3);
203
+ }
204
+
205
+ .fp-header {
206
+ display: flex;
207
+ align-items: center;
208
+ justify-content: space-between;
209
+ }
210
+
211
+ .fp-title {
212
+ font-weight: 600;
213
+ font-size: 0.875rem;
214
+ }
215
+
216
+ .fp-add-btn {
217
+ font-size: 0.8rem;
218
+ color: var(--cg-accent, var(--color-primary-500, #6366f1));
219
+ background: none;
220
+ border: none;
221
+ cursor: pointer;
222
+ padding: 2px 4px;
223
+ }
224
+
225
+ .fp-empty {
226
+ color: var(--cg-text-muted, var(--color-surface-500, #64748b));
227
+ font-size: 0.85rem;
228
+ margin: 4px 0;
229
+ }
230
+
231
+ .fp-row {
232
+ display: flex;
233
+ gap: 6px;
234
+ align-items: center;
235
+ flex-wrap: wrap;
236
+ }
237
+
238
+ .fp-select {
239
+ padding: 5px 7px;
240
+ border: 1px solid var(--cg-border, var(--color-surface-200, #e2e8f0));
241
+ border-radius: 6px;
242
+ font-size: 0.8rem;
243
+ background: var(--cg-surface-raised, #fff);
244
+ color: var(--cg-text, #0f172a);
245
+ cursor: pointer;
246
+ }
247
+
248
+ .fp-select-op {
249
+ min-width: 120px;
250
+ }
251
+
252
+ .fp-input {
253
+ padding: 5px 8px;
254
+ border: 1px solid var(--cg-border, var(--color-surface-200, #e2e8f0));
255
+ border-radius: 6px;
256
+ font-size: 0.8rem;
257
+ min-width: 100px;
258
+ flex: 1;
259
+ background: var(--cg-surface-raised, #fff);
260
+ color: var(--cg-text, #0f172a);
261
+ }
262
+ .fp-input::placeholder { color: var(--cg-text-muted, #94a3b8); }
263
+
264
+ .fp-remove-btn {
265
+ background: none;
266
+ border: none;
267
+ cursor: pointer;
268
+ color: var(--cg-text-muted, #94a3b8);
269
+ padding: 2px 4px;
270
+ font-size: 0.875rem;
271
+ flex-shrink: 0;
272
+ }
273
+
274
+ .fp-remove-btn:hover {
275
+ color: var(--color-error-500, #ef4444);
276
+ }
277
+
278
+ .fp-actions {
279
+ display: flex;
280
+ justify-content: space-between;
281
+ align-items: center;
282
+ padding-top: 6px;
283
+ border-top: 1px solid var(--cg-border, var(--color-surface-200, #e2e8f0));
284
+ margin-top: 2px;
285
+ }
286
+
287
+ .fp-actions-right {
288
+ display: flex;
289
+ gap: 6px;
290
+ }
291
+
292
+ .fp-btn {
293
+ padding: 5px 12px;
294
+ border-radius: 6px;
295
+ font-size: 0.8rem;
296
+ cursor: pointer;
297
+ border: 1px solid var(--cg-border, var(--color-surface-200, #e2e8f0));
298
+ background: var(--cg-surface-raised, #fff);
299
+ color: var(--cg-text, #0f172a);
300
+ transition: background 120ms;
301
+ }
302
+ .fp-btn:hover { background: var(--cg-border-subtle, var(--color-surface-100, #f1f5f9)); }
303
+
304
+ .fp-btn-apply {
305
+ background: var(--cg-accent, var(--color-primary-500, #6366f1));
306
+ color: #fff;
307
+ border-color: var(--cg-accent, var(--color-primary-500, #6366f1));
308
+ }
309
+ .fp-btn-apply:hover {
310
+ background: var(--color-primary-600, #4f46e5);
311
+ border-color: var(--color-primary-600, #4f46e5);
312
+ }
313
+
314
+ .fp-btn-clear {
315
+ color: var(--color-error-500, #ef4444);
316
+ border-color: transparent;
317
+ background: none;
318
+ }
319
+ .fp-btn-clear:hover { background: color-mix(in oklab, var(--color-error-500, #ef4444) 10%, transparent); }
320
+ </style>
@@ -0,0 +1,11 @@
1
+ import type { CardGridColumn } from './cardGrid.js';
2
+ import type { GridColumnFilters } from '../Types/generic_grid.js';
3
+ interface Props {
4
+ columns: CardGridColumn[];
5
+ filters: GridColumnFilters;
6
+ onApply: (filters: GridColumnFilters) => void;
7
+ onCancel: () => void;
8
+ }
9
+ declare const CardGridFilterPanel: import("svelte").Component<Props, {}, "">;
10
+ type CardGridFilterPanel = ReturnType<typeof CardGridFilterPanel>;
11
+ export default CardGridFilterPanel;
@@ -0,0 +1,287 @@
1
+ <script lang="ts">const { record, columns, selected, focused } = $props();
2
+ // First column → title, second → subtitle, rest → detail rows
3
+ const titleCol = $derived(columns[0]);
4
+ const subtitleCol = $derived(columns.length > 1 ? columns[1] : undefined);
5
+ const detailCols = $derived(columns.slice(subtitleCol ? 2 : 1));
6
+ function getVal(key, row) {
7
+ const parts = key.split(".");
8
+ let val = row;
9
+ for (const part of parts) {
10
+ if (val == null || typeof val !== "object") return undefined;
11
+ val = val[part];
12
+ }
13
+ return val;
14
+ }
15
+ function formatVal(val, col) {
16
+ if (val == null || val === "") return "—";
17
+ switch (col.format) {
18
+ case "currency": {
19
+ const n = Number(val);
20
+ if (!isNaN(n)) return new Intl.NumberFormat(undefined, {
21
+ style: "currency",
22
+ currency: "ZAR",
23
+ maximumFractionDigits: 0
24
+ }).format(n);
25
+ break;
26
+ }
27
+ case "number": {
28
+ const n = Number(val);
29
+ if (!isNaN(n)) return n.toLocaleString();
30
+ break;
31
+ }
32
+ case "percentage": {
33
+ const n = Number(val);
34
+ if (!isNaN(n)) return `${(n * 100).toFixed(1)}%`;
35
+ break;
36
+ }
37
+ case "date": {
38
+ const d = val instanceof Date ? val : new Date(String(val));
39
+ if (!isNaN(d.getTime())) return d.toLocaleDateString();
40
+ break;
41
+ }
42
+ case "datetime": {
43
+ const d = val instanceof Date ? val : new Date(String(val));
44
+ if (!isNaN(d.getTime())) return d.toLocaleString();
45
+ break;
46
+ }
47
+ }
48
+ return String(val);
49
+ }
50
+ // Status chip logic — keyed on common English status words
51
+ const STATUS_MAP = {
52
+ active: "success",
53
+ enabled: "success",
54
+ approved: "success",
55
+ confirmed: "success",
56
+ completed: "success",
57
+ done: "success",
58
+ live: "success",
59
+ pending: "warning",
60
+ review: "warning",
61
+ draft: "warning",
62
+ in_progress: "warning",
63
+ processing: "warning",
64
+ queued: "warning",
65
+ inactive: "error",
66
+ disabled: "error",
67
+ cancelled: "error",
68
+ rejected: "error",
69
+ failed: "error",
70
+ error: "error",
71
+ blocked: "error",
72
+ archived: "neutral",
73
+ closed: "neutral",
74
+ deprecated: "neutral"
75
+ };
76
+ function isStatusCol(col) {
77
+ const id = col.id.toLowerCase();
78
+ return id.includes("status") || id.includes("state") || id === "type";
79
+ }
80
+ function statusVariant(val) {
81
+ if (val == null) return null;
82
+ return STATUS_MAP[String(val).toLowerCase().replace(/[\s-]/g, "_")] ?? null;
83
+ }
84
+ export {};
85
+ </script>
86
+
87
+ <article class="dc-card" class:dc-selected={selected} class:dc-focused={focused}>
88
+ <!-- Header -->
89
+ {#if titleCol}
90
+ {@const titleKey = titleCol.dataKey ?? titleCol.id}
91
+ {@const titleVal = getVal(titleKey, record)}
92
+ <div class="dc-header">
93
+ <p class="dc-title">{titleVal ?? '—'}</p>
94
+ {#if subtitleCol}
95
+ {@const subKey = subtitleCol.dataKey ?? subtitleCol.id}
96
+ {@const subVal = getVal(subKey, record)}
97
+ <p class="dc-subtitle">{formatVal(subVal, subtitleCol)}</p>
98
+ {/if}
99
+ </div>
100
+ {/if}
101
+
102
+ <!-- Detail rows -->
103
+ {#if detailCols.length > 0}
104
+ <div class="dc-divider"></div>
105
+ <dl class="dc-fields">
106
+ {#each detailCols as col (col.id)}
107
+ {@const key = col.dataKey ?? col.id}
108
+ {@const raw = getVal(key, record)}
109
+ {@const isChip = isStatusCol(col)}
110
+ {@const chip = isChip ? statusVariant(raw) : null}
111
+ <div class="dc-field">
112
+ <dt class="dc-label">{col.title}</dt>
113
+ <dd class="dc-value">
114
+ {#if chip}
115
+ <span class="dc-chip dc-chip-{chip}">{raw ?? '—'}</span>
116
+ {:else}
117
+ {formatVal(raw, col)}
118
+ {/if}
119
+ </dd>
120
+ </div>
121
+ {/each}
122
+ </dl>
123
+ {/if}
124
+ </article>
125
+
126
+ <style>
127
+ .dc-card {
128
+ padding: 0;
129
+ font-size: 0.875rem;
130
+ height: 100%;
131
+ display: flex;
132
+ flex-direction: column;
133
+ }
134
+
135
+ /* ── Header ───────────────────────────────────────────────────────── */
136
+
137
+ .dc-header {
138
+ padding: 14px 16px 12px;
139
+ display: flex;
140
+ flex-direction: column;
141
+ gap: 3px;
142
+ }
143
+
144
+ .dc-title {
145
+ font-size: 0.9375rem;
146
+ font-weight: 600;
147
+ color: var(--color-surface-900, #1e293b);
148
+ line-height: 1.3;
149
+ margin: 0;
150
+ white-space: nowrap;
151
+ overflow: hidden;
152
+ text-overflow: ellipsis;
153
+ }
154
+
155
+ :global(.dark) .dc-title {
156
+ color: var(--color-surface-50, #f8fafc);
157
+ }
158
+
159
+ .dc-subtitle {
160
+ font-size: 0.8rem;
161
+ color: var(--color-surface-500, #64748b);
162
+ margin: 0;
163
+ white-space: nowrap;
164
+ overflow: hidden;
165
+ text-overflow: ellipsis;
166
+ }
167
+
168
+ :global(.dark) .dc-subtitle {
169
+ color: var(--color-surface-400, #94a3b8);
170
+ }
171
+
172
+ /* ── Divider ──────────────────────────────────────────────────────── */
173
+
174
+ .dc-divider {
175
+ height: 1px;
176
+ background: var(--cg-border-subtle, #f4f6f9);
177
+ margin: 0 16px;
178
+ }
179
+
180
+ :global(.dark) .dc-divider {
181
+ background: var(--cg-border, var(--color-surface-700, #334155));
182
+ }
183
+
184
+ /* ── Fields grid ──────────────────────────────────────────────────── */
185
+
186
+ .dc-fields {
187
+ padding: 10px 16px 14px;
188
+ display: flex;
189
+ flex-direction: column;
190
+ gap: 5px;
191
+ margin: 0;
192
+ flex: 1;
193
+ }
194
+
195
+ .dc-field {
196
+ display: grid;
197
+ grid-template-columns: minmax(60px, 38%) 1fr;
198
+ gap: 8px;
199
+ align-items: baseline;
200
+ }
201
+
202
+ .dc-label {
203
+ font-size: 0.75rem;
204
+ color: var(--color-surface-400, #94a3b8);
205
+ white-space: nowrap;
206
+ overflow: hidden;
207
+ text-overflow: ellipsis;
208
+ }
209
+
210
+ :global(.dark) .dc-label {
211
+ color: var(--color-surface-500, #64748b);
212
+ }
213
+
214
+ .dc-value {
215
+ font-size: 0.8125rem;
216
+ font-weight: 500;
217
+ color: var(--color-surface-800, #1e293b);
218
+ text-align: right;
219
+ word-break: break-word;
220
+ min-width: 0;
221
+ margin: 0;
222
+ }
223
+
224
+ :global(.dark) .dc-value {
225
+ color: var(--color-surface-100, #f1f5f9);
226
+ }
227
+
228
+ /* ── Status chips ─────────────────────────────────────────────────── */
229
+
230
+ .dc-chip {
231
+ display: inline-flex;
232
+ align-items: center;
233
+ gap: 4px;
234
+ padding: 1px 7px;
235
+ border-radius: 999px;
236
+ font-size: 0.72rem;
237
+ font-weight: 600;
238
+ letter-spacing: 0.01em;
239
+ text-transform: capitalize;
240
+ }
241
+
242
+ .dc-chip::before {
243
+ content: '';
244
+ display: inline-block;
245
+ width: 5px;
246
+ height: 5px;
247
+ border-radius: 50%;
248
+ background: currentColor;
249
+ flex-shrink: 0;
250
+ }
251
+
252
+ .dc-chip-success {
253
+ background: var(--color-success-100, #dcfce7);
254
+ color: var(--color-success-700, #15803d);
255
+ }
256
+ :global(.dark) .dc-chip-success {
257
+ background: color-mix(in oklab, var(--color-success-700, #15803d) 20%, transparent);
258
+ color: var(--color-success-400, #4ade80);
259
+ }
260
+
261
+ .dc-chip-warning {
262
+ background: var(--color-warning-100, #fef9c3);
263
+ color: var(--color-warning-700, #a16207);
264
+ }
265
+ :global(.dark) .dc-chip-warning {
266
+ background: color-mix(in oklab, var(--color-warning-700, #a16207) 20%, transparent);
267
+ color: var(--color-warning-400, #facc15);
268
+ }
269
+
270
+ .dc-chip-error {
271
+ background: var(--color-error-100, #fee2e2);
272
+ color: var(--color-error-700, #b91c1c);
273
+ }
274
+ :global(.dark) .dc-chip-error {
275
+ background: color-mix(in oklab, var(--color-error-700, #b91c1c) 20%, transparent);
276
+ color: var(--color-error-400, #f87171);
277
+ }
278
+
279
+ .dc-chip-neutral {
280
+ background: var(--color-surface-100, #f1f5f9);
281
+ color: var(--color-surface-600, #475569);
282
+ }
283
+ :global(.dark) .dc-chip-neutral {
284
+ background: var(--color-surface-700, #334155);
285
+ color: var(--color-surface-300, #94a3b8);
286
+ }
287
+ </style>
@@ -0,0 +1,10 @@
1
+ import type { CardGridColumn } from './cardGrid.js';
2
+ interface Props {
3
+ record: Record<string, unknown>;
4
+ columns: CardGridColumn[];
5
+ selected: boolean;
6
+ focused: boolean;
7
+ }
8
+ declare const DefaultCard: import("svelte").Component<Props, {}, "">;
9
+ type DefaultCard = ReturnType<typeof DefaultCard>;
10
+ export default DefaultCard;