@svadmin/lite 0.7.0 → 0.8.1

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 (72) hide show
  1. package/README.md +18 -5
  2. package/dist/components/LiteActivityFeed.svelte +98 -0
  3. package/dist/components/LiteActivityFeed.svelte.d.ts +19 -0
  4. package/dist/components/LiteApprovalActionCard.svelte +75 -0
  5. package/dist/components/LiteApprovalActionCard.svelte.d.ts +10 -0
  6. package/dist/components/LiteCanvasAnnotation.svelte +136 -0
  7. package/dist/components/LiteCanvasAnnotation.svelte.d.ts +16 -0
  8. package/dist/components/LiteColumnHeaderFilter.svelte +38 -0
  9. package/dist/components/LiteColumnHeaderFilter.svelte.d.ts +8 -0
  10. package/dist/components/LiteColumnSettings.svelte +82 -0
  11. package/dist/components/LiteColumnSettings.svelte.d.ts +15 -0
  12. package/dist/components/LiteDecisionTable.svelte +178 -0
  13. package/dist/components/LiteDecisionTable.svelte.d.ts +21 -0
  14. package/dist/components/LiteDraggableRowTable.svelte +70 -0
  15. package/dist/components/LiteDraggableRowTable.svelte.d.ts +14 -0
  16. package/dist/components/LiteDrawerForm.svelte +85 -0
  17. package/dist/components/LiteDrawerForm.svelte.d.ts +13 -0
  18. package/dist/components/LiteEditableTable.svelte +84 -0
  19. package/dist/components/LiteEditableTable.svelte.d.ts +19 -0
  20. package/dist/components/LiteGanttChart.svelte +144 -0
  21. package/dist/components/LiteGanttChart.svelte.d.ts +17 -0
  22. package/dist/components/LiteImageCropper.svelte +81 -0
  23. package/dist/components/LiteImageCropper.svelte.d.ts +9 -0
  24. package/dist/components/LiteImportWizard.svelte +99 -0
  25. package/dist/components/LiteImportWizard.svelte.d.ts +11 -0
  26. package/dist/components/LiteJsonSchemaForm.svelte +102 -0
  27. package/dist/components/LiteJsonSchemaForm.svelte.d.ts +10 -0
  28. package/dist/components/LiteKanbanBoard.svelte +169 -0
  29. package/dist/components/LiteKanbanBoard.svelte.d.ts +21 -0
  30. package/dist/components/LiteMasterDetailView.svelte +113 -0
  31. package/dist/components/LiteMasterDetailView.svelte.d.ts +11 -0
  32. package/dist/components/LiteMediaLibraryModal.svelte +110 -0
  33. package/dist/components/LiteMediaLibraryModal.svelte.d.ts +17 -0
  34. package/dist/components/LiteMentionsInput.svelte +98 -0
  35. package/dist/components/LiteMentionsInput.svelte.d.ts +19 -0
  36. package/dist/components/LiteModalForm.svelte +84 -0
  37. package/dist/components/LiteModalForm.svelte.d.ts +13 -0
  38. package/dist/components/LiteMultiTabKeepAlive.svelte +79 -0
  39. package/dist/components/LiteMultiTabKeepAlive.svelte.d.ts +15 -0
  40. package/dist/components/LiteOfflineSyncBanner.svelte +75 -0
  41. package/dist/components/LiteOfflineSyncBanner.svelte.d.ts +15 -0
  42. package/dist/components/LitePdfDocumentViewer.svelte +138 -0
  43. package/dist/components/LitePdfDocumentViewer.svelte.d.ts +17 -0
  44. package/dist/components/LitePivotTable.svelte +163 -0
  45. package/dist/components/LitePivotTable.svelte.d.ts +15 -0
  46. package/dist/components/LitePresenceAvatarGroup.svelte +52 -0
  47. package/dist/components/LitePresenceAvatarGroup.svelte.d.ts +12 -0
  48. package/dist/components/LitePrintableBill.svelte +119 -0
  49. package/dist/components/LitePrintableBill.svelte.d.ts +19 -0
  50. package/dist/components/LiteSensitiveDataMask.svelte +35 -0
  51. package/dist/components/LiteSensitiveDataMask.svelte.d.ts +7 -0
  52. package/dist/components/LiteSignaturePad.svelte +99 -0
  53. package/dist/components/LiteSignaturePad.svelte.d.ts +11 -0
  54. package/dist/components/LiteSplitPaneLayout.svelte +66 -0
  55. package/dist/components/LiteSplitPaneLayout.svelte.d.ts +9 -0
  56. package/dist/components/LiteSpreadsheetView.svelte +148 -0
  57. package/dist/components/LiteSpreadsheetView.svelte.d.ts +16 -0
  58. package/dist/components/LiteStepForm.svelte +144 -0
  59. package/dist/components/LiteStepForm.svelte.d.ts +18 -0
  60. package/dist/components/LiteTableSummary.svelte +93 -0
  61. package/dist/components/LiteTableSummary.svelte.d.ts +17 -0
  62. package/dist/components/LiteTreeTable.svelte +85 -0
  63. package/dist/components/LiteTreeTable.svelte.d.ts +13 -0
  64. package/dist/components/LiteVersionDiffViewer.svelte +129 -0
  65. package/dist/components/LiteVersionDiffViewer.svelte.d.ts +10 -0
  66. package/dist/components/LiteVirtualTable.svelte +52 -0
  67. package/dist/components/LiteVirtualTable.svelte.d.ts +14 -0
  68. package/dist/components/LiteWatermark.svelte +35 -0
  69. package/dist/components/LiteWatermark.svelte.d.ts +11 -0
  70. package/dist/index.d.ts +35 -0
  71. package/dist/index.js +34 -0
  72. package/package.json +2 -2
