@placeholderco/placeholder-ui 1.0.12 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (276) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +134 -130
  3. package/dist/app.css +178 -0
  4. package/dist/bootstrap.min.css +3600 -0
  5. package/dist/cards/GitCard.svelte +115 -0
  6. package/dist/cards/GitCard.svelte.d.ts +22 -0
  7. package/dist/display/Avatar.svelte +29 -17
  8. package/dist/display/Avatar.svelte.d.ts +12 -2
  9. package/dist/display/LinkCollection.svelte +40 -18
  10. package/dist/display/LinkCollection.svelte.d.ts +9 -2
  11. package/dist/display/Paper.svelte +31 -19
  12. package/dist/display/Paper.svelte.d.ts +12 -3
  13. package/dist/display/alert/Alert.svelte +129 -0
  14. package/dist/display/alert/Alert.svelte.d.ts +15 -0
  15. package/dist/display/alert/AlertVariant.d.ts +1 -0
  16. package/dist/display/alert/AlertVariant.js +1 -0
  17. package/dist/display/filetree/FileTree.svelte +259 -0
  18. package/dist/display/filetree/FileTree.svelte.d.ts +76 -0
  19. package/dist/display/filetree/FileTreeItem.svelte +396 -0
  20. package/dist/display/filetree/FileTreeItem.svelte.d.ts +40 -0
  21. package/dist/display/filetree/FileTreeNode.d.ts +46 -0
  22. package/dist/display/filetree/FileTreeNode.js +115 -0
  23. package/dist/display/filetree/index.d.ts +5 -0
  24. package/dist/display/filetree/index.js +3 -0
  25. package/dist/editors/CodeMirror.svelte +306 -0
  26. package/dist/editors/CodeMirror.svelte.d.ts +31 -0
  27. package/dist/editors/tiptap/LinkActionMenu.svelte +307 -0
  28. package/dist/editors/tiptap/LinkActionMenu.svelte.d.ts +8 -0
  29. package/dist/editors/tiptap/TipTap.svelte +856 -0
  30. package/dist/editors/tiptap/TipTap.svelte.d.ts +54 -0
  31. package/dist/editors/tiptap/TipTapBubble.svelte +790 -0
  32. package/dist/editors/tiptap/TipTapBubble.svelte.d.ts +44 -0
  33. package/dist/editors/tiptap/collaboration.d.ts +55 -0
  34. package/dist/editors/tiptap/collaboration.js +1 -0
  35. package/dist/editors/tiptap/extensions/Alert.d.ts +18 -0
  36. package/dist/editors/tiptap/extensions/Alert.js +72 -0
  37. package/dist/editors/tiptap/extensions/CodeBlockEditor.d.ts +21 -0
  38. package/dist/editors/tiptap/extensions/CodeBlockEditor.js +326 -0
  39. package/dist/editors/tiptap/extensions/Div.d.ts +19 -0
  40. package/dist/editors/tiptap/extensions/Div.js +89 -0
  41. package/dist/editors/tiptap/extensions/Span.d.ts +5 -0
  42. package/dist/editors/tiptap/extensions/Span.js +56 -0
  43. package/dist/editors/tiptap/extensions/codeBlockEditor.css +236 -0
  44. package/dist/editors/tiptap/extensions/codeBlockLanguages.d.ts +17 -0
  45. package/dist/editors/tiptap/extensions/codeBlockLanguages.js +124 -0
  46. package/dist/editors/tiptap/extensions/index.d.ts +7 -0
  47. package/dist/editors/tiptap/extensions/index.js +5 -0
  48. package/dist/editors/tiptap/toolbar/AlertButton.svelte +261 -0
  49. package/dist/editors/tiptap/toolbar/AlertButton.svelte.d.ts +7 -0
  50. package/dist/editors/tiptap/toolbar/AlignCenterButton.svelte +18 -0
  51. package/dist/editors/tiptap/toolbar/AlignCenterButton.svelte.d.ts +7 -0
  52. package/dist/editors/tiptap/toolbar/AlignJustifyButton.svelte +18 -0
  53. package/dist/editors/tiptap/toolbar/AlignJustifyButton.svelte.d.ts +7 -0
  54. package/dist/editors/tiptap/toolbar/AlignLeftButton.svelte +18 -0
  55. package/dist/editors/tiptap/toolbar/AlignLeftButton.svelte.d.ts +7 -0
  56. package/dist/editors/tiptap/toolbar/AlignRightButton.svelte +18 -0
  57. package/dist/editors/tiptap/toolbar/AlignRightButton.svelte.d.ts +7 -0
  58. package/dist/editors/tiptap/toolbar/BoldButton.svelte +18 -0
  59. package/dist/editors/tiptap/toolbar/BoldButton.svelte.d.ts +7 -0
  60. package/dist/editors/tiptap/toolbar/BulletListButton.svelte +18 -0
  61. package/dist/editors/tiptap/toolbar/BulletListButton.svelte.d.ts +7 -0
  62. package/dist/editors/tiptap/toolbar/CodeBlockButton.svelte +18 -0
  63. package/dist/editors/tiptap/toolbar/CodeBlockButton.svelte.d.ts +7 -0
  64. package/dist/editors/tiptap/toolbar/CodeButton.svelte +18 -0
  65. package/dist/editors/tiptap/toolbar/CodeButton.svelte.d.ts +7 -0
  66. package/dist/editors/tiptap/toolbar/ColorButton.svelte +299 -0
  67. package/dist/editors/tiptap/toolbar/ColorButton.svelte.d.ts +20 -0
  68. package/dist/editors/tiptap/toolbar/DocumentButton.svelte +58 -0
  69. package/dist/editors/tiptap/toolbar/DocumentButton.svelte.d.ts +9 -0
  70. package/dist/editors/tiptap/toolbar/HeadingButton.svelte +30 -0
  71. package/dist/editors/tiptap/toolbar/HeadingButton.svelte.d.ts +8 -0
  72. package/dist/editors/tiptap/toolbar/HtmlButton.svelte +68 -0
  73. package/dist/editors/tiptap/toolbar/HtmlButton.svelte.d.ts +7 -0
  74. package/dist/editors/tiptap/toolbar/ImageButton.svelte +54 -0
  75. package/dist/editors/tiptap/toolbar/ImageButton.svelte.d.ts +8 -0
  76. package/dist/editors/tiptap/toolbar/ItalicButton.svelte +18 -0
  77. package/dist/editors/tiptap/toolbar/ItalicButton.svelte.d.ts +7 -0
  78. package/dist/editors/tiptap/toolbar/LinkButton.svelte +257 -0
  79. package/dist/editors/tiptap/toolbar/LinkButton.svelte.d.ts +7 -0
  80. package/dist/editors/tiptap/toolbar/OrderedListButton.svelte +18 -0
  81. package/dist/editors/tiptap/toolbar/OrderedListButton.svelte.d.ts +7 -0
  82. package/dist/editors/tiptap/toolbar/RedoButton.svelte +18 -0
  83. package/dist/editors/tiptap/toolbar/RedoButton.svelte.d.ts +7 -0
  84. package/dist/editors/tiptap/toolbar/StrikethroughButton.svelte +18 -0
  85. package/dist/editors/tiptap/toolbar/StrikethroughButton.svelte.d.ts +7 -0
  86. package/dist/editors/tiptap/toolbar/SubscriptButton.svelte +18 -0
  87. package/dist/editors/tiptap/toolbar/SubscriptButton.svelte.d.ts +7 -0
  88. package/dist/editors/tiptap/toolbar/SuperscriptButton.svelte +18 -0
  89. package/dist/editors/tiptap/toolbar/SuperscriptButton.svelte.d.ts +7 -0
  90. package/dist/editors/tiptap/toolbar/TableButton.svelte +258 -0
  91. package/dist/editors/tiptap/toolbar/TableButton.svelte.d.ts +7 -0
  92. package/dist/editors/tiptap/toolbar/ToolbarButton.svelte +84 -0
  93. package/dist/editors/tiptap/toolbar/ToolbarButton.svelte.d.ts +10 -0
  94. package/dist/editors/tiptap/toolbar/UnderlineButton.svelte +18 -0
  95. package/dist/editors/tiptap/toolbar/UnderlineButton.svelte.d.ts +7 -0
  96. package/dist/editors/tiptap/toolbar/UndoButton.svelte +18 -0
  97. package/dist/editors/tiptap/toolbar/UndoButton.svelte.d.ts +7 -0
  98. package/dist/form/Autocomplete.svelte +86 -135
  99. package/dist/form/Autocomplete.svelte.d.ts +6 -8
  100. package/dist/form/AutocompleteMulti.svelte +385 -227
  101. package/dist/form/AutocompleteMulti.svelte.d.ts +59 -10
  102. package/dist/form/Checkbox.svelte +59 -54
  103. package/dist/form/Checkbox.svelte.d.ts +14 -5
  104. package/dist/form/Chips.svelte +29 -13
  105. package/dist/form/Chips.svelte.d.ts +16 -2
  106. package/dist/form/ComboBox.svelte +30 -20
  107. package/dist/form/ComboBox.svelte.d.ts +1 -1
  108. package/dist/form/ComboBoxItemBuilder.svelte +161 -188
  109. package/dist/form/ComboBoxItemBuilder.svelte.d.ts +19 -2
  110. package/dist/form/ComboBoxMulti.svelte +38 -24
  111. package/dist/form/ComboBoxMulti.svelte.d.ts +1 -1
  112. package/dist/form/CronBuilder.svelte +84 -67
  113. package/dist/form/CronBuilder.svelte.d.ts +16 -3
  114. package/dist/form/DatePicker.svelte +148 -195
  115. package/dist/form/DateRangePicker.svelte +101 -98
  116. package/dist/form/DateTimePicker.svelte +167 -208
  117. package/dist/form/DateTimePicker.svelte.d.ts +10 -2
  118. package/dist/form/FormGroup.svelte +33 -10
  119. package/dist/form/FormGroup.svelte.d.ts +12 -2
  120. package/dist/form/Number.svelte +70 -47
  121. package/dist/form/Number.svelte.d.ts +36 -6
  122. package/dist/form/Radio.svelte +18 -18
  123. package/dist/form/RadioGroup.svelte +23 -12
  124. package/dist/form/RadioGroup.svelte.d.ts +13 -2
  125. package/dist/form/SegmentedControl.svelte +76 -56
  126. package/dist/form/SegmentedControl.svelte.d.ts +9 -4
  127. package/dist/form/Select.svelte +150 -233
  128. package/dist/form/Select.svelte.d.ts +29 -5
  129. package/dist/form/SelectMulti.svelte +163 -277
  130. package/dist/form/SelectMulti.svelte.d.ts +36 -7
  131. package/dist/form/Slider.svelte +183 -269
  132. package/dist/form/Slider.svelte.d.ts +32 -17
  133. package/dist/form/StringArrayBuilder.svelte +249 -0
  134. package/dist/form/StringArrayBuilder.svelte.d.ts +15 -0
  135. package/dist/form/Switch.svelte +164 -106
  136. package/dist/form/Switch.svelte.d.ts +22 -3
  137. package/dist/form/TextArea.svelte +49 -29
  138. package/dist/form/TextArea.svelte.d.ts +23 -3
  139. package/dist/form/Textbox.svelte +69 -68
  140. package/dist/form/Textbox.svelte.d.ts +32 -4
  141. package/dist/form/TimePicker.svelte +42 -47
  142. package/dist/form/TimePicker.svelte.d.ts +12 -2
  143. package/dist/form/comboBoxCore.svelte.d.ts +99 -0
  144. package/dist/form/comboBoxCore.svelte.js +243 -0
  145. package/dist/icon/Icon.svelte +8 -3
  146. package/dist/icon/Icon.svelte.d.ts +8 -3
  147. package/dist/icon/file-type-bmp.svg +1 -0
  148. package/dist/icon/file-type-csv.svg +1 -0
  149. package/dist/icon/file-type-doc.svg +1 -0
  150. package/dist/icon/file-type-docx.svg +1 -0
  151. package/dist/icon/file-type-jpg.svg +1 -0
  152. package/dist/icon/file-type-pdf.svg +1 -0
  153. package/dist/icon/file-type-png.svg +1 -0
  154. package/dist/icon/file-type-ppt.svg +1 -0
  155. package/dist/icon/file-type-svg.svg +1 -0
  156. package/dist/icon/file-type-txt.svg +1 -0
  157. package/dist/icon/file-type-xls.svg +1 -0
  158. package/dist/icon/file-type-zip.svg +1 -0
  159. package/dist/icon/index.d.ts +166 -151
  160. package/dist/icon/index.js +166 -152
  161. package/dist/icon/logo-placeholder.svg +6 -0
  162. package/dist/icon/source-code.svg +1 -0
  163. package/dist/icon/sun-moon.svg +1 -0
  164. package/dist/icon/table.svg +6 -0
  165. package/dist/index.d.ts +87 -26
  166. package/dist/index.js +25 -28
  167. package/dist/layout/AppShell.svelte +12 -162
  168. package/dist/layout/AppShell.svelte.d.ts +6 -27
  169. package/dist/layout/CustomNavbar.svelte +9 -7
  170. package/dist/layout/CustomNavbar.svelte.d.ts +4 -2
  171. package/dist/layout/Navbar.svelte +161 -129
  172. package/dist/layout/Navbar.svelte.d.ts +29 -15
  173. package/dist/layout/NavbarItemDisplay.svelte +2 -2
  174. package/dist/layout/NavbarItemView.svelte +182 -0
  175. package/dist/layout/NavbarItemView.svelte.d.ts +10 -0
  176. package/dist/layout/Sidenav.svelte +280 -341
  177. package/dist/layout/Sidenav.svelte.d.ts +36 -4
  178. package/dist/models/ComboBoxItem.d.ts +2 -0
  179. package/dist/models/Employee.d.ts +53 -0
  180. package/dist/models/Employee.js +23 -0
  181. package/dist/models/Hyperlink.d.ts +3 -1
  182. package/dist/models/LinkCollectionItem.d.ts +3 -1
  183. package/dist/models/NavbarItem.d.ts +6 -2
  184. package/dist/theme.svelte.d.ts +0 -1
  185. package/dist/theme.svelte.js +46 -58
  186. package/dist/ui/Accordion.svelte +81 -242
  187. package/dist/ui/Accordion.svelte.d.ts +37 -14
  188. package/dist/ui/AccordionItem.svelte +303 -0
  189. package/dist/ui/AccordionItem.svelte.d.ts +21 -0
  190. package/dist/ui/ActionIcon.svelte +25 -21
  191. package/dist/ui/ActionIcon.svelte.d.ts +15 -2
  192. package/dist/ui/Badge.svelte +163 -171
  193. package/dist/ui/Badge.svelte.d.ts +13 -5
  194. package/dist/ui/Button.svelte +178 -205
  195. package/dist/ui/Button.svelte.d.ts +19 -2
  196. package/dist/ui/ButtonVariant.d.ts +15 -1
  197. package/dist/ui/ContextMenu.svelte +146 -0
  198. package/dist/ui/ContextMenu.svelte.d.ts +37 -0
  199. package/dist/ui/ContextMenuList.svelte +188 -0
  200. package/dist/ui/ContextMenuList.svelte.d.ts +12 -0
  201. package/dist/ui/Dialog.svelte +183 -111
  202. package/dist/ui/Dialog.svelte.d.ts +21 -4
  203. package/dist/ui/Drawer.svelte +237 -184
  204. package/dist/ui/Drawer.svelte.d.ts +20 -2
  205. package/dist/ui/Dropdown.svelte +24 -14
  206. package/dist/ui/Dropdown.svelte.d.ts +11 -2
  207. package/dist/ui/Dropzone.svelte +159 -57
  208. package/dist/ui/Dropzone.svelte.d.ts +29 -2
  209. package/dist/ui/Link.svelte +12 -4
  210. package/dist/ui/Link.svelte.d.ts +8 -2
  211. package/dist/ui/Loader.svelte +23 -18
  212. package/dist/ui/Loader.svelte.d.ts +5 -2
  213. package/dist/ui/Logo.svelte +37 -0
  214. package/dist/ui/Logo.svelte.d.ts +16 -0
  215. package/dist/ui/MultiSortable.svelte +287 -0
  216. package/dist/ui/MultiSortable.svelte.d.ts +50 -0
  217. package/dist/ui/Pagination.svelte +15 -9
  218. package/dist/ui/Pagination.svelte.d.ts +6 -2
  219. package/dist/ui/Popover.svelte +43 -65
  220. package/dist/ui/Popover.svelte.d.ts +14 -5
  221. package/dist/ui/ProgressBar.svelte +28 -30
  222. package/dist/ui/ProgressBarVariant.d.ts +7 -1
  223. package/dist/ui/SegmentedControl.svelte +0 -0
  224. package/dist/ui/SegmentedControl.svelte.d.ts +26 -0
  225. package/dist/ui/Sortable.svelte +163 -0
  226. package/dist/ui/Sortable.svelte.d.ts +47 -0
  227. package/dist/ui/Table.svelte +383 -80
  228. package/dist/ui/Table.svelte.d.ts +43 -2
  229. package/dist/ui/Tabs.svelte +22 -18
  230. package/dist/ui/Tabs.svelte.d.ts +6 -2
  231. package/dist/ui/ThemeSwitcher.svelte +26 -7
  232. package/dist/ui/ThemeSwitcher.svelte.d.ts +4 -2
  233. package/dist/ui/Tooltip.svelte +312 -180
  234. package/dist/ui/Tooltip.svelte.d.ts +4 -3
  235. package/dist/uiTheme.svelte.d.ts +86 -0
  236. package/dist/uiTheme.svelte.js +214 -0
  237. package/dist/util/DateFunctions.js +1 -1
  238. package/dist/util/DragDrop.d.ts +50 -0
  239. package/dist/util/DragDrop.js +154 -0
  240. package/dist/util/NavigateTo.js +2 -2
  241. package/dist/util/dayjs.d.ts +4 -0
  242. package/dist/util/dayjs.js +15 -0
  243. package/package.json +66 -26
  244. package/dist/display/Alert.svelte +0 -179
  245. package/dist/display/Alert.svelte.d.ts +0 -13
  246. package/dist/form/FileInput.svelte +0 -235
  247. package/dist/form/FileInput.svelte.d.ts +0 -23
  248. package/dist/form/PasswordInput.svelte +0 -253
  249. package/dist/form/PasswordInput.svelte.d.ts +0 -34
  250. package/dist/form/Rating.svelte +0 -235
  251. package/dist/form/Rating.svelte.d.ts +0 -21
  252. package/dist/styles/components.css +0 -199
  253. package/dist/styles/dark.css +0 -146
  254. package/dist/styles/index.css +0 -116
  255. package/dist/styles/reset.css +0 -110
  256. package/dist/styles/semantic.css +0 -86
  257. package/dist/styles/tokens.css +0 -203
  258. package/dist/styles/utilities.css +0 -523
  259. package/dist/ui/Breadcrumbs.svelte +0 -131
  260. package/dist/ui/Breadcrumbs.svelte.d.ts +0 -16
  261. package/dist/ui/LoadingOverlay.svelte +0 -53
  262. package/dist/ui/LoadingOverlay.svelte.d.ts +0 -11
  263. package/dist/ui/Progress.svelte +0 -191
  264. package/dist/ui/Progress.svelte.d.ts +0 -15
  265. package/dist/ui/RingProgress.svelte +0 -141
  266. package/dist/ui/RingProgress.svelte.d.ts +0 -18
  267. package/dist/ui/Skeleton.svelte +0 -85
  268. package/dist/ui/Skeleton.svelte.d.ts +0 -12
  269. package/dist/ui/Stepper.svelte +0 -355
  270. package/dist/ui/Stepper.svelte.d.ts +0 -24
  271. package/dist/ui/Timeline.svelte +0 -225
  272. package/dist/ui/Timeline.svelte.d.ts +0 -23
  273. /package/dist/{actions → util}/ClickOutside.d.ts +0 -0
  274. /package/dist/{actions → util}/ClickOutside.js +0 -0
  275. /package/dist/{transitions → util}/Transitions.d.ts +0 -0
  276. /package/dist/{transitions → util}/Transitions.js +0 -0
