@svadmin/lite 0.7.0 → 0.8.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.
Files changed (71) hide show
  1. package/dist/components/LiteActivityFeed.svelte +98 -0
  2. package/dist/components/LiteActivityFeed.svelte.d.ts +19 -0
  3. package/dist/components/LiteApprovalActionCard.svelte +75 -0
  4. package/dist/components/LiteApprovalActionCard.svelte.d.ts +10 -0
  5. package/dist/components/LiteCanvasAnnotation.svelte +136 -0
  6. package/dist/components/LiteCanvasAnnotation.svelte.d.ts +16 -0
  7. package/dist/components/LiteColumnHeaderFilter.svelte +38 -0
  8. package/dist/components/LiteColumnHeaderFilter.svelte.d.ts +8 -0
  9. package/dist/components/LiteColumnSettings.svelte +82 -0
  10. package/dist/components/LiteColumnSettings.svelte.d.ts +15 -0
  11. package/dist/components/LiteDecisionTable.svelte +178 -0
  12. package/dist/components/LiteDecisionTable.svelte.d.ts +21 -0
  13. package/dist/components/LiteDraggableRowTable.svelte +70 -0
  14. package/dist/components/LiteDraggableRowTable.svelte.d.ts +14 -0
  15. package/dist/components/LiteDrawerForm.svelte +85 -0
  16. package/dist/components/LiteDrawerForm.svelte.d.ts +13 -0
  17. package/dist/components/LiteEditableTable.svelte +84 -0
  18. package/dist/components/LiteEditableTable.svelte.d.ts +19 -0
  19. package/dist/components/LiteGanttChart.svelte +144 -0
  20. package/dist/components/LiteGanttChart.svelte.d.ts +17 -0
  21. package/dist/components/LiteImageCropper.svelte +81 -0
  22. package/dist/components/LiteImageCropper.svelte.d.ts +9 -0
  23. package/dist/components/LiteImportWizard.svelte +99 -0
  24. package/dist/components/LiteImportWizard.svelte.d.ts +11 -0
  25. package/dist/components/LiteJsonSchemaForm.svelte +102 -0
  26. package/dist/components/LiteJsonSchemaForm.svelte.d.ts +10 -0
  27. package/dist/components/LiteKanbanBoard.svelte +169 -0
  28. package/dist/components/LiteKanbanBoard.svelte.d.ts +21 -0
  29. package/dist/components/LiteMasterDetailView.svelte +113 -0
  30. package/dist/components/LiteMasterDetailView.svelte.d.ts +11 -0
  31. package/dist/components/LiteMediaLibraryModal.svelte +110 -0
  32. package/dist/components/LiteMediaLibraryModal.svelte.d.ts +17 -0
  33. package/dist/components/LiteMentionsInput.svelte +98 -0
  34. package/dist/components/LiteMentionsInput.svelte.d.ts +19 -0
  35. package/dist/components/LiteModalForm.svelte +84 -0
  36. package/dist/components/LiteModalForm.svelte.d.ts +13 -0
  37. package/dist/components/LiteMultiTabKeepAlive.svelte +79 -0
  38. package/dist/components/LiteMultiTabKeepAlive.svelte.d.ts +15 -0
  39. package/dist/components/LiteOfflineSyncBanner.svelte +75 -0
  40. package/dist/components/LiteOfflineSyncBanner.svelte.d.ts +15 -0
  41. package/dist/components/LitePdfDocumentViewer.svelte +138 -0
  42. package/dist/components/LitePdfDocumentViewer.svelte.d.ts +17 -0
  43. package/dist/components/LitePivotTable.svelte +163 -0
  44. package/dist/components/LitePivotTable.svelte.d.ts +15 -0
  45. package/dist/components/LitePresenceAvatarGroup.svelte +52 -0
  46. package/dist/components/LitePresenceAvatarGroup.svelte.d.ts +12 -0
  47. package/dist/components/LitePrintableBill.svelte +119 -0
  48. package/dist/components/LitePrintableBill.svelte.d.ts +19 -0
  49. package/dist/components/LiteSensitiveDataMask.svelte +35 -0
  50. package/dist/components/LiteSensitiveDataMask.svelte.d.ts +7 -0
  51. package/dist/components/LiteSignaturePad.svelte +99 -0
  52. package/dist/components/LiteSignaturePad.svelte.d.ts +11 -0
  53. package/dist/components/LiteSplitPaneLayout.svelte +66 -0
  54. package/dist/components/LiteSplitPaneLayout.svelte.d.ts +9 -0
  55. package/dist/components/LiteSpreadsheetView.svelte +148 -0
  56. package/dist/components/LiteSpreadsheetView.svelte.d.ts +16 -0
  57. package/dist/components/LiteStepForm.svelte +144 -0
  58. package/dist/components/LiteStepForm.svelte.d.ts +18 -0
  59. package/dist/components/LiteTableSummary.svelte +93 -0
  60. package/dist/components/LiteTableSummary.svelte.d.ts +17 -0
  61. package/dist/components/LiteTreeTable.svelte +85 -0
  62. package/dist/components/LiteTreeTable.svelte.d.ts +13 -0
  63. package/dist/components/LiteVersionDiffViewer.svelte +129 -0
  64. package/dist/components/LiteVersionDiffViewer.svelte.d.ts +10 -0
  65. package/dist/components/LiteVirtualTable.svelte +52 -0
  66. package/dist/components/LiteVirtualTable.svelte.d.ts +14 -0
  67. package/dist/components/LiteWatermark.svelte +35 -0
  68. package/dist/components/LiteWatermark.svelte.d.ts +11 -0
  69. package/dist/index.d.ts +35 -0
  70. package/dist/index.js +34 -0
  71. package/package.json +2 -2
