@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,181 +1,282 @@
1
- <script lang="ts">
2
- import ComboBox from "./ComboBox.svelte";
3
- import { clickOutside } from "../actions/ClickOutside.js";
4
- import Textbox from "./Textbox.svelte";
5
- import type { NotifyModel } from "../models/NotifyModel.js";
1
+ <script module lang="ts">
6
2
  import type {
3
+ ComboBoxGroup,
7
4
  ComboBoxItem,
8
5
  FetchFunctionType,
9
- SearchFunctionType,
10
- } from "../models/ComboBoxItem.js";
11
- import { onMount } from "svelte";
6
+ RetrieveLabelFunctionType,
7
+ SearchFunctionType
8
+ } from '../models/ComboBoxItem.js';
9
+ import type { Snippet } from 'svelte';
12
10
 
13
- interface Props {
11
+ export interface AutocompleteProps {
12
+ /** HTML name attribute for the input */
13
+ name?: string;
14
+ /** Label text displayed above the input */
14
15
  label?: string;
16
+ /** Current input value (bindable) */
15
17
  value?: string;
18
+ /** Full selected item object (bindable) */
19
+ rawValue?: ComboBoxItem;
20
+ /** Placeholder text when input is empty */
16
21
  placeholder?: string;
17
- showRequired?: boolean;
18
- classes?: string;
22
+ /** Mark field as required (shows asterisk) */
23
+ required?: boolean;
24
+ /** CSS classes for the input element */
25
+ class?: string;
26
+ /** CSS classes for the container element */
27
+ containerClass?: string;
28
+ /** Disable the input */
29
+ disabled?: boolean;
30
+ /** Show error state styling */
31
+ showError?: boolean;
32
+ /** Error message to display */
33
+ errorText?: string;
34
+ /** Auto-focus the input on mount */
19
35
  autofocus?: boolean;
20
- showNoResultsMessage?: boolean;
36
+ /** Auto-focus on every dialog open */
37
+ autofocusDialog?: boolean;
38
+ /** Hide "no results" message when search finds nothing */
39
+ hideNoResults?: boolean;
40
+ /** Prevent updating value when an option is selected */
21
41
  preventChangeOnSelection?: boolean;
22
- allowCreate?: boolean;
42
+ /** Allow suggesting a new value be added when the input doesn't match an existing option */
43
+ allowAdd?: boolean;
44
+ /** Builds the label shown for the "add new value" suggestion (default: `Add "<value>"`) */
45
+ addItemLabel?: (value: string) => string;
46
+ /** Text shown while loading options */
23
47
  loadingText?: string;
48
+ /** CSS classes for the FormGroup wrapper */
24
49
  groupClass?: string;
50
+ /** SVG icon displayed on the left side */
25
51
  leftIconSvg?: string;
52
+ /** Static list of autocomplete options */
26
53
  options?: ComboBoxItem[];
27
- fetchFunction?: FetchFunctionType;
28
- searchFunction?: SearchFunctionType;
54
+ /** Options organized into groups */
55
+ groupedOptions?: ComboBoxGroup[];
56
+ /** Position of the tooltip */
57
+ tooltipLocation?: 'top' | 'bottom' | 'left' | 'right';
58
+ /** Async function to fetch all options */
59
+ fetchFunction?: FetchFunctionType | null;
60
+ /** Async function to search/filter options based on input */
61
+ searchFunction?: SearchFunctionType | null;
62
+ /** Async function to retrieve label for a value */
63
+ retrieveLabelFunction?: RetrieveLabelFunctionType | null;
64
+ /** Rich tooltip content using a Svelte snippet */
65
+ tooltipContent?: Snippet;
66
+ /** Simple tooltip text */
67
+ tooltipText?: string;
68
+ /** Callback when input value changes */
29
69
  onchange?: (e: string) => void;
70
+ /** Callback when Enter key is pressed */
30
71
  enterPressed?: (e: string) => void;
72
+ /** Callback when an option is selected */
31
73
  onSelect?: (e: ComboBoxItem) => void;
32
- onCreate?: (value: string) => void;
74
+ /** Callback when selection changes (returns full item object) */
75
+ onchangeRaw?: (e: ComboBoxItem | undefined) => void;
76
+ /** Callback when the user chooses to add the typed value as a new item */
77
+ onItemAdded?: (value: string) => void;
33
78
  }
79
+ </script>
80
+
81
+ <script lang="ts">
82
+ import ComboBoxMulti from './ComboBoxMulti.svelte';
83
+ import { clickOutside } from '../util/ClickOutside.js';
84
+ import Textbox from './Textbox.svelte';
85
+ import { ComboBoxCore, findItemByValue } from './comboBoxCore.svelte.js';
86
+ import { tick } from 'svelte';
87
+ import { useDialogEvents } from '../ui/DialogEvents.svelte.js';
34
88
 
35
89
  let {
90
+ name,
36
91
  label,
37
- value = $bindable(""),
38
- placeholder = "Start typing to search...",
39
- showRequired = false,
40
- classes = "",
92
+ value = $bindable(''),
93
+ rawValue = $bindable(undefined),
94
+ placeholder = 'Start typing to search...',
95
+ required = false,
96
+ class: classes = '',
97
+ containerClass = '',
98
+ disabled = false,
99
+ showError = false,
100
+ errorText = '',
41
101
  autofocus = false,
42
- showNoResultsMessage = false,
102
+ autofocusDialog = false,
103
+ hideNoResults = false,
43
104
  preventChangeOnSelection = false,
44
- allowCreate = false,
45
- loadingText = "",
46
- groupClass = "",
105
+ allowAdd = false,
106
+ addItemLabel = (v: string) => `Add "${v}"`,
107
+ loadingText = '',
108
+ groupClass = '',
47
109
  leftIconSvg,
48
- options,
49
- fetchFunction,
50
- searchFunction,
110
+ options = [],
111
+ groupedOptions = [],
112
+ tooltipLocation = 'top',
113
+ fetchFunction = null,
114
+ searchFunction = null,
115
+ retrieveLabelFunction = null,
116
+ tooltipContent,
117
+ tooltipText,
51
118
  onchange = undefined,
52
119
  enterPressed = undefined,
53
120
  onSelect = undefined,
54
- onCreate = undefined,
55
- }: Props = $props();
121
+ onchangeRaw = undefined,
122
+ onItemAdded = undefined
123
+ }: AutocompleteProps = $props();
56
124
 