@@ -1,104 +1,157 @@
1
1
  <script lang="ts">
2
- import { clickOutside } from "../actions/ClickOutside.js";
3
- import type { NotifyModel } from "../models/NotifyModel.js";
2
+ import { clickOutside } from '../util/ClickOutside.js';
4
3
  import type {
5
4
  ComboBoxGroup,
6
5
  ComboBoxItem,
7
6
  FetchFunctionType,
8
7
  RetrieveLabelFunctionType,
9
- SearchFunctionType,
10
- } from "../models/ComboBoxItem.js";
11
- import ComboBoxMulti from "./ComboBoxMulti.svelte";
12
- import FormGroup from "./FormGroup.svelte";
13
- import Icon from "../icon/Icon.svelte";
14
- import Loader from "../ui/Loader.svelte";
15
- import { useDialogEvents } from "../ui/DialogEvents.svelte.js";
16
- import selectorSvg from "../icon/selector.svg?raw";
17
- import Textbox from "./Textbox.svelte";
18
- import Badge from "../ui/Badge.svelte";
19
- import ActionIcon from "../ui/ActionIcon.svelte";
20
- import { iconX } from "../icon/index.js";
21
- import { onMount, tick, type Snippet } from "svelte";
22
-
23
- interface Props {
24
- /** HTML name attribute for the select */
8
+ SearchFunctionType
9
+ } from '../models/ComboBoxItem.js';
10
+ import { ComboBoxMultiCore } from './comboBoxCore.svelte.js';
11
+ import ComboBoxMulti from './ComboBoxMulti.svelte';
12
+ import FormGroup from './FormGroup.svelte';
13
+ import Icon from '../icon/Icon.svelte';
14
+ import Loader from '../ui/Loader.svelte';
15
+ import { useDialogEvents } from '../ui/DialogEvents.svelte.js';
16
+ import selectorSvg from '../icon/selector.svg?raw';
17
+ import Textbox from './Textbox.svelte';
18
+ import Badge from '../ui/Badge.svelte';
19
+ import ActionIcon from '../ui/ActionIcon.svelte';
20
+ import { iconX } from '../icon/index.js';
21
+ import { onMount, tick, type Snippet } from 'svelte';
22
+
23
+ const autoName = `selectmulti-${crypto.randomUUID()}`;
24
+
25
+ export interface SelectMultiProps {
26
+ /** HTML name attribute for the select (falls back to label, then auto-generated) */
25
27
  name?: string;
28
+ /** Label text displayed above the select */
26
29
  label?: string;
30
+ /** Array of selected values (bindable) */
27
31
  values?: string[];
32
+ /**
33
+ * Array of full selected item objects (bindable). Can be passed initially to preset
34
+ * the selection with known labels — no `values` or retrieveLabelFunction needed.
35
+ */
28
36
  rawValues?: ComboBoxItem[];
37
+ /** Placeholder text when no values are selected */
29
38
  placeholder?: string;
39
+ /** CSS classes for the select element */
30
40
  class?: string;
41
+ /** CSS classes for the container element */
31
42
  containerClass?: string;
43
+ /** Disable the select */
32
44
  disabled?: boolean;
45
+ /** Mark field as required (shows asterisk) */
33
46
  required?: boolean;
47
+ /** Show error state styling */
34
48
  showError?: boolean;
49
+ /** Error message to display */
35
50
  errorText?: string;
36
- autofocus?: boolean; // will also cause dialog autofocus on first open only
37
- autofocusDialog?: boolean; // will cause autofocus on every open, not just the first time
51
+ /** Auto-focus on mount (first dialog open only) */
52
+ autofocus?: boolean;
53
+ /** Auto-focus on every dialog open */
54
+ autofocusDialog?: boolean;
55
+ /** Enable search/filter within options */
38
56
  allowSearch?: boolean;
39
- showNoResultsMessage?: boolean;
57
+ /** Hide "no results" message when search finds nothing */
58
+ hideNoResults?: boolean;
59
+ /** Close dropdown after selecting an option */
40
60
  closeOnSelection?: boolean;
61
+ /** Text shown while loading options */
41
62
  loadingText?: string;
63
+ /** Flat list of selectable options */
42
64
  options?: ComboBoxItem[];
65
+ /** Options organized into groups */
43
66
  groupedOptions?: ComboBoxGroup[];
44
- tooltipLocation?: "top" | "bottom" | "left" | "right";
67
+ /** Position of the tooltip */
68
+ tooltipLocation?: 'top' | 'bottom' | 'left' | 'right';
69
+ /** Async function to fetch options */
45
70
  fetchFunction?: FetchFunctionType | null;
71
+ /** Async function to search/filter options */
46
72
  searchFunction?: SearchFunctionType | null;
73
+ /** Async function to retrieve labels for values */
47
74
  retrieveLabelFunction?: RetrieveLabelFunctionType | null;
75
+ /** Rich tooltip content using a Svelte snippet */
48
76
  tooltipContent?: Snippet;
77
+ /** Simple tooltip text */
49
78
  tooltipText?: string;
79
+ /** Callback when selection changes (returns value strings) */
50
80
  onchange?: (e: string[]) => void;
81
+ /** Callback when selection changes (returns full item objects) */
51
82
  onchangeRaw?: (e: ComboBoxItem[]) => void;
52
83
  }
