@joewinke/jatui 0.1.25 → 0.1.27

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 +686 -0
  110. package/dist/components/SearchDropdown.svelte.d.ts +40 -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,40 @@
1
+ export type SearchDropdownOption = {
2
+ value: string;
3
+ label: string;
4
+ icon?: string;
5
+ image?: string;
6
+ };
7
+ export type SearchDropdownGroup = {
8
+ label: string;
9
+ options: SearchDropdownOption[];
10
+ };
11
+ import type { Snippet } from 'svelte';
12
+ type $$ComponentProps = {
13
+ value: string;
14
+ groups: SearchDropdownGroup[];
15
+ placeholder?: string;
16
+ disabled?: boolean;
17
+ displayValue?: string;
18
+ colorFn?: (value: string) => string | undefined;
19
+ footer?: Snippet;
20
+ /** 'chip' renders a compact TopBar-style pill instead of the full-width trigger */
21
+ variant?: 'default' | 'chip';
22
+ /** 'sm' renders a smaller trigger matching text-[11px] compact UIs */
23
+ size?: 'sm' | 'md';
24
+ /** Open the panel above the trigger instead of below */
25
+ dropup?: boolean;
26
+ /** Render panel with position:fixed using viewport coordinates — escapes table/overflow stacking contexts */
27
+ appendToBody?: boolean;
28
+ /** Show an × button on the trigger when a value is selected */
29
+ clearable?: boolean;
30
+ /** Extra attributes forwarded to the trigger button (e.g. data-rail-id) */
31
+ triggerAttrs?: Record<string, string>;
32
+ /** Render inline without a trigger button — panel content is always visible, no dropdown behavior */
33
+ embedded?: boolean;
34
+ onChange: (value: string) => void;
35
+ };
36
+ declare const SearchDropdown: import("svelte").Component<$$ComponentProps, {
37
+ openDropdown: () => void;
38
+ }, "">;
39
+ type SearchDropdown = ReturnType<typeof SearchDropdown>;
40
+ export default SearchDropdown;
@@ -0,0 +1,237 @@
1
+ <script lang="ts">
2
+ /**
3
+ * SelectInput Component
4
+ *
5
+ * Dropdown select with searchable mode, grouped options, and validation.
6
+ * Converted from Svelte 4 to Svelte 5 runes.
7
+ */
8
+
9
+ import { onMount } from 'svelte';
10
+
11
+ export interface SelectOption {
12
+ value: string | number;
13
+ label: string;
14
+ disabled?: boolean;
15
+ group?: string;
16
+ }
17
+
18
+ interface Props {
19
+ label: string;
20
+ value?: string | number;
21
+ options?: SelectOption[];
22
+ placeholder?: string;
23
+ required?: boolean;
24
+ disabled?: boolean;
25
+ searchable?: boolean;
26
+ error?: string;
27
+ helpText?: string;
28
+ size?: 'sm' | 'md' | 'lg';
29
+ class?: string;
30
+ id?: string;
31
+ name?: string;
32
+ clearSearchOnSelect?: boolean;
33
+ onchange?: (value: string | number, option: SelectOption | undefined) => void;
34
+ }
35
+
36
+ let {
37
+ label,
38
+ value = $bindable(''),
39
+ options = [],
40
+ placeholder = 'Select an option',
41
+ required = false,
42
+ disabled = false,
43
+ searchable = false,
44
+ error = '',
45
+ helpText = '',
46
+ size = 'md',
47
+ class: className = '',
48
+ id = `select-input-${Math.random().toString(36).substr(2, 9)}`,
49
+ name = '',
50
+ clearSearchOnSelect = true,
51
+ onchange
52
+ }: Props = $props();
53
+
54
+ let searchTerm = $state('');
55
+ let isOpen = $state(false);
56
+ let selectElement: HTMLElement | undefined = $state();
57
+
58
+ const sizeClass = $derived({ sm: 'select-sm', md: '', lg: 'select-lg' }[size]);
59
+
60
+ const selectClass = $derived(
61
+ ['select select-bordered w-full', sizeClass, error ? 'select-error' : '', disabled ? 'select-disabled' : '', className]
62
+ .filter(Boolean)
63
+ .join(' ')
64
+ );
65
+
66
+ const filteredOptions = $derived(
67
+ searchable && searchTerm
68
+ ? options.filter((o) => o.label.toLowerCase().includes(searchTerm.toLowerCase()))
69
+ : options
70
+ );
71
+
72
+ const selectedOption = $derived(options.find((o) => o.value === value));
73
+
74
+ const groupedOptions = $derived(
75
+ filteredOptions.reduce(
76
+ (acc, option) => {
77
+ const group = option.group || 'default';
78
+ if (!acc[group]) acc[group] = [];
79
+ acc[group].push(option);
80
+ return acc;
81
+ },
82
+ {} as Record<string, SelectOption[]>
83
+ )
84
+ );
85
+
86
+ function handleSelect(option: SelectOption) {
87
+ if (option.disabled) return;
88
+ value = option.value;
89
+ onchange?.(value, option);
90
+ if (clearSearchOnSelect) searchTerm = '';
91
+ isOpen = false;
92
+ }
93
+
94
+ function handleKeydown(event: KeyboardEvent) {
95
+ if (event.key === 'Escape') {
96
+ isOpen = false;
97
+ searchTerm = '';
98
+ }
99
+ }
100
+
101
+ function handleClickOutside(event: Event) {
102
+ if (selectElement && !selectElement.contains(event.target as Node)) {
103
+ isOpen = false;
104
+ searchTerm = '';
105
+ }
106
+ }
107
+
108
+ function handleNativeChange(e: Event) {
109
+ const target = e.currentTarget as HTMLSelectElement;
110
+ const option = options.find((opt) => opt.value.toString() === target.value);
111
+ onchange?.(value!, option);
112
+ }
113
+
114
+ onMount(() => {
115
+ document.addEventListener('click', handleClickOutside);
116
+ return () => document.removeEventListener('click', handleClickOutside);
117
+ });
118
+
119
+ // Error-state shake (transitions.dev). Shakes the whole control wrapper when
120
+ // `error` becomes set. See TextInput for the rationale.
121
+ let hadError = false;
122
+ $effect(() => {
123
+ const hasError = !!error;
124
+ if (hasError && !hadError && selectElement) {
125
+ const reduce =
126
+ typeof window !== 'undefined' &&
127
+ window.matchMedia?.('(prefers-reduced-motion: reduce)').matches;
128
+ if (!reduce) {
129
+ selectElement.classList.remove('t-input-shake');
130
+ void selectElement.offsetWidth;
131
+ selectElement.classList.add('t-input-shake');
132
+ }
133
+ }
134
+ hadError = hasError;
135
+ });
136
+ </script>
137
+
138
+ <div
139
+ class="form-control w-full"
140
+ bind:this={selectElement}
141
+ onanimationend={() => selectElement?.classList.remove('t-input-shake')}
142
+ >
143
+ <label for={id} class="label">
144
+ <span class="text-[0.8125rem] font-medium text-base-content/85">
145
+ {label}
146
+ {#if required}
147
+ <span class="text-error ml-1">*</span>
148
+ {/if}
149
+ </span>
150
+ </label>
151
+
152
+ {#if searchable}
153
+ <div class="dropdown w-full {isOpen ? 'dropdown-open' : ''}">
154
+ <div tabindex="0" role="button" class="btn btn-outline w-full justify-between {sizeClass}">
155
+ <input
156
+ {id}
157
+ {name}
158
+ type="text"
159
+ placeholder={selectedOption?.label || placeholder}
160
+ bind:value={searchTerm}
161
+ onfocus={() => (isOpen = true)}
162
+ onkeydown={handleKeydown}
163
+ class="input input-ghost w-full p-0 h-auto min-h-0 focus:outline-none"
164
+ {disabled}
165
+ {required}
166
+ aria-describedby={error ? `${id}-error` : helpText ? `${id}-help` : undefined}
167
+ aria-invalid={error ? 'true' : 'false'}
168
+ />
169
+ <svg class="w-4 h-4 opacity-70" fill="none" stroke="currentColor" viewBox="0 0 24 24">
170
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
171
+ </svg>
172
+ </div>
173
+
174
+ {#if isOpen}
175
+ <div class="dropdown-content z-[1] menu p-2 shadow bg-base-100 rounded-box w-full max-h-60 overflow-auto">
176
+ {#each Object.entries(groupedOptions) as [groupName, groupOpts]}
177
+ {#if groupName !== 'default'}
178
+ <div class="menu-title"><span>{groupName}</span></div>
179
+ {/if}
180
+ {#each groupOpts as option}
181
+ <li>
182
+ <button
183
+ type="button"
184
+ class="text-left {option.disabled ? 'disabled' : ''}"
185
+ class:active={option.value === value}
186
+ onclick={() => handleSelect(option)}
187
+ disabled={option.disabled}
188
+ >
189
+ {option.label}
190
+ </button>
191
+ </li>
192
+ {/each}
193
+ {/each}
194
+ </div>
195
+ {/if}
196
+ </div>
197
+ {:else}
198
+ <select
199
+ {id}
200
+ {name}
201
+ {required}
202
+ {disabled}
203
+ bind:value
204
+ class={selectClass}
205
+ aria-describedby={error ? `${id}-error` : helpText ? `${id}-help` : undefined}
206
+ aria-invalid={error ? 'true' : 'false'}
207
+ onchange={handleNativeChange}
208
+ >
209
+ {#if placeholder}
210
+ <option value="" disabled>{placeholder}</option>
211
+ {/if}
212
+ {#each Object.entries(groupedOptions) as [groupName, groupOpts]}
213
+ {#if groupName !== 'default'}
214
+ <optgroup label={groupName}>
215
+ {#each groupOpts as option}
216
+ <option value={option.value} disabled={option.disabled}>{option.label}</option>
217
+ {/each}
218
+ </optgroup>
219
+ {:else}
220
+ {#each groupOpts as option}
221
+ <option value={option.value} disabled={option.disabled}>{option.label}</option>
222
+ {/each}
223
+ {/if}
224
+ {/each}
225
+ </select>
226
+ {/if}
227
+
228
+ {#if error}
229
+ <div class="label">
230
+ <span id="{id}-error" class="text-[0.8125rem] text-error" role="alert">{error}</span>
231
+ </div>
232
+ {:else if helpText}
233
+ <div class="label">
234
+ <span id="{id}-help" class="text-[0.8125rem] text-base-content/45">{helpText}</span>
235
+ </div>
236
+ {/if}
237
+ </div>
@@ -0,0 +1,26 @@
1
+ export interface SelectOption {
2
+ value: string | number;
3
+ label: string;
4
+ disabled?: boolean;
5
+ group?: string;
6
+ }
7
+ interface Props {
8
+ label: string;
9
+ value?: string | number;
10
+ options?: SelectOption[];
11
+ placeholder?: string;
12
+ required?: boolean;
13
+ disabled?: boolean;
14
+ searchable?: boolean;
15
+ error?: string;
16
+ helpText?: string;
17
+ size?: 'sm' | 'md' | 'lg';
18
+ class?: string;
19
+ id?: string;
20
+ name?: string;
21
+ clearSearchOnSelect?: boolean;
22
+ onchange?: (value: string | number, option: SelectOption | undefined) => void;
23
+ }
24
+ declare const SelectInput: import("svelte").Component<Props, {}, "value">;
25
+ type SelectInput = ReturnType<typeof SelectInput>;
26
+ export default SelectInput;
@@ -0,0 +1,34 @@
1
+ <!--
2
+ SidebarUserFooter — avatar + name (links to account) + sign out button.
3
+ Placed at the bottom of app sidebar drawers (admin, facilitator, portal).
4
+ Used in JST template layouts.
5
+ -->
6
+ <script lang="ts">
7
+ import UserAvatar from './UserAvatar.svelte'
8
+
9
+ interface Props {
10
+ profile?: { full_name?: string | null; avatar_url?: string | null } | null
11
+ user?: { email?: string | null } | null
12
+ /** Where the avatar/name links to. Defaults to /account */
13
+ accountHref?: string
14
+ /** Sign out URL. Defaults to /account/sign_out */
15
+ signOutHref?: string
16
+ }
17
+
18
+ let {
19
+ profile,
20
+ user,
21
+ accountHref = '/account',
22
+ signOutHref = '/account/sign_out',
23
+ }: Props = $props()
24
+
25
+ let displayName = $derived(profile?.full_name || user?.email || '?')
26
+ </script>
27
+
28
+ <div class="flex items-center gap-2 px-2 py-1">
29
+ <a href={accountHref} class="flex items-center gap-2 flex-1 min-w-0" title="Account settings">
30
+ <UserAvatar name={profile?.full_name} email={user?.email} avatarUrl={profile?.avatar_url} size="sm" />
31
+ <span class="truncate text-[0.9375rem]">{displayName}</span>
32
+ </a>
33
+ <a href={signOutHref} class="btn btn-ghost btn-xs shrink-0">Sign out</a>
34
+ </div>
@@ -0,0 +1,16 @@
1
+ interface Props {
2
+ profile?: {
3
+ full_name?: string | null;
4
+ avatar_url?: string | null;
5
+ } | null;
6
+ user?: {
7
+ email?: string | null;
8
+ } | null;
9
+ /** Where the avatar/name links to. Defaults to /account */
10
+ accountHref?: string;
11
+ /** Sign out URL. Defaults to /account/sign_out */
12
+ signOutHref?: string;
13
+ }
14
+ declare const SidebarUserFooter: import("svelte").Component<Props, {}, "">;
15
+ type SidebarUserFooter = ReturnType<typeof SidebarUserFooter>;
16
+ export default SidebarUserFooter;
@@ -0,0 +1,175 @@
1
+ <script lang="ts">
2
+ /**
3
+ * SignaturePad — Canvas-based digital signature capture with mouse and touch support.
4
+ *
5
+ * Exports `clear()` and `isEmpty()` methods. Outputs signature as base64 PNG data URL
6
+ * via the bindable `signatureData` prop or `onSignatureChange` callback.
7
+ */
8
+ import { onMount } from 'svelte';
9
+
10
+ let {
11
+ signatureData = $bindable(''),
12
+ onSignatureChange,
13
+ width = 400,
14
+ height = 150,
15
+ strokeColor = '',
16
+ strokeWidth = 2,
17
+ label = 'Signature',
18
+ required = false,
19
+ placeholder = 'Sign here',
20
+ }: {
21
+ signatureData?: string;
22
+ onSignatureChange?: (data: string) => void;
23
+ width?: number;
24
+ height?: number;
25
+ strokeColor?: string;
26
+ strokeWidth?: number;
27
+ label?: string;
28
+ required?: boolean;
29
+ placeholder?: string;
30
+ } = $props();
31
+
32
+ let canvas: HTMLCanvasElement;
33
+ let isDrawing = false;
34
+ let lastX = 0;
35
+ let lastY = 0;
36
+ let hasDrawn = $state(false);
37
+
38
+ onMount(() => {
39
+ if (!canvas) return;
40
+
41
+ const ctx = canvas.getContext('2d');
42
+ if (!ctx) return;
43
+
44
+ const resolvedStroke = strokeColor ||
45
+ getComputedStyle(canvas).getPropertyValue('--color-base-content').trim() ||
46
+ 'oklch(0.21 0 0)';
47
+ ctx.strokeStyle = resolvedStroke;
48
+ ctx.lineWidth = strokeWidth;
49
+ ctx.lineCap = 'round';
50
+ ctx.lineJoin = 'round';
51
+
52
+ function getCoordinates(e: MouseEvent | TouchEvent) {
53
+ const rect = canvas.getBoundingClientRect();
54
+ const scaleX = canvas.width / rect.width;
55
+ const scaleY = canvas.height / rect.height;
56
+
57
+ if (e instanceof MouseEvent) {
58
+ return {
59
+ x: (e.clientX - rect.left) * scaleX,
60
+ y: (e.clientY - rect.top) * scaleY
61
+ };
62
+ } else {
63
+ return {
64
+ x: (e.touches[0].clientX - rect.left) * scaleX,
65
+ y: (e.touches[0].clientY - rect.top) * scaleY
66
+ };
67
+ }
68
+ }
69
+
70
+ function startDrawing(e: MouseEvent | TouchEvent) {
71
+ e.preventDefault();
72
+ isDrawing = true;
73
+ const coords = getCoordinates(e);
74
+ lastX = coords.x;
75
+ lastY = coords.y;
76
+ }
77
+
78
+ function draw(e: MouseEvent | TouchEvent) {
79
+ if (!isDrawing || !ctx) return;
80
+ e.preventDefault();
81
+
82
+ const coords = getCoordinates(e);
83
+ ctx.beginPath();
84
+ ctx.moveTo(lastX, lastY);
85
+ ctx.lineTo(coords.x, coords.y);
86
+ ctx.stroke();
87
+ lastX = coords.x;
88
+ lastY = coords.y;
89
+ hasDrawn = true;
90
+ }
91
+
92
+ function stopDrawing() {
93
+ if (isDrawing && hasDrawn) {
94
+ isDrawing = false;
95
+ signatureData = canvas.toDataURL();
96
+ onSignatureChange?.(signatureData);
97
+ }
98
+ isDrawing = false;
99
+ }
100
+
101
+ canvas.addEventListener('mousedown', startDrawing);
102
+ canvas.addEventListener('mousemove', draw);
103
+ canvas.addEventListener('mouseup', stopDrawing);
104
+ canvas.addEventListener('mouseleave', stopDrawing);
105
+
106
+ canvas.addEventListener('touchstart', startDrawing, { passive: false });
107
+ canvas.addEventListener('touchmove', draw, { passive: false });
108
+ canvas.addEventListener('touchend', stopDrawing);
109
+ canvas.addEventListener('touchcancel', stopDrawing);
110
+
111
+ return () => {
112
+ canvas.removeEventListener('mousedown', startDrawing);
113
+ canvas.removeEventListener('mousemove', draw);
114
+ canvas.removeEventListener('mouseup', stopDrawing);
115
+ canvas.removeEventListener('mouseleave', stopDrawing);
116
+ canvas.removeEventListener('touchstart', startDrawing);
117
+ canvas.removeEventListener('touchmove', draw);
118
+ canvas.removeEventListener('touchend', stopDrawing);
119
+ canvas.removeEventListener('touchcancel', stopDrawing);
120
+ };
121
+ });
122
+
123
+ export function clear() {
124
+ if (!canvas) return;
125
+ const ctx = canvas.getContext('2d');
126
+ if (!ctx) return;
127
+
128
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
129
+ signatureData = '';
130
+ hasDrawn = false;
131
+ onSignatureChange?.('');
132
+ }
133
+
134
+ export function isEmpty(): boolean {
135
+ return !hasDrawn || !signatureData;
136
+ }
137
+ </script>
138
+
139
+ <div class="signature-pad-container">
140
+ {#if label}
141
+ <div class="flex justify-between items-center mb-2">
142
+ <span class="text-sm text-base-content/70">
143
+ {label}
144
+ {#if required}
145
+ <span class="text-error">*</span>
146
+ {/if}
147
+ </span>
148
+ <button type="button" onclick={() => clear()} class="btn btn-ghost btn-xs">
149
+ Clear
150
+ </button>
151
+ </div>
152
+ {/if}
153
+
154
+ <div class="rounded-lg bg-base-100 overflow-hidden relative transition-all duration-300
155
+ {hasDrawn ? 'border-2 border-success/60' : 'border-2 border-base-300'}">
156
+ <canvas
157
+ bind:this={canvas}
158
+ {width}
159
+ {height}
160
+ class="w-full touch-none cursor-crosshair"
161
+ ></canvas>
162
+
163
+ {#if !hasDrawn}
164
+ <div class="absolute inset-0 flex items-center justify-center pointer-events-none">
165
+ <span class="text-base-content/30 text-sm">{placeholder}</span>
166
+ </div>
167
+ {/if}
168
+ </div>
169
+ </div>
170
+
171
+ <style>
172
+ canvas {
173
+ display: block;
174
+ }
175
+ </style>
@@ -0,0 +1,17 @@
1
+ type $$ComponentProps = {
2
+ signatureData?: string;
3
+ onSignatureChange?: (data: string) => void;
4
+ width?: number;
5
+ height?: number;
6
+ strokeColor?: string;
7
+ strokeWidth?: number;
8
+ label?: string;
9
+ required?: boolean;
10
+ placeholder?: string;
11
+ };
12
+ declare const SignaturePad: import("svelte").Component<$$ComponentProps, {
13
+ clear: () => void;
14
+ isEmpty: () => boolean;
15
+ }, "signatureData">;
16
+ type SignaturePad = ReturnType<typeof SignaturePad>;
17
+ export default SignaturePad;