@@ -0,0 +1,148 @@
1
+ <script lang="ts">
2
+ export interface SheetData {
3
+ id: string;
4
+ name: string;
5
+ rows: number;
6
+ cols: number;
7
+ cells: Record<string, string>;
8
+ }
9
+
10
+ interface Props {
11
+ sheets?: SheetData[];
12
+ activeSheetId?: string;
13
+ formAction?: string;
14
+ class?: string;
15
+ }
16
+
17
+ let {
18
+ sheets = [
19
+ {
20
+ id: 'sheet1',
21
+ name: 'Summary',
22
+ rows: 5,
23
+ cols: 4,
24
+ cells: { A1: 'Item', B1: 'Amount', A2: 'Sales', B2: '1000' },
25
+ },
26
+ ],
27
+ activeSheetId = 'sheet1',
28
+ formAction = '',
29
+ class: className = '',
30
+ }: Props = $props();
31
+
32
+ const currentSheet = $derived(
33
+ sheets.find((s) => s.id === activeSheetId) ?? sheets[0]
34
+ );
35
+
36
+ function getColName(index: number): string {
37
+ return String.fromCharCode(index + 65);
38
+ }
39
+ </script>
40
+
41
+ <div class="sv-lite-sheet-container {className}">
42
+ <div class="sv-lite-sheet-header">
43
+ <strong>Spreadsheet: {currentSheet?.name ?? 'Sheet'}</strong>
44
+ </div>
45
+
46
+ {#if currentSheet}
47
+ <form method="POST" action={formAction}>
48
+ <table class="sv-lite-sheet-table">
49
+ <thead>
50
+ <tr>
51
+ <th class="sv-lite-sheet-th-num">#</th>
52
+ {#each Array.from({ length: currentSheet.cols }) as _, c (c)}
53
+ <th class="sv-lite-sheet-th">{getColName(c)}</th>
54
+ {/each}
55
+ </tr>
56
+ </thead>
57
+ <tbody>
58
+ {#each Array.from({ length: currentSheet.rows }) as _, r (r)}
59
+ {@const rowNum = r + 1}
60
+ <tr>
61
+ <td class="sv-lite-sheet-td-num">{rowNum}</td>
62
+ {#each Array.from({ length: currentSheet.cols }) as _, c (c)}
63
+ {@const key = `${getColName(c)}${rowNum}`}
64
+ <td class="sv-lite-sheet-td">
65
+ <input
66
+ type="text"
67
+ name={`cell_${key}`}
68
+ value={currentSheet.cells[key] ?? ''}
69
+ class="sv-lite-cell-input"
70
+ />
71
+ </td>
72
+ {/each}
73
+ </tr>
74
+ {/each}
75
+ </tbody>
76
+ </table>
77
+
78
+ {#if formAction}
79
+ <div class="sv-lite-sheet-footer">
80
+ <button type="submit" class="sv-lite-sheet-save">Save Spreadsheet</button>
81
+ </div>
82
+ {/if}
83
+ </form>
84
+ {/if}
85
+ </div>
86
+
87
+ <style>
88
+ .sv-lite-sheet-container {
89
+ display: block;
90
+ border: 1px solid #e2e8f0;
91
+ border-radius: 6px;
92
+ padding: 12px;
93
+ background-color: #ffffff;
94
+ font-size: 12px;
95
+ overflow-x: auto;
96
+ }
97
+ .sv-lite-sheet-header {
98
+ margin-bottom: 10px;
99
+ font-size: 13px;
100
+ color: #0f172a;
101
+ }
102
+ .sv-lite-sheet-table {
103
+ width: 100%;
104
+ border-collapse: collapse;
105
+ font-family: monospace;
106
+ }
107
+ .sv-lite-sheet-table th, .sv-lite-sheet-table td {
108
+ border: 1px solid #cbd5e1;
109
+ padding: 2px;
110
+ }
111
+ .sv-lite-sheet-th-num, .sv-lite-sheet-td-num {
112
+ background-color: #f1f5f9;
113
+ text-align: center;
114
+ width: 32px;
115
+ color: #64748b;
116
+ font-weight: bold;
117
+ font-size: 10px;
118
+ }
119
+ .sv-lite-sheet-th {
120
+ background-color: #f8fafc;
121
+ text-align: center;
122
+ font-weight: 600;
123
+ color: #1e293b;
124
+ min-width: 80px;
125
+ }
126
+ .sv-lite-cell-input {
127
+ width: 100%;
128
+ border: none;
129
+ padding: 4px;
130
+ font-size: 11px;
131
+ box-sizing: border-box;
132
+ font-family: inherit;
133
+ background: transparent;
134
+ }
135
+ .sv-lite-sheet-footer {
136
+ margin-top: 10px;
137
+ text-align: right;
138
+ }
139
+ .sv-lite-sheet-save {
140
+ padding: 5px 12px;
141
+ background-color: #4f46e5;
142
+ color: #ffffff;
143
+ border: none;
144
+ border-radius: 3px;
145
+ font-size: 11px;
146
+ cursor: pointer;
147
+ }
148
+ </style>
@@ -0,0 +1,16 @@
1
+ export interface SheetData {
2
+ id: string;
3
+ name: string;
4
+ rows: number;
5
+ cols: number;
6
+ cells: Record<string, string>;
7
+ }
8
+ interface Props {
9
+ sheets?: SheetData[];
10
+ activeSheetId?: string;
11
+ formAction?: string;
12
+ class?: string;
13
+ }
14
+ declare const LiteSpreadsheetView: import("svelte").Component<Props, {}, "">;
15
+ type LiteSpreadsheetView = ReturnType<typeof LiteSpreadsheetView>;
16
+ export default LiteSpreadsheetView;
@@ -0,0 +1,144 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+
4
+ export interface LiteFormStep {
5
+ title: string;
6
+ description?: string;
7
+ }
8
+
9
+ interface Props {
10
+ steps: LiteFormStep[];
11
+ currentStep?: number;
12
+ action?: string;
13
+ method?: "get" | "post" | "dialog" | "GET" | "POST" | "DIALOG" | null;
14
+ submitText?: string;
15
+ nextText?: string;
16
+ prevText?: string;
17
+ children?: Snippet;
18
+ }
19
+
20
+ let {
21
+ steps,
22
+ currentStep = 0,
23
+ action = '',
24
+ method = 'POST',
25
+ submitText = 'Submit',
26
+ nextText = 'Next Step',
27
+ prevText = 'Previous Step',
28
+ children,
29
+ }: Props = $props();
30
+ </script>
31
+
32
+ <div class="lite-step-form-card">
33
+ <!-- Step Indicators -->
34
+ <div class="lite-step-header">
35
+ {#each steps as step, index (index)}
36
+ {@const isCompleted = index < currentStep}
37
+ {@const isCurrent = index === currentStep}
38
+ <div class="lite-step-item {isCurrent ? 'lite-step-current' : isCompleted ? 'lite-step-completed' : ''}">
39
+ <span class="lite-step-circle">
40
+ {#if isCompleted}✔{:else}{index + 1}{/if}
41
+ </span>
42
+ <span class="lite-step-title">{step.title}</span>
43
+ </div>
44
+ {#if index < steps.length - 1}
45
+ <span class="lite-step-arrow">→</span>
46
+ {/if}
47
+ {/each}
48
+ </div>
49
+
50
+ <!-- Form Body -->
51
+ <form {action} {method} class="lite-step-body">
52
+ <input type="hidden" name="_step" value={currentStep} />
53
+
54
+ <div class="lite-step-content">
55
+ {#if children}
56
+ {@render children()}
57
+ {/if}
58
+ </div>
59
+
60
+ <!-- Actions -->
61
+ <div class="lite-step-actions">
62
+ {#if currentStep > 0}
63
+ <button type="submit" name="_step_action" value="prev" class="lite-btn lite-btn-outline lite-btn-sm">
64
+ ← {prevText}
65
+ </button>
66
+ {/if}
67
+ <div class="lite-step-right-action" style="margin-left: auto;">
68
+ {#if currentStep < steps.length - 1}
69
+ <button type="submit" name="_step_action" value="next" class="lite-btn lite-btn-primary lite-btn-sm">
70
+ {nextText} →
71
+ </button>
72
+ {:else}
73
+ <button type="submit" name="_step_action" value="finish" class="lite-btn lite-btn-primary lite-btn-sm">
74
+ {submitText}
75
+ </button>
76
+ {/if}
77
+ </div>
78
+ </div>
79
+ </form>
80
+ </div>
81
+
82
+ <style>
83
+ .lite-step-form-card {
84
+ padding: 16px;
85
+ background: #ffffff;
86
+ border: 1px solid #cbd5e1;
87
+ border-radius: 6px;
88
+ margin-bottom: 16px;
89
+ }
90
+ .lite-step-header {
91
+ display: flex;
92
+ align-items: center;
93
+ padding-bottom: 12px;
94
+ border-bottom: 1px solid #e2e8f0;
95
+ margin-bottom: 16px;
96
+ overflow-x: auto;
97
+ }
98
+ .lite-step-item {
99
+ display: flex;
100
+ align-items: center;
101
+ font-size: 12px;
102
+ color: #64748b;
103
+ }
104
+ .lite-step-item.lite-step-current {
105
+ color: #3b82f6;
106
+ font-weight: bold;
107
+ }
108
+ .lite-step-item.lite-step-completed {
109
+ color: #10b981;
110
+ }
111
+ .lite-step-circle {
112
+ display: inline-block;
113
+ width: 20px;
114
+ height: 20px;
115
+ line-height: 20px;
116
+ text-align: center;
117
+ border-radius: 50%;
118
+ background: #f1f5f9;
119
+ margin-right: 6px;
120
+ font-size: 11px;
121
+ }
122
+ .lite-step-current .lite-step-circle {
123
+ background: #3b82f6;
124
+ color: #ffffff;
125
+ }
126
+ .lite-step-completed .lite-step-circle {
127
+ background: #10b981;
128
+ color: #ffffff;
129
+ }
130
+ .lite-step-arrow {
131
+ margin: 0 10px;
132
+ color: #cbd5e1;
133
+ }
134
+ .lite-step-content {
135
+ min-height: 100px;
136
+ margin-bottom: 16px;
137
+ }
138
+ .lite-step-actions {
139
+ display: flex;
140
+ align-items: center;
141
+ padding-top: 12px;
142
+ border-top: 1px solid #e2e8f0;
143
+ }
144
+ </style>
@@ -0,0 +1,18 @@
1
+ import type { Snippet } from 'svelte';
2
+ export interface LiteFormStep {
3
+ title: string;
4
+ description?: string;
5
+ }
6
+ interface Props {
7
+ steps: LiteFormStep[];
8
+ currentStep?: number;
9
+ action?: string;
10
+ method?: "get" | "post" | "dialog" | "GET" | "POST" | "DIALOG" | null;
11
+ submitText?: string;
12
+ nextText?: string;
13
+ prevText?: string;
14
+ children?: Snippet;
15
+ }
16
+ declare const LiteStepForm: import("svelte").Component<Props, {}, "">;
17
+ type LiteStepForm = ReturnType<typeof LiteStepForm>;
18
+ export default LiteStepForm;
@@ -0,0 +1,93 @@
1
+ <script lang="ts">
2
+ interface LiteTableSummaryColumn {
3
+ key: string;
4
+ label?: string;
5
+ align?: 'left' | 'center' | 'right';
6
+ }
7
+
8
+ interface Props {
9
+ columns: LiteTableSummaryColumn[];
10
+ data?: Record<string, unknown>[];
11
+ aggregations?: Record<string, string | ((rows: Record<string, unknown>[]) => string | number)>;
12
+ title?: string;
13
+ titleColumnKey?: string;
14
+ prefix?: Record<string, string>;
15
+ suffix?: Record<string, string>;
16
+ }
17
+
18
+ let {
19
+ columns,
20
+ data = [],
21
+ aggregations = {},
22
+ title = 'Total',
23
+ titleColumnKey,
24
+ prefix = {},
25
+ suffix = {},
26
+ }: Props = $props();
27
+
28
+ const titleKey = $derived(titleColumnKey || (columns[0]?.key ?? ''));
29
+
30
+ function calculate(key: string, type: unknown): string | number {
31
+ if (!data.length) return '—';
32
+ if (typeof type === 'function') return type(data);
33
+ if (type === 'count') return data.length;
34
+
35
+ const numbers = data
36
+ .map((row) => Number(row[key]))
37
+ .filter((val) => !isNaN(val) && val !== null && val !== undefined);
38
+
39
+ if (numbers.length === 0) return '—';
40
+
41
+ if (type === 'sum') {
42
+ const sum = numbers.reduce((acc, curr) => acc + curr, 0);
43
+ return Number.isInteger(sum) ? sum : sum.toFixed(2);
44
+ }
45
+ if (type === 'avg') {
46
+ const avg = numbers.reduce((acc, curr) => acc + curr, 0) / numbers.length;
47
+ return avg.toFixed(2);
48
+ }
49
+ return '—';
50
+ }
51
+ </script>
52
+
53
+ <div class="lite-table-summary-container">
54
+ <table class="lite-table lite-summary-table">
55
+ <tbody>
56
+ <tr>
57
+ {#each columns as col (col.key)}
58
+ {@const isTitle = col.key === titleKey}
59
+ {@const aggType = aggregations[col.key]}
60
+ <td style="text-align: {col.align || 'left'}; font-weight: {isTitle || aggType ? 'bold' : 'normal'};">
61
+ {#if isTitle && !aggType}
62
+ <span>{title}</span>
63
+ {:else if aggType}
64
+ <span>
65
+ {#if prefix[col.key]}{prefix[col.key]}{/if}{calculate(col.key, aggType)}{#if suffix[col.key]}{suffix[col.key]}{/if}
66
+ </span>
67
+ {:else}
68
+ <span style="color: #94a3b8;">—</span>
69
+ {/if}
70
+ </td>
71
+ {/each}
72
+ </tr>
73
+ </tbody>
74
+ </table>
75
+ </div>
76
+
77
+ <style>
78
+ .lite-table-summary-container {
79
+ width: 100%;
80
+ overflow-x: auto;
81
+ background: #f8fafc;
82
+ border-top: 2px solid #cbd5e1;
83
+ }
84
+ .lite-summary-table {
85
+ width: 100%;
86
+ margin-bottom: 0;
87
+ }
88
+ .lite-summary-table td {
89
+ padding: 8px 12px;
90
+ font-size: 12px;
91
+ color: #1e293b;
92
+ }
93
+ </style>
@@ -0,0 +1,17 @@
1
+ interface LiteTableSummaryColumn {
2
+ key: string;
3
+ label?: string;
4
+ align?: 'left' | 'center' | 'right';
5
+ }
6
+ interface Props {
7
+ columns: LiteTableSummaryColumn[];
8
+ data?: Record<string, unknown>[];
9
+ aggregations?: Record<string, string | ((rows: Record<string, unknown>[]) => string | number)>;
10
+ title?: string;
11
+ titleColumnKey?: string;
12
+ prefix?: Record<string, string>;
13
+ suffix?: Record<string, string>;
14
+ }
15
+ declare const LiteTableSummary: import("svelte").Component<Props, {}, "">;
16
+ type LiteTableSummary = ReturnType<typeof LiteTableSummary>;
17
+ export default LiteTableSummary;
@@ -0,0 +1,85 @@
1
+ <script lang="ts">
2
+ export interface LiteTreeColumn {
3
+ key: string;
4
+ label: string;
5
+ }
6
+
7
+ interface Props {
8
+ data: Record<string, unknown>[];
9
+ columns: LiteTreeColumn[];
10
+ primaryKey?: string;
11
+ childrenKey?: string;
12
+ }
13
+
14
+ let {
15
+ data = [],
16
+ columns = [],
17
+ primaryKey = 'id',
18
+ childrenKey = 'children',
19
+ }: Props = $props();
20
+
21
+ interface FlatLiteItem {
22
+ record: Record<string, unknown>;
23
+ id: string | number;
24
+ level: number;
25
+ hasChildren: boolean;
26
+ }
27
+
28
+ function flatten(items: Record<string, unknown>[], level = 0): FlatLiteItem[] {
29
+ const flat: FlatLiteItem[] = [];
30
+ for (const item of items) {
31
+ const id = item[primaryKey] as string | number;
32
+ const children = item[childrenKey] as Record<string, unknown>[] | undefined;
33
+ const hasChildren = Array.isArray(children) && children.length > 0;
34
+ flat.push({ record: item, id, level, hasChildren });
35
+ if (hasChildren && children) {
36
+ flat.push(...flatten(children, level + 1));
37
+ }
38
+ }
39
+ return flat;
40
+ }
41
+
42
+ const flattened = $derived(flatten(data));
43
+ </script>
44
+
45
+ <div class="lite-tree-table lite-form-group">
46
+ <table class="lite-table">
47
+ <thead>
48
+ <tr>
49
+ {#each columns as col (col.key)}
50
+ <th>{col.label}</th>
51
+ {/each}
52
+ </tr>
53
+ </thead>
54
+ <tbody>
55
+ {#each flattened as row (row.id)}
56
+ <tr>
57
+ {#each columns as col, idx (col.key)}
58
+ <td>
59
+ {#if idx === 0}
60
+ <span style="display: inline-block; padding-left: {row.level * 16}px;">
61
+ {#if row.level > 0}↳ {/if}
62
+ {row.record[col.key] ?? '—'}
63
+ </span>
64
+ {:else}
65
+ {row.record[col.key] ?? '—'}
66
+ {/if}
67
+ </td>
68
+ {/each}
69
+ </tr>
70
+ {:else}
71
+ <tr>
72
+ <td colspan={columns.length} style="text-align: center; color: #94a3b8; padding: 24px;">
73
+ No tree data
74
+ </td>
75
+ </tr>
76
+ {/each}
77
+ </tbody>
78
+ </table>
79
+ </div>
80
+
81
+ <style>
82
+ .lite-tree-table {
83
+ overflow-x: auto;
84
+ }
85
+ </style>
@@ -0,0 +1,13 @@
1
+ export interface LiteTreeColumn {
2
+ key: string;
3
+ label: string;
4
+ }
5
+ interface Props {
6
+ data: Record<string, unknown>[];
7
+ columns: LiteTreeColumn[];
8
+ primaryKey?: string;
9
+ childrenKey?: string;
10
+ }
11
+ declare const LiteTreeTable: import("svelte").Component<Props, {}, "">;
12
+ type LiteTreeTable = ReturnType<typeof LiteTreeTable>;
13
+ export default LiteTreeTable;
@@ -0,0 +1,129 @@
1
+ <script lang="ts">
2
+ interface Props {
3
+ oldValue?: Record<string, unknown>;
4
+ newValue?: Record<string, unknown>;
5
+ oldTitle?: string;
6
+ newTitle?: string;
7
+ fieldLabels?: Record<string, string>;
8
+ }
9
+
10
+ let {
11
+ oldValue = {},
12
+ newValue = {},
13
+ oldTitle = 'Previous Version',
14
+ newTitle = 'Current Version',
15
+ fieldLabels = {},
16
+ }: Props = $props();
17
+
18
+ const allKeys = $derived(
19
+ Array.from(new Set([...Object.keys(oldValue || {}), ...Object.keys(newValue || {})]))
20
+ );
21
+
22
+ function getStatus(key: string): 'added' | 'removed' | 'modified' | 'unchanged' {
23
+ const hasOld = key in oldValue && oldValue[key] !== undefined;
24
+ const hasNew = key in newValue && newValue[key] !== undefined;
25
+ if (!hasOld && hasNew) return 'added';
26
+ if (hasOld && !hasNew) return 'removed';
27
+ if (JSON.stringify(oldValue[key]) !== JSON.stringify(newValue[key])) return 'modified';
28
+ return 'unchanged';
29
+ }
30
+
31
+ function formatVal(val: unknown): string {
32
+ if (val === undefined || val === null) return '—';
33
+ if (typeof val === 'object') return JSON.stringify(val);
34
+ return String(val);
35
+ }
36
+ </script>
37
+
38
+ <div class="lite-diff-viewer">
39
+ <div class="lite-diff-header">
40
+ <strong>Record Comparison</strong>
41
+ </div>
42
+
43
+ <table class="lite-table lite-diff-table">
44
+ <thead>
45
+ <tr>
46
+ <th style="width: 25%;">Field</th>
47
+ <th style="width: 37.5%;">{oldTitle}</th>
48
+ <th style="width: 37.5%;">{newTitle}</th>
49
+ </tr>
50
+ </thead>
51
+ <tbody>
52
+ {#each allKeys as key (key)}
53
+ {@const status = getStatus(key)}
54
+ <tr class="lite-diff-row lite-diff-{status}">
55
+ <td>
56
+ <strong>{fieldLabels[key] || key}</strong>
57
+ {#if status !== 'unchanged'}
58
+ <span class="lite-badge lite-badge-{status}">[{status.toUpperCase()}]</span>
59
+ {/if}
60
+ </td>
61
+ <td class="lite-diff-old">
62
+ {#if status === 'removed'}
63
+ <del>{formatVal(oldValue[key])}</del>
64
+ {:else}
65
+ {formatVal(oldValue[key])}
66
+ {/if}
67
+ </td>
68
+ <td class="lite-diff-new">
69
+ {#if status === 'added'}
70
+ <ins>{formatVal(newValue[key])}</ins>
71
+ {:else if status === 'modified'}
72
+ <strong>{formatVal(newValue[key])}</strong>
73
+ {:else}
74
+ {formatVal(newValue[key])}
75
+ {/if}
76
+ </td>
77
+ </tr>
78
+ {/each}
79
+ </tbody>
80
+ </table>
81
+ </div>
82
+
83
+ <style>
84
+ .lite-diff-viewer {
85
+ background: #ffffff;
86
+ border: 1px solid #cbd5e1;
87
+ border-radius: 6px;
88
+ margin-bottom: 16px;
89
+ }
90
+ .lite-diff-header {
91
+ padding: 10px 12px;
92
+ background: #f8fafc;
93
+ border-bottom: 1px solid #e2e8f0;
94
+ font-size: 13px;
95
+ }
96
+ .lite-diff-table {
97
+ width: 100%;
98
+ margin-bottom: 0;
99
+ font-size: 12px;
100
+ }
101
+ .lite-diff-table th {
102
+ background: #f1f5f9;
103
+ padding: 8px 12px;
104
+ text-align: left;
105
+ }
106
+ .lite-diff-table td {
107
+ padding: 8px 12px;
108
+ border-top: 1px solid #e2e8f0;
109
+ }
110
+ .lite-diff-added {
111
+ background: #ecfdf5;
112
+ }
113
+ .lite-diff-removed {
114
+ background: #fef2f2;
115
+ }
116
+ .lite-diff-modified {
117
+ background: #fffbeb;
118
+ }
119
+ .lite-diff-old del {
120
+ color: #ef4444;
121
+ }
122
+ .lite-diff-new ins {
123
+ color: #10b981;
124
+ text-decoration: none;
125
+ }
126
+ .lite-diff-new strong {
127
+ color: #f59e0b;
128
+ }
129
+ </style>
@@ -0,0 +1,10 @@
1
+ interface Props {
2
+ oldValue?: Record<string, unknown>;
3
+ newValue?: Record<string, unknown>;
4
+ oldTitle?: string;
5
+ newTitle?: string;
6
+ fieldLabels?: Record<string, string>;
7
+ }
8
+ declare const LiteVersionDiffViewer: import("svelte").Component<Props, {}, "">;
9
+ type LiteVersionDiffViewer = ReturnType<typeof LiteVersionDiffViewer>;
10
+ export default LiteVersionDiffViewer;