53
84
 
54
85
  let {
55
86
  name,
56
- label = "",
87
+ label = '',
57
88
  values = $bindable([]),
58
89
  rawValues = $bindable([]),
59
- placeholder = "\u00a0", // non-breaking space to prevent the height collapsing
60
- class: classes = "",
61
- containerClass = "",
90
+ placeholder = '\u00a0', // non-breaking space to prevent the height collapsing
91
+ class: classes = '',
92
+ containerClass = '',
62
93
  disabled = false,
63
94
  required = false,
64
95
  showError = false,
65
- errorText = "",
96
+ errorText = '',
66
97
  autofocus = false,
67
98
  autofocusDialog = false,
68
99
  allowSearch = false,
69
- showNoResultsMessage = true,
100
+ hideNoResults = false,
70
101
  closeOnSelection = false,
71
- loadingText = "",
102
+ loadingText = '',
72
103
  options = [],
73
104
  groupedOptions = [],
74
- tooltipLocation = "top",
105
+ tooltipLocation = 'top',
75
106
  fetchFunction = null,
76
107
  searchFunction = null,
77
108
  retrieveLabelFunction = null,
78
109
  tooltipContent,
79
110
  tooltipText,
80
111
  onchange,
81
- onchangeRaw,
82
- }: Props = $props();
112
+ onchangeRaw
113
+ }: SelectMultiProps = $props();
114
+
115
+ let resolvedName = $derived((name || label || autoName).replace(/[^a-zA-Z0-9_\-:.]/g, '_'));
116
+ let computedAllowSearch = $derived(searchFunction ? true : allowSearch);
83
117
 
