@svadmin/ui 0.64.0 → 0.65.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 (73) hide show
  1. package/dist/components/ActivityFeed.svelte +135 -0
  2. package/dist/components/ActivityFeed.svelte.d.ts +21 -0
  3. package/dist/components/ApprovalActionCard.svelte +248 -0
  4. package/dist/components/ApprovalActionCard.svelte.d.ts +22 -0
  5. package/dist/components/CanvasAnnotation.svelte +389 -0
  6. package/dist/components/CanvasAnnotation.svelte.d.ts +17 -0
  7. package/dist/components/ColumnHeaderFilter.svelte +178 -0
  8. package/dist/components/ColumnHeaderFilter.svelte.d.ts +12 -0
  9. package/dist/components/ColumnSettings.svelte +198 -0
  10. package/dist/components/ColumnSettings.svelte.d.ts +19 -0
  11. package/dist/components/DecisionTable.svelte +240 -0
  12. package/dist/components/DecisionTable.svelte.d.ts +22 -0
  13. package/dist/components/DraggableRowTable.svelte +117 -0
  14. package/dist/components/DraggableRowTable.svelte.d.ts +15 -0
  15. package/dist/components/DrawerForm.svelte +110 -26
  16. package/dist/components/DrawerForm.svelte.d.ts +20 -8
  17. package/dist/components/EditableTable.svelte +195 -0
  18. package/dist/components/EditableTable.svelte.d.ts +25 -0
  19. package/dist/components/GanttChart.svelte +144 -0
  20. package/dist/components/GanttChart.svelte.d.ts +20 -0
  21. package/dist/components/ImageCropper.svelte +122 -0
  22. package/dist/components/ImageCropper.svelte.d.ts +12 -0
  23. package/dist/components/ImportWizard.svelte +336 -0
  24. package/dist/components/ImportWizard.svelte.d.ts +12 -0
  25. package/dist/components/JsonSchemaForm.svelte +149 -0
  26. package/dist/components/JsonSchemaForm.svelte.d.ts +10 -0
  27. package/dist/components/KanbanBoard.svelte +212 -0
  28. package/dist/components/KanbanBoard.svelte.d.ts +30 -0
  29. package/dist/components/MasterDetailView.svelte +134 -0
  30. package/dist/components/MasterDetailView.svelte.d.ts +16 -0
  31. package/dist/components/MediaLibraryModal.svelte +195 -0
  32. package/dist/components/MediaLibraryModal.svelte.d.ts +22 -0
  33. package/dist/components/MentionsInput.svelte +174 -0
  34. package/dist/components/MentionsInput.svelte.d.ts +21 -0
  35. package/dist/components/ModalForm.svelte +113 -28
  36. package/dist/components/ModalForm.svelte.d.ts +20 -8
  37. package/dist/components/MultiTabKeepAlive.svelte +143 -0
  38. package/dist/components/MultiTabKeepAlive.svelte.d.ts +21 -0
  39. package/dist/components/OfflineSyncBanner.svelte +172 -0
  40. package/dist/components/OfflineSyncBanner.svelte.d.ts +19 -0
  41. package/dist/components/PdfDocumentViewer.svelte +255 -0
  42. package/dist/components/PdfDocumentViewer.svelte.d.ts +22 -0
  43. package/dist/components/PivotTable.svelte +140 -0
  44. package/dist/components/PivotTable.svelte.d.ts +16 -0
  45. package/dist/components/PresenceAvatarGroup.svelte +89 -0
  46. package/dist/components/PresenceAvatarGroup.svelte.d.ts +15 -0
  47. package/dist/components/PrintableBill.svelte +149 -0
  48. package/dist/components/PrintableBill.svelte.d.ts +24 -0
  49. package/dist/components/SensitiveDataMask.svelte +125 -0
  50. package/dist/components/SensitiveDataMask.svelte.d.ts +13 -0
  51. package/dist/components/SignaturePad.svelte +264 -0
  52. package/dist/components/SignaturePad.svelte.d.ts +17 -0
  53. package/dist/components/SplitPaneLayout.svelte +113 -0
  54. package/dist/components/SplitPaneLayout.svelte.d.ts +13 -0
  55. package/dist/components/SpreadsheetView.svelte +363 -0
  56. package/dist/components/SpreadsheetView.svelte.d.ts +18 -0
  57. package/dist/components/StepForm.svelte +180 -0
  58. package/dist/components/StepForm.svelte.d.ts +23 -0
  59. package/dist/components/TableSummary.svelte +110 -0
  60. package/dist/components/TableSummary.svelte.d.ts +20 -0
  61. package/dist/components/TreeTable.svelte +237 -0
  62. package/dist/components/TreeTable.svelte.d.ts +28 -0
  63. package/dist/components/UnsavedChangesPrompt.svelte +44 -0
  64. package/dist/components/UnsavedChangesPrompt.svelte.d.ts +7 -0
  65. package/dist/components/VersionDiffViewer.svelte +196 -0
  66. package/dist/components/VersionDiffViewer.svelte.d.ts +12 -0
  67. package/dist/components/VirtualTable.svelte +86 -54
  68. package/dist/components/VirtualTable.svelte.d.ts +14 -21
  69. package/dist/components/Watermark.svelte +166 -0
  70. package/dist/components/Watermark.svelte.d.ts +26 -0
  71. package/dist/index.d.ts +58 -3
  72. package/dist/index.js +35 -3
  73. package/package.json +2 -2
