@joewinke/jatui 0.1.24 → 0.1.26

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 (242) hide show
  1. package/dist/actions/collapse.d.ts +80 -0
  2. package/dist/actions/collapse.js +134 -0
  3. package/dist/actions/railNav.d.ts +167 -0
  4. package/dist/actions/railNav.js +351 -0
  5. package/dist/components/AnnotationLayer.svelte +108 -0
  6. package/dist/components/AnnotationLayer.svelte.d.ts +12 -0
  7. package/dist/components/AnnotationPanel.svelte +319 -0
  8. package/dist/components/AnnotationPanel.svelte.d.ts +13 -0
  9. package/dist/components/AudioWaveform.svelte +701 -0
  10. package/dist/components/AudioWaveform.svelte.d.ts +19 -0
  11. package/dist/components/AvailabilityModal.svelte +177 -0
  12. package/dist/components/AvailabilityModal.svelte.d.ts +14 -0
  13. package/dist/components/AvatarUpload.svelte +234 -0
  14. package/dist/components/AvatarUpload.svelte.d.ts +23 -0
  15. package/dist/components/Badge.svelte +38 -0
  16. package/dist/components/Badge.svelte.d.ts +14 -0
  17. package/dist/components/BookingForm.svelte +278 -0
  18. package/dist/components/BookingForm.svelte.d.ts +37 -0
  19. package/dist/components/BurndownChart.svelte +778 -0
  20. package/dist/components/BurndownChart.svelte.d.ts +17 -0
  21. package/dist/components/Button.svelte +81 -0
  22. package/dist/components/Button.svelte.d.ts +21 -0
  23. package/dist/components/CalendarPicker.svelte +284 -0
  24. package/dist/components/CalendarPicker.svelte.d.ts +22 -0
  25. package/dist/components/Card.svelte +67 -0
  26. package/dist/components/Card.svelte.d.ts +17 -0
  27. package/dist/components/CharacterCounter.svelte +82 -0
  28. package/dist/components/CharacterCounter.svelte.d.ts +10 -0
  29. package/dist/components/ChipInput.svelte +609 -0
  30. package/dist/components/ChipInput.svelte.d.ts +55 -0
  31. package/dist/components/Collapse.svelte +91 -0
  32. package/dist/components/Collapse.svelte.d.ts +18 -0
  33. package/dist/components/ColorSelector.svelte +167 -0
  34. package/dist/components/ColorSelector.svelte.d.ts +9 -0
  35. package/dist/components/CommentThread.svelte +773 -0
  36. package/dist/components/CommentThread.svelte.d.ts +32 -0
  37. package/dist/components/ConfirmDialog.svelte +348 -0
  38. package/dist/components/ConfirmDialog.svelte.d.ts +3 -0
  39. package/dist/components/ConfirmModal.svelte +142 -0
  40. package/dist/components/ConfirmModal.svelte.d.ts +15 -0
  41. package/dist/components/ContextMenu.svelte +188 -0
  42. package/dist/components/ContextMenu.svelte.d.ts +31 -0
  43. package/dist/components/CountdownTimer.svelte +94 -0
  44. package/dist/components/CountdownTimer.svelte.d.ts +10 -0
  45. package/dist/components/DateRangePicker.svelte +194 -0
  46. package/dist/components/DateRangePicker.svelte.d.ts +10 -0
  47. package/dist/components/Drawer.svelte +143 -0
  48. package/dist/components/Drawer.svelte.d.ts +33 -0
  49. package/dist/components/EmojiPicker.svelte +159 -0
  50. package/dist/components/EmojiPicker.svelte.d.ts +8 -0
  51. package/dist/components/EntityPreviewCard.svelte +104 -0
  52. package/dist/components/EntityPreviewCard.svelte.d.ts +9 -0
  53. package/dist/components/FileDropzone.svelte +493 -0
  54. package/dist/components/FileDropzone.svelte.d.ts +40 -0
  55. package/dist/components/FilePicker.svelte +488 -0
  56. package/dist/components/FilePicker.svelte.d.ts +20 -0
  57. package/dist/components/FileThumbnail.svelte +80 -0
  58. package/dist/components/FileThumbnail.svelte.d.ts +12 -0
  59. package/dist/components/FilterDropdown.svelte +203 -0
  60. package/dist/components/FilterDropdown.svelte.d.ts +48 -0
  61. package/dist/components/FloatingActionBar.svelte +354 -0
  62. package/dist/components/FloatingActionBar.svelte.d.ts +51 -0
  63. package/dist/components/GPSTracker.svelte +202 -0
  64. package/dist/components/GPSTracker.svelte.d.ts +34 -0
  65. package/dist/components/HunkDiffView.svelte +348 -0
  66. package/dist/components/HunkDiffView.svelte.d.ts +9 -0
  67. package/dist/components/ImageLightbox.svelte +274 -0
  68. package/dist/components/ImageLightbox.svelte.d.ts +25 -0
  69. package/dist/components/ImageUpload.svelte +147 -0
  70. package/dist/components/ImageUpload.svelte.d.ts +10 -0
  71. package/dist/components/InlineEdit.svelte +291 -0
  72. package/dist/components/InlineEdit.svelte.d.ts +45 -0
  73. package/dist/components/InputDialog.svelte +327 -0
  74. package/dist/components/InputDialog.svelte.d.ts +3 -0
  75. package/dist/components/KeyboardShortcutsOverlay.svelte +296 -0
  76. package/dist/components/KeyboardShortcutsOverlay.svelte.d.ts +42 -0
  77. package/dist/components/LazyImage.svelte +123 -0
  78. package/dist/components/LazyImage.svelte.d.ts +14 -0
  79. package/dist/components/LinkShortener.svelte +274 -0
  80. package/dist/components/LinkShortener.svelte.d.ts +27 -0
  81. package/dist/components/LoadingSpinner.svelte +106 -0
  82. package/dist/components/LoadingSpinner.svelte.d.ts +21 -0
  83. package/dist/components/LocationMap.svelte +186 -0
  84. package/dist/components/LocationMap.svelte.d.ts +11 -0
  85. package/dist/components/MapView.svelte +341 -0
  86. package/dist/components/MapView.svelte.d.ts +20 -0
  87. package/dist/components/MarkupEditor.svelte +485 -0
  88. package/dist/components/MarkupEditor.svelte.d.ts +10 -0
  89. package/dist/components/MarkupOverlay.svelte +55 -0
  90. package/dist/components/MarkupOverlay.svelte.d.ts +8 -0
  91. package/dist/components/MediaWorkbench.svelte +871 -0
  92. package/dist/components/MediaWorkbench.svelte.d.ts +9 -0
  93. package/dist/components/MilestoneCard.svelte +98 -0
  94. package/dist/components/MilestoneCard.svelte.d.ts +16 -0
  95. package/dist/components/MilestoneTimeline.svelte +91 -0
  96. package/dist/components/MilestoneTimeline.svelte.d.ts +15 -0
  97. package/dist/components/Modal.svelte +244 -0
  98. package/dist/components/Modal.svelte.d.ts +35 -0
  99. package/dist/components/PDFViewer.svelte +105 -0
  100. package/dist/components/PDFViewer.svelte.d.ts +9 -0
  101. package/dist/components/PdfThumbnail.svelte +63 -0
  102. package/dist/components/PdfThumbnail.svelte.d.ts +9 -0
  103. package/dist/components/PhoneInput.svelte +212 -0
  104. package/dist/components/PhoneInput.svelte.d.ts +25 -0
  105. package/dist/components/ResizableDivider.svelte +307 -0
  106. package/dist/components/ResizableDivider.svelte.d.ts +26 -0
  107. package/dist/components/ResizablePanel.svelte +305 -0
  108. package/dist/components/ResizablePanel.svelte.d.ts +18 -0
  109. package/dist/components/SearchDropdown.svelte +421 -0
  110. package/dist/components/SearchDropdown.svelte.d.ts +24 -0
  111. package/dist/components/SelectInput.svelte +237 -0
  112. package/dist/components/SelectInput.svelte.d.ts +26 -0
  113. package/dist/components/SidebarUserFooter.svelte +34 -0
  114. package/dist/components/SidebarUserFooter.svelte.d.ts +16 -0
  115. package/dist/components/SignaturePad.svelte +175 -0
  116. package/dist/components/SignaturePad.svelte.d.ts +17 -0
  117. package/dist/components/SmartImageEditor.svelte +720 -0
  118. package/dist/components/SmartImageEditor.svelte.d.ts +24 -0
  119. package/dist/components/SortDropdown.svelte +156 -0
  120. package/dist/components/SortDropdown.svelte.d.ts +40 -0
  121. package/dist/components/Sparkline.svelte +116 -0
  122. package/dist/components/Sparkline.svelte.d.ts +12 -0
  123. package/dist/components/SpeechForm.svelte +114 -0
  124. package/dist/components/SpeechForm.svelte.d.ts +17 -0
  125. package/dist/components/StatusBadge.svelte +157 -0
  126. package/dist/components/StatusBadge.svelte.d.ts +24 -0
  127. package/dist/components/TaskTypeIcon.svelte +54 -0
  128. package/dist/components/TaskTypeIcon.svelte.d.ts +17 -0
  129. package/dist/components/TextArea.svelte +162 -0
  130. package/dist/components/TextArea.svelte.d.ts +30 -0
  131. package/dist/components/TextInput.svelte +131 -0
  132. package/dist/components/TextInput.svelte.d.ts +29 -0
  133. package/dist/components/ThemeSelector.svelte +206 -0
  134. package/dist/components/ThemeSelector.svelte.d.ts +14 -0
  135. package/dist/components/TimeSlotPicker.svelte +162 -0
  136. package/dist/components/TimeSlotPicker.svelte.d.ts +18 -0
  137. package/dist/components/UserAvatar.svelte +72 -0
  138. package/dist/components/UserAvatar.svelte.d.ts +11 -0
  139. package/dist/components/VariablePicker.svelte +170 -0
  140. package/dist/components/VariablePicker.svelte.d.ts +40 -0
  141. package/dist/components/VoicePlayer.svelte +422 -0
  142. package/dist/components/VoicePlayer.svelte.d.ts +18 -0
  143. package/dist/components/linked-columns/LinkedColumns.svelte +526 -0
  144. package/dist/components/linked-columns/LinkedColumns.svelte.d.ts +130 -0
  145. package/dist/components/markup.d.ts +47 -0
  146. package/dist/components/markup.js +201 -0
  147. package/dist/components/messaging/Avatar.svelte +81 -0
  148. package/dist/components/messaging/Avatar.svelte.d.ts +11 -0
  149. package/dist/components/messaging/ChannelInfoModal.svelte +163 -0
  150. package/dist/components/messaging/ChannelInfoModal.svelte.d.ts +18 -0
  151. package/dist/components/messaging/ChannelList.svelte +107 -0
  152. package/dist/components/messaging/ChannelList.svelte.d.ts +11 -0
  153. package/dist/components/messaging/ChannelMemberAvatarStack.svelte +69 -0
  154. package/dist/components/messaging/ChannelMemberAvatarStack.svelte.d.ts +11 -0
  155. package/dist/components/messaging/ChannelMembersModal.svelte +182 -0
  156. package/dist/components/messaging/ChannelMembersModal.svelte.d.ts +16 -0
  157. package/dist/components/messaging/CreateChannelModal.svelte +190 -0
  158. package/dist/components/messaging/CreateChannelModal.svelte.d.ts +14 -0
  159. package/dist/components/messaging/DirectMessageList.svelte +145 -0
  160. package/dist/components/messaging/DirectMessageList.svelte.d.ts +12 -0
  161. package/dist/components/messaging/EmojiSelector.svelte +261 -0
  162. package/dist/components/messaging/EmojiSelector.svelte.d.ts +19 -0
  163. package/dist/components/messaging/MentionAutocomplete.svelte +193 -0
  164. package/dist/components/messaging/MentionAutocomplete.svelte.d.ts +17 -0
  165. package/dist/components/messaging/MessageAttachment.svelte +273 -0
  166. package/dist/components/messaging/MessageAttachment.svelte.d.ts +13 -0
  167. package/dist/components/messaging/MessageAttachmentUpload.svelte +243 -0
  168. package/dist/components/messaging/MessageAttachmentUpload.svelte.d.ts +19 -0
  169. package/dist/components/messaging/MessageInput.svelte +452 -0
  170. package/dist/components/messaging/MessageInput.svelte.d.ts +28 -0
  171. package/dist/components/messaging/MessageItem.svelte +341 -0
  172. package/dist/components/messaging/MessageItem.svelte.d.ts +41 -0
  173. package/dist/components/messaging/MessageThread.svelte +306 -0
  174. package/dist/components/messaging/MessageThread.svelte.d.ts +30 -0
  175. package/dist/components/messaging/NotificationSettingsModal.svelte +234 -0
  176. package/dist/components/messaging/NotificationSettingsModal.svelte.d.ts +13 -0
  177. package/dist/components/messaging/QuotedMessageDisplay.svelte +123 -0
  178. package/dist/components/messaging/QuotedMessageDisplay.svelte.d.ts +10 -0
  179. package/dist/components/messaging/StartDMModal.svelte +100 -0
  180. package/dist/components/messaging/StartDMModal.svelte.d.ts +14 -0
  181. package/dist/components/messaging/ThreadPanel.svelte +153 -0
  182. package/dist/components/messaging/ThreadPanel.svelte.d.ts +16 -0
  183. package/dist/components/pipeline/DESIGN.md +361 -0
  184. package/dist/components/pipeline/Pipeline.svelte +391 -0
  185. package/dist/components/pipeline/Pipeline.svelte.d.ts +28 -0
  186. package/dist/components/pipeline/PipelineCard.svelte +94 -0
  187. package/dist/components/pipeline/PipelineCard.svelte.d.ts +13 -0
  188. package/dist/components/pipeline/PipelineColumn.svelte +163 -0
  189. package/dist/components/pipeline/PipelineColumn.svelte.d.ts +30 -0
  190. package/dist/components/replay/ChapterTimeline.svelte +326 -0
  191. package/dist/components/replay/ChapterTimeline.svelte.d.ts +39 -0
  192. package/dist/components/session-nav/transcriptModel.d.ts +132 -0
  193. package/dist/components/session-nav/transcriptModel.js +285 -0
  194. package/dist/data/emojis.d.ts +18 -0
  195. package/dist/data/emojis.js +1196 -0
  196. package/dist/index.d.ts +142 -0
  197. package/dist/index.js +142 -0
  198. package/dist/stores/confirmDialog.svelte.d.ts +21 -0
  199. package/dist/stores/confirmDialog.svelte.js +31 -0
  200. package/dist/stores/inputDialog.svelte.d.ts +24 -0
  201. package/dist/stores/inputDialog.svelte.js +32 -0
  202. package/dist/styles/rail.css +63 -0
  203. package/dist/types/annotation.d.ts +32 -0
  204. package/dist/types/annotation.js +3 -0
  205. package/dist/types/booking.d.ts +115 -0
  206. package/dist/types/booking.js +24 -0
  207. package/dist/types/comments.d.ts +65 -0
  208. package/dist/types/comments.js +22 -0
  209. package/dist/types/entityPreview.d.ts +42 -0
  210. package/dist/types/entityPreview.js +10 -0
  211. package/dist/types/filePicker.d.ts +15 -0
  212. package/dist/types/filePicker.js +1 -0
  213. package/dist/types/googleMaps.d.ts +51 -0
  214. package/dist/types/maps.d.ts +43 -0
  215. package/dist/types/maps.js +4 -0
  216. package/dist/types/messaging.d.ts +282 -0
  217. package/dist/types/messaging.js +100 -0
  218. package/dist/types/milestone.d.ts +20 -0
  219. package/dist/types/milestone.js +7 -0
  220. package/dist/types/pipeline.d.ts +115 -0
  221. package/dist/types/pipeline.js +15 -0
  222. package/dist/types/smartImageEditor.d.ts +42 -0
  223. package/dist/types/smartImageEditor.js +1 -0
  224. package/dist/types/templateVars.d.ts +34 -0
  225. package/dist/types/templateVars.js +12 -0
  226. package/dist/utils/currency.d.ts +8 -0
  227. package/dist/utils/currency.js +17 -0
  228. package/dist/utils/daisyuiColors.d.ts +37 -0
  229. package/dist/utils/daisyuiColors.js +152 -0
  230. package/dist/utils/dateFormatters.d.ts +69 -0
  231. package/dist/utils/dateFormatters.js +199 -0
  232. package/dist/utils/googleMapsLoader.d.ts +19 -0
  233. package/dist/utils/googleMapsLoader.js +121 -0
  234. package/dist/utils/mentionParser.d.ts +67 -0
  235. package/dist/utils/mentionParser.js +132 -0
  236. package/dist/utils/phone.d.ts +13 -0
  237. package/dist/utils/phone.js +67 -0
  238. package/dist/utils/phoneFormat.d.ts +26 -0
  239. package/dist/utils/phoneFormat.js +69 -0
  240. package/dist/utils/taskUtils.d.ts +37 -0
  241. package/dist/utils/taskUtils.js +71 -0
  242. package/package.json +3 -2