84
118
  let internalValues: string[] = $state([]);
85
- let filterString = $state("");
86
- let preloading = $state(false);
87
- let searching = $state(false);
88
- let timeout: number;
119
+ let filterString = $state('');
89
120
 
90
- let allGroups: ComboBoxGroup[] = $state([]);
91
- let filteredGroups: ComboBoxGroup[] = $state([]);
121
+ if (values === null || values === undefined) values = [];
122
+ if (rawValues === null || rawValues === undefined) rawValues = [];
123
+ // Callers may preset the selection by passing only rawValues — adopt their values
124
+ if (values.length === 0 && rawValues.length > 0) values = rawValues.map((r) => r.value);
125
+
126
+ const core = new ComboBoxMultiCore({
127
+ getOptions: () => options,
128
+ getGroupedOptions: () => groupedOptions,
129
+ getFetchFunction: () => fetchFunction,
130
+ getSearchFunction: () => searchFunction,
131
+ getRetrieveLabelFunction: () => retrieveLabelFunction,
132
+ getValues: () => values,
133
+ setValues: (v) => (values = v),
134
+ setRawValues: (r) => (rawValues = r),
135
+ initialRawValues: [...rawValues],
136
+ clearUnknownStatic: true,
137
+ onValuesCleared: () => {
138
+ internalValues = [...values];
139
+ onchange?.(values);
140
+ onchangeRaw?.(rawValues);
141
+ },
142
+ onRawValuesResolved: (raw) => onchangeRaw?.(raw),
143
+ // Re-apply the active filter so a mid-search options swap doesn't drop results
144
+ onOptionsChanged: () => {
145
+ if (filterString) onFilterChange(filterString);
146
+ }
147
+ });
92
148
 