57
- let preloading = $state(false);
58
- let searching = $state(false);
59
- let timeout = setTimeout(() => {});
125
+ const core = new ComboBoxCore({
126
+ getOptions: () => options,
127
+ getGroupedOptions: () => groupedOptions,
128
+ getValue: () => value,
129
+ setValue: (v) => (value = v ?? ''),
130
+ setRawValue: (r) => (rawValue = r),
131
+ getFetchFunction: () => fetchFunction,
132
+ getSearchFunction: () => searchFunction,
133
+ getRetrieveLabelFunction: () => retrieveLabelFunction,
134
+ clearUnknownStatic: false,
135
+ onOptionsChanged: () => {
136
+ if (value) core.filterLocal(value);
137
+ }
138
+ });
139
+
140
+ /** Synthetic "add new value" suggestion shown when the typed value matches no option */
141
+ let addOption: ComboBoxItem | undefined = $derived.by(() => {
142
+ const trimmed = value?.trim();
143
+ if (!allowAdd || !trimmed) return undefined;
144
+
145
+ const exists = core.allGroups
146
+ .flatMap((x) => x.items)
147
+ .some(
148
+ (x) =>
149
+ x.label.toLowerCase() === trimmed.toLowerCase() ||
150
+ x.value.toLowerCase() === trimmed.toLowerCase()
151
+ );
152
+ if (exists) return undefined;
153
+
154
+ return { label: addItemLabel(trimmed), value: trimmed, isAddNew: true };
155
+ });
60
156
 
61
- let allOptions: ComboBoxItem[] = [];
62
- let filteredOptions: ComboBoxItem[] = $state([]);
157
+ /** Groups passed to the dropdown, including the add suggestion when applicable */
158
+ let displayGroups: ComboBoxGroup[] = $derived(
159
+ addOption
160
+ ? [...core.filteredGroups, { label: '', items: [addOption], showGroupName: false }]
161
+ : core.filteredGroups
162
+ );
63
163
 
64
164
  let open = $state(false);
165
+ let internalValue = $state(value);
65
166
 
