@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,22 +1,41 @@
1
1
  <script lang="ts">
2
2
  import type { ComboBoxItem } from '../models/ComboBoxItem.js';
3
3
  import FormGroup from './FormGroup.svelte';
4
+ import Radio from './Radio.svelte';
4
5
 
5
- interface Props {
6
+ export interface RadioGroupProps {
7
+ /** HTML name attribute for the radio group (falls back to label, then auto-generated) */
8
+ name?: string;
9
+ /** Label text displayed above the radio group */
6
10
  label: string;
11
+ /** Available radio options */
7
12
  options: ComboBoxItem[];
13
+ /** Mark field as required (shows asterisk) */
8
14
  required?: boolean;
15
+ /** Show error state styling */
9
16
  showError?: boolean;
17
+ /** Error message to display */
10
18
  errorText?: string;
19
+ /** Additional CSS classes */
11
20
  class?: string;
21
+ /** CSS classes for the container element */
12
22
  containerClass?: string;
23
+ /** Disable all radio options */
13
24
  disabled?: boolean;
25
+ /** When true, the radio state is controlled externally only. Clicking will call onchange but won't change the value. */
26
+ controlled?: boolean;
27
+ /** Display options vertically instead of horizontally */
28
+ vertical?: boolean;
29
+ /** Currently selected value (bindable) */
14
30
  value?: string;
31
+ /** Position of the tooltip */
15
32
  tooltipLocation?: 'top' | 'bottom' | 'left' | 'right';
33
+ /** Callback when selection changes */
16
34
  onchange?: (e: string | undefined) => void;
17
35
  }
18
36
 
19
37
  let {
38
+ name,
20
39
  label,
21
40
  options,
22
41
  required = false,
@@ -24,20 +43,26 @@
24
43
  errorText = '',
25
44
  class: classes = '',
26
45
  containerClass = '',
46
+ vertical = false,
27
47
  disabled = false,
48
+ controlled = false,
28
49
  tooltipLocation = 'top',
29
50
  value = $bindable(),
30
51
  onchange = undefined
31
- }: Props = $props();
52
+ }: RadioGroupProps = $props();
32
53
 
33
- const nonce = Math.random().toString(36).substring(2, 15);
34
- const randomName = $derived(label + nonce);
54
+ const autoName = `radio-${crypto.randomUUID()}`;
55
+ const resolvedName = $derived(
56
+ (name || label || autoName).replace(/[^a-zA-Z0-9_\-:.]/g, '_')
57
+ );
35
58
 
36
59
  function handleChange(option: ComboBoxItem) {
37
60
  if (option.value === value) return;
38
61
 
39
- value = option.value;
40
- onchange?.(value);
62
+ if (!controlled) {
63
+ value = option.value;
64
+ }
65
+ onchange?.(option.value);
41
66
  }
42
67
  </script>
43
68
 
@@ -46,26 +71,21 @@
46
71
  <div
47
72
  class="
48
73
  radio-options
74
+ {vertical ? 'radio-vertical' : ''}
49
75
  {required ? 'radio-required' : ''}
50
76
  {showError ? 'error' : ''}
51
77
  {disabled ? 'disabled' : ''}"
52
78
  >