93
149
  let open = $state(false);
94
150
  let hideCombobox = $state(false);
95
151
 
96
- if (values === null || values === undefined) values = [];
97
-
98
152
  let isEmpty: boolean = $derived(rawValues?.length === 0);
99
153
 
100
154
  onMount(() => {
101
- initOptions(options);
102
155
  if (autofocus) {
103
156
  tick().then(() => {
104
157
  focus();
@@ -122,7 +175,7 @@
122
175
  focus();
123
176
  }, 150);
124
177
  }
125
- },
178
+ }
126
179
  });
127
180
 
128
181
  function focus() {
@@ -130,173 +183,31 @@
130
183
  tick().then(() => textboxElement?.focus());
131
184
  }
132
185
 
133
- let canReloadValues = false;
134
-
135
186
  $effect(() => {
136
187
  const currentValues = values || [];
137
188
  const currentInternalValues = internalValues || [];
138
189
 
139
190
  const arraysAreDifferent =
140
191
  currentValues.length !== currentInternalValues.length ||
141
- currentValues.some(
142
- (value, index) => value !== currentInternalValues[index],
143
- );
192
+ currentValues.some((value, index) => value !== currentInternalValues[index]);
144
193
 
145
194
  if (arraysAreDifferent) {
146
195
  internalValues = [...currentValues];
147
-
148
- // Reset all options selection state
149
- for (let option of allGroups.flatMap((x) => x.items)) {
150
- option.selected = false;
196
+ const selfChanged = core.preloadValues();
197
+ // preloadValues fires its own callbacks when it drops invalid values; don't double-notify
198
+ if (!selfChanged) {
199
+ onchangeRaw?.(rawValues);
151
200
  }
152
-
153
- // Update rawValues based on new values
154
- let newRawValues: ComboBoxItem[] = [];
155
-
156
- for (let value of currentValues) {
157
- const foundOption = allGroups
158
- .flatMap((x) => x.items)
159
- .find((x) => x.value === value);
160
-
161
- if (foundOption) {
162
- foundOption.selected = true;
163
- newRawValues.push({ ...foundOption });
164
- } else if (value != null) {
165
- // Create a new option if not found in allOptions
166
- const newOption = {
167
- label: String(value),
168
- value: value,
169
- selected: true,
170
- };
171
- newRawValues.push(newOption);
172
- }
173
- }
174
-
175
- rawValues = newRawValues;
176
201
  }
177
202
  });
178
203
 
179
- function initOptions(options: ComboBoxItem[]) {
180
- if (fetchFunction) {
181
- preloading = true;
182
- fetchFunction().then((response: NotifyModel<ComboBoxItem[]>) => {
183
- convertOptions(response.object!);
184
- preloadValues();
185
- preloading = false;
186
- });
187
- } else {
188
- if (groupedOptions.length) {
189
- convertGroupOptions(groupedOptions);
190
- } else if (options.length) {
191
- convertOptions(options);
192
- } else if (searchFunction) {
193
- // Initialize empty groups for search function
194
- allGroups = [];
195
- filteredGroups = [];
196
- allowSearch = true;
197
- } else {
198
- filteredGroups = [];
199
- }
200
- preloadValues();
201
- }
202
- }
203
-
204
- function preloadValues(overwrite: boolean = false) {
205
- if (!values || values.length === 0) {
206
- rawValues = [];
207
- values = [];
208
-
209
- for (let option of allGroups.flatMap((x) => x.items))
210
- option.selected = false;
211
-
212
- return;
213
- }
214
-
215
- if (rawValues.length > 0 && !overwrite) return;
216
-
217
- let promises = [];
218
- let newRawValues: ComboBoxItem[] = [];
219
-
220
- for (let option of allGroups.flatMap((x) => x.items))
221
- option.selected = false;
222
-
223
- for (let value of values) {
224
- const foundOption = allGroups
225
- .flatMap((x) => x.items)
226
- .find((x) => x.value === value);
227
-
228
- if (foundOption !== undefined) {
229
- foundOption.selected = true;
230
- newRawValues.push(foundOption);
231
- } else if (searchFunction && retrieveLabelFunction) {
232
- preloading = true;
233
- let object = { label: value, value: value, selected: true };
234
-
235
- promises.push(
236
- retrieveLabelFunction(value).then((response) => {
237
- object.label = response.object ?? value!.toString();
238
- }),
239
- );
240
-
241
- newRawValues.push(object);
242
- } else {
243
- newRawValues.push({
244
- label: value,
245
- value: value,
246
- selected: true,
247
- });
248
- }
249
- }
250
-
251
- if (promises && promises.length > 0) {
252
- preloading = true;
253
-
254
- Promise.all(promises).then((x) => {
255
- rawValues = newRawValues;
256
- preloading = false;
257
- });
258
- } else {
259
- rawValues = newRawValues;
260
- }
261
- }
262
-
263
- function convertOptions(options: ComboBoxItem[]) {
264
- const allOptions = options.map((option) => {
265
- return {
266
- ...option,
267
- selected: values && values.some((x) => x === option.value),
268
- };
269
- });
270
- allGroups = [{ label: "", items: [...allOptions] }];
271
- filteredGroups = [{ label: "", items: [...allOptions] }];
272
- }
273
-
274
- function convertGroupOptions(groups: ComboBoxGroup[]) {
275
- allGroups = groups.map((group) => {
276
- return {
277
- ...group,
278
- items: group.items.map((option) => {
279
- return {
280
- ...option,
281
- selected:
282
- values && values.some((x) => x === option.value),
283
- groupName: group.label,
284
- };
285
- }),
286
- };
287
- });
288
- filteredGroups = [...allGroups];
289
- }
290
-
291
204
  function onSelection(selectedValue: ComboBoxItem) {
292
205
  // Find the actual option in allOptions to ensure we're working with the correct reference
293
- const optionIndex = allGroups
206
+ const optionIndex = core.allGroups
294
207
  .flatMap((x) => x.items)
295
208
  .findIndex((opt) => opt.value === selectedValue.value);
296
209
  const actualOption =
297
- optionIndex !== -1
298
- ? allGroups.flatMap((x) => x.items)[optionIndex]
299
- : selectedValue;
210
+ optionIndex !== -1 ? core.allGroups.flatMap((x) => x.items)[optionIndex] : selectedValue;
300
211
 
301
212
  actualOption.selected = !actualOption.selected;
302
213
 
@@ -305,11 +216,11 @@
305
216
  rawValues = [...rawValues, actualOption];
306
217
  } else if (!actualOption.selected) {
307
218
  values = values.filter((value) => value !== actualOption.value);
308
- rawValues = rawValues.filter(
309
- (rawValue) => rawValue.value !== actualOption.value,
310
- );
219
+ rawValues = rawValues.filter((rawValue) => rawValue.value !== actualOption.value);
311
220
  }
312
221
 
222
+ internalValues = [...values];
223
+
313
224
  onchange?.(values);
314
225
  onchangeRaw?.(rawValues);
315
226
 
@@ -323,49 +234,25 @@
323
234
 
324
235
  function onFilterChange(value: string) {
325
236
  if (searchFunction && value) {
326
- clearTimeout(timeout);
327
- searching = true;
328
- timeout = window.setTimeout(() => {
329
- searchFunction(value).then(
330
- (response: NotifyModel<ComboBoxItem[]>) => {
331
- convertOptions(response.object!);
332
- searching = false;
333
- open = true;
334
- hideCombobox = false;
335
- },
336
- );
337
- }, 300);
338
- } else if (allGroups) {
339
- filteredGroups = allGroups.filter((group) =>
340
- group.items.some((x) =>
341
- x.label.toLowerCase().includes(value.toLowerCase()),
342
- ),
343
- );
344
-
345
- if (filteredGroups.length > 0) {
346
- filteredGroups = filteredGroups.map((group) => ({
347
- ...group,
348
- items: group.items.filter((x) =>
349
- x.label.toLowerCase().includes(value.toLowerCase()),
350
- ),
351
- }));
352
- }
353
-
237
+ core.search(value, () => {
238
+ open = true;
239
+ hideCombobox = false;
240
+ });
241
+ } else {
242
+ core.filterLocal(value);
354
243
  hideCombobox = false;
355
244
  }
356
245
  }
357
246
 
358
- let comboBoxEl: ReturnType<typeof ComboBoxMulti> | undefined =
359
- $state(undefined);
247
+ let comboBoxEl: ReturnType<typeof ComboBoxMulti> | undefined = $state(undefined);
360
248
  let textboxElement: HTMLElement | undefined = $state(undefined);
361
249
  let buttonElement: HTMLElement;
362
- let comboBoxKeyDown: ((e: KeyboardEvent) => void) | undefined =
363
- $state(undefined);
250
+ let comboBoxKeyDown: ((e: KeyboardEvent) => void) | undefined = $state(undefined);
364
251
 
365
252
  function onKeyDown(e: KeyboardEvent) {
366
253
  if (!open) return;
367
254
 
368
- if (e.key === "Escape") {
255
+ if (e.key === 'Escape') {
369
256
  e.preventDefault();
370
257
  open = false;
371
258
  } else if (comboBoxKeyDown) {
@@ -376,8 +263,7 @@
376
263
  }
377
264
 
378
265
  function closePopover(e: MouseEvent) {
379
- if (textboxElement && textboxElement.contains(e.target as HTMLElement))
380
- return;
266
+ if (textboxElement && textboxElement.contains(e.target as HTMLElement)) return;
381
267
  if (buttonElement.contains(e.target as HTMLElement)) return;
382
268
 
383
269
  open = false;
@@ -385,11 +271,10 @@
385
271
 
386
272
  const extraClasses: string[] = $derived.by(() => {
387
273
  const classes = [];
388
- if (open) classes.push("!border-accent");
389
- if (allowSearch && open && !isEmpty)
390
- classes.push("border-b-0 rounded-b-none");
391
- if (disabled) classes.push("disabled");
392
- if (showError) classes.push("show-error");
274
+ if (open) classes.push('!border-accent');
275
+ if (computedAllowSearch && open && !isEmpty) classes.push('border-b-0 rounded-b-none');
276
+ if (disabled) classes.push('disabled');
277
+ if (showError) classes.push('show-error');
393
278
 
394
279
  return classes;
395
280
  });
@@ -397,25 +282,24 @@
397
282
 
398
283
  <div
399
284
  class="select-container {containerClass}"
400
- class:mb-[1px]={allowSearch && open && !isEmpty}
285
+ class:mb-[1px]={computedAllowSearch && open && !isEmpty}
401
286
  >
402
287
  <FormGroup {label} {required} {tooltipContent} {tooltipLocation}>
403
288
  <button
404
289
  bind:this={buttonElement}
405
290
  onclick={() => {
406
291
  open = !open;
407
- if (open && allowSearch) {
292
+ if (open && computedAllowSearch) {
408
293
  tick().then(() => textboxElement?.focus());
409
294
  }
410
295
  }}
411
296
  onkeydown={(e) => onKeyDown(e)}
412
- disabled={preloading || disabled}
297
+ name={resolvedName}
298
+ disabled={core.preloading || disabled}
413
299
  class="select {extraClasses} {classes}"
414
300
  >
415
- {#if isEmpty || preloading}
416
- <span class="placeholder"
417
- >{preloading ? "Loading..." : placeholder}</span
418
- >
301
+ {#if isEmpty || core.preloading}
302
+ <span class="placeholder">{core.preloading ? 'Loading...' : placeholder}</span>
419
303
  {:else}
420
304
  <div class="selected-items">
421
305
  {#each rawValues as value}
@@ -423,13 +307,13 @@
423
307
  <Badge>{value.label}</Badge>
424
308
  {:else}
425
309
  <Badge
426
- onDelete={() => {
310
+ ondelete={() => {
427
311
  onSelection(value);
428
312
  tick().then(() => (open = false));
429
313
  }}>{value.label}</Badge
430
314
  >
431
315
  {/if}
432
- <!-- <span class="inline-flex items-center bg-primary text-white rounded-md px-2">
316
+ <!-- <span class="inline-flex items-center bg-midnight text-white rounded-md px-2">
433
317
  {value.label}
434
318
  <button
435
319
  class="leading-0 ml-1 size-4 rounded hover:bg-neutral-400/25"
@@ -448,7 +332,7 @@
448
332
  {/each}
449
333
  </div>
450
334
  {/if}
451
- {#if preloading || searching}
335
+ {#if core.preloading || core.searching}
452
336
  <div class="loader">
453
337
  <Loader sizeOverride="1.125rem" />
454
338
  </div>
@@ -460,15 +344,13 @@
460
344
  size="0.75rem"
461
345
  onclick={(e: MouseEvent) => {
462
346
  values = [];
347
+ internalValues = [];
463
348
  rawValues = [];
464
349
 
465
350
  onchange?.(values);
466
351
  onchangeRaw?.(rawValues);
467
352
 
468
- options.forEach((x) => (x.selected = false));
469
- allGroups
470
- .flatMap((x) => x.items)
471
- .forEach((x) => (x.selected = false));
353
+ core.allGroups.flatMap((x) => x.items).forEach((x) => (x.selected = false));
472
354
 
473
355
  e.stopPropagation();
474
356
  }}
@@ -486,7 +368,7 @@
486
368
  {open ? '' : 'hidden'}"
487
369
  >
488
370
  <div class="select-panel-inner" use:clickOutside={closePopover}>
489
- {#if allowSearch}
371
+ {#if computedAllowSearch}
490
372
  <Textbox
491
373
  bind:value={filterString}
492
374
  class="w-full
@@ -494,7 +376,7 @@
494
376
  {open && !isEmpty ? 'border-t-0 rounded-t-none' : ''}"
495
377
  bind:textboxElement
496
378
  placeholder="Start typing to search..."
497
- disabled={preloading}
379
+ disabled={core.preloading}
498
380
  autocomplete="off"
499
381
  oninput={() => onFilterChange(filterString)}
500
382
  onkeydown={onKeyDown}
@@ -506,10 +388,10 @@
506
388
  values={rawValues}
507
389
  {onSelection}
508
390
  onkeydown={comboBoxKeyDown}
509
- groupedOptions={filteredGroups}
391
+ groupedOptions={core.filteredGroups}
510
392
  open={open && !hideCombobox}
511
- loading={searching}
512
- {showNoResultsMessage}
393
+ loading={core.searching}
394
+ {hideNoResults}
513
395
  {loadingText}
514
396
  />
515
397
  </div>
@@ -525,53 +407,57 @@
525
407
  }
526
408
 
527
409
  .select {
528
- font-size: var(--pui-font-size-md);
529
- line-height: var(--pui-line-height-normal);
410
+ font-size: 1rem;
411
+ line-height: 1.5rem;
530
412
  position: relative;
531
413
  display: flex;
532
414
  flex-wrap: wrap;
533
- padding: var(--pui-spacing-1) var(--pui-spacing-2);
534
- border-radius: var(--pui-radius-base);
415
+ padding: 0.25rem 0.5rem;
416
+ border-radius: 0.25rem;
535
417
  text-align: left;
536
- background-color: var(--pui-input-bg);
537
- color: var(--pui-text-primary);
538
- border-color: var(--pui-border-default);
418
+ background-color: var(--input-bg-color);
419
+ color: var(--text-color);
420
+ border-color: var(--border-color);
539
421
  cursor: pointer;
540
422
  width: 100%;
541
423
  align-items: center;
542
- border: 1px solid var(--pui-border-default);
543
- padding-right: var(--pui-spacing-8);
424
+ border: 1px solid var(--border-color);
425
+ padding-right: 2rem;
426
+ /* relative flex flex-wrap gap-x-1 gap-y-1 text-base rounded py-1 px-2 text-left focus:outline-none
427
+ bg-input border border-main-border focus-visible:border-accent
428
+ placeholder:text-zinc-400 dark:placeholder:text-neutral-500
429
+ disabled:bg-main-border disabled:cursor-not-allowed */
544
430
  }
545
431
 
546
432
  .select:focus {
547
- border-color: var(--pui-accent-color);
433
+ border-color: var(--accent-color);
548
434
  outline: 2px solid transparent;
549
- outline-offset: var(--pui-focus-ring-offset);
435
+ outline-offset: 2px;
550
436
  }
551
437
 
552
438
  .select-icon {
553
439
  position: absolute;
554
- right: var(--pui-spacing-2);
555
- width: var(--pui-spacing-4);
440
+ right: 0.5rem;
441
+ width: 1rem;
556
442
  display: flex;
557
443
  }
558
444
 
559
445
  .btn-clear-container {
560
446
  position: absolute;
561
447
  line-height: 0;
562
- right: var(--pui-spacing-1);
448
+ right: 0.4rem;
563
449
  }
564
450
 
565
451
  .select-btn-clear {
566
- border-radius: var(--pui-radius-base);
567
- padding: var(--pui-spacing-1);
452
+ border-radius: 0.25rem;
453
+ padding: 0.25rem;
568
454
  }
569
455
 
570
456
  .select-panel {
571
457
  position: absolute;
572
458
  width: 100%;
573
459
  left: 0;
574
- z-index: var(--pui-z-dropdown);
460
+ z-index: 70;
575
461
  }
576
462
 
577
463
  .select-panel-inner {
@@ -580,7 +466,7 @@
580
466
  }
581
467
 
582
468
  .placeholder {
583
- color: var(--pui-text-placeholder);
469
+ color: var(--placeholder-color);
584
470
  }
585
471
 
586
472
  .hidden {
@@ -590,27 +476,27 @@
590
476
  .disabled {
591
477
  opacity: 0.5;
592
478
  cursor: not-allowed;
593
- background-color: var(--pui-bg-disabled);
479
+ background-color: var(--border-color);
594
480
  }
595
481
 
596
482
  .selected-items {
597
483
  display: flex;
598
484
  flex-wrap: wrap;
599
- gap: var(--pui-spacing-1);
485
+ gap: 0.25rem;
600
486
  }
601
487
 
602
488
  .loader {
603
489
  position: absolute;
604
- right: var(--pui-spacing-2_5);
605
- width: var(--pui-spacing-4);
490
+ right: 0.65rem;
491
+ width: 1rem;
606
492
  top: 6px;
607
493
  }
608
494
 
609
495
  .show-error {
610
- border-color: var(--pui-text-danger);
496
+ border-color: var(--danger-text);
611
497
  }
612
498
 
613
499
  .text-error {
614
- color: var(--pui-text-danger);
500
+ color: var(--danger-text);
615
501
  }
616
502
  </style>