@svadmin/lite 0.25.6 → 0.26.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 (34) hide show
  1. package/README.md +11 -5
  2. package/dist/components/LiteAuditLog.svelte +102 -36
  3. package/dist/components/LiteAuditLog.svelte.d.ts +6 -3
  4. package/dist/components/LiteConcurrentConflictModal.svelte +110 -0
  5. package/dist/components/LiteConcurrentConflictModal.svelte.d.ts +21 -0
  6. package/dist/components/LiteCronExpressionBuilder.svelte +78 -0
  7. package/dist/components/LiteCronExpressionBuilder.svelte.d.ts +17 -0
  8. package/dist/components/LiteMetricGrid.svelte +143 -0
  9. package/dist/components/LiteMetricGrid.svelte.d.ts +31 -0
  10. package/dist/components/LiteMicroAppFrame.svelte +57 -0
  11. package/dist/components/LiteMicroAppFrame.svelte.d.ts +15 -0
  12. package/dist/components/LiteOrgChart.svelte +78 -0
  13. package/dist/components/LiteOrgChart.svelte.d.ts +17 -0
  14. package/dist/components/LitePresenceAvatarGroup.svelte +118 -15
  15. package/dist/components/LitePresenceAvatarGroup.svelte.d.ts +8 -3
  16. package/dist/components/LiteSafeDeleteConfirm.svelte +86 -0
  17. package/dist/components/LiteSafeDeleteConfirm.svelte.d.ts +21 -0
  18. package/dist/components/LiteSignaturePad.svelte +3 -1
  19. package/dist/components/LiteSignaturePad.svelte.d.ts +1 -0
  20. package/dist/components/LiteTaskCenter.svelte +133 -0
  21. package/dist/components/LiteTaskCenter.svelte.d.ts +24 -0
  22. package/dist/components/LiteTour.svelte +4 -0
  23. package/dist/components/LiteTour.svelte.d.ts +4 -0
  24. package/dist/components/LiteTrendIndicator.svelte +119 -0
  25. package/dist/components/LiteTrendIndicator.svelte.d.ts +19 -0
  26. package/dist/components/LiteVirtualTreeSelect.svelte +92 -0
  27. package/dist/components/LiteVirtualTreeSelect.svelte.d.ts +20 -0
  28. package/dist/components/widgets/LiteGaugeChart.svelte +50 -3
  29. package/dist/components/widgets/LiteGaugeChart.svelte.d.ts +12 -0
  30. package/dist/index.d.ts +14 -0
  31. package/dist/index.js +10 -0
  32. package/dist/rich-content.js +1 -1
  33. package/dist/server-adapter.d.ts +2 -2
  34. package/package.json +8 -8
@@ -0,0 +1,24 @@
1
+ export type LiteTaskStatus = 'running' | 'pending' | 'completed' | 'failed' | 'cancelled';
2
+ export interface LiteBackgroundTask {
3
+ id: string;
4
+ title: string;
5
+ status: LiteTaskStatus;
6
+ progress?: number;
7
+ processed?: number;
8
+ total?: number;
9
+ error?: string;
10
+ downloadHref?: string;
11
+ cancelAction?: string;
12
+ retryAction?: string;
13
+ }
14
+ interface Props {
15
+ tasks?: LiteBackgroundTask[];
16
+ title?: string;
17
+ emptyText?: string;
18
+ clearAction?: string;
19
+ clearLabel?: string;
20
+ class?: string;
21
+ }
22
+ declare const LiteTaskCenter: import("svelte").Component<Props, {}, "">;
23
+ type LiteTaskCenter = ReturnType<typeof LiteTaskCenter>;
24
+ export default LiteTaskCenter;
@@ -3,6 +3,10 @@
3
3
  title: string;
4
4
  description?: string;
5
5
  href?: string;
6
+ /** SPA target selector; ignored by the server-rendered Lite fallback. */
7
+ target?: string;
8
+ /** SPA placement; ignored by the server-rendered Lite fallback. */
9
+ placement?: 'top' | 'bottom' | 'left' | 'right';
6
10
  }
7
11
 