53
79
  {#each options as option, i}
54
- {@const id = randomName + i}
55
- <div class="flex">
56
- <label class={disabled ? 'cursor-not-allowed' : ''} for={id}>
57
- <input
58
- {id}
59
- type="radio"
60
- class="disabled:cursor-not-allowed accent-accent"
61
- name={randomName}
62
- {disabled}
63
- checked={option.value === value}
64
- onchange={() => handleChange(option)}
65
- />
66
- {option.label}
67
- </label>
68
- </div>
80
+ <Radio
81
+ id={resolvedName + i}
82
+ name={resolvedName}
83
+ label={option.label}
84
+ {disabled}
85
+ checked={option.value === value}
86
+ controlled={true}
87
+ onchange={() => handleChange(option)}
88
+ />
69
89
  {/each}
70
90
  </div>
71
91
  {#if showError && errorText}
@@ -75,136 +95,30 @@
75
95
  </div>
76
96
 
77
97
  <style>
78
- label {
79
- display: flex;
80
- flex-direction: row;
81
- align-items: center;
82
- user-select: none;
83
- cursor: pointer;
84
- }
85
-
86
- .disabled label,
87
- .disabled input {
88
- cursor: not-allowed;
89
- }
90
-
91
- input {
92
- position: relative !important;
93
- -webkit-appearance: none;
94
- -moz-appearance: none;
95
- appearance: none;
96
- margin: var(--pui-spacing-2);
97
- box-sizing: content-box;
98
- overflow: hidden;
99
- cursor: pointer;
100
- }
101
-
102
- input:before {
103
- content: '';
104
- display: block;
105
- box-sizing: content-box;
106
- width: 16px;
107
- height: 16px;
108
- border: 2px solid var(--pui-border-disabled);
109
- transition: var(--pui-transition-fast) border-color var(--pui-ease-out);
110
- }
111
-
112
98
  .radio-options {
113
99
  display: flex;
114
- gap: var(--pui-spacing-2);
115
- border-radius: var(--pui-radius-lg);
100
+ gap: 0.5rem;
101
+ border-radius: 6px;
116
102
  }
117
103
 
118
- .radio-required {
119
- padding: var(--pui-spacing-1);
104
+ .radio-vertical.radio-options {
105
+ gap: 0.25rem;
120
106
  }
121
107
 
122
- :global(.dark) input:before {
123
- border-color: var(--pui-color-gray-600);
124
- }
125
- input:checked:before {
126
- border-color: var(--pui-color-primary);
127
- transition: var(--pui-transition-slow) border-color var(--pui-ease-out);
128
- }
129
- :global(.dark) input:checked:before {
130
- border-color: var(--pui-color-primary);
108
+ .radio-vertical {
109
+ flex-direction: column;
131
110
  }
132
111
 
133
- input:disabled:before {
134
- border-color: var(--pui-border-disabled);
135
- background-color: var(--pui-border-disabled);
136
- opacity: 0.5;
137
- }
138
- :global(.dark) input:disabled:before {
139
- border-color: var(--pui-color-gray-600);
140
- background-color: var(--pui-color-gray-600);
141
- }
142
- input:after {
143
- content: '';
144
- display: block;
145
- position: absolute;
146
- box-sizing: content-box;
147
- top: 50%;
148
- left: 50%;
149
- transform-origin: 50% 50%;
150
- background-color: var(--pui-color-primary);
151
- width: 16px;
152
- height: 16px;
153
- border-radius: var(--pui-radius-full);
154
- transform: translate(-50%, -50%) scale(0);
155
- }
156
- input:disabled:after {
157
- opacity: 0.5;
158
- }
159
- :global(.dark) input:after {
160
- background-color: var(--pui-color-primary);
161
- }
162
- input[type='radio']:before {
163
- border-radius: var(--pui-radius-full);
164
- }
165
- input[type='radio']:after {
166
- width: 16px;
167
- height: 16px;
168
- border-radius: var(--pui-radius-full);
169
- transform: translate(-50%, -50%) scale(0);
170
- }
171
- input[type='radio']:checked:after {
172
- -webkit-animation: toggleOnRadio var(--pui-transition-fast) ease forwards;
173
- animation: toggleOnRadio var(--pui-transition-fast) ease forwards;
174
- }
175
- @-webkit-keyframes toggleOnRadio {
176
- 0% {
177
- opacity: 0;
178
- transform: translate(-50%, -50%) scale(0);
179
- }
180
- 70% {
181
- opacity: 1;
182
- transform: translate(-50%, -50%) scale(0.9);
183
- }
184
- 100% {
185
- transform: translate(-50%, -50%) scale(0.8);
186
- }
187
- }
188
- @keyframes toggleOnRadio {
189
- 0% {
190
- opacity: 0;
191
- transform: translate(-50%, -50%) scale(0);
192
- }
193
- 70% {
194
- opacity: 1;
195
- transform: translate(-50%, -50%) scale(0.9);
196
- }
197
- 100% {
198
- transform: translate(-50%, -50%) scale(0.8);
199
- }
112
+ .radio-required {
113
+ padding: 0.25rem;
200
114
  }
201
115
 
202
116
  .error {
203
- border: 1px solid var(--pui-text-danger);
117
+ border: 1px solid var(--danger-text);
204
118
  }
205
119
 
206
120
  .error-text {
207
- margin-top: var(--pui-spacing-1);
208
- color: var(--pui-text-danger);
121
+ margin-top: 0.25rem;
122
+ color: var(--danger-text);
209
123
  }
210
124
  </style>
@@ -1,17 +1,34 @@
1
1
  import type { ComboBoxItem } from '../models/ComboBoxItem.js';
2
- interface Props {
2
+ export interface RadioGroupProps {
3
+ /** HTML name attribute for the radio group (falls back to label, then auto-generated) */
4
+ name?: string;
5
+ /** Label text displayed above the radio group */
3
6
  label: string;
7
+ /** Available radio options */
4
8
  options: ComboBoxItem[];
9
+ /** Mark field as required (shows asterisk) */
5
10
  required?: boolean;
11
+ /** Show error state styling */
6
12
  showError?: boolean;
13
+ /** Error message to display */
7
14
  errorText?: string;
15
+ /** Additional CSS classes */
8
16
  class?: string;
17
+ /** CSS classes for the container element */
9
18
  containerClass?: string;
19
+ /** Disable all radio options */
10
20
  disabled?: boolean;
21
+ /** When true, the radio state is controlled externally only. Clicking will call onchange but won't change the value. */
22
+ controlled?: boolean;
23
+ /** Display options vertically instead of horizontally */
24
+ vertical?: boolean;
25
+ /** Currently selected value (bindable) */
11
26
  value?: string;
27
+ /** Position of the tooltip */
12
28
  tooltipLocation?: 'top' | 'bottom' | 'left' | 'right';
29
+ /** Callback when selection changes */
13
30
  onchange?: (e: string | undefined) => void;
14
31
  }
15
- declare const RadioGroup: import("svelte").Component<Props, {}, "value">;
32
+ declare const RadioGroup: import("svelte").Component<RadioGroupProps, {}, "value">;
16
33
  type RadioGroup = ReturnType<typeof RadioGroup>;
17
34
  export default RadioGroup;
@@ -1,14 +1,30 @@
1
1
  <script lang="ts">
2
2
  import type { ComboBoxItem } from '../models/ComboBoxItem.js';
3
- import { onMount, tick } from 'svelte';
3
+ import FormGroup from './FormGroup.svelte';
4
+ import { onMount } from 'svelte';
4
5
 
5
- interface Props {
6
+ export type SegmentedControlSize = 'default' | 'lg';
7
+
8
+ export interface SegmentedControlProps {
9
+ /** Available segment options */
6
10
  options: ComboBoxItem[];
11
+ /** Currently selected value (bindable) */
7
12
  value?: any;
13
+ /** Label text displayed above the control */
14
+ label?: string;
15
+ /** Visual size: 'default' (sized to match Textbox/Select) or 'lg' */
16
+ size?: SegmentedControlSize;
17
+ /** Callback when selection changes */
8
18
  onchange?: (value: string) => void;
9
19
  }
10
20
 
11
- let { options, value = $bindable(), onchange }: Props = $props();
21
+ let {
22
+ options,
23
+ value = $bindable(),
24
+ label,
25
+ size = 'default',
26
+ onchange
27
+ }: SegmentedControlProps = $props();
12
28
 
13
29
  let containerRef: HTMLDivElement;
14
30
  let buttonElements = $state<HTMLButtonElement[]>([]);
@@ -18,25 +34,13 @@
18
34
  const selectedIndex = $derived(options.findIndex((option) => option.value === value));
19
35
 
20
36
  onMount(() => {
21
- // Recalculate after initial DOM render
22
- tick().then(() => {
23
- if (selectedIndex >= 0 && buttonElements[selectedIndex]) {
24
- recalculateBackground();
25
- }
26
- });
27
-
28
- // Use ResizeObserver to handle layout shifts (e.g. font loading, container resize)
29
- const observer = new ResizeObserver(() => {
30
- if (selectedIndex >= 0 && buttonElements[selectedIndex]) {
31
- recalculateBackground();
32
- }
33
- });
34
- observer.observe(containerRef);
35
-
36
- return () => observer.disconnect();
37
+ // recalculate 10 times on startup to ensure background is positioned correctly
38
+ for (let i = 0; i < 10; i++) {
39
+ setTimeout(() => recalculateBackground(), 100 * i);
40
+ }
37
41
  });
38
42
 
39
- // Update background position and size when selection changes
43
+ // Update background position and size based on selected element
40
44
  $effect(() => {
41
45
  if (selectedIndex >= 0 && buttonElements[selectedIndex] && containerRef) {
42
46
  recalculateBackground();
@@ -50,16 +54,30 @@
50
54
  }
51
55
  });
52
56
 
53
- function recalculateBackground() {
57
+ /**
58
+ * Recalculate the selected item background position/size (e.g. after a layout change).
59
+ * @param delay Optional delay in milliseconds before measuring (e.g. to let a transition finish)
60
+ */
61
+ export function recalculateBackground(delay?: number) {
62
+ if (delay) {
63
+ setTimeout(() => recalculateBackground(), delay);
64
+ return;
65
+ }
66
+
54
67
  const selectedButton = buttonElements[selectedIndex];
68
+ if (!selectedButton || !containerRef) return;
69
+
55
70
  const containerRect = containerRef.getBoundingClientRect();
56
71
  const buttonRect = selectedButton.getBoundingClientRect();
57
72
 
58
- const offsetLeft = buttonRect.left - containerRect.left;
73
+ // Absolute positioning is relative to the padding box, but getBoundingClientRect
74
+ // measures from the border's outer edge — subtract the border (clientLeft/clientTop)
75
+ const offsetLeft = buttonRect.left - containerRect.left - containerRef.clientLeft;
76
+ const offsetTop = buttonRect.top - containerRect.top - containerRef.clientTop;
59
77
  const width = buttonRect.width;
60
78
  const height = buttonRect.height;
61
79
 
62
- backgroundStyle = `left: ${offsetLeft}px; width: ${width}px; height: ${height}px; top: ${buttonRect.top - containerRect.top}px;`;
80
+ backgroundStyle = `left: ${offsetLeft}px; width: ${width}px; height: ${height}px; top: ${offsetTop}px;`;
63
81
  }
64
82
 
65
83
  function setValue(newValue: any) {
@@ -68,82 +86,110 @@
68
86
  }
69
87
  </script>
70
88
 
71
- <div class="segmented-control" bind:this={containerRef}>
72
- <div class="segmented-control-background" style={backgroundStyle}></div>
73
- {#each options as option, index}
74
- {@const isSelected = option.value === value}
75
-
76
- <button
77
- bind:this={buttonElements[index]}
78
- class="segmented-control-item {isSelected ? 'selected' : ''}"
79
- onclick={() => setValue(option.value)}
80
- >
81
- {option.label}
82
- </button>
83
- {/each}
84
- </div>
89
+ {#snippet control()}
90
+ <div class="segmented-control {size === 'lg' ? 'large' : ''}" bind:this={containerRef}>
91
+ <div class="segmented-control-background" style={backgroundStyle}></div>
92
+ {#each options as option, index}
93
+ {@const isSelected = option.value === value}
94
+
95
+ <button
96
+ bind:this={buttonElements[index]}
97
+ class="segmented-control-item {isSelected ? 'selected' : ''}"
98
+ onclick={() => setValue(option.value)}
99
+ >
100
+ {option.label}
101
+ </button>
102
+ {/each}
103
+ </div>
104
+ {/snippet}
105
+
106
+ {#if label}
107
+ <FormGroup {label}>
108
+ <div class="segmented-control-container">
109
+ {@render control()}
110
+ </div>
111
+ </FormGroup>
112
+ {:else}
113
+ {@render control()}
114
+ {/if}
85
115
 
86
116
  <style>
87
117
  .segmented-control {
88
118
  position: relative;
89
119
  display: inline-flex;
90
- background-color: var(--pui-color-gray-200);
91
- border-radius: var(--pui-radius-lg);
92
- padding: var(--pui-spacing-1);
120
+ background-color: #e5e7eb;
121
+ border-radius: 0.5rem;
122
+ border: 1px solid var(--border-color);
123
+ padding: 0.125rem;
93
124
  user-select: none;
94
125
  }
95
126
 
96
127
  .segmented-control-background {
97
128
  position: absolute;
98
- background-color: var(--pui-color-white);
99
- border-radius: var(--pui-radius-md);
100
- transition: all var(--pui-transition-fast) var(--pui-ease-in-out);
101
- box-shadow: var(--pui-shadow-sm);
129
+ background-color: white;
130
+ border-radius: 0.375rem;
131
+ transition: all 0.2s ease-in-out;
132
+ box-shadow:
133
+ 0 1px 3px 0 rgba(0, 0, 0, 0.1),
134
+ 0 1px 2px 0 rgba(0, 0, 0, 0.06);
102
135
  }
103
136
 
104
137
  .segmented-control-item {
105
138
  position: relative;
106
139
  align-items: center;
107
140
  justify-content: center;
108
- padding: var(--pui-spacing-2) var(--pui-spacing-4);
109
141
  cursor: pointer;
110
142
  user-select: none;
111
143
  border: none;
112
144
  background: transparent;
113
- border-radius: var(--pui-radius-md);
114
- font-size: var(--pui-font-size-sm);
115
- font-weight: var(--pui-font-weight-medium);
116
- color: var(--pui-color-gray-500);
117
- transition: color var(--pui-transition-fast) var(--pui-ease-in-out);
145
+ border-radius: 0.375rem;
146
+ padding: 0.125rem 0.75rem;
147
+ font-size: 0.875rem;
148
+ line-height: 1.5rem;
149
+ font-weight: 500;
150
+ color: #6b7280;
151
+ transition: color 0.2s ease-in-out;
118
152
  z-index: 1;
119
153
  }
120
154
 
121
155
  .segmented-control-item:hover {
122
- color: var(--pui-color-gray-700);
156
+ color: #374151;
123
157
  }
124
158
 
125
159
  .segmented-control-item.selected {
126
- color: var(--pui-color-gray-900);
160
+ color: #111827;
161
+ }
162
+
163
+ /* Large size */
164
+ .segmented-control.large {
165
+ border: none;
166
+ padding: 0.25rem;
167
+ }
168
+
169
+ .segmented-control.large .segmented-control-item {
170
+ padding: 0.5rem 1rem;
171
+ line-height: inherit;
127
172
  }
128
173
 
129
174
  /* Dark mode styles */
130
175
  :global(.dark) .segmented-control {
131
- background-color: var(--pui-input-bg);
176
+ background-color: var(--input-bg-color);
177
+ border-color: var(--border-color);
132
178
  }
133
179
 
134
180
  :global(.dark) .segmented-control-background {
135
- background-color: var(--pui-color-primary);
181
+ background-color: var(--ui-primary);
136
182
  }
137
183
 
138
184
  :global(.dark) .segmented-control-item {
139
- color: var(--pui-color-gray-400);
185
+ color: #9ca3af;
140
186
  }
141
187
 
142
188
  :global(.dark) .segmented-control-item:hover {
143
- color: var(--pui-color-gray-300);
189
+ color: #d1d5db;
144
190
  }
145
191
 
146
192
  :global(.dark) .segmented-control-item.selected {
147
- color: var(--pui-color-gray-100);
193
+ color: #f9fafb;
148
194
  }
149
195
  </style>
@@ -1,9 +1,19 @@
1
1
  import type { ComboBoxItem } from '../models/ComboBoxItem.js';
2
- interface Props {
2
+ export type SegmentedControlSize = 'default' | 'lg';
3
+ export interface SegmentedControlProps {
4
+ /** Available segment options */
3
5
  options: ComboBoxItem[];
6
+ /** Currently selected value (bindable) */
4
7
  value?: any;
8
+ /** Label text displayed above the control */
9
+ label?: string;
10
+ /** Visual size: 'default' (sized to match Textbox/Select) or 'lg' */
11
+ size?: SegmentedControlSize;
12
+ /** Callback when selection changes */
5
13
  onchange?: (value: string) => void;
6
14
  }
7
- declare const SegmentedControl: import("svelte").Component<Props, {}, "value">;
15
+ declare const SegmentedControl: import("svelte").Component<SegmentedControlProps, {
16
+ recalculateBackground: (delay?: number) => void;
17
+ }, "value">;
8
18
  type SegmentedControl = ReturnType<typeof SegmentedControl>;
9
19
  export default SegmentedControl;