@@ -0,0 +1,135 @@
1
+ <script lang="ts">
2
+ import { Button } from './ui/button/index.js';
3
+ import { Badge } from './ui/badge/index.js';
4
+ import { Send, Clock, MessageSquare, Loader2 } from '@lucide/svelte';
5
+ import { cn } from '../utils.js';
6
+
7
+ export interface ActivityItem {
8
+ id: string;
9
+ user?: {
10
+ name: string;
11
+ avatar?: string;
12
+ };
13
+ action: string;
14
+ target?: string;
15
+ timestamp: string;
16
+ comment?: string;
17
+ status?: 'info' | 'success' | 'warning' | 'destructive';
18
+ }
19
+
20
+ interface Props {
21
+ activities?: ActivityItem[];
22
+ onaddcomment?: (comment: string) => void | Promise<void>;
23
+ allowComment?: boolean;
24
+ class?: string;
25
+ }
26
+
27
+ let {
28
+ activities = [],
29
+ onaddcomment,
30
+ allowComment = true,
31
+ class: className = '',
32
+ }: Props = $props();
33
+
34
+ let newComment = $state('');
35
+ let isSending = $state(false);
36
+
37
+ async function handleSend() {
38
+ if (!newComment.trim()) return;
39
+ isSending = true;
40
+ try {
41
+ await onaddcomment?.(newComment.trim());
42
+ newComment = '';
43
+ } finally {
44
+ isSending = false;
45
+ }
46
+ }
47
+
48
+ function getStatusBadge(status?: ActivityItem['status']) {
49
+ switch (status) {
50
+ case 'success':
51
+ return 'bg-success/15 text-success border-success/20';
52
+ case 'warning':
53
+ return 'bg-warning/15 text-warning-foreground border-warning/20';
54
+ case 'destructive':
55
+ return 'bg-destructive/15 text-destructive border-destructive/20';
56
+ default:
57
+ return 'bg-primary/10 text-primary border-primary/20';
58
+ }
59
+ }
60
+ </script>
61
+
62
+ <div class={cn('space-y-4 rounded-xl border border-border bg-card p-4 shadow-xs text-xs', className)}>
63
+ <div class="flex items-center justify-between pb-2 border-b border-border/60">
64
+ <h4 class="font-semibold text-foreground flex items-center gap-1.5">
65
+ <MessageSquare class="h-4 w-4 text-primary" />
66
+ Activity & Timeline Stream
67
+ </h4>
68
+ <span class="text-muted-foreground text-[11px]">{activities.length} event{activities.length === 1 ? '' : 's'}</span>
69
+ </div>
70
+
71
+ <!-- Timeline Stream -->
72
+ <div class="relative space-y-4 pl-4 before:absolute before:left-1.5 before:top-2 before:bottom-2 before:w-0.5 before:bg-border/60">
73
+ {#each activities as act (act.id)}
74
+ <div class="relative flex items-start gap-3 group">
75
+ <!-- Bullet dot -->
76
+ <div class="absolute -left-4 top-1 h-3 w-3 rounded-full border-2 border-card bg-primary ring-2 ring-primary/20"></div>
77
+
78
+ <div class="flex-1 space-y-1.5">
79
+ <div class="flex flex-wrap items-center justify-between gap-1">
80
+ <div class="flex items-center gap-1.5">
81
+ <span class="font-semibold text-foreground">{act.user?.name ?? 'System'}</span>
82
+ <span class="text-muted-foreground">{act.action}</span>
83
+ {#if act.target}
84
+ <strong class="text-foreground">{act.target}</strong>
85
+ {/if}
86
+ {#if act.status}
87
+ <Badge variant="outline" class={cn('text-[10px] px-1 py-0', getStatusBadge(act.status))}>
88
+ {act.status}
89
+ </Badge>
90
+ {/if}
91
+ </div>
92
+
93
+ <div class="flex items-center gap-1 text-[11px] text-muted-foreground">
94
+ <Clock class="h-3 w-3" />
95
+ <span>{act.timestamp}</span>
96
+ </div>
97
+ </div>
98
+
99
+ {#if act.comment}
100
+ <div class="rounded-lg bg-muted/40 p-2.5 text-foreground border border-border/40 font-normal">
101
+ {act.comment}
102
+ </div>
103
+ {/if}
104
+ </div>
105
+ </div>
106
+ {/each}
107
+
108
+ {#if activities.length === 0}
109
+ <div class="py-6 text-center text-muted-foreground">
110
+ No recent activities
111
+ </div>
112
+ {/if}
113
+ </div>
114
+
115
+ <!-- Add Comment Box -->
116
+ {#if allowComment && onaddcomment}
117
+ <div class="pt-3 border-t border-border/60 space-y-2">
118
+ <textarea
119
+ bind:value={newComment}
120
+ placeholder="Add an internal comment or update note..."
121
+ class="w-full h-16 rounded-md border border-input bg-background p-2 text-xs focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
122
+ ></textarea>
123
+ <div class="flex justify-end">
124
+ <Button size="sm" class="h-7 text-xs gap-1" disabled={isSending || !newComment.trim()} onclick={handleSend}>
125
+ {#if isSending}
126
+ <Loader2 class="h-3.5 w-3.5 animate-spin" />
127
+ {:else}
128
+ <Send class="h-3.5 w-3.5" />
129
+ {/if}
130
+ Send Comment
131
+ </Button>
132
+ </div>
133
+ </div>
134
+ {/if}
135
+ </div>
@@ -0,0 +1,21 @@
1
+ export interface ActivityItem {
2
+ id: string;
3
+ user?: {
4
+ name: string;
5
+ avatar?: string;
6
+ };
7
+ action: string;
8
+ target?: string;
9
+ timestamp: string;
10
+ comment?: string;
11
+ status?: 'info' | 'success' | 'warning' | 'destructive';
12
+ }
13
+ interface Props {
14
+ activities?: ActivityItem[];
15
+ onaddcomment?: (comment: string) => void | Promise<void>;
16
+ allowComment?: boolean;
17
+ class?: string;
18
+ }
19
+ declare const ActivityFeed: import("svelte").Component<Props, {}, "">;
20
+ type ActivityFeed = ReturnType<typeof ActivityFeed>;
21
+ export default ActivityFeed;
@@ -0,0 +1,248 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import { Check, X, ArrowRightLeft, Clock, CheckCircle2, XCircle, Undo2 } from '@lucide/svelte';
4
+ import { Button } from './ui/button/index.js';
5
+ import { Badge } from './ui/badge/index.js';
6
+ import * as Dialog from './ui/dialog/index.js';
7
+ import { cn } from '../utils.js';
8
+
9
+ export type ApprovalStatus = 'pending' | 'approved' | 'rejected' | 'recalled';
10
+
11
+ interface ApplicantInfo {
12
+ name: string;
13
+ avatar?: string;
14
+ department?: string;
15
+ time?: string;
16
+ }
17
+
18
+ interface Props {
19
+ title?: string;
20
+ status?: ApprovalStatus;
21
+ applicant?: ApplicantInfo;
22
+ onapprove?: (comment?: string) => void | Promise<void>;
23
+ onreject?: (reason: string) => void | Promise<void>;
24
+ ontransfer?: (targetUser: string, note?: string) => void | Promise<void>;
25
+ disabled?: boolean;
26
+ class?: string;
27
+ children?: Snippet;
28
+ }
29
+
30
+ let {
31
+ title = 'Approval Request',
32
+ status = 'pending',
33
+ applicant,
34
+ onapprove,
35
+ onreject,
36
+ ontransfer,
37
+ disabled = false,
38
+ class: className = '',
39
+ children,
40
+ }: Props = $props();
41
+
42
+ let approveDialogOpen = $state(false);
43
+ let rejectDialogOpen = $state(false);
44
+ let transferDialogOpen = $state(false);
45
+
46
+ let approveComment = $state('');
47
+ let rejectReason = $state('');
48
+ let transferTarget = $state('');
49
+ let isSubmitting = $state(false);
50
+
51
+ const statusConfig = $derived.by(() => {
52
+ switch (status) {
53
+ case 'approved':
54
+ return { label: 'Approved', badge: 'bg-success/15 text-success border-success/20', icon: CheckCircle2 };
55
+ case 'rejected':
56
+ return { label: 'Rejected', badge: 'bg-destructive/15 text-destructive border-destructive/20', icon: XCircle };
57
+ case 'recalled':
58
+ return { label: 'Recalled', badge: 'bg-muted text-muted-foreground border-border', icon: Undo2 };
59
+ default:
60
+ return { label: 'Pending Approval', badge: 'bg-warning/15 text-warning-foreground border-warning/20', icon: Clock };
61
+ }
62
+ });
63
+
64
+ async function handleApprove() {
65
+ isSubmitting = true;
66
+ try {
67
+ await onapprove?.(approveComment || undefined);
68
+ approveDialogOpen = false;
69
+ approveComment = '';
70
+ } finally {
71
+ isSubmitting = false;
72
+ }
73
+ }
74
+
75
+ async function handleReject() {
76
+ if (!rejectReason.trim()) return;
77
+ isSubmitting = true;
78
+ try {
79
+ await onreject?.(rejectReason);
80
+ rejectDialogOpen = false;
81
+ rejectReason = '';
82
+ } finally {
83
+ isSubmitting = false;
84
+ }
85
+ }
86
+
87
+ async function handleTransfer() {
88
+ if (!transferTarget.trim()) return;
89
+ isSubmitting = true;
90
+ try {
91
+ await ontransfer?.(transferTarget);
92
+ transferDialogOpen = false;
93
+ transferTarget = '';
94
+ } finally {
95
+ isSubmitting = false;
96
+ }
97
+ }
98
+ </script>
99
+
100
+ <div class={cn('rounded-xl border border-border bg-card p-4 shadow-xs space-y-3', className)}>
101
+ <div class="flex flex-wrap items-center justify-between gap-2 pb-3 border-b border-border/50">
102
+ <div class="space-y-0.5">
103
+ <div class="flex items-center gap-2">
104
+ <h4 class="text-sm font-semibold text-foreground">{title}</h4>
105
+ <Badge variant="outline" class={cn('gap-1 text-[11px] font-medium', statusConfig.badge)}>
106
+ <statusConfig.icon class="h-3 w-3" />
107
+ {statusConfig.label}
108
+ </Badge>
109
+ </div>
110
+ {#if applicant}
111
+ <p class="text-xs text-muted-foreground">
112
+ Submitted by <strong class="text-foreground">{applicant.name}</strong>
113
+ {#if applicant.department} ({applicant.department}){/if}
114
+ {#if applicant.time} · {applicant.time}{/if}
115
+ </p>
116
+ {/if}
117
+ </div>
118
+
119
+ {#if status === 'pending'}
120
+ <div class="flex items-center gap-1.5">
121
+ {#if ontransfer}
122
+ <Button
123
+ variant="outline"
124
+ size="sm"
125
+ class="h-8 text-xs gap-1 text-muted-foreground"
126
+ {disabled}
127
+ onclick={() => { transferDialogOpen = true; }}
128
+ >
129
+ <ArrowRightLeft class="h-3.5 w-3.5" />
130
+ Transfer
131
+ </Button>
132
+ {/if}
133
+
134
+ {#if onreject}
135
+ <Button
136
+ variant="outline"
137
+ size="sm"
138
+ class="h-8 text-xs gap-1 border-destructive/30 text-destructive hover:bg-destructive/10"
139
+ {disabled}
140
+ onclick={() => { rejectDialogOpen = true; }}
141
+ >
142
+ <X class="h-3.5 w-3.5" />
143
+ Reject
144
+ </Button>
145
+ {/if}
146
+
147
+ {#if onapprove}
148
+ <Button
149
+ size="sm"
150
+ class="h-8 text-xs gap-1 bg-success hover:bg-success/90 text-success-foreground"
151
+ {disabled}
152
+ onclick={() => { approveDialogOpen = true; }}
153
+ >
154
+ <Check class="h-3.5 w-3.5" />
155
+ Approve
156
+ </Button>
157
+ {/if}
158
+ </div>
159
+ {/if}
160
+ </div>
161
+
162
+ {#if children}
163
+ <div class="text-xs text-foreground space-y-2">
164
+ {@render children()}
165
+ </div>
166
+ {/if}
167
+ </div>
168
+
169
+ <!-- Approve Dialog -->
170
+ {#if approveDialogOpen}
171
+ <Dialog.Dialog bind:open={approveDialogOpen}>
172
+ <Dialog.DialogContent class="sm:max-w-md">
173
+ <Dialog.DialogHeader>
174
+ <Dialog.DialogTitle>Confirm Approval</Dialog.DialogTitle>
175
+ </Dialog.DialogHeader>
176
+ <div class="space-y-3 py-2 text-xs">
177
+ <label class="block font-medium text-foreground" for="approve_comment_input">Optional Approval Comment</label>
178
+ <textarea
179
+ id="approve_comment_input"
180
+ bind:value={approveComment}
181
+ placeholder="e.g. Verified and approved"
182
+ class="w-full h-20 rounded-md border border-input bg-background p-2 text-xs focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
183
+ ></textarea>
184
+ </div>
185
+ <Dialog.DialogFooter>
186
+ <Button variant="outline" size="sm" onclick={() => { approveDialogOpen = false; }}>Cancel</Button>
187
+ <Button size="sm" class="bg-success hover:bg-success/90 text-success-foreground" disabled={isSubmitting} onclick={handleApprove}>
188
+ Confirm Approval
189
+ </Button>
190
+ </Dialog.DialogFooter>
191
+ </Dialog.DialogContent>
192
+ </Dialog.Dialog>
193
+ {/if}
194
+
195
+ <!-- Reject Dialog -->
196
+ {#if rejectDialogOpen}
197
+ <Dialog.Dialog bind:open={rejectDialogOpen}>
198
+ <Dialog.DialogContent class="sm:max-w-md">
199
+ <Dialog.DialogHeader>
200
+ <Dialog.DialogTitle class="text-destructive">Reject Request</Dialog.DialogTitle>
201
+ </Dialog.DialogHeader>
202
+ <div class="space-y-3 py-2 text-xs">
203
+ <label class="block font-medium text-foreground" for="reject_reason_input">Rejection Reason (Required)</label>
204
+ <textarea
205
+ id="reject_reason_input"
206
+ bind:value={rejectReason}
207
+ placeholder="Please explain why this request is being rejected..."
208
+ class="w-full h-20 rounded-md border border-destructive/40 bg-background p-2 text-xs focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-destructive"
209
+ required
210
+ ></textarea>
211
+ </div>
212
+ <Dialog.DialogFooter>
213
+ <Button variant="outline" size="sm" onclick={() => { rejectDialogOpen = false; }}>Cancel</Button>
214
+ <Button variant="destructive" size="sm" disabled={isSubmitting || !rejectReason.trim()} onclick={handleReject}>
215
+ Confirm Rejection
216
+ </Button>
217
+ </Dialog.DialogFooter>
218
+ </Dialog.DialogContent>
219
+ </Dialog.Dialog>
220
+ {/if}
221
+
222
+ <!-- Transfer Dialog -->
223
+ {#if transferDialogOpen}
224
+ <Dialog.Dialog bind:open={transferDialogOpen}>
225
+ <Dialog.DialogContent class="sm:max-w-md">
226
+ <Dialog.DialogHeader>
227
+ <Dialog.DialogTitle>Transfer Approval</Dialog.DialogTitle>
228
+ </Dialog.DialogHeader>
229
+ <div class="space-y-3 py-2 text-xs">
230
+ <label class="block font-medium text-foreground" for="transfer_target_input">Target Approver Email / Username</label>
231
+ <input
232
+ id="transfer_target_input"
233
+ type="text"
234
+ bind:value={transferTarget}
235
+ placeholder="e.g. manager@example.com"
236
+ class="w-full h-8 rounded-md border border-input bg-background px-2 text-xs focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
237
+ required
238
+ />
239
+ </div>
240
+ <Dialog.DialogFooter>
241
+ <Button variant="outline" size="sm" onclick={() => { transferDialogOpen = false; }}>Cancel</Button>
242
+ <Button size="sm" disabled={isSubmitting || !transferTarget.trim()} onclick={handleTransfer}>
243
+ Transfer
244
+ </Button>
245
+ </Dialog.DialogFooter>
246
+ </Dialog.DialogContent>
247
+ </Dialog.Dialog>
248
+ {/if}
@@ -0,0 +1,22 @@
1
+ import type { Snippet } from 'svelte';
2
+ export type ApprovalStatus = 'pending' | 'approved' | 'rejected' | 'recalled';
3
+ interface ApplicantInfo {
4
+ name: string;
5
+ avatar?: string;
6
+ department?: string;
7
+ time?: string;
8
+ }
9
+ interface Props {
10
+ title?: string;
11
+ status?: ApprovalStatus;
12
+ applicant?: ApplicantInfo;
13
+ onapprove?: (comment?: string) => void | Promise<void>;
14
+ onreject?: (reason: string) => void | Promise<void>;
15
+ ontransfer?: (targetUser: string, note?: string) => void | Promise<void>;
16
+ disabled?: boolean;
17
+ class?: string;
18
+ children?: Snippet;
19
+ }
20
+ declare const ApprovalActionCard: import("svelte").Component<Props, {}, "">;
21
+ type ApprovalActionCard = ReturnType<typeof ApprovalActionCard>;
22
+ export default ApprovalActionCard;