66
- onMount(() => {
67
- if (fetchFunction) {
68
- preloading = true;
69
- fetchFunction().then((response: NotifyModel<ComboBoxItem[]>) => {
70
- convertOptions(response.object!);
71
- preloading = false;
72
- });
73
- } else {
74
- if (options) convertOptions(options);
167
+ useDialogEvents({
168
+ onFirstOpen: () => {
169
+ if (autofocus) {
170
+ setTimeout(() => {
171
+ doFocus();
172
+ }, 150);
173
+ }
174
+ },
175
+ onOpen: () => {
176
+ if (autofocusDialog) {
177
+ setTimeout(() => {
178
+ doFocus();
179
+ }, 150);
180
+ }
75
181
  }
76
182
  });
77
183
 
78
- function convertOptions(options: ComboBoxItem[]) {
79
- allOptions = options.map((option) => {
80
- return {
81
- html: option.label,
82
- value: option.value,
83
- selected: option.value == value,
84
- label: option.label,
85
- };
86
- });
87
- filteredOptions = [...allOptions];
184
+ function doFocus() {
185
+ open = true;
186
+ tick().then(() => textboxElement?.focus());
88
187
  }
89
188
 
90
- const CREATE_PREFIX = "__create__";
189
+ $effect(() => {
190
+ if (value !== internalValue) {
191
+ internalValue = value;
192
+ rawValue = findItemByValue(core.allGroups, value);
193
+ onchangeRaw?.(rawValue);
194
+ core.preloadValues();
195
+ }
196
+ });
197
+
198
+ let selectionJustMade = false;
91
199
 
92
200
  function onSelection(selectedValue: ComboBoxItem) {
201
+ selectionJustMade = true;
93
202
  open = false;
94
203
 
95
- if (selectedValue.value.startsWith(CREATE_PREFIX)) {
96
- const newValue = selectedValue.value.slice(CREATE_PREFIX.length);
97
- const newItem: ComboBoxItem = { label: newValue, value: newValue };
98
- if (options) {
99
- options.push(newItem);
100
- convertOptions(options);
101
- } else {
102
- allOptions.push({ ...newItem, selected: true });
103
- }
104
- if (!preventChangeOnSelection) value = newValue;
105
- onSelect?.(newItem);
106
- onCreate?.(newValue);
204
+ if (selectedValue.isAddNew) {
205
+ addItem(selectedValue.value);
107
206
  return;
108
207
  }
109
208
 
110
- if (options) {
111
- options.forEach((option) => {
112
- option.selected = option.value == selectedValue.value;
209
+ core.allGroups
210
+ .flatMap((x) => x.items)
211
+ .forEach((option) => {
212
+ option.selected = option.value === selectedValue.value;
113
213
  });
214
+
215
+ if (!preventChangeOnSelection) {
216
+ value = selectedValue.value;
217
+ internalValue = value;
114
218
  }
115
219
 
116
- if (!preventChangeOnSelection) value = selectedValue.value;
220
+ rawValue = selectedValue;
117
221
 
118
222
  onSelect?.(selectedValue);
223
+ onchangeRaw?.(selectedValue);
119
224
  }
120
225
 
121
- function appendCreateOption(opts: ComboBoxItem[], query: string): ComboBoxItem[] {
122
- if (!allowCreate || !query.trim()) return opts;
123
- const normalizedQuery = query.trim().toLowerCase();
124
- const exactMatch = opts.some((o) => o.label.toLowerCase() === normalizedQuery);
125
- if (exactMatch) return opts;
126
- return [...opts, { label: `Create "${query.trim()}"`, value: CREATE_PREFIX + query.trim() }];
226
+ function addItem(newValue: string) {
227
+ open = false;
228
+
229
+ const newItem: ComboBoxItem = { label: newValue, value: newValue };
230
+
231
+ if (!preventChangeOnSelection) {
232
+ value = newValue;
233
+ internalValue = value;
234
+ }
235
+
236
+ rawValue = newItem;
237
+
238
+ onItemAdded?.(newValue);
239
+ onchangeRaw?.(newItem);
127
240
  }
128
241
 
129
- function onFilterChange(value: string) {
130
- if (searchFunction && value) {
131
- clearTimeout(timeout);
132
- searching = true;
133
- timeout = setTimeout(() => {
134
- searchFunction(value).then(
135
- (response: NotifyModel<ComboBoxItem[]>) => {
136
- convertOptions(response.object!);
137
- filteredOptions = appendCreateOption(filteredOptions, value);
138
- searching = false;
139
- },
140
- );
141
- }, 300);
142
- } else if (allOptions) {
143
- filteredOptions = allOptions.filter((option) =>
144
- option.label.toLowerCase().includes(value.toLowerCase()),
145
- );
146
- filteredOptions = appendCreateOption(filteredOptions, value);
242
+ function onFilterChange(filterValue: string) {
243
+ if (filterValue.length === 0) {
244
+ core.filterLocal('');
245
+ open = true;
246
+ return;
247
+ }
248
+
249
+ if (searchFunction && filterValue) {
250
+ core.search(filterValue, () => (open = true));
251
+ } else {
252
+ core.filterLocal(filterValue);
147
253
  open = true;
148
254
  }
149
255
 
150
- onchange?.(value);
256
+ onchange?.(filterValue);
151
257
  }
152
258
 
153
259
  let textboxElement: HTMLElement | undefined = $state(undefined);
154
-
155
- // returns true if an item was selected, false otherwise
156
- let comboBoxKeyDown: ((e: KeyboardEvent) => boolean) | undefined =
157
- $state(undefined);
260
+ let comboBoxEl: ReturnType<typeof ComboBoxMulti> | undefined = $state(undefined);
158
261
 
159
262
  function onKeyDown(e: KeyboardEvent) {
160
263
  if (!open) {
161
- if (e.key === "Enter")
162
- // If combobox is not open return user entered string
163
- enterPressed?.(value);
164
-
264
+ if (e.key === 'Enter') enterPressed?.(value);
165
265
  return;
166
266
  }
167
267
 
168
- if (e.key === "Escape") {
268
+ if (e.key === 'Escape') {
169
269
  e.preventDefault();
170
270
  open = false;
171
- } else if (comboBoxKeyDown) {
172
- const itemSelected = comboBoxKeyDown(e);
271
+ } else {
272
+ selectionJustMade = false;
273
+ comboBoxEl?.handleKeyDown?.(e);
173
274
 
174
- if (!itemSelected && e.key === "Enter") enterPressed?.(value);
275
+ if (!selectionJustMade && e.key === 'Enter') enterPressed?.(value);
175
276
  }
176
277
  }
177
278
 
178
- function onFocus(e: any) {
279
+ function onFocus(_value: string, e: FocusEvent) {
179
280
  open = true;
180
281
  const target = e?.target as HTMLInputElement;
181
282
  target?.select();
@@ -188,36 +289,68 @@
188
289
  }
189
290
  </script>
190
291
 
191
- <Textbox
192
- {label}
193
- class={classes}
194
- required={showRequired}
195
- bind:textboxElement
196
- placeholder={preloading ? "Loading..." : placeholder}
197
- loading={searching || preloading}
198
- disabled={preloading}
199
- noAutocomplete
200
- {groupClass}
201
- {leftIconSvg}
202
- bind:value
203
- oninput={() => {
204
- onFilterChange(value);
205
- }}
206
- onfocus={onFocus}
207
- onkeydown={onKeyDown}
208
- >
209
- <div class="relative w-full" use:clickOutside={closePopover}>
210
- <ComboBox
211
- {value}
212
- onselection={onSelection}
213
- options={filteredOptions}
214
- {open}
215
- loading={searching}
216
- {showNoResultsMessage}
217
- {loadingText}
218
- />
292
+ <div class="autocomplete-container">
293
+ <Textbox
294
+ {name}
295
+ type="search"
296
+ {label}
297
+ class={classes}
298
+ {containerClass}
299
+ {required}
300
+ {showError}
301
+ {errorText}
302
+ {tooltipLocation}
303
+ {tooltipContent}
304
+ {tooltipText}
305
+ bind:textboxElement
306
+ placeholder={core.preloading ? 'Loading...' : placeholder}
307
+ loading={core.searching || core.preloading}
308
+ disabled={core.preloading || disabled}
309
+ autocomplete="off"
310
+ {groupClass}
311
+ {leftIconSvg}
312
+ bind:value
313
+ oninput={() => {
314
+ onFilterChange(value);
315
+ }}
316
+ onfocus={onFocus}
317
+ onkeydown={onKeyDown}
318
+ />
319
+ <div class="autocomplete-panel {open ? '' : 'hidden'}">
320
+ <div class="autocomplete-panel-inner" use:clickOutside={closePopover}>
321
+ <ComboBoxMulti
322
+ bind:this={comboBoxEl}
323
+ filterString={value}
324
+ values={rawValue ? [rawValue] : []}
325
+ {onSelection}
326
+ groupedOptions={displayGroups}
327
+ {open}
328
+ loading={core.searching}
329
+ {hideNoResults}
330
+ {loadingText}
331
+ />
332
+ </div>
219
333
  </div>
220
- </Textbox>
334
+ </div>
221
335
 
222
336
  <style>
337
+ .autocomplete-container {
338
+ position: relative;
339
+ }
340
+
341
+ .autocomplete-panel {
342
+ position: absolute;
343
+ width: 100%;
344
+ left: 0;
345
+ z-index: 70;
346
+ }
347
+
348
+ .autocomplete-panel-inner {
349
+ position: relative;
350
+ width: 100%;
351
+ }
352
+
353
+ .hidden {
354
+ display: none;
355
+ }
223
356
  </style>
@@ -1,25 +1,73 @@
1
- import type { ComboBoxItem, FetchFunctionType, SearchFunctionType } from "../models/ComboBoxItem.js";
2
- interface Props {
1
+ import type { ComboBoxGroup, ComboBoxItem, FetchFunctionType, RetrieveLabelFunctionType, SearchFunctionType } from '../models/ComboBoxItem.js';
2
+ import type { Snippet } from 'svelte';
3
+ export interface AutocompleteProps {
4
+ /** HTML name attribute for the input */
5
+ name?: string;
6
+ /** Label text displayed above the input */
3
7
  label?: string;
8
+ /** Current input value (bindable) */
4
9
  value?: string;
10
+ /** Full selected item object (bindable) */
11
+ rawValue?: ComboBoxItem;
12
+ /** Placeholder text when input is empty */
5
13
  placeholder?: string;
6
- showRequired?: boolean;
7
- classes?: string;
14
+ /** Mark field as required (shows asterisk) */
15
+ required?: boolean;
16
+ /** CSS classes for the input element */
17
+ class?: string;
18
+ /** CSS classes for the container element */
19
+ containerClass?: string;
20
+ /** Disable the input */
21
+ disabled?: boolean;
22
+ /** Show error state styling */
23
+ showError?: boolean;
24
+ /** Error message to display */
25
+ errorText?: string;
26
+ /** Auto-focus the input on mount */
8
27
  autofocus?: boolean;
9
- showNoResultsMessage?: boolean;
28
+ /** Auto-focus on every dialog open */
29
+ autofocusDialog?: boolean;
30
+ /** Hide "no results" message when search finds nothing */
31
+ hideNoResults?: boolean;
32
+ /** Prevent updating value when an option is selected */
10
33
  preventChangeOnSelection?: boolean;
11
- allowCreate?: boolean;
34
+ /** Allow suggesting a new value be added when the input doesn't match an existing option */
35
+ allowAdd?: boolean;
36
+ /** Builds the label shown for the "add new value" suggestion (default: `Add "<value>"`) */
37
+ addItemLabel?: (value: string) => string;
38
+ /** Text shown while loading options */
12
39
  loadingText?: string;
40
+ /** CSS classes for the FormGroup wrapper */
13
41
  groupClass?: string;
42
+ /** SVG icon displayed on the left side */
14
43
  leftIconSvg?: string;
44
+ /** Static list of autocomplete options */
15
45
  options?: ComboBoxItem[];
16
- fetchFunction?: FetchFunctionType;
17
- searchFunction?: SearchFunctionType;
46
+ /** Options organized into groups */
47
+ groupedOptions?: ComboBoxGroup[];
48
+ /** Position of the tooltip */
49
+ tooltipLocation?: 'top' | 'bottom' | 'left' | 'right';
50
+ /** Async function to fetch all options */
51
+ fetchFunction?: FetchFunctionType | null;
52
+ /** Async function to search/filter options based on input */
53
+ searchFunction?: SearchFunctionType | null;
54
+ /** Async function to retrieve label for a value */
55
+ retrieveLabelFunction?: RetrieveLabelFunctionType | null;
56
+ /** Rich tooltip content using a Svelte snippet */
57
+ tooltipContent?: Snippet;
58
+ /** Simple tooltip text */
59
+ tooltipText?: string;
60
+ /** Callback when input value changes */
18
61
  onchange?: (e: string) => void;
62
+ /** Callback when Enter key is pressed */
19
63
  enterPressed?: (e: string) => void;
64
+ /** Callback when an option is selected */
20
65
  onSelect?: (e: ComboBoxItem) => void;
21
- onCreate?: (value: string) => void;
66
+ /** Callback when selection changes (returns full item object) */
67
+ onchangeRaw?: (e: ComboBoxItem | undefined) => void;
68
+ /** Callback when the user chooses to add the typed value as a new item */
69
+ onItemAdded?: (value: string) => void;
22
70
  }
23
- declare const Autocomplete: import("svelte").Component<Props, {}, "value">;
71
+ declare const Autocomplete: import("svelte").Component<AutocompleteProps, {}, "value" | "rawValue">;
24
72
  type Autocomplete = ReturnType<typeof Autocomplete>;
25
73
  export default Autocomplete;