8
12
  interface Props {
@@ -2,6 +2,10 @@ interface TourStep {
2
2
  title: string;
3
3
  description?: string;
4
4
  href?: string;
5
+ /** SPA target selector; ignored by the server-rendered Lite fallback. */
6
+ target?: string;
7
+ /** SPA placement; ignored by the server-rendered Lite fallback. */
8
+ placement?: 'top' | 'bottom' | 'left' | 'right';
5
9
  }
6
10
  interface Props {
7
11
  steps: TourStep[];
@@ -0,0 +1,119 @@
1
+ <script lang="ts">
2
+ type TrendDirection = 'up' | 'down' | 'neutral';
3
+ type TrendVariant = 'text' | 'badge' | 'solid';
4
+ type TrendSize = 'sm' | 'md' | 'lg';
5
+
6
+ interface Props {
7
+ value?: number | string;
8
+ prevValue?: number;
9
+ direction?: TrendDirection;
10
+ prefix?: string;
11
+ suffix?: string;
12
+ precision?: number;
13
+ reverseColor?: boolean;
14
+ variant?: TrendVariant;
15
+ size?: TrendSize;
16
+ showIcon?: boolean;
17
+ class?: string;
18
+ }
19
+
20
+ let {
21
+ value,
22
+ prevValue,
23
+ direction,
24
+ prefix,
25
+ suffix = '%',
26
+ precision = 1,
27
+ reverseColor = false,
28
+ variant = 'text',
29
+ size = 'md',
30
+ showIcon = true,
31
+ class: className = '',
32
+ }: Props = $props();
33
+
34
+ const computed = $derived.by(() => {
35
+ if (typeof value === 'number' && typeof prevValue === 'number' && prevValue !== 0) {
36
+ const diff = value - prevValue;
37
+ return {
38
+ direction: direction ?? (diff > 0 ? 'up' : diff < 0 ? 'down' : 'neutral'),
39
+ text: `${Math.abs((diff / Math.abs(prevValue)) * 100).toFixed(precision)}${suffix}`,
40
+ } satisfies { direction: TrendDirection; text: string };
41
+ }
42
+ if (typeof value === 'number') {
43
+ return {
44
+ direction: direction ?? (value > 0 ? 'up' : value < 0 ? 'down' : 'neutral'),
45
+ text: `${Math.abs(value).toFixed(precision)}${suffix}`,
46
+ } satisfies { direction: TrendDirection; text: string };
47
+ }
48
+ if (typeof value === 'string') {
49
+ const text = value.trim();
50
+ const parsed = Number.parseFloat(text);
51
+ const resolved = direction ?? (text.startsWith('-') || parsed < 0
52
+ ? 'down'
53
+ : text.startsWith('+') || parsed > 0
54
+ ? 'up'
55
+ : 'neutral');
56
+ return { direction: resolved, text } satisfies { direction: TrendDirection; text: string };
57
+ }
58
+ return { direction: direction ?? 'neutral', text: '--' } satisfies {
59
+ direction: TrendDirection;
60
+ text: string;
61
+ };
62
+ });
63
+
64
+ const tone = $derived.by(() => {
65
+ if (computed.direction === 'neutral') return 'neutral';
66
+ const improving = reverseColor ? computed.direction === 'down' : computed.direction === 'up';
67
+ return improving ? 'positive' : 'negative';
68
+ });
69
+ </script>
70
+
71
+ <span
72
+ class="lite-trend lite-trend-{variant} lite-trend-{size} {className}"
73
+ data-tone={tone}
74
+ data-direction={computed.direction}
75
+ >
76
+ {#if prefix}<span class="lite-trend-prefix">{prefix}</span>{/if}
77
+ {#if showIcon}<span class="lite-trend-icon" aria-hidden="true">{computed.direction === 'up' ? '↑' : computed.direction === 'down' ? '↓' : '→'}</span>{/if}
78
+ <span class="lite-trend-value">{computed.text}</span>
79
+ <span class="lite-sr-only">{tone === 'positive' ? 'Improving' : tone === 'negative' ? 'Declining' : 'Unchanged'}</span>
80
+ </span>
81
+
82
+ <style>
83
+ .lite-trend {
84
+ display: inline-flex;
85
+ align-items: center;
86
+ color: #4e5969;
87
+ font-size: 12px;
88
+ font-weight: 600;
89
+ line-height: 1.4;
90
+ white-space: nowrap;
91
+ }
92
+ .lite-trend-sm { font-size: 11px; }
93
+ .lite-trend-lg { font-size: 14px; }
94
+ .lite-trend-badge,
95
+ .lite-trend-solid {
96
+ padding: 2px 6px;
97
+ border-radius: 999px;
98
+ border: 1px solid #e5e6eb;
99
+ background: #f7f8fa;
100
+ }
101
+ .lite-trend-solid[data-tone='positive'] { color: #fff; background: #00b42a; border-color: #00b42a; }
102
+ .lite-trend-solid[data-tone='negative'] { color: #fff; background: #f53f3f; border-color: #f53f3f; }
103
+ .lite-trend[data-tone='positive'] { color: #00b42a; }
104
+ .lite-trend[data-tone='negative'] { color: #f53f3f; }
105
+ .lite-trend[data-tone='neutral'] { color: #86909c; }
106
+ .lite-trend-prefix, .lite-trend-icon { margin-right: 4px; }
107
+ .lite-trend-prefix { color: #86909c; font-weight: 500; }
108
+ .lite-sr-only {
109
+ position: absolute;
110
+ width: 1px;
111
+ height: 1px;
112
+ padding: 0;
113
+ margin: -1px;
114
+ overflow: hidden;
115
+ clip: rect(0, 0, 0, 0);
116
+ white-space: nowrap;
117
+ border: 0;
118
+ }
119
+ </style>
@@ -0,0 +1,19 @@
1
+ type TrendDirection = 'up' | 'down' | 'neutral';
2
+ type TrendVariant = 'text' | 'badge' | 'solid';
3
+ type TrendSize = 'sm' | 'md' | 'lg';
4
+ interface Props {
5
+ value?: number | string;
6
+ prevValue?: number;
7
+ direction?: TrendDirection;
8
+ prefix?: string;
9
+ suffix?: string;
10
+ precision?: number;
11
+ reverseColor?: boolean;
12
+ variant?: TrendVariant;
13
+ size?: TrendSize;
14
+ showIcon?: boolean;
15
+ class?: string;
16
+ }
17
+ declare const LiteTrendIndicator: import("svelte").Component<Props, {}, "">;
18
+ type LiteTrendIndicator = ReturnType<typeof LiteTrendIndicator>;
19
+ export default LiteTrendIndicator;
@@ -0,0 +1,92 @@
1
+ <script lang="ts">
2
+ export interface LiteVirtualTreeNode {
3
+ value: string | number;
4
+ label: string;
5
+ children?: LiteVirtualTreeNode[];
6
+ disabled?: boolean;
7
+ }
8
+
9
+ interface Props {
10
+ nodes?: LiteVirtualTreeNode[];
11
+ value?: string | number | Array<string | number>;
12
+ name?: string;
13
+ label?: string;
14
+ placeholder?: string;
15
+ multiple?: boolean;
16
+ disabled?: boolean;
17
+ required?: boolean;
18
+ class?: string;
19
+ }
20
+
21
+ let {
22
+ nodes = [],
23
+ value = '',
24
+ name = 'value',
25
+ label = 'Tree selection',
26
+ placeholder = 'Select an option',
27
+ multiple = false,
28
+ disabled = false,
29
+ required = false,
30
+ class: className = '',
31
+ }: Props = $props();
32
+
33
+ interface FlatNode {
34
+ node: LiteVirtualTreeNode;
35
+ depth: number;
36
+ }
37
+
38
+ function flatten(items: LiteVirtualTreeNode[], depth = 0, out: FlatNode[] = []): FlatNode[] {
39
+ for (const node of items) {
40
+ out.push({ node, depth });
41
+ if (node.children?.length) flatten(node.children, depth + 1, out);
42
+ }
43
+ return out;
44
+ }
45
+
46
+ const options = $derived(flatten(nodes));
47
+ let current = $state(value);
48
+ </script>
49
+
50
+ {#snippet treeOptions()}
51
+ {#each options as option (option.node.value)}
52
+ <option value={option.node.value} disabled={option.node.disabled}>
53
+ {'\u00a0'.repeat(option.depth * 3)}{option.depth > 0 ? '└ ' : ''}{option.node.label}
54
+ </option>
55
+ {/each}
56
+ {/snippet}
57
+
58
+ <div class="lite-virtual-tree-select {className}">
59
+ <label for="lite-tree-select">{label}</label>
60
+ {#if multiple}
61
+ <select
62
+ id="lite-tree-select"
63
+ {name}
64
+ multiple
65
+ {disabled}
66
+ {required}
67
+ bind:value={current}
68
+ aria-label={label}
69
+ >
70
+ {@render treeOptions()}
71
+ </select>
72
+ {:else}
73
+ <select
74
+ id="lite-tree-select"
75
+ {name}
76
+ {disabled}
77
+ {required}
78
+ bind:value={current}
79
+ aria-label={label}
80
+ >
81
+ <option value="" disabled={required}>{placeholder}</option>
82
+ {@render treeOptions()}
83
+ </select>
84
+ {/if}
85
+ <p class="lite-virtual-tree-help">Large-tree search and virtual scrolling are client-side enhancements.</p>
86
+ </div>
87
+
88
+ <style>
89
+ .lite-virtual-tree-select label { display: block; margin-bottom: 4px; color: #4e5969; font-size: 12px; font-weight: 600; }
90
+ .lite-virtual-tree-select select { width: 100%; box-sizing: border-box; min-height: 34px; padding: 6px 8px; border: 1px solid #e5e6eb; border-radius: 4px; background: #fff; color: #1d2129; font: inherit; font-size: 13px; }
91
+ .lite-virtual-tree-help { margin: 4px 0 0; color: #86909c; font-size: 11px; }
92
+ </style>
@@ -0,0 +1,20 @@
1
+ export interface LiteVirtualTreeNode {
2
+ value: string | number;
3
+ label: string;
4
+ children?: LiteVirtualTreeNode[];
5
+ disabled?: boolean;
6
+ }
7
+ interface Props {
8
+ nodes?: LiteVirtualTreeNode[];
9
+ value?: string | number | Array<string | number>;
10
+ name?: string;
11
+ label?: string;
12
+ placeholder?: string;
13
+ multiple?: boolean;
14
+ disabled?: boolean;
15
+ required?: boolean;
16
+ class?: string;
17
+ }
18
+ declare const LiteVirtualTreeSelect: import("svelte").Component<Props, {}, "">;
19
+ type LiteVirtualTreeSelect = ReturnType<typeof LiteVirtualTreeSelect>;
20
+ export default LiteVirtualTreeSelect;
@@ -2,19 +2,66 @@
2
2
  import LiteChartSvg from './LiteChartSvg.svelte';
3
3
  import LiteChartTable from './LiteChartTable.svelte';
4
4
 
5
+ interface GaugeThreshold {
6
+ value: number;
7
+ label?: string;
8
+ }
9
+
5
10
  interface Props {
6
11
  title?: string;
12
+ label?: string;
13
+ value?: number;
14
+ min?: number;
15
+ max?: number;
16
+ unit?: string;
17
+ target?: number;
18
+ targetLabel?: string;
19
+ thresholds?: GaugeThreshold[];
7
20
  data?: Array<Record<string, unknown>>;
8
21
  rows?: Array<Array<string | number>>;
9
22
  columns?: string[];
10
23
  emptyText?: string;
11
24
  }
12
25
 
13
- let { title, data = [], rows, columns, emptyText }: Props = $props();
14
- const normalized = $derived(rows ?? data.map((item) => Object.values(item).map((value) => typeof value === 'number' ? value : String(value ?? ''))));
26
+ let {
27
+ title,
28
+ label,
29
+ value,
30
+ min = 0,
31
+ max = 100,
32
+ unit = '',
33
+ target,
34
+ targetLabel = 'Target',
35
+ thresholds = [],
36
+ data = [],
37
+ rows,
38
+ columns,
39
+ emptyText,
40
+ }: Props = $props();
41
+
42
+ const heading = $derived(title ?? label ?? 'Gauge');
43
+
44
+ const normalized = $derived.by(() => {
45
+ if (rows) return rows;
46
+ if (data.length > 0) {
47
+ return data.map((item) => Object.values(item).map((entry) =>
48
+ typeof entry === 'number' ? entry : String(entry ?? '')
49
+ ));
50
+ }
51
+ if (typeof value !== 'number') return [];
52
+ const metricRows: Array<Array<string | number>> = [
53
+ ['Value', `${value}${unit}`],
54
+ ['Range', `${min} – ${max}${unit}`],
55
+ ];
56
+ if (typeof target === 'number') metricRows.push([targetLabel, `${target}${unit}`]);
57
+ for (const threshold of thresholds) {
58
+ metricRows.push([threshold.label ?? 'Threshold', `${threshold.value}${unit}`]);
59
+ }
60
+ return metricRows;
61
+ });
15
62
  </script>
16
63
 
17
64
  <div class="lite-chart-widget">
18
- <LiteChartSvg kind="gauge" rows={normalized} {title} />
65
+ <LiteChartSvg kind="gauge" rows={normalized} title={heading} />
19
66
  <LiteChartTable {title} kind="gauge" columns={columns ?? ['Label', 'Value']} rows={normalized} {emptyText} />
20
67
  </div>
@@ -1,5 +1,17 @@
1
+ interface GaugeThreshold {
2
+ value: number;
3
+ label?: string;
4
+ }
1
5
  interface Props {
2
6
  title?: string;
7
+ label?: string;
8
+ value?: number;
9
+ min?: number;
10
+ max?: number;
11
+ unit?: string;
12
+ target?: number;
13
+ targetLabel?: string;
14
+ thresholds?: GaugeThreshold[];
3
15
  data?: Array<Record<string, unknown>>;
4
16
  rows?: Array<Array<string | number>>;
5
17
  columns?: string[];
package/dist/index.d.ts CHANGED
@@ -111,6 +111,20 @@ export { default as LiteSkeleton } from './components/LiteSkeleton.svelte';
111
111
  export { default as LiteSpin } from './components/LiteSpin.svelte';
112
112
  export { default as LiteProgress } from './components/LiteProgress.svelte';
113
113
  export { default as LiteTree } from './components/LiteTree.svelte';
114
+ export { default as LiteTrendIndicator } from './components/LiteTrendIndicator.svelte';
115
+ export { default as LiteMetricGrid } from './components/LiteMetricGrid.svelte';
116
+ export { default as LiteOrgChart } from './components/LiteOrgChart.svelte';
117
+ export { default as LiteCronExpressionBuilder } from './components/LiteCronExpressionBuilder.svelte';
118
+ export { default as LiteVirtualTreeSelect } from './components/LiteVirtualTreeSelect.svelte';
119
+ export { default as LiteConcurrentConflictModal } from './components/LiteConcurrentConflictModal.svelte';
120
+ export { default as LiteTaskCenter } from './components/LiteTaskCenter.svelte';
121
+ export { default as LiteMicroAppFrame } from './components/LiteMicroAppFrame.svelte';
122
+ export { default as LiteSafeDeleteConfirm } from './components/LiteSafeDeleteConfirm.svelte';
123
+ export type { LiteOrgNode } from './components/LiteOrgChart.svelte';
124
+ export type { LiteVirtualTreeNode } from './components/LiteVirtualTreeSelect.svelte';
125
+ export type { LiteConflictField } from './components/LiteConcurrentConflictModal.svelte';
126
+ export type { LiteTaskStatus, LiteBackgroundTask } from './components/LiteTaskCenter.svelte';
127
+ export type { LiteDeleteImpact } from './components/LiteSafeDeleteConfirm.svelte';
114
128
  export { configureMarkdownRenderer, escapeHtml, renderInlineMarkdown, renderMarkdown, renderRichContent, sanitizeRichTextHtml, } from './rich-content';
115
129
  export type { MarkdownRenderOptions } from './rich-content';
116
130
  export * from './components/compatibility/index';
package/dist/index.js CHANGED
@@ -115,6 +115,16 @@ export { default as LiteSkeleton } from './components/LiteSkeleton.svelte';
115
115
  export { default as LiteSpin } from './components/LiteSpin.svelte';
116
116
  export { default as LiteProgress } from './components/LiteProgress.svelte';
117
117
  export { default as LiteTree } from './components/LiteTree.svelte';
118
+ // Recent enterprise UI parity fallbacks
119
+ export { default as LiteTrendIndicator } from './components/LiteTrendIndicator.svelte';
120
+ export { default as LiteMetricGrid } from './components/LiteMetricGrid.svelte';
121
+ export { default as LiteOrgChart } from './components/LiteOrgChart.svelte';
122
+ export { default as LiteCronExpressionBuilder } from './components/LiteCronExpressionBuilder.svelte';
123
+ export { default as LiteVirtualTreeSelect } from './components/LiteVirtualTreeSelect.svelte';
124
+ export { default as LiteConcurrentConflictModal } from './components/LiteConcurrentConflictModal.svelte';
125
+ export { default as LiteTaskCenter } from './components/LiteTaskCenter.svelte';
126
+ export { default as LiteMicroAppFrame } from './components/LiteMicroAppFrame.svelte';
127
+ export { default as LiteSafeDeleteConfirm } from './components/LiteSafeDeleteConfirm.svelte';
118
128
  // Rich content rendering (SSR-safe Markdown + rich HTML allowlist)
119
129
  export { configureMarkdownRenderer, escapeHtml, renderInlineMarkdown, renderMarkdown, renderRichContent, sanitizeRichTextHtml, } from './rich-content';
120
130
  // Compatibility fallbacks for browser-only and third-party UI capabilities.
@@ -458,7 +458,7 @@ export function sanitizeRichTextHtml(value) {
458
458
  }
459
459
  const attributes = [];
460
460
  const isCheckbox = tag === 'input' && /type\s*=\s*["']?checkbox/iu.test(match[2] ?? '');
461
- if ((tag === 'input' && !isCheckbox) || (tag === 'input' && closing))
461
+ if (tag === 'input' && !isCheckbox)
462
462
  continue;
463
463
  for (const attribute of (match[2] ?? '').matchAll(ATTRIBUTE)) {
464
464
  const name = (attribute[1] ?? '').toLowerCase();
@@ -126,10 +126,10 @@ export declare function createAuthActions(authProvider: AuthProvider): {
126
126
  }>;
127
127
  register: ({ request }: RequestEvent) => Promise<{
128
128
  success: boolean;
129
- error: string;
129
+ error?: never;
130
130
  } | {
131
131
  success: boolean;
132
- error?: never;
132
+ error: string;
133
133
  }>;
134
134
  forgot_password: ({ request }: RequestEvent) => Promise<{
135
135
  success: boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@svadmin/lite",
3
- "version": "0.25.6",
3
+ "version": "0.26.0",
4
4
  "description": "SSR-first lightweight admin UI for @svadmin with optional progressive enhancement",
5
5
  "type": "module",
6
6
  "sideEffects": [
@@ -56,23 +56,23 @@
56
56
  },
57
57
  "peerDependencies": {
58
58
  "svelte": "^5.57.1",
59
- "@svadmin/core": ">=0.34.2 <0.80.0",
59
+ "@svadmin/core": ">=0.34.2 <0.81.0",
60
60
  "@sveltejs/kit": "^3.0.0",
61
61
  "qrcode": "^1.5.4"
62
62
  },
63
63
  "dependencies": {
64
- "@lucide/svelte": "^1.35.0",
65
- "typebox": "^1.3.34"
64
+ "@lucide/svelte": "^1.71.0",
65
+ "typebox": "^1.4.6"
66
66
  },
67
67
  "devDependencies": {
68
- "@happy-dom/global-registrator": "^20.11.12",
69
- "@sveltejs/vite-plugin-svelte": "^7.3.0",
70
- "@testing-library/dom": "^10.4.1",
68
+ "@happy-dom/global-registrator": "^21.0.4",
69
+ "@sveltejs/vite-plugin-svelte": "^7.3.1",
70
+ "@testing-library/dom": "^10.4.2",
71
71
  "@testing-library/svelte": "^5.4.2",
72
72
  "@types/bun": "^1.4.4",
73
73
  "@types/qrcode": "^1.5.6",
74
74
  "happy-dom": "^21.0.4",
75
- "vitest": "^4.1.11"
75
+ "vitest": "^5.1.0"
76
76
  },
77
77
  "scripts": {
78
78
  "build": "node ../../node_modules/@sveltejs/package/svelte-package.js -i src -o dist && bun ../../scripts/copy-package-assets.ts static/enhance.js dist/enhance.js",