@@ -0,0 +1,84 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+
4
+ interface Props {
5
+ title: string;
6
+ description?: string;
7
+ action?: string;
8
+ method?: "get" | "post" | "dialog" | "GET" | "POST" | "DIALOG" | null;
9
+ submitText?: string;
10
+ cancelText?: string;
11
+ children?: Snippet;
12
+ }
13
+
14
+ let {
15
+ title,
16
+ description,
17
+ action = '',
18
+ method = 'POST',
19
+ submitText = 'Confirm',
20
+ cancelText = 'Cancel',
21
+ children,
22
+ }: Props = $props();
23
+ </script>
24
+
25
+ <div class="lite-modal-card">
26
+ <div class="lite-modal-header">
27
+ <h3 class="lite-modal-title">{title}</h3>
28
+ {#if description}
29
+ <p class="lite-modal-desc">{description}</p>
30
+ {/if}
31
+ </div>
32
+
33
+ <form {action} {method} class="lite-modal-form">
34
+ <div class="lite-modal-body">
35
+ {#if children}
36
+ {@render children()}
37
+ {/if}
38
+ </div>
39
+
40
+ <div class="lite-modal-footer">
41
+ <button type="button" class="lite-btn lite-btn-outline lite-btn-sm" onclick={() => history.back()}>
42
+ {cancelText}
43
+ </button>
44
+ <button type="submit" class="lite-btn lite-btn-primary lite-btn-sm" style="margin-left: 8px;">
45
+ {submitText}
46
+ </button>
47
+ </div>
48
+ </form>
49
+ </div>
50
+
51
+ <style>
52
+ .lite-modal-card {
53
+ padding: 16px;
54
+ background: #ffffff;
55
+ border: 1px solid #cbd5e1;
56
+ border-radius: 6px;
57
+ margin-bottom: 16px;
58
+ }
59
+ .lite-modal-header {
60
+ padding-bottom: 10px;
61
+ border-bottom: 1px solid #e2e8f0;
62
+ margin-bottom: 12px;
63
+ }
64
+ .lite-modal-title {
65
+ margin: 0;
66
+ font-size: 14px;
67
+ color: #1e293b;
68
+ }
69
+ .lite-modal-desc {
70
+ margin: 4px 0 0 0;
71
+ font-size: 12px;
72
+ color: #64748b;
73
+ }
74
+ .lite-modal-body {
75
+ margin-bottom: 16px;
76
+ font-size: 13px;
77
+ }
78
+ .lite-modal-footer {
79
+ display: flex;
80
+ justify-content: flex-end;
81
+ padding-top: 12px;
82
+ border-top: 1px solid #e2e8f0;
83
+ }
84
+ </style>
@@ -0,0 +1,13 @@
1
+ import type { Snippet } from 'svelte';
2
+ interface Props {
3
+ title: string;
4
+ description?: string;
5
+ action?: string;
6
+ method?: "get" | "post" | "dialog" | "GET" | "POST" | "DIALOG" | null;
7
+ submitText?: string;
8
+ cancelText?: string;
9
+ children?: Snippet;
10
+ }
11
+ declare const LiteModalForm: import("svelte").Component<Props, {}, "">;
12
+ type LiteModalForm = ReturnType<typeof LiteModalForm>;
13
+ export default LiteModalForm;
@@ -0,0 +1,79 @@
1
+ <script lang="ts">
2
+ export interface WorkspaceTab {
3
+ id: string;
4
+ title: string;
5
+ path: string;
6
+ closable?: boolean;
7
+ pinned?: boolean;
8
+ }
9
+
10
+ interface Props {
11
+ tabs?: WorkspaceTab[];
12
+ activeTabId?: string;
13
+ class?: string;
14
+ }
15
+
16
+ let {
17
+ tabs = [],
18
+ activeTabId = '',
19
+ class: className = '',
20
+ }: Props = $props();
21
+ </script>
22
+
23
+ <div class="sv-lite-tab-bar {className}">
24
+ <div class="sv-lite-tab-list">
25
+ {#each tabs as tab (tab.id)}
26
+ {@const isActive = tab.id === activeTabId}
27
+ <a
28
+ href={tab.path}
29
+ class="sv-lite-tab {isActive ? 'sv-lite-tab-active' : ''}"
30
+ >
31
+ {#if tab.pinned}
32
+ <span class="sv-lite-pin-icon">📌</span>
33
+ {/if}
34
+ <span class="sv-lite-tab-title">{tab.title}</span>
35
+ </a>
36
+ {/each}
37
+ </div>
38
+ </div>
39
+
40
+ <style>
41
+ .sv-lite-tab-bar {
42
+ display: block;
43
+ background-color: #f1f5f9;
44
+ border-bottom: 1px solid #cbd5e1;
45
+ padding: 4px 8px 0 8px;
46
+ font-size: 12px;
47
+ }
48
+ .sv-lite-tab-list {
49
+ white-space: nowrap;
50
+ overflow-x: auto;
51
+ }
52
+ .sv-lite-tab {
53
+ display: inline-block;
54
+ padding: 6px 12px;
55
+ margin-right: 4px;
56
+ background-color: #e2e8f0;
57
+ color: #475569;
58
+ text-decoration: none;
59
+ border-top-left-radius: 4px;
60
+ border-top-right-radius: 4px;
61
+ border: 1px solid #cbd5e1;
62
+ border-bottom: none;
63
+ font-weight: 500;
64
+ }
65
+ .sv-lite-tab-active {
66
+ background-color: #ffffff;
67
+ color: #0f172a;
68
+ font-weight: 600;
69
+ border-bottom: 1px solid #ffffff;
70
+ margin-bottom: -1px;
71
+ }
72
+ .sv-lite-pin-icon {
73
+ font-size: 10px;
74
+ margin-right: 4px;
75
+ }
76
+ .sv-lite-tab-title {
77
+ vertical-align: middle;
78
+ }
79
+ </style>
@@ -0,0 +1,15 @@
1
+ export interface WorkspaceTab {
2
+ id: string;
3
+ title: string;
4
+ path: string;
5
+ closable?: boolean;
6
+ pinned?: boolean;
7
+ }
8
+ interface Props {
9
+ tabs?: WorkspaceTab[];
10
+ activeTabId?: string;
11
+ class?: string;
12
+ }
13
+ declare const LiteMultiTabKeepAlive: import("svelte").Component<Props, {}, "">;
14
+ type LiteMultiTabKeepAlive = ReturnType<typeof LiteMultiTabKeepAlive>;
15
+ export default LiteMultiTabKeepAlive;
@@ -0,0 +1,75 @@
1
+ <script lang="ts">
2
+ export interface PendingMutation {
3
+ id: string;
4
+ action: string;
5
+ resource: string;
6
+ timestamp: string;
7
+ }
8
+
9
+ interface Props {
10
+ isOnline?: boolean;
11
+ pendingMutations?: PendingMutation[];
12
+ formAction?: string;
13
+ class?: string;
14
+ }
15
+
16
+ let {
17
+ isOnline = true,
18
+ pendingMutations = [],
19
+ formAction = '',
20
+ class: className = '',
21
+ }: Props = $props();
22
+ </script>
23
+
24
+ {#if !isOnline || pendingMutations.length > 0}
25
+ <div class="sv-lite-sync-banner {isOnline ? 'sv-lite-sync-online' : 'sv-lite-sync-offline'} {className}">
26
+ <div class="sv-lite-sync-title">
27
+ {#if !isOnline}
28
+ <strong>Offline Connection:</strong> Changes are buffered locally.
29
+ {:else}
30
+ <strong>Online Connection:</strong> {pendingMutations.length} pending local mutations.
31
+ {/if}
32
+ </div>
33
+
34
+ {#if pendingMutations.length > 0 && formAction}
35
+ <form method="POST" action={formAction} class="sv-lite-sync-form">
36
+ <button type="submit" class="sv-lite-sync-btn">Force Sync All ({pendingMutations.length})</button>
37
+ </form>
38
+ {/if}
39
+ </div>
40
+ {/if}
41
+
42
+ <style>
43
+ .sv-lite-sync-banner {
44
+ display: block;
45
+ padding: 8px 12px;
46
+ border-radius: 4px;
47
+ font-size: 12px;
48
+ margin-bottom: 10px;
49
+ }
50
+ .sv-lite-sync-offline {
51
+ background-color: #fef3c7;
52
+ border: 1px solid #fde68a;
53
+ color: #92400e;
54
+ }
55
+ .sv-lite-sync-online {
56
+ background-color: #f1f5f9;
57
+ border: 1px solid #cbd5e1;
58
+ color: #334155;
59
+ }
60
+ .sv-lite-sync-title {
61
+ display: inline-block;
62
+ }
63
+ .sv-lite-sync-form {
64
+ float: right;
65
+ }
66
+ .sv-lite-sync-btn {
67
+ padding: 3px 8px;
68
+ font-size: 11px;
69
+ background-color: #4f46e5;
70
+ color: #ffffff;
71
+ border: none;
72
+ border-radius: 3px;
73
+ cursor: pointer;
74
+ }
75
+ </style>
@@ -0,0 +1,15 @@
1
+ export interface PendingMutation {
2
+ id: string;
3
+ action: string;
4
+ resource: string;
5
+ timestamp: string;
6
+ }
7
+ interface Props {
8
+ isOnline?: boolean;
9
+ pendingMutations?: PendingMutation[];
10
+ formAction?: string;
11
+ class?: string;
12
+ }
13
+ declare const LiteOfflineSyncBanner: import("svelte").Component<Props, {}, "">;
14
+ type LiteOfflineSyncBanner = ReturnType<typeof LiteOfflineSyncBanner>;
15
+ export default LiteOfflineSyncBanner;
@@ -0,0 +1,138 @@
1
+ <script lang="ts">
2
+ export interface DocumentStamp {
3
+ id: string;
4
+ text: string;
5
+ page: number;
6
+ status?: string;
7
+ }
8
+
9
+ interface Props {
10
+ fileUrl?: string;
11
+ fileName?: string;
12
+ totalPages?: number;
13
+ currentPage?: number;
14
+ stamps?: DocumentStamp[];
15
+ class?: string;
16
+ }
17
+
18
+ let {
19
+ fileUrl = '',
20
+ fileName = 'Document.pdf',
21
+ totalPages = 1,
22
+ currentPage = 1,
23
+ stamps = [],
24
+ class: className = '',
25
+ }: Props = $props();
26
+ </script>
27
+
28
+ <div class="sv-lite-pdf-viewer {className}">
29
+ <div class="sv-lite-pdf-header">
30
+ <strong>{fileName}</strong>
31
+ <span class="sv-lite-pdf-page">Page {currentPage} of {totalPages}</span>
32
+ {#if fileUrl}
33
+ <a href={fileUrl} download={fileName} class="sv-lite-pdf-dl">Download PDF</a>
34
+ {/if}
35
+ </div>
36
+
37
+ {#if fileUrl}
38
+ <div class="sv-lite-pdf-embed-wrapper">
39
+ <object data={fileUrl} type="application/pdf" title={fileName} class="sv-lite-pdf-object">
40
+ <p class="sv-lite-pdf-fallback">
41
+ Your browser does not support inline PDF viewing.
42
+ <a href={fileUrl} target="_blank" rel="noreferrer">Click here to open the PDF directly.</a>
43
+ </p>
44
+ </object>
45
+ </div>
46
+ {:else}
47
+ <div class="sv-lite-pdf-placeholder">
48
+ <div class="sv-lite-pdf-sheet">
49
+ <h3>{fileName}</h3>
50
+ <p>Document preview placeholder (SSR Lite mode). Provide a valid PDF URL to stream directly.</p>
51
+ {#if stamps.length > 0}
52
+ <div class="sv-lite-stamps">
53
+ <strong>Approved Stamps:</strong>
54
+ {#each stamps as s (s.id)}
55
+ <span class="sv-lite-stamp-badge">{s.text} (P.{s.page})</span>
56
+ {/each}
57
+ </div>
58
+ {/if}
59
+ </div>
60
+ </div>
61
+ {/if}
62
+ </div>
63
+
64
+ <style>
65
+ .sv-lite-pdf-viewer {
66
+ display: block;
67
+ border: 1px solid #e2e8f0;
68
+ border-radius: 6px;
69
+ padding: 12px;
70
+ background-color: #ffffff;
71
+ font-size: 12px;
72
+ }
73
+ .sv-lite-pdf-header {
74
+ margin-bottom: 10px;
75
+ font-size: 13px;
76
+ color: #0f172a;
77
+ padding-bottom: 8px;
78
+ border-bottom: 1px solid #cbd5e1;
79
+ }
80
+ .sv-lite-pdf-page {
81
+ color: #64748b;
82
+ font-size: 11px;
83
+ margin-left: 8px;
84
+ }
85
+ .sv-lite-pdf-dl {
86
+ float: right;
87
+ color: #4338ca;
88
+ text-decoration: none;
89
+ font-weight: 600;
90
+ }
91
+ .sv-lite-pdf-embed-wrapper {
92
+ width: 100%;
93
+ height: 600px;
94
+ background-color: #f8fafc;
95
+ border: 1px solid #cbd5e1;
96
+ border-radius: 4px;
97
+ }
98
+ .sv-lite-pdf-object {
99
+ width: 100%;
100
+ height: 100%;
101
+ }
102
+ .sv-lite-pdf-fallback {
103
+ padding: 20px;
104
+ text-align: center;
105
+ color: #64748b;
106
+ }
107
+ .sv-lite-pdf-placeholder {
108
+ padding: 20px;
109
+ background-color: #f8fafc;
110
+ border: 1px solid #cbd5e1;
111
+ border-radius: 4px;
112
+ text-align: center;
113
+ }
114
+ .sv-lite-pdf-sheet {
115
+ display: inline-block;
116
+ width: 400px;
117
+ background: #ffffff;
118
+ border: 1px solid #cbd5e1;
119
+ padding: 20px;
120
+ text-align: left;
121
+ box-shadow: 0 1px 3px rgba(0,0,0,0.05);
122
+ }
123
+ .sv-lite-stamps {
124
+ margin-top: 15px;
125
+ padding-top: 10px;
126
+ border-top: 1px solid #e2e8f0;
127
+ }
128
+ .sv-lite-stamp-badge {
129
+ display: inline-block;
130
+ padding: 2px 6px;
131
+ background-color: #dcfce7;
132
+ color: #166534;
133
+ border-radius: 3px;
134
+ font-size: 10px;
135
+ margin-left: 6px;
136
+ font-weight: bold;
137
+ }
138
+ </style>
@@ -0,0 +1,17 @@
1
+ export interface DocumentStamp {
2
+ id: string;
3
+ text: string;
4
+ page: number;
5
+ status?: string;
6
+ }
7
+ interface Props {
8
+ fileUrl?: string;
9
+ fileName?: string;
10
+ totalPages?: number;
11
+ currentPage?: number;
12
+ stamps?: DocumentStamp[];
13
+ class?: string;
14
+ }
15
+ declare const LitePdfDocumentViewer: import("svelte").Component<Props, {}, "">;
16
+ type LitePdfDocumentViewer = ReturnType<typeof LitePdfDocumentViewer>;
17
+ export default LitePdfDocumentViewer;
@@ -0,0 +1,163 @@
1
+ <script lang="ts">
2
+ export type AggregationFn = 'sum' | 'count' | 'avg' | 'min' | 'max';
3
+
4
+ interface Props {
5
+ data?: Record<string, unknown>[];
6
+ rowField: string;
7
+ rowLabel?: string;
8
+ columnField: string;
9
+ columnLabel?: string;
10
+ valueField: string;
11
+ valueLabel?: string;
12
+ aggregator?: AggregationFn;
13
+ class?: string;
14
+ }
15
+
16
+ let {
17
+ data = [],
18
+ rowField,
19
+ rowLabel = rowField,
20
+ columnField,
21
+ columnLabel = columnField,
22
+ valueField,
23
+ valueLabel = valueField,
24
+ aggregator = 'sum',
25
+ class: className = '',
26
+ }: Props = $props();
27
+
28
+ const distinctRowValues = $derived(
29
+ Array.from(new Set(data.map((d) => String(d[rowField] ?? '—')))).sort()
30
+ );
31
+
32
+ const distinctColValues = $derived(
33
+ Array.from(new Set(data.map((d) => String(d[columnField] ?? '—')))).sort()
34
+ );
35
+
36
+ function computeAgg(vals: number[]): number {
37
+ if (vals.length === 0) return 0;
38
+ if (aggregator === 'count') return vals.length;
39
+ if (aggregator === 'sum') return vals.reduce((a, b) => a + b, 0);
40
+ if (aggregator === 'avg') return vals.reduce((a, b) => a + b, 0) / vals.length;
41
+ if (aggregator === 'min') return Math.min(...vals);
42
+ if (aggregator === 'max') return Math.max(...vals);
43
+ return 0;
44
+ }
45
+
46
+ function getCellValue(r: string, c: string): number {
47
+ const vals = data
48
+ .filter((d) => String(d[rowField] ?? '—') === r && String(d[columnField] ?? '—') === c)
49
+ .map((d) => Number(d[valueField]) || 0);
50
+ return computeAgg(vals);
51
+ }
52
+
53
+ function getRowTotal(r: string): number {
54
+ const vals = data
55
+ .filter((d) => String(d[rowField] ?? '—') === r)
56
+ .map((d) => Number(d[valueField]) || 0);
57
+ return computeAgg(vals);
58
+ }
59
+
60
+ function getColTotal(c: string): number {
61
+ const vals = data
62
+ .filter((d) => String(d[columnField] ?? '—') === c)
63
+ .map((d) => Number(d[valueField]) || 0);
64
+ return computeAgg(vals);
65
+ }
66
+
67
+ const grandTotal = $derived(
68
+ computeAgg(data.map((d) => Number(d[valueField]) || 0))
69
+ );
70
+ </script>
71
+
72
+ <div class="sv-lite-pivot-container {className}">
73
+ <div class="sv-lite-pivot-header">
74
+ <strong>Pivot Analysis</strong> ({rowLabel} × {columnLabel})
75
+ <span class="sv-lite-pivot-sub">Aggregator: {aggregator.toUpperCase()} ({valueLabel})</span>
76
+ </div>
77
+
78
+ <table class="sv-lite-pivot-table">
79
+ <thead>
80
+ <tr>
81
+ <th class="sv-lite-th-dim">{rowLabel} \ {columnLabel}</th>
82
+ {#each distinctColValues as col (col)}
83
+ <th>{col}</th>
84
+ {/each}
85
+ <th class="sv-lite-th-total">Total</th>
86
+ </tr>
87
+ </thead>
88
+ <tbody>
89
+ {#each distinctRowValues as row (row)}
90
+ <tr>
91
+ <td class="sv-lite-td-dim">{row}</td>
92
+ {#each distinctColValues as col (col)}
93
+ {@const val = getCellValue(row, col)}
94
+ <td class="sv-lite-td-val">{val.toLocaleString(undefined, { maximumFractionDigits: 2 })}</td>
95
+ {/each}
96
+ <td class="sv-lite-td-rowtotal">{getRowTotal(row).toLocaleString(undefined, { maximumFractionDigits: 2 })}</td>
97
+ </tr>
98
+ {/each}
99
+ </tbody>
100
+ <tfoot>
101
+ <tr>
102
+ <td class="sv-lite-td-dim">Grand Total</td>
103
+ {#each distinctColValues as col (col)}
104
+ <td class="sv-lite-td-val">{getColTotal(col).toLocaleString(undefined, { maximumFractionDigits: 2 })}</td>
105
+ {/each}
106
+ <td class="sv-lite-td-grandtotal">{grandTotal.toLocaleString(undefined, { maximumFractionDigits: 2 })}</td>
107
+ </tr>
108
+ </tfoot>
109
+ </table>
110
+ </div>
111
+
112
+ <style>
113
+ .sv-lite-pivot-container {
114
+ display: block;
115
+ border: 1px solid #e2e8f0;
116
+ border-radius: 6px;
117
+ padding: 12px;
118
+ background-color: #ffffff;
119
+ font-size: 12px;
120
+ overflow-x: auto;
121
+ }
122
+ .sv-lite-pivot-header {
123
+ margin-bottom: 10px;
124
+ font-size: 13px;
125
+ color: #0f172a;
126
+ }
127
+ .sv-lite-pivot-sub {
128
+ color: #64748b;
129
+ font-size: 11px;
130
+ margin-left: 8px;
131
+ }
132
+ .sv-lite-pivot-table {
133
+ width: 100%;
134
+ border-collapse: collapse;
135
+ text-align: right;
136
+ }
137
+ .sv-lite-pivot-table th, .sv-lite-pivot-table td {
138
+ border: 1px solid #e2e8f0;
139
+ padding: 6px 10px;
140
+ }
141
+ .sv-lite-pivot-table thead {
142
+ background-color: #f8fafc;
143
+ color: #475569;
144
+ }
145
+ .sv-lite-th-dim, .sv-lite-td-dim {
146
+ text-align: left;
147
+ background-color: #f8fafc;
148
+ font-weight: 600;
149
+ }
150
+ .sv-lite-th-total, .sv-lite-td-rowtotal {
151
+ background-color: #f1f5f9;
152
+ font-weight: 600;
153
+ }
154
+ .sv-lite-pivot-table tfoot {
155
+ background-color: #f1f5f9;
156
+ font-weight: bold;
157
+ border-top: 2px solid #cbd5e1;
158
+ }
159
+ .sv-lite-td-grandtotal {
160
+ color: #4338ca;
161
+ font-weight: bold;
162
+ }
163
+ </style>
@@ -0,0 +1,15 @@
1
+ export type AggregationFn = 'sum' | 'count' | 'avg' | 'min' | 'max';
2
+ interface Props {
3
+ data?: Record<string, unknown>[];
4
+ rowField: string;
5
+ rowLabel?: string;
6
+ columnField: string;
7
+ columnLabel?: string;
8
+ valueField: string;
9
+ valueLabel?: string;
10
+ aggregator?: AggregationFn;
11
+ class?: string;
12
+ }
13
+ declare const LitePivotTable: import("svelte").Component<Props, {}, "">;
14
+ type LitePivotTable = ReturnType<typeof LitePivotTable>;
15
+ export default LitePivotTable;
@@ -0,0 +1,52 @@
1
+ <script lang="ts">
2
+ interface LitePresenceUser {
3
+ id: string;
4
+ name: string;
5
+ status?: 'online' | 'idle' | 'editing';
6
+ }
7
+
8
+ interface Props {
9
+ users?: LitePresenceUser[];
10
+ label?: string;
11
+ }
12
+
13
+ let {
14
+ users = [],
15
+ label = 'Viewing now',
16
+ }: Props = $props();
17
+ </script>
18
+
19
+ <div class="lite-presence-container">
20
+ <span class="lite-presence-label">{label}:</span>
21
+ {#each users as user (user.id)}
22
+ <span class="lite-presence-badge" title="{user.name} ({user.status ?? 'online'})">
23
+ 👤 {user.name}
24
+ </span>
25
+ {/each}
26
+ {#if users.length === 0}
27
+ <span style="color: #94a3b8; font-size: 11px;">No active viewers</span>
28
+ {/if}
29
+ </div>
30
+
31
+ <style>
32
+ .lite-presence-container {
33
+ display: inline-flex;
34
+ align-items: center;
35
+ font-size: 12px;
36
+ margin-bottom: 8px;
37
+ }
38
+ .lite-presence-label {
39
+ color: #64748b;
40
+ margin-right: 6px;
41
+ font-weight: 500;
42
+ }
43
+ .lite-presence-badge {
44
+ display: inline-block;
45
+ padding: 2px 6px;
46
+ background: #f1f5f9;
47
+ border: 1px solid #cbd5e1;
48
+ border-radius: 4px;
49
+ margin-right: 4px;
50
+ font-size: 11px;
51
+ }
52
+ </style>
@@ -0,0 +1,12 @@
1
+ interface LitePresenceUser {
2
+ id: string;
3
+ name: string;
4
+ status?: 'online' | 'idle' | 'editing';
5
+ }
6
+ interface Props {
7
+ users?: LitePresenceUser[];
8
+ label?: string;
9
+ }
10
+ declare const LitePresenceAvatarGroup: import("svelte").Component<Props, {}, "">;
11
+ type LitePresenceAvatarGroup = ReturnType<typeof LitePresenceAvatarGroup>;
12
+ export default LitePresenceAvatarGroup;