@joewinke/jatui 0.1.25 → 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,54 @@
1
+ <script lang="ts">
2
+ /**
3
+ * Stroke icon for a task issue_type value.
4
+ * Replaces the emoji returned by `typeIcon()`.
5
+ *
6
+ * Usage:
7
+ * <TaskTypeIcon type={task.issue_type} />
8
+ * <TaskTypeIcon type="bug" size={14} class="text-base-content/60" />
9
+ */
10
+
11
+ interface Props {
12
+ type: string | null | undefined
13
+ size?: number
14
+ class?: string
15
+ title?: string
16
+ }
17
+
18
+ let { type, size = 14, class: className = "", title }: Props = $props()
19
+
20
+ // Path data per type. Single-path lucide-style strokes so they inherit
21
+ // currentColor and size cleanly inside dense tables.
22
+ const paths: Record<string, string> = {
23
+ // Bug: body + antennae/legs
24
+ bug: "M12 4v2M8 6l1 2M16 6l-1 2M6 12H4M20 12h-2M6 16l-2 1M18 16l2 1M9 9h6v5a3 3 0 11-6 0V9z",
25
+ // Feature (spark): 4-point star
26
+ feature: "M12 3v4M12 17v4M3 12h4M17 12h4M5.5 5.5l2.5 2.5M16 16l2.5 2.5M5.5 18.5L8 16M16 8l2.5-2.5",
27
+ // Task: checklist
28
+ task: "M9 4h9a1 1 0 011 1v14a1 1 0 01-1 1H6a1 1 0 01-1-1V5a1 1 0 011-1h1M9 3h6v2H9zM8.5 11.5l1.5 1.5 3-3M8.5 16.5l1.5 1.5 3-3",
29
+ // Epic: mountain peaks
30
+ epic: "M3 19l5-8 4 6 3-4 6 6H3z",
31
+ // Default: document
32
+ document: "M13 3H6a1 1 0 00-1 1v16a1 1 0 001 1h12a1 1 0 001-1V9l-6-6zM13 3v6h6",
33
+ }
34
+
35
+ const d = $derived(paths[type ?? ""] ?? paths.document)
36
+ const label = $derived(title ?? (type ? `${type[0].toUpperCase()}${type.slice(1)}` : "Document"))
37
+ </script>
38
+
39
+ <svg
40
+ viewBox="0 0 24 24"
41
+ fill="none"
42
+ stroke="currentColor"
43
+ stroke-width="2"
44
+ stroke-linecap="round"
45
+ stroke-linejoin="round"
46
+ width={size}
47
+ height={size}
48
+ class={className}
49
+ role="img"
50
+ aria-label={label}
51
+ >
52
+ <title>{label}</title>
53
+ <path d={d} />
54
+ </svg>
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Stroke icon for a task issue_type value.
3
+ * Replaces the emoji returned by `typeIcon()`.
4
+ *
5
+ * Usage:
6
+ * <TaskTypeIcon type={task.issue_type} />
7
+ * <TaskTypeIcon type="bug" size={14} class="text-base-content/60" />
8
+ */
9
+ interface Props {
10
+ type: string | null | undefined;
11
+ size?: number;
12
+ class?: string;
13
+ title?: string;
14
+ }
15
+ declare const TaskTypeIcon: import("svelte").Component<Props, {}, "">;
16
+ type TaskTypeIcon = ReturnType<typeof TaskTypeIcon>;
17
+ export default TaskTypeIcon;
@@ -0,0 +1,162 @@
1
+ <script lang="ts">
2
+ /**
3
+ * TextArea Component
4
+ *
5
+ * Multi-line text input with validation, auto-resize, and character count.
6
+ * Converted from Svelte 4 to Svelte 5 runes.
7
+ */
8
+
9
+ interface Props {
10
+ label?: string;
11
+ value?: string;
12
+ placeholder?: string;
13
+ rows?: number;
14
+ maxlength?: number;
15
+ minlength?: number;
16
+ required?: boolean;
17
+ disabled?: boolean;
18
+ readonly?: boolean;
19
+ autoResize?: boolean;
20
+ error?: string;
21
+ helpText?: string;
22
+ showCount?: boolean;
23
+ size?: 'sm' | 'md' | 'lg';
24
+ class?: string;
25
+ id?: string;
26
+ name?: string;
27
+ resize?: 'none' | 'vertical' | 'horizontal' | 'both';
28
+ oninput?: (value: string) => void;
29
+ }
30
+
31
+ let {
32
+ label = '',
33
+ value = $bindable(''),
34
+ placeholder = '',
35
+ rows = 3,
36
+ maxlength,
37
+ minlength,
38
+ required = false,
39
+ disabled = false,
40
+ readonly = false,
41
+ autoResize = false,
42
+ error = '',
43
+ helpText = '',
44
+ showCount = false,
45
+ size = 'md',
46
+ class: className = '',
47
+ id = `textarea-${Math.random().toString(36).substr(2, 9)}`,
48
+ name = '',
49
+ resize = 'vertical',
50
+ oninput
51
+ }: Props = $props();
52
+
53
+ let textareaElement: HTMLTextAreaElement | undefined = $state();
54
+
55
+ const sizeClass = $derived({ sm: 'textarea-sm', md: '', lg: 'textarea-lg' }[size]);
56
+ const resizeClass = $derived(
57
+ { none: 'resize-none', vertical: 'resize-y', horizontal: 'resize-x', both: 'resize' }[resize]
58
+ );
59
+
60
+ const textareaClass = $derived(
61
+ [
62
+ 'textarea textarea-bordered w-full transition-shadow duration-200',
63
+ sizeClass,
64
+ resizeClass,
65
+ error ? 'textarea-error' : '',
66
+ disabled ? 'textarea-disabled' : '',
67
+ className
68
+ ]
69
+ .filter(Boolean)
70
+ .join(' ')
71
+ );
72
+
73
+ const characterCount = $derived(value?.length || 0);
74
+ const isOverLimit = $derived(maxlength ? characterCount > maxlength : false);
75
+
76
+ function handleInput(event: Event) {
77
+ const target = event.target as HTMLTextAreaElement;
78
+ value = target.value;
79
+ if (autoResize) autoResizeTextarea(target);
80
+ oninput?.(value);
81
+ }
82
+
83
+ function autoResizeTextarea(textarea: HTMLTextAreaElement) {
84
+ textarea.style.height = 'auto';
85
+ textarea.style.height = textarea.scrollHeight + 'px';
86
+ }
87
+
88
+ $effect(() => {
89
+ if (autoResize && textareaElement && value !== undefined) {
90
+ autoResizeTextarea(textareaElement);
91
+ }
92
+ });
93
+
94
+ // Error-state shake (transitions.dev). Replays `t-input-shake` when `error`
95
+ // becomes set. See TextInput for the full rationale.
96
+ let hadError = false;
97
+ $effect(() => {
98
+ const hasError = !!error;
99
+ if (hasError && !hadError && textareaElement) {
100
+ const reduce =
101
+ typeof window !== 'undefined' &&
102
+ window.matchMedia?.('(prefers-reduced-motion: reduce)').matches;
103
+ if (!reduce) {
104
+ textareaElement.classList.remove('t-input-shake');
105
+ void textareaElement.offsetWidth;
106
+ textareaElement.classList.add('t-input-shake');
107
+ }
108
+ }
109
+ hadError = hasError;
110
+ });
111
+ </script>
112
+
113
+ <div class="form-control w-full">
114
+ {#if label}
115
+ <label for={id} class="label">
116
+ <span class="text-[0.8125rem] font-medium text-base-content/85">
117
+ {label}
118
+ {#if required}
119
+ <span class="text-error ml-1">*</span>
120
+ {/if}
121
+ </span>
122
+ </label>
123
+ {/if}
124
+
125
+ <textarea
126
+ bind:this={textareaElement}
127
+ {id}
128
+ {name}
129
+ {placeholder}
130
+ {rows}
131
+ {maxlength}
132
+ {minlength}
133
+ {required}
134
+ {disabled}
135
+ {readonly}
136
+ bind:value
137
+ oninput={handleInput}
138
+ class={textareaClass}
139
+ aria-describedby={error ? `${id}-error` : helpText ? `${id}-help` : undefined}
140
+ aria-invalid={error ? 'true' : 'false'}
141
+ style={autoResize ? `min-height: calc(1.5em * ${rows} + 1rem);` : ''}
142
+ onanimationend={() => textareaElement?.classList.remove('t-input-shake')}
143
+ ></textarea>
144
+
145
+ {#if showCount && maxlength}
146
+ <div class="label">
147
+ <span class="text-[0.8125rem] {isOverLimit ? 'text-error' : 'text-base-content/45'}">
148
+ {characterCount}/{maxlength}
149
+ </span>
150
+ </div>
151
+ {/if}
152
+
153
+ {#if error}
154
+ <div class="label">
155
+ <span id="{id}-error" class="text-[0.8125rem] text-error" role="alert">{error}</span>
156
+ </div>
157
+ {:else if helpText}
158
+ <div class="label">
159
+ <span id="{id}-help" class="text-[0.8125rem] text-base-content/45">{helpText}</span>
160
+ </div>
161
+ {/if}
162
+ </div>
@@ -0,0 +1,30 @@
1
+ /**
2
+ * TextArea Component
3
+ *
4
+ * Multi-line text input with validation, auto-resize, and character count.
5
+ * Converted from Svelte 4 to Svelte 5 runes.
6
+ */
7
+ interface Props {
8
+ label?: string;
9
+ value?: string;
10
+ placeholder?: string;
11
+ rows?: number;
12
+ maxlength?: number;
13
+ minlength?: number;
14
+ required?: boolean;
15
+ disabled?: boolean;
16
+ readonly?: boolean;
17
+ autoResize?: boolean;
18
+ error?: string;
19
+ helpText?: string;
20
+ showCount?: boolean;
21
+ size?: 'sm' | 'md' | 'lg';
22
+ class?: string;
23
+ id?: string;
24
+ name?: string;
25
+ resize?: 'none' | 'vertical' | 'horizontal' | 'both';
26
+ oninput?: (value: string) => void;
27
+ }
28
+ declare const TextArea: import("svelte").Component<Props, {}, "value">;
29
+ type TextArea = ReturnType<typeof TextArea>;
30
+ export default TextArea;
@@ -0,0 +1,131 @@
1
+ <script lang="ts">
2
+ /**
3
+ * TextInput Component
4
+ *
5
+ * Labeled text input with validation, error state, help text, and character count.
6
+ * Converted from Svelte 4 to Svelte 5 runes.
7
+ */
8
+
9
+ interface Props {
10
+ label: string;
11
+ value?: string;
12
+ placeholder?: string;
13
+ type?: 'text' | 'email' | 'password' | 'number' | 'tel' | 'url';
14
+ required?: boolean;
15
+ disabled?: boolean;
16
+ readonly?: boolean;
17
+ error?: string;
18
+ helpText?: string;
19
+ size?: 'sm' | 'md' | 'lg';
20
+ maxlength?: number;
21
+ minlength?: number;
22
+ pattern?: string;
23
+ showCount?: boolean;
24
+ class?: string;
25
+ id?: string;
26
+ name?: string;
27
+ autocomplete?: string;
28
+ }
29
+
30
+ let {
31
+ label,
32
+ value = $bindable(''),
33
+ placeholder = '',
34
+ type = 'text',
35
+ required = false,
36
+ disabled = false,
37
+ readonly = false,
38
+ error = '',
39
+ helpText = '',
40
+ size = 'md',
41
+ maxlength,
42
+ minlength,
43
+ pattern,
44
+ showCount = false,
45
+ class: className = '',
46
+ id = `text-input-${Math.random().toString(36).substr(2, 9)}`,
47
+ name = '',
48
+ autocomplete = ''
49
+ }: Props = $props();
50
+
51
+ const sizeClass = $derived({ sm: 'input-sm', md: '', lg: 'input-lg' }[size]);
52
+
53
+ const inputClass = $derived(
54
+ ['input input-bordered w-full transition-shadow duration-200', sizeClass, error ? 'input-error' : '', disabled ? 'input-disabled' : '', className]
55
+ .filter(Boolean)
56
+ .join(' ')
57
+ );
58
+
59
+ const characterCount = $derived(value?.length || 0);
60
+ const isOverLimit = $derived(maxlength ? characterCount > maxlength : false);
61
+
62
+ // Error-state shake (transitions.dev "Error state shake"). When `error`
63
+ // transitions from empty to set, replay the `t-input-shake` keyframe from the
64
+ // host app's transitions.css. Self-contained reflow-restart so a repeat error
65
+ // re-shakes. Colour stays on the `input-error` class; this owns only motion.
66
+ let inputEl: HTMLInputElement | undefined = $state();
67
+ let hadError = false;
68
+ $effect(() => {
69
+ const hasError = !!error;
70
+ if (hasError && !hadError && inputEl) {
71
+ const reduce =
72
+ typeof window !== 'undefined' &&
73
+ window.matchMedia?.('(prefers-reduced-motion: reduce)').matches;
74
+ if (!reduce) {
75
+ inputEl.classList.remove('t-input-shake');
76
+ void inputEl.offsetWidth; // force reflow to restart the animation
77
+ inputEl.classList.add('t-input-shake');
78
+ }
79
+ }
80
+ hadError = hasError;
81
+ });
82
+ </script>
83
+
84
+ <div class="form-control w-full">
85
+ <label for={id} class="label">
86
+ <span class="text-[0.8125rem] font-medium text-base-content/85">
87
+ {label}
88
+ {#if required}
89
+ <span class="text-error ml-1">*</span>
90
+ {/if}
91
+ </span>
92
+ </label>
93
+
94
+ <input
95
+ bind:this={inputEl}
96
+ {id}
97
+ {name}
98
+ {type}
99
+ {placeholder}
100
+ {required}
101
+ {disabled}
102
+ {readonly}
103
+ {maxlength}
104
+ {minlength}
105
+ {pattern}
106
+ autocomplete={autocomplete as AutoFill}
107
+ bind:value
108
+ class={inputClass}
109
+ aria-describedby={error ? `${id}-error` : helpText ? `${id}-help` : undefined}
110
+ aria-invalid={error ? 'true' : 'false'}
111
+ onanimationend={() => inputEl?.classList.remove('t-input-shake')}
112
+ />
113
+
114
+ {#if showCount && maxlength}
115
+ <div class="label">
116
+ <span class="text-[0.8125rem] {isOverLimit ? 'text-error' : 'text-base-content/45'}">
117
+ {characterCount}/{maxlength}
118
+ </span>
119
+ </div>
120
+ {/if}
121
+
122
+ {#if error}
123
+ <div class="label">
124
+ <span id="{id}-error" class="text-[0.8125rem] text-error" role="alert">{error}</span>
125
+ </div>
126
+ {:else if helpText}
127
+ <div class="label">
128
+ <span id="{id}-help" class="text-[0.8125rem] text-base-content/45">{helpText}</span>
129
+ </div>
130
+ {/if}
131
+ </div>
@@ -0,0 +1,29 @@
1
+ /**
2
+ * TextInput Component
3
+ *
4
+ * Labeled text input with validation, error state, help text, and character count.
5
+ * Converted from Svelte 4 to Svelte 5 runes.
6
+ */
7
+ interface Props {
8
+ label: string;
9
+ value?: string;
10
+ placeholder?: string;
11
+ type?: 'text' | 'email' | 'password' | 'number' | 'tel' | 'url';
12
+ required?: boolean;
13
+ disabled?: boolean;
14
+ readonly?: boolean;
15
+ error?: string;
16
+ helpText?: string;
17
+ size?: 'sm' | 'md' | 'lg';
18
+ maxlength?: number;
19
+ minlength?: number;
20
+ pattern?: string;
21
+ showCount?: boolean;
22
+ class?: string;
23
+ id?: string;
24
+ name?: string;
25
+ autocomplete?: string;
26
+ }
27
+ declare const TextInput: import("svelte").Component<Props, {}, "value">;
28
+ type TextInput = ReturnType<typeof TextInput>;
29
+ export default TextInput;
@@ -0,0 +1,206 @@
1
+ <script lang="ts">
2
+ /**
3
+ * ThemeSelector — DaisyUI theme switcher with visual preview dots, localStorage persistence,
4
+ * and animated transitions on theme change.
5
+ *
6
+ * By default includes all 33 standard DaisyUI themes. Pass custom `themes` prop to override.
7
+ */
8
+ import { onMount } from 'svelte';
9
+
10
+ interface ThemeOption {
11
+ name: string;
12
+ label: string;
13
+ }
14
+
15
+ const DEFAULT_THEMES: ThemeOption[] = [
16
+ { name: 'light', label: 'Light' },
17
+ { name: 'dark', label: 'Dark' },
18
+ { name: 'cupcake', label: 'Cupcake' },
19
+ { name: 'bumblebee', label: 'Bumblebee' },
20
+ { name: 'emerald', label: 'Emerald' },
21
+ { name: 'corporate', label: 'Corporate' },
22
+ { name: 'synthwave', label: 'Synthwave' },
23
+ { name: 'retro', label: 'Retro' },
24
+ { name: 'cyberpunk', label: 'Cyberpunk' },
25
+ { name: 'valentine', label: 'Valentine' },
26
+ { name: 'halloween', label: 'Halloween' },
27
+ { name: 'garden', label: 'Garden' },
28
+ { name: 'forest', label: 'Forest' },
29
+ { name: 'aqua', label: 'Aqua' },
30
+ { name: 'lofi', label: 'Lofi' },
31
+ { name: 'pastel', label: 'Pastel' },
32
+ { name: 'fantasy', label: 'Fantasy' },
33
+ { name: 'wireframe', label: 'Wireframe' },
34
+ { name: 'black', label: 'Black' },
35
+ { name: 'luxury', label: 'Luxury' },
36
+ { name: 'dracula', label: 'Dracula' },
37
+ { name: 'cmyk', label: 'CMYK' },
38
+ { name: 'autumn', label: 'Autumn' },
39
+ { name: 'business', label: 'Business' },
40
+ { name: 'acid', label: 'Acid' },
41
+ { name: 'lemonade', label: 'Lemonade' },
42
+ { name: 'night', label: 'Night' },
43
+ { name: 'coffee', label: 'Coffee' },
44
+ { name: 'winter', label: 'Winter' },
45
+ { name: 'dim', label: 'Dim' },
46
+ { name: 'nord', label: 'Nord' },
47
+ { name: 'sunset', label: 'Sunset' }
48
+ ];
49
+
50
+ let {
51
+ dropUp = false,
52
+ themes = DEFAULT_THEMES,
53
+ defaultTheme = 'light',
54
+ storageKey = 'theme',
55
+ onThemeChange,
56
+ }: {
57
+ dropUp?: boolean;
58
+ themes?: ThemeOption[];
59
+ defaultTheme?: string;
60
+ storageKey?: string;
61
+ onThemeChange?: (theme: string) => void;
62
+ } = $props();
63
+
64
+ // svelte-ignore state_referenced_locally
65
+ let currentTheme = $state(defaultTheme);
66
+ let isAnimating = $state(false);
67
+
68
+ const currentThemeLabel = $derived(
69
+ themes.find((t) => t.name === currentTheme)?.label || currentTheme
70
+ );
71
+
72
+ onMount(() => {
73
+ const savedTheme = localStorage.getItem(storageKey);
74
+ if (savedTheme && themes.some((t) => t.name === savedTheme)) {
75
+ currentTheme = savedTheme;
76
+ document.documentElement.setAttribute('data-theme', savedTheme);
77
+ } else {
78
+ currentTheme = defaultTheme;
79
+ document.documentElement.setAttribute('data-theme', defaultTheme);
80
+ localStorage.setItem(storageKey, defaultTheme);
81
+ }
82
+ });
83
+
84
+ function handleThemeChange(themeName: string) {
85
+ if (themeName === currentTheme) return;
86
+
87
+ // Close dropdown first so the trigger is visible during animation
88
+ if (document.activeElement instanceof HTMLElement) {
89
+ document.activeElement.blur();
90
+ }
91
+
92
+ isAnimating = true;
93
+
94
+ setTimeout(() => {
95
+ currentTheme = themeName;
96
+ document.documentElement.setAttribute('data-theme', themeName);
97
+ localStorage.setItem(storageKey, themeName);
98
+ onThemeChange?.(themeName);
99
+ }, 150);
100
+
101
+ setTimeout(() => {
102
+ isAnimating = false;
103
+ }, 500);
104
+ }
105
+ </script>
106
+
107
+ <div class="dropdown dropdown-end" class:dropdown-top={dropUp}>
108
+ <!-- svelte-ignore a11y_no_noninteractive_tabindex -->
109
+ <div
110
+ tabindex="0"
111
+ role="button"
112
+ class="flex items-center gap-2 cursor-pointer hover:bg-base-300 transition-colors px-2 py-1 rounded"
113
+ aria-label="Change Theme"
114
+ title="Change Theme"
115
+ >
116
+ <div
117
+ data-theme={currentTheme}
118
+ class="bg-base-100 grid shrink-0 grid-cols-2 gap-0.5 rounded-md p-1 transition-transform duration-300 ease-out"
119
+ class:theme-spin={isAnimating}
120
+ >
121
+ <div class="bg-base-content size-1 rounded-full transition-all duration-300 theme-dot" class:theme-dot-animate={isAnimating} style="--dot-delay: 0ms"></div>
122
+ <div class="bg-primary size-1 rounded-full transition-all duration-300 theme-dot" class:theme-dot-animate={isAnimating} style="--dot-delay: 50ms"></div>
123
+ <div class="bg-secondary size-1 rounded-full transition-all duration-300 theme-dot" class:theme-dot-animate={isAnimating} style="--dot-delay: 100ms"></div>
124
+ <div class="bg-accent size-1 rounded-full transition-all duration-300 theme-dot" class:theme-dot-animate={isAnimating} style="--dot-delay: 150ms"></div>
125
+ </div>
126
+ <span class="text-sm font-medium transition-opacity duration-200" class:opacity-50={isAnimating}>{currentThemeLabel}</span>
127
+ </div>
128
+
129
+ <!-- svelte-ignore a11y_no_noninteractive_tabindex -->
130
+ <div
131
+ tabindex="0"
132
+ class="dropdown-content bg-base-200 text-base-content rounded-box
133
+ top-px max-h-[calc(50vh-6.5rem)] overflow-y-auto border border-base-300
134
+ mb-2 z-50"
135
+ >
136
+ <ul class="menu w-56">
137
+ <li class="menu-title text-xs">Theme</li>
138
+
139
+ {#each themes as theme}
140
+ <li>
141
+ <button
142
+ class="gap-3 px-2"
143
+ onclick={() => handleThemeChange(theme.name)}
144
+ class:active={currentTheme === theme.name}
145
+ >
146
+ <div
147
+ data-theme={theme.name}
148
+ class="bg-base-100 grid shrink-0 grid-cols-2 gap-0.5
149
+ rounded-md p-1"
150
+ >
151
+ <div class="bg-base-content size-1 rounded-full"></div>
152
+ <div class="bg-primary size-1 rounded-full"></div>
153
+ <div class="bg-secondary size-1 rounded-full"></div>
154
+ <div class="bg-accent size-1 rounded-full"></div>
155
+ </div>
156
+
157
+ <div class="w-32 truncate">{theme.label}</div>
158
+
159
+ {#if currentTheme === theme.name}
160
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" class="h-3 w-3 shrink-0">
161
+ <path d="M20.285 2l-11.285 11.567-5.286-5.011-3.714 3.716 9 8.728 15-15.285z"></path>
162
+ </svg>
163
+ {:else}
164
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" class="invisible h-3 w-3 shrink-0">
165
+ <path d="M20.285 2l-11.285 11.567-5.286-5.011-3.714 3.716 9 8.728 15-15.285z"></path>
166
+ </svg>
167
+ {/if}
168
+ </button>
169
+ </li>
170
+ {/each}
171
+ </ul>
172
+ </div>
173
+ </div>
174
+
175
+ <style>
176
+ /* NOTE: DaisyUI v5.0.0 has a stuck-animation bug on `.dropdown-content`
177
+ (`@keyframes dropdown { 0% { opacity: 0 } }` pins opacity to 0). If the
178
+ theme dropdown opens but stays invisible, the consuming project needs
179
+ the global fix in its `app.css`:
180
+
181
+ .dropdown-content { animation: none !important; }
182
+
183
+ Upstream daisyui >= 5.5.x patches this; once you upgrade you can drop
184
+ the override. */
185
+
186
+ .theme-spin {
187
+ animation: theme-spin 0.4s cubic-bezier(0.4, 0, 0.2, 1);
188
+ }
189
+
190
+ @keyframes theme-spin {
191
+ 0% { transform: rotate(0deg) scale(1); }
192
+ 50% { transform: rotate(180deg) scale(1.2); }
193
+ 100% { transform: rotate(360deg) scale(1); }
194
+ }
195
+
196
+ .theme-dot-animate {
197
+ animation: dot-pulse 0.35s cubic-bezier(0.4, 0, 0.2, 1);
198
+ animation-delay: var(--dot-delay);
199
+ }
200
+
201
+ @keyframes dot-pulse {
202
+ 0% { transform: scale(1); opacity: 1; }
203
+ 50% { transform: scale(0.5); opacity: 0.5; }
204
+ 100% { transform: scale(1); opacity: 1; }
205
+ }
206
+ </style>
@@ -0,0 +1,14 @@
1
+ interface ThemeOption {
2
+ name: string;
3
+ label: string;
4
+ }
5
+ type $$ComponentProps = {
6
+ dropUp?: boolean;
7
+ themes?: ThemeOption[];
8
+ defaultTheme?: string;
9
+ storageKey?: string;
10
+ onThemeChange?: (theme: string) => void;
11
+ };
12
+ declare const ThemeSelector: import("svelte").Component<$$ComponentProps, {}, "">;
13
+ type ThemeSelector = ReturnType<typeof ThemeSelector>;
14
+ export default ThemeSelector;