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