@@ -0,0 +1,32 @@
1
+ import type { Comment, CommentCallbacks, CommentEntityType } from '../types/comments';
2
+ interface Props {
3
+ entityType: CommentEntityType;
4
+ entityId: string;
5
+ /** Used for own-comment ownership checks (edit/delete). */
6
+ currentUserId: string;
7
+ /** false = portal mode (client-only, no internal toggle). Default true. */
8
+ staffView?: boolean;
9
+ /**
10
+ * When false, the staff internal/client visibility toggle is hidden,
11
+ * every new comment is forced to 'internal', and the per-comment
12
+ * visibility badge is suppressed. Use for entities that are never
13
+ * portal-exposed (e.g. internal tasks). Default true (no change to
14
+ * existing staff threads). Ignored when staffView is false.
15
+ */
16
+ allowClientVisibility?: boolean;
17
+ /** Allow edit/delete of other people's comments (team owner/admin). */
18
+ isAdmin?: boolean;
19
+ /** Initial top-level comments (typically from a server load). */
20
+ comments?: Comment[];
21
+ /** Total top-level comments matching the visibility filter (for paging). */
22
+ total?: number;
23
+ /** Show the skeleton loading state instead of the thread. */
24
+ loading?: boolean;
25
+ callbacks: CommentCallbacks;
26
+ placeholder?: string;
27
+ emptyText?: string;
28
+ class?: string;
29
+ }
30
+ declare const CommentThread: import("svelte").Component<Props, {}, "">;
31
+ type CommentThread = ReturnType<typeof CommentThread>;
32
+ export default CommentThread;
@@ -0,0 +1,348 @@
1
+ <script lang="ts">
2
+ import { fly, fade } from 'svelte/transition';
3
+ import { cubicOut } from 'svelte/easing';
4
+ import { tick } from 'svelte';
5
+ import { getPending, confirmDialog, cancelDialog } from '../stores/confirmDialog.svelte';
6
+
7
+ const pending = $derived(getPending());
8
+ const opts = $derived(pending?.options ?? null);
9
+ const visible = $derived(pending !== null);
10
+
11
+ // Countdown state
12
+ let remaining = $state(0);
13
+ let total = $state(0);
14
+ let intervalId: ReturnType<typeof setInterval> | null = null;
15
+ let cancelRef = $state<HTMLButtonElement | null>(null);
16
+
17
+ $effect(() => {
18
+ if (!visible || !opts) {
19
+ if (intervalId) { clearInterval(intervalId); intervalId = null; }
20
+ remaining = 0;
21
+ total = 0;
22
+ return;
23
+ }
24
+
25
+ const ms = opts.timeoutMs ?? 8000;
26
+ if (ms > 0) {
27
+ total = ms;
28
+ remaining = ms;
29
+ intervalId = setInterval(() => {
30
+ remaining -= 60;
31
+ if (remaining <= 0) {
32
+ remaining = 0;
33
+ clearInterval(intervalId!);
34
+ intervalId = null;
35
+ cancelDialog();
36
+ }
37
+ }, 60);
38
+ } else {
39
+ total = 0;
40
+ remaining = 0;
41
+ }
42
+
43
+ // Focus the cancel button (safe default for destructive actions)
44
+ tick().then(() => cancelRef?.focus());
45
+
46
+ return () => {
47
+ if (intervalId) { clearInterval(intervalId); intervalId = null; }
48
+ };
49
+ });
50
+
51
+ const pct = $derived(total > 0 ? (remaining / total) * 100 : 0);
52
+ const secs = $derived(total > 0 ? Math.ceil(remaining / 1000) : 0);
53
+ const isDanger = $derived(opts?.danger !== false); // default true
54
+
55
+ function handleKeydown(e: KeyboardEvent) {
56
+ if (!visible) return;
57
+ if (e.key === 'Escape') { e.preventDefault(); e.stopPropagation(); cancelDialog(); }
58
+ if (e.key === 'Enter') { e.preventDefault(); e.stopPropagation(); confirmDialog(); }
59
+ }
60
+
61
+ $effect(() => {
62
+ window.addEventListener('keydown', handleKeydown, true);
63
+ return () => window.removeEventListener('keydown', handleKeydown, true);
64
+ });
65
+ </script>
66
+
67
+ {#if visible && opts}
68
+ <!-- Scrim -->
69
+ <div
70
+ class="cd-scrim"
71
+ transition:fade={{ duration: 120 }}
72
+ role="presentation"
73
+ onclick={cancelDialog}
74
+ ></div>
75
+
76
+ <!-- Dialog -->
77
+ <div
78
+ class="cd-wrap"
79
+ role="alertdialog"
80
+ aria-modal="true"
81
+ aria-label="{opts.title}. {isDanger ? 'Destructive action.' : ''} Press Enter to confirm or Escape to cancel."
82
+ in:fly={{ y: -20, duration: 200, easing: cubicOut }}
83
+ out:fade={{ duration: 120 }}
84
+ >
85
+ <div class="cd-box" class:cd-danger={isDanger}>
86
+ <!-- Icon + title -->
87
+ <div class="cd-header">
88
+ <div class="cd-icon" class:cd-icon-danger={isDanger}>
89
+ {#if isDanger}
90
+ <!-- Terminal/skull icon for destructive actions -->
91
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round">
92
+ <rect x="2" y="3" width="20" height="14" rx="2"/>
93
+ <path d="M8 21h8M12 17v4"/>
94
+ <path d="M9 8l2 2-2 2M13 12h2"/>
95
+ </svg>
96
+ {:else}
97
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round">
98
+ <circle cx="12" cy="12" r="10"/>
99
+ <line x1="12" y1="8" x2="12" y2="12"/>
100
+ <line x1="12" y1="16" x2="12.01" y2="16"/>
101
+ </svg>
102
+ {/if}
103
+ </div>
104
+ <div class="cd-title-group">
105
+ <h3 class="cd-title">{opts.title}</h3>
106
+ {#if opts.body}
107
+ <p class="cd-body">{opts.body}</p>
108
+ {/if}
109
+ </div>
110
+ </div>
111
+
112
+ <!-- Actions -->
113
+ <div class="cd-actions">
114
+ <button
115
+ bind:this={cancelRef}
116
+ type="button"
117
+ class="cd-btn cd-btn-cancel"
118
+ onclick={cancelDialog}
119
+ >
120
+ <kbd class="cd-kbd">Esc</kbd>
121
+ <span>{opts.cancelLabel ?? 'Cancel'}</span>
122
+ </button>
123
+
124
+ <button
125
+ type="button"
126
+ class="cd-btn cd-btn-confirm"
127
+ class:cd-btn-danger={isDanger}
128
+ onclick={confirmDialog}
129
+ >
130
+ <kbd class="cd-kbd">{isDanger ? '⏎' : 'Enter'}</kbd>
131
+ <span>{opts.confirmLabel ?? (isDanger ? 'Confirm' : 'OK')}</span>
132
+ </button>
133
+
134
+ {#if total > 0}
135
+ <span class="cd-timer" aria-live="off">{secs}s</span>
136
+ {/if}
137
+ </div>
138
+
139
+ <!-- Progress bar (drains to 0; auto-cancels at 0) -->
140
+ {#if total > 0}
141
+ <div class="cd-progress" aria-hidden="true">
142
+ <div class="cd-progress-fill" class:cd-progress-danger={isDanger} style="width: {pct}%"></div>
143
+ </div>
144
+ {/if}
145
+ </div>
146
+ </div>
147
+ {/if}
148
+
149
+ <style>
150
+ .cd-scrim {
151
+ position: fixed;
152
+ inset: 0;
153
+ background: oklch(0 0 0 / 0.45);
154
+ z-index: 9990;
155
+ backdrop-filter: blur(2px);
156
+ }
157
+
158
+ .cd-wrap {
159
+ position: fixed;
160
+ top: 5rem;
161
+ left: 50%;
162
+ transform: translateX(-50%);
163
+ z-index: 9991;
164
+ pointer-events: auto;
165
+ width: max-content;
166
+ max-width: min(92vw, 460px);
167
+ }
168
+
169
+ .cd-box {
170
+ display: flex;
171
+ flex-direction: column;
172
+ gap: 0.625rem;
173
+ padding: 1rem 1.125rem 0.625rem;
174
+ background: color-mix(in oklch, var(--color-base-100) 98%, transparent);
175
+ border: 1px solid var(--color-base-300);
176
+ border-radius: 0.875rem;
177
+ box-shadow:
178
+ 0 20px 60px oklch(0 0 0 / 0.55),
179
+ 0 0 0 1px color-mix(in oklch, var(--color-base-300) 50%, transparent);
180
+ backdrop-filter: blur(16px);
181
+ }
182
+
183
+ .cd-box.cd-danger {
184
+ border-color: color-mix(in oklch, var(--color-error) 55%, transparent);
185
+ box-shadow:
186
+ 0 20px 60px oklch(0 0 0 / 0.6),
187
+ 0 0 0 1px color-mix(in oklch, var(--color-error) 25%, transparent),
188
+ 0 0 30px color-mix(in oklch, var(--color-error) 12%, transparent);
189
+ }
190
+
191
+ .cd-header {
192
+ display: flex;
193
+ align-items: flex-start;
194
+ gap: 0.875rem;
195
+ }
196
+
197
+ .cd-icon {
198
+ flex-shrink: 0;
199
+ width: 2.25rem;
200
+ height: 2.25rem;
201
+ border-radius: 0.5rem;
202
+ display: flex;
203
+ align-items: center;
204
+ justify-content: center;
205
+ background: color-mix(in oklch, var(--color-info) 35%, transparent);
206
+ border: 1px solid color-mix(in oklch, var(--color-info) 40%, transparent);
207
+ color: var(--color-info);
208
+ }
209
+
210
+ .cd-icon svg {
211
+ width: 1.125rem;
212
+ height: 1.125rem;
213
+ }
214
+
215
+ .cd-icon.cd-icon-danger {
216
+ background: color-mix(in oklch, var(--color-error) 35%, transparent);
217
+ border-color: color-mix(in oklch, var(--color-error) 40%, transparent);
218
+ color: var(--color-error-content);
219
+ }
220
+
221
+ .cd-title-group {
222
+ display: flex;
223
+ flex-direction: column;
224
+ gap: 0.25rem;
225
+ flex: 1;
226
+ min-width: 0;
227
+ }
228
+
229
+ .cd-title {
230
+ margin: 0;
231
+ font-size: 0.9375rem;
232
+ font-weight: 600;
233
+ color: var(--color-base-content);
234
+ line-height: 1.3;
235
+ }
236
+
237
+ .cd-body {
238
+ margin: 0;
239
+ font-size: 0.8rem;
240
+ color: color-mix(in oklch, var(--color-base-content) 65%, transparent);
241
+ line-height: 1.5;
242
+ }
243
+
244
+ .cd-actions {
245
+ display: flex;
246
+ align-items: center;
247
+ gap: 0.375rem;
248
+ padding-top: 0.125rem;
249
+ }
250
+
251
+ .cd-btn {
252
+ display: inline-flex;
253
+ align-items: center;
254
+ gap: 0.375rem;
255
+ padding: 0.3125rem 0.75rem;
256
+ font-size: 0.8rem;
257
+ font-weight: 500;
258
+ border-radius: 0.5rem;
259
+ border: 1px solid transparent;
260
+ background: transparent;
261
+ color: color-mix(in oklch, var(--color-base-content) 85%, transparent);
262
+ cursor: pointer;
263
+ transition: background 0.1s ease, border-color 0.1s ease, color 0.1s ease;
264
+ white-space: nowrap;
265
+ }
266
+
267
+ .cd-btn:hover {
268
+ background: var(--color-base-200);
269
+ border-color: var(--color-base-300);
270
+ }
271
+
272
+ .cd-btn-cancel:focus-visible {
273
+ outline: 2px solid var(--color-primary);
274
+ outline-offset: 2px;
275
+ }
276
+
277
+ .cd-btn-confirm {
278
+ background: color-mix(in oklch, var(--color-base-300) 60%, transparent);
279
+ border-color: var(--color-base-300);
280
+ }
281
+
282
+ .cd-btn-confirm:hover {
283
+ background: var(--color-base-200);
284
+ border-color: color-mix(in oklch, var(--color-base-content) 30%, transparent);
285
+ }
286
+
287
+ .cd-btn-danger {
288
+ background: color-mix(in oklch, var(--color-error) 45%, transparent);
289
+ border-color: color-mix(in oklch, var(--color-error) 55%, transparent);
290
+ color: var(--color-error-content);
291
+ }
292
+
293
+ .cd-btn-danger:hover {
294
+ background: color-mix(in oklch, var(--color-error) 65%, transparent);
295
+ border-color: color-mix(in oklch, var(--color-error) 70%, transparent);
296
+ color: var(--color-error-content);
297
+ }
298
+
299
+ .cd-btn-confirm:focus-visible,
300
+ .cd-btn-danger:focus-visible {
301
+ outline: 2px solid var(--color-error);
302
+ outline-offset: 2px;
303
+ }
304
+
305
+ .cd-kbd {
306
+ font-family: ui-monospace, monospace;
307
+ font-size: 0.65rem;
308
+ padding: 0.0625rem 0.3125rem;
309
+ background: var(--color-base-200);
310
+ border: 1px solid var(--color-base-300);
311
+ border-bottom-width: 2px;
312
+ border-radius: 0.25rem;
313
+ color: color-mix(in oklch, var(--color-base-content) 70%, transparent);
314
+ line-height: 1.6;
315
+ }
316
+
317
+ .cd-timer {
318
+ margin-left: auto;
319
+ font-family: ui-monospace, monospace;
320
+ font-size: 0.7rem;
321
+ color: color-mix(in oklch, var(--color-base-content) 45%, transparent);
322
+ font-variant-numeric: tabular-nums;
323
+ min-width: 2ch;
324
+ text-align: right;
325
+ }
326
+
327
+ .cd-progress {
328
+ height: 2px;
329
+ background: var(--color-base-200);
330
+ border-radius: 999px;
331
+ overflow: hidden;
332
+ }
333
+
334
+ .cd-progress-fill {
335
+ height: 100%;
336
+ background: linear-gradient(90deg, var(--color-info), var(--color-info));
337
+ border-radius: 999px;
338
+ transition: width 60ms linear;
339
+ }
340
+
341
+ .cd-progress-fill.cd-progress-danger {
342
+ background: linear-gradient(90deg, var(--color-error), var(--color-error));
343
+ }
344
+
345
+ @media (prefers-reduced-motion: reduce) {
346
+ .cd-progress-fill { transition: none; }
347
+ }
348
+ </style>
@@ -0,0 +1,3 @@
1
+ declare const ConfirmDialog: import("svelte").Component<Record<string, never>, {}, "">;
2
+ type ConfirmDialog = ReturnType<typeof ConfirmDialog>;
3
+ export default ConfirmDialog;
@@ -0,0 +1,142 @@
1
+ <script lang="ts">
2
+ import { fly, fade } from 'svelte/transition';
3
+ import { cubicOut } from 'svelte/easing';
4
+
5
+ let {
6
+ open = $bindable(true),
7
+ title = "Confirm Action",
8
+ message = "Are you sure you want to proceed?",
9
+ confirmText = "Confirm",
10
+ cancelText = "Cancel",
11
+ confirmButtonClass = "btn-primary",
12
+ isLoading = false,
13
+ iconClass = "text-warning",
14
+ onconfirm,
15
+ oncancel,
16
+ } = $props<{
17
+ open?: boolean
18
+ title?: string
19
+ message?: string
20
+ confirmText?: string
21
+ cancelText?: string
22
+ confirmButtonClass?: string
23
+ isLoading?: boolean
24
+ iconClass?: string
25
+ onconfirm?: () => void
26
+ oncancel?: () => void
27
+ }>()
28
+
29
+ // Respect prefers-reduced-motion: skip the entrance/exit transitions.
30
+ const reducedMotion =
31
+ typeof window !== 'undefined' &&
32
+ window.matchMedia?.('(prefers-reduced-motion: reduce)').matches
33
+
34
+ // Focus management: move focus into the dialog on open, trap Tab within it,
35
+ // and restore focus to the trigger on close. Escape is handled window-level
36
+ // so it works regardless of which control inside the box currently has focus.
37
+ let modalBox = $state<HTMLElement | null>(null)
38
+ let previousFocus: HTMLElement | null = null
39
+
40
+ const FOCUSABLE =
41
+ 'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])'
42
+
43
+ $effect(() => {
44
+ if (typeof document === 'undefined') return
45
+ if (open) {
46
+ previousFocus = document.activeElement as HTMLElement
47
+ requestAnimationFrame(() => {
48
+ if (modalBox?.contains(document.activeElement)) return
49
+ const first = modalBox?.querySelector<HTMLElement>(FOCUSABLE)
50
+ ;(first ?? modalBox)?.focus()
51
+ })
52
+ } else if (previousFocus) {
53
+ previousFocus.focus?.()
54
+ previousFocus = null
55
+ }
56
+ })
57
+
58
+ function confirm() {
59
+ onconfirm?.()
60
+ }
61
+
62
+ function cancel() {
63
+ open = false
64
+ oncancel?.()
65
+ }
66
+
67
+ function onWindowKeydown(e: KeyboardEvent) {
68
+ if (open && e.key === 'Escape') {
69
+ e.preventDefault()
70
+ e.stopImmediatePropagation()
71
+ cancel()
72
+ }
73
+ }
74
+
75
+ function trapTab(e: KeyboardEvent) {
76
+ if (e.key !== 'Tab' || !modalBox) return
77
+ const f = Array.from(modalBox.querySelectorAll<HTMLElement>(FOCUSABLE))
78
+ if (f.length === 0) return
79
+ const first = f[0]
80
+ const last = f[f.length - 1]
81
+ if (e.shiftKey && document.activeElement === first) {
82
+ e.preventDefault()
83
+ last.focus()
84
+ } else if (!e.shiftKey && document.activeElement === last) {
85
+ e.preventDefault()
86
+ first.focus()
87
+ }
88
+ }
89
+ </script>
90
+
91
+ <svelte:window onkeydown={onWindowKeydown} />
92
+
93
+ {#if open}
94
+ <div class="modal modal-open" transition:fade={{ duration: reducedMotion ? 0 : 200 }}>
95
+ <div
96
+ bind:this={modalBox}
97
+ class="modal-box max-w-md"
98
+ role="dialog"
99
+ aria-modal="true"
100
+ aria-label={title}
101
+ tabindex="-1"
102
+ onkeydown={trapTab}
103
+ in:fly={{ y: reducedMotion ? 0 : 10, duration: reducedMotion ? 0 : 220, easing: cubicOut }}
104
+ out:fade={{ duration: reducedMotion ? 0 : 150 }}
105
+ >
106
+ <div class="flex justify-between items-center mb-4">
107
+ <h3 class="font-semibold">{title}</h3>
108
+ <button class="btn btn-ghost btn-sm" onclick={cancel} aria-label="Close">
109
+ <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>
110
+ </button>
111
+ </div>
112
+
113
+ <div class="py-4">
114
+ <div class="flex justify-center mb-4">
115
+ <div class="p-4 rounded-full bg-base-200">
116
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class={iconClass}><path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3"/><path d="M12 9v4"/><path d="M12 17h.01"/></svg>
117
+ </div>
118
+ </div>
119
+
120
+ <p class="text-center mb-6">{message}</p>
121
+
122
+ <div class="flex gap-2">
123
+ <button type="button" class="btn btn-outline flex-1" onclick={cancel} disabled={isLoading}>
124
+ {cancelText}
125
+ </button>
126
+ <button type="button" class={`btn ${confirmButtonClass} flex-1`} onclick={confirm} disabled={isLoading}>
127
+ {#if isLoading}
128
+ <span class="loading loading-spinner loading-xs"></span>
129
+ Processing...
130
+ {:else}
131
+ {confirmText}
132
+ {/if}
133
+ </button>
134
+ </div>
135
+ </div>
136
+ </div>
137
+
138
+ <!-- svelte-ignore a11y_click_events_have_key_events -->
139
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
140
+ <div class="modal-backdrop" onclick={cancel}></div>
141
+ </div>
142
+ {/if}
@@ -0,0 +1,15 @@
1
+ type $$ComponentProps = {
2
+ open?: boolean;
3
+ title?: string;
4
+ message?: string;
5
+ confirmText?: string;
6
+ cancelText?: string;
7
+ confirmButtonClass?: string;
8
+ isLoading?: boolean;
9
+ iconClass?: string;
10
+ onconfirm?: () => void;
11
+ oncancel?: () => void;
12
+ };
13
+ declare const ConfirmModal: import("svelte").Component<$$ComponentProps, {}, "open">;
14
+ type ConfirmModal = ReturnType<typeof ConfirmModal>;
15
+ export default ConfirmModal;