@placeholderco/placeholder-ui 1.0.12 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (276) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +134 -130
  3. package/dist/app.css +178 -0
  4. package/dist/bootstrap.min.css +3600 -0
  5. package/dist/cards/GitCard.svelte +115 -0
  6. package/dist/cards/GitCard.svelte.d.ts +22 -0
  7. package/dist/display/Avatar.svelte +29 -17
  8. package/dist/display/Avatar.svelte.d.ts +12 -2
  9. package/dist/display/LinkCollection.svelte +40 -18
  10. package/dist/display/LinkCollection.svelte.d.ts +9 -2
  11. package/dist/display/Paper.svelte +31 -19
  12. package/dist/display/Paper.svelte.d.ts +12 -3
  13. package/dist/display/alert/Alert.svelte +129 -0
  14. package/dist/display/alert/Alert.svelte.d.ts +15 -0
  15. package/dist/display/alert/AlertVariant.d.ts +1 -0
  16. package/dist/display/alert/AlertVariant.js +1 -0
  17. package/dist/display/filetree/FileTree.svelte +259 -0
  18. package/dist/display/filetree/FileTree.svelte.d.ts +76 -0
  19. package/dist/display/filetree/FileTreeItem.svelte +396 -0
  20. package/dist/display/filetree/FileTreeItem.svelte.d.ts +40 -0
  21. package/dist/display/filetree/FileTreeNode.d.ts +46 -0
  22. package/dist/display/filetree/FileTreeNode.js +115 -0
  23. package/dist/display/filetree/index.d.ts +5 -0
  24. package/dist/display/filetree/index.js +3 -0
  25. package/dist/editors/CodeMirror.svelte +306 -0
  26. package/dist/editors/CodeMirror.svelte.d.ts +31 -0
  27. package/dist/editors/tiptap/LinkActionMenu.svelte +307 -0
  28. package/dist/editors/tiptap/LinkActionMenu.svelte.d.ts +8 -0
  29. package/dist/editors/tiptap/TipTap.svelte +856 -0
  30. package/dist/editors/tiptap/TipTap.svelte.d.ts +54 -0
  31. package/dist/editors/tiptap/TipTapBubble.svelte +790 -0
  32. package/dist/editors/tiptap/TipTapBubble.svelte.d.ts +44 -0
  33. package/dist/editors/tiptap/collaboration.d.ts +55 -0
  34. package/dist/editors/tiptap/collaboration.js +1 -0
  35. package/dist/editors/tiptap/extensions/Alert.d.ts +18 -0
  36. package/dist/editors/tiptap/extensions/Alert.js +72 -0
  37. package/dist/editors/tiptap/extensions/CodeBlockEditor.d.ts +21 -0
  38. package/dist/editors/tiptap/extensions/CodeBlockEditor.js +326 -0
  39. package/dist/editors/tiptap/extensions/Div.d.ts +19 -0
  40. package/dist/editors/tiptap/extensions/Div.js +89 -0
  41. package/dist/editors/tiptap/extensions/Span.d.ts +5 -0
  42. package/dist/editors/tiptap/extensions/Span.js +56 -0
  43. package/dist/editors/tiptap/extensions/codeBlockEditor.css +236 -0
  44. package/dist/editors/tiptap/extensions/codeBlockLanguages.d.ts +17 -0
  45. package/dist/editors/tiptap/extensions/codeBlockLanguages.js +124 -0
  46. package/dist/editors/tiptap/extensions/index.d.ts +7 -0
  47. package/dist/editors/tiptap/extensions/index.js +5 -0
  48. package/dist/editors/tiptap/toolbar/AlertButton.svelte +261 -0
  49. package/dist/editors/tiptap/toolbar/AlertButton.svelte.d.ts +7 -0
  50. package/dist/editors/tiptap/toolbar/AlignCenterButton.svelte +18 -0
  51. package/dist/editors/tiptap/toolbar/AlignCenterButton.svelte.d.ts +7 -0
  52. package/dist/editors/tiptap/toolbar/AlignJustifyButton.svelte +18 -0
  53. package/dist/editors/tiptap/toolbar/AlignJustifyButton.svelte.d.ts +7 -0
  54. package/dist/editors/tiptap/toolbar/AlignLeftButton.svelte +18 -0
  55. package/dist/editors/tiptap/toolbar/AlignLeftButton.svelte.d.ts +7 -0
  56. package/dist/editors/tiptap/toolbar/AlignRightButton.svelte +18 -0
  57. package/dist/editors/tiptap/toolbar/AlignRightButton.svelte.d.ts +7 -0
  58. package/dist/editors/tiptap/toolbar/BoldButton.svelte +18 -0
  59. package/dist/editors/tiptap/toolbar/BoldButton.svelte.d.ts +7 -0
  60. package/dist/editors/tiptap/toolbar/BulletListButton.svelte +18 -0
  61. package/dist/editors/tiptap/toolbar/BulletListButton.svelte.d.ts +7 -0
  62. package/dist/editors/tiptap/toolbar/CodeBlockButton.svelte +18 -0
  63. package/dist/editors/tiptap/toolbar/CodeBlockButton.svelte.d.ts +7 -0
  64. package/dist/editors/tiptap/toolbar/CodeButton.svelte +18 -0
  65. package/dist/editors/tiptap/toolbar/CodeButton.svelte.d.ts +7 -0
  66. package/dist/editors/tiptap/toolbar/ColorButton.svelte +299 -0
  67. package/dist/editors/tiptap/toolbar/ColorButton.svelte.d.ts +20 -0
  68. package/dist/editors/tiptap/toolbar/DocumentButton.svelte +58 -0
  69. package/dist/editors/tiptap/toolbar/DocumentButton.svelte.d.ts +9 -0
  70. package/dist/editors/tiptap/toolbar/HeadingButton.svelte +30 -0
  71. package/dist/editors/tiptap/toolbar/HeadingButton.svelte.d.ts +8 -0
  72. package/dist/editors/tiptap/toolbar/HtmlButton.svelte +68 -0
  73. package/dist/editors/tiptap/toolbar/HtmlButton.svelte.d.ts +7 -0
  74. package/dist/editors/tiptap/toolbar/ImageButton.svelte +54 -0
  75. package/dist/editors/tiptap/toolbar/ImageButton.svelte.d.ts +8 -0
  76. package/dist/editors/tiptap/toolbar/ItalicButton.svelte +18 -0
  77. package/dist/editors/tiptap/toolbar/ItalicButton.svelte.d.ts +7 -0
  78. package/dist/editors/tiptap/toolbar/LinkButton.svelte +257 -0
  79. package/dist/editors/tiptap/toolbar/LinkButton.svelte.d.ts +7 -0
  80. package/dist/editors/tiptap/toolbar/OrderedListButton.svelte +18 -0
  81. package/dist/editors/tiptap/toolbar/OrderedListButton.svelte.d.ts +7 -0
  82. package/dist/editors/tiptap/toolbar/RedoButton.svelte +18 -0
  83. package/dist/editors/tiptap/toolbar/RedoButton.svelte.d.ts +7 -0
  84. package/dist/editors/tiptap/toolbar/StrikethroughButton.svelte +18 -0
  85. package/dist/editors/tiptap/toolbar/StrikethroughButton.svelte.d.ts +7 -0
  86. package/dist/editors/tiptap/toolbar/SubscriptButton.svelte +18 -0
  87. package/dist/editors/tiptap/toolbar/SubscriptButton.svelte.d.ts +7 -0
  88. package/dist/editors/tiptap/toolbar/SuperscriptButton.svelte +18 -0
  89. package/dist/editors/tiptap/toolbar/SuperscriptButton.svelte.d.ts +7 -0
  90. package/dist/editors/tiptap/toolbar/TableButton.svelte +258 -0
  91. package/dist/editors/tiptap/toolbar/TableButton.svelte.d.ts +7 -0
  92. package/dist/editors/tiptap/toolbar/ToolbarButton.svelte +84 -0
  93. package/dist/editors/tiptap/toolbar/ToolbarButton.svelte.d.ts +10 -0
  94. package/dist/editors/tiptap/toolbar/UnderlineButton.svelte +18 -0
  95. package/dist/editors/tiptap/toolbar/UnderlineButton.svelte.d.ts +7 -0
  96. package/dist/editors/tiptap/toolbar/UndoButton.svelte +18 -0
  97. package/dist/editors/tiptap/toolbar/UndoButton.svelte.d.ts +7 -0
  98. package/dist/form/Autocomplete.svelte +86 -135
  99. package/dist/form/Autocomplete.svelte.d.ts +6 -8
  100. package/dist/form/AutocompleteMulti.svelte +385 -227
  101. package/dist/form/AutocompleteMulti.svelte.d.ts +59 -10
  102. package/dist/form/Checkbox.svelte +59 -54
  103. package/dist/form/Checkbox.svelte.d.ts +14 -5
  104. package/dist/form/Chips.svelte +29 -13
  105. package/dist/form/Chips.svelte.d.ts +16 -2
  106. package/dist/form/ComboBox.svelte +30 -20
  107. package/dist/form/ComboBox.svelte.d.ts +1 -1
  108. package/dist/form/ComboBoxItemBuilder.svelte +161 -188
  109. package/dist/form/ComboBoxItemBuilder.svelte.d.ts +19 -2
  110. package/dist/form/ComboBoxMulti.svelte +38 -24
  111. package/dist/form/ComboBoxMulti.svelte.d.ts +1 -1
  112. package/dist/form/CronBuilder.svelte +84 -67
  113. package/dist/form/CronBuilder.svelte.d.ts +16 -3
  114. package/dist/form/DatePicker.svelte +148 -195
  115. package/dist/form/DateRangePicker.svelte +101 -98
  116. package/dist/form/DateTimePicker.svelte +167 -208
  117. package/dist/form/DateTimePicker.svelte.d.ts +10 -2
  118. package/dist/form/FormGroup.svelte +33 -10
  119. package/dist/form/FormGroup.svelte.d.ts +12 -2
  120. package/dist/form/Number.svelte +70 -47
  121. package/dist/form/Number.svelte.d.ts +36 -6
  122. package/dist/form/Radio.svelte +18 -18
  123. package/dist/form/RadioGroup.svelte +23 -12
  124. package/dist/form/RadioGroup.svelte.d.ts +13 -2
  125. package/dist/form/SegmentedControl.svelte +76 -56
  126. package/dist/form/SegmentedControl.svelte.d.ts +9 -4
  127. package/dist/form/Select.svelte +150 -233
  128. package/dist/form/Select.svelte.d.ts +29 -5
  129. package/dist/form/SelectMulti.svelte +163 -277
  130. package/dist/form/SelectMulti.svelte.d.ts +36 -7
  131. package/dist/form/Slider.svelte +183 -269
  132. package/dist/form/Slider.svelte.d.ts +32 -17
  133. package/dist/form/StringArrayBuilder.svelte +249 -0
  134. package/dist/form/StringArrayBuilder.svelte.d.ts +15 -0
  135. package/dist/form/Switch.svelte +164 -106
  136. package/dist/form/Switch.svelte.d.ts +22 -3
  137. package/dist/form/TextArea.svelte +49 -29
  138. package/dist/form/TextArea.svelte.d.ts +23 -3
  139. package/dist/form/Textbox.svelte +69 -68
  140. package/dist/form/Textbox.svelte.d.ts +32 -4
  141. package/dist/form/TimePicker.svelte +42 -47
  142. package/dist/form/TimePicker.svelte.d.ts +12 -2
  143. package/dist/form/comboBoxCore.svelte.d.ts +99 -0
  144. package/dist/form/comboBoxCore.svelte.js +243 -0
  145. package/dist/icon/Icon.svelte +8 -3
  146. package/dist/icon/Icon.svelte.d.ts +8 -3
  147. package/dist/icon/file-type-bmp.svg +1 -0
  148. package/dist/icon/file-type-csv.svg +1 -0
  149. package/dist/icon/file-type-doc.svg +1 -0
  150. package/dist/icon/file-type-docx.svg +1 -0
  151. package/dist/icon/file-type-jpg.svg +1 -0
  152. package/dist/icon/file-type-pdf.svg +1 -0
  153. package/dist/icon/file-type-png.svg +1 -0
  154. package/dist/icon/file-type-ppt.svg +1 -0
  155. package/dist/icon/file-type-svg.svg +1 -0
  156. package/dist/icon/file-type-txt.svg +1 -0
  157. package/dist/icon/file-type-xls.svg +1 -0
  158. package/dist/icon/file-type-zip.svg +1 -0
  159. package/dist/icon/index.d.ts +166 -151
  160. package/dist/icon/index.js +166 -152
  161. package/dist/icon/logo-placeholder.svg +6 -0
  162. package/dist/icon/source-code.svg +1 -0
  163. package/dist/icon/sun-moon.svg +1 -0
  164. package/dist/icon/table.svg +6 -0
  165. package/dist/index.d.ts +87 -26
  166. package/dist/index.js +25 -28
  167. package/dist/layout/AppShell.svelte +12 -162
  168. package/dist/layout/AppShell.svelte.d.ts +6 -27
  169. package/dist/layout/CustomNavbar.svelte +9 -7
  170. package/dist/layout/CustomNavbar.svelte.d.ts +4 -2
  171. package/dist/layout/Navbar.svelte +161 -129
  172. package/dist/layout/Navbar.svelte.d.ts +29 -15
  173. package/dist/layout/NavbarItemDisplay.svelte +2 -2
  174. package/dist/layout/NavbarItemView.svelte +182 -0
  175. package/dist/layout/NavbarItemView.svelte.d.ts +10 -0
  176. package/dist/layout/Sidenav.svelte +280 -341
  177. package/dist/layout/Sidenav.svelte.d.ts +36 -4
  178. package/dist/models/ComboBoxItem.d.ts +2 -0
  179. package/dist/models/Employee.d.ts +53 -0
  180. package/dist/models/Employee.js +23 -0
  181. package/dist/models/Hyperlink.d.ts +3 -1
  182. package/dist/models/LinkCollectionItem.d.ts +3 -1
  183. package/dist/models/NavbarItem.d.ts +6 -2
  184. package/dist/theme.svelte.d.ts +0 -1
  185. package/dist/theme.svelte.js +46 -58
  186. package/dist/ui/Accordion.svelte +81 -242
  187. package/dist/ui/Accordion.svelte.d.ts +37 -14
  188. package/dist/ui/AccordionItem.svelte +303 -0
  189. package/dist/ui/AccordionItem.svelte.d.ts +21 -0
  190. package/dist/ui/ActionIcon.svelte +25 -21
  191. package/dist/ui/ActionIcon.svelte.d.ts +15 -2
  192. package/dist/ui/Badge.svelte +163 -171
  193. package/dist/ui/Badge.svelte.d.ts +13 -5
  194. package/dist/ui/Button.svelte +178 -205
  195. package/dist/ui/Button.svelte.d.ts +19 -2
  196. package/dist/ui/ButtonVariant.d.ts +15 -1
  197. package/dist/ui/ContextMenu.svelte +146 -0
  198. package/dist/ui/ContextMenu.svelte.d.ts +37 -0
  199. package/dist/ui/ContextMenuList.svelte +188 -0
  200. package/dist/ui/ContextMenuList.svelte.d.ts +12 -0
  201. package/dist/ui/Dialog.svelte +183 -111
  202. package/dist/ui/Dialog.svelte.d.ts +21 -4
  203. package/dist/ui/Drawer.svelte +237 -184
  204. package/dist/ui/Drawer.svelte.d.ts +20 -2
  205. package/dist/ui/Dropdown.svelte +24 -14
  206. package/dist/ui/Dropdown.svelte.d.ts +11 -2
  207. package/dist/ui/Dropzone.svelte +159 -57
  208. package/dist/ui/Dropzone.svelte.d.ts +29 -2
  209. package/dist/ui/Link.svelte +12 -4
  210. package/dist/ui/Link.svelte.d.ts +8 -2
  211. package/dist/ui/Loader.svelte +23 -18
  212. package/dist/ui/Loader.svelte.d.ts +5 -2
  213. package/dist/ui/Logo.svelte +37 -0
  214. package/dist/ui/Logo.svelte.d.ts +16 -0
  215. package/dist/ui/MultiSortable.svelte +287 -0
  216. package/dist/ui/MultiSortable.svelte.d.ts +50 -0
  217. package/dist/ui/Pagination.svelte +15 -9
  218. package/dist/ui/Pagination.svelte.d.ts +6 -2
  219. package/dist/ui/Popover.svelte +43 -65
  220. package/dist/ui/Popover.svelte.d.ts +14 -5
  221. package/dist/ui/ProgressBar.svelte +28 -30
  222. package/dist/ui/ProgressBarVariant.d.ts +7 -1
  223. package/dist/ui/SegmentedControl.svelte +0 -0
  224. package/dist/ui/SegmentedControl.svelte.d.ts +26 -0
  225. package/dist/ui/Sortable.svelte +163 -0
  226. package/dist/ui/Sortable.svelte.d.ts +47 -0
  227. package/dist/ui/Table.svelte +383 -80
  228. package/dist/ui/Table.svelte.d.ts +43 -2
  229. package/dist/ui/Tabs.svelte +22 -18
  230. package/dist/ui/Tabs.svelte.d.ts +6 -2
  231. package/dist/ui/ThemeSwitcher.svelte +26 -7
  232. package/dist/ui/ThemeSwitcher.svelte.d.ts +4 -2
  233. package/dist/ui/Tooltip.svelte +312 -180
  234. package/dist/ui/Tooltip.svelte.d.ts +4 -3
  235. package/dist/uiTheme.svelte.d.ts +86 -0
  236. package/dist/uiTheme.svelte.js +214 -0
  237. package/dist/util/DateFunctions.js +1 -1
  238. package/dist/util/DragDrop.d.ts +50 -0
  239. package/dist/util/DragDrop.js +154 -0
  240. package/dist/util/NavigateTo.js +2 -2
  241. package/dist/util/dayjs.d.ts +4 -0
  242. package/dist/util/dayjs.js +15 -0
  243. package/package.json +66 -26
  244. package/dist/display/Alert.svelte +0 -179
  245. package/dist/display/Alert.svelte.d.ts +0 -13
  246. package/dist/form/FileInput.svelte +0 -235
  247. package/dist/form/FileInput.svelte.d.ts +0 -23
  248. package/dist/form/PasswordInput.svelte +0 -253
  249. package/dist/form/PasswordInput.svelte.d.ts +0 -34
  250. package/dist/form/Rating.svelte +0 -235
  251. package/dist/form/Rating.svelte.d.ts +0 -21
  252. package/dist/styles/components.css +0 -199
  253. package/dist/styles/dark.css +0 -146
  254. package/dist/styles/index.css +0 -116
  255. package/dist/styles/reset.css +0 -110
  256. package/dist/styles/semantic.css +0 -86
  257. package/dist/styles/tokens.css +0 -203
  258. package/dist/styles/utilities.css +0 -523
  259. package/dist/ui/Breadcrumbs.svelte +0 -131
  260. package/dist/ui/Breadcrumbs.svelte.d.ts +0 -16
  261. package/dist/ui/LoadingOverlay.svelte +0 -53
  262. package/dist/ui/LoadingOverlay.svelte.d.ts +0 -11
  263. package/dist/ui/Progress.svelte +0 -191
  264. package/dist/ui/Progress.svelte.d.ts +0 -15
  265. package/dist/ui/RingProgress.svelte +0 -141
  266. package/dist/ui/RingProgress.svelte.d.ts +0 -18
  267. package/dist/ui/Skeleton.svelte +0 -85
  268. package/dist/ui/Skeleton.svelte.d.ts +0 -12
  269. package/dist/ui/Stepper.svelte +0 -355
  270. package/dist/ui/Stepper.svelte.d.ts +0 -24
  271. package/dist/ui/Timeline.svelte +0 -225
  272. package/dist/ui/Timeline.svelte.d.ts +0 -23
  273. /package/dist/{actions → util}/ClickOutside.d.ts +0 -0
  274. /package/dist/{actions → util}/ClickOutside.js +0 -0
  275. /package/dist/{transitions → util}/Transitions.d.ts +0 -0
  276. /package/dist/{transitions → util}/Transitions.js +0 -0
@@ -1,31 +1,41 @@
1
1
  <!-- drop down component that displays a button, when clicked on the children expand -->
2
2
  <script lang="ts">
3
3
  import Button from './Button.svelte';
4
+ import type { ButtonVariant } from './ButtonVariant.js';
4
5
  import type { Hyperlink } from '../models/Hyperlink.js';
5
6
  import LinkCollection from '../display/LinkCollection.svelte';
6
7
  import { iconChevronDown } from '../icon/index.js';
7
8
 
8
- interface Props {
9
+ export interface DropdownProps {
10
+ /** Whether the dropdown is expanded (bindable) */
9
11
  show?: boolean;
12
+ /** Text label for the trigger button */
10
13
  label: string | undefined;
14
+ /** Visual style variant of the trigger button */
15
+ variant?: ButtonVariant;
16
+ /** SVG icon for the trigger button */
11
17
  buttonSvg?: string | undefined;
18
+ /** Navigation links to display (flat array or grouped arrays) */
12
19
  links?: Hyperlink[] | Hyperlink[][];
20
+ /** Additional CSS classes */
13
21
  class?: string;
22
+ /** Horizontal alignment of dropdown content relative to button */
14
23
  alignToButton?: 'left' | 'right';
15
24
  }
16
25
 
17
26
  let {
18
27
  show = $bindable(false),
19
28
  label,
29
+ variant = 'primary',
20
30
  buttonSvg = iconChevronDown,
21
31
  links = [],
22
32
  class: classes = '',
23
33
  alignToButton = 'left'
24
- }: Props = $props();
34
+ }: DropdownProps = $props();
25
35
  </script>
26
36
 
27
37
  <div class="dropdown {classes} {alignToButton}">
28
- <Button class="ps-2" svg={buttonSvg} onclick={() => (show = !show)}>
38
+ <Button class="ps-2" {variant} svg={buttonSvg} onclick={() => (show = !show)}>
29
39
  {label}
30
40
  </Button>
31
41
  {#if show}
@@ -54,13 +64,13 @@
54
64
  position: absolute;
55
65
  top: 100%;
56
66
  left: 0;
57
- background-color: var(--pui-paper-body-bg);
67
+ background-color: var(--paper-body-bg);
58
68
  min-width: 160px;
59
- box-shadow: var(--pui-shadow-lg);
60
- z-index: var(--pui-z-dropdown);
61
- border-radius: var(--pui-radius-sm);
69
+ box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2);
70
+ z-index: 1000;
71
+ border-radius: 4px;
62
72
  overflow: hidden;
63
- border: 1px solid var(--pui-border-default);
73
+ border: 1px solid var(--border-color);
64
74
  }
65
75
 
66
76
  .dropdown-group {
@@ -69,19 +79,19 @@
69
79
  }
70
80
 
71
81
  .dropdown-group:not(:last-child) {
72
- border-bottom: 1px solid var(--pui-border-default);
82
+ border-bottom: 1px solid var(--border-color);
73
83
  }
74
84
 
75
85
  .dropdown-content :global(a) {
76
86
  display: block;
77
- padding: var(--pui-spacing-2) var(--pui-spacing-3);
87
+ padding: 8px 12px;
78
88
  text-decoration: none;
79
- color: var(--pui-color-primary);
80
- transition: background-color var(--pui-transition-fast) var(--pui-ease-out);
89
+ color: var(--ui-primary);
90
+ transition: background-color 0.2s ease;
81
91
  }
82
92
 
83
93
  .dropdown-content :global(a:hover) {
84
- background-color: var(--pui-bg-hover);
94
+ background-color: var(--border-color);
85
95
  }
86
96
 
87
97
  .dropdown.right .dropdown-content {
@@ -91,7 +101,7 @@
91
101
 
92
102
  :global(.dark) {
93
103
  .dropdown-content :global(a) {
94
- color: var(--pui-color-secondary);
104
+ color: var(--ui-accent);
95
105
  }
96
106
  }
97
107
  </style>
@@ -1,12 +1,21 @@
1
+ import type { ButtonVariant } from './ButtonVariant.js';
1
2
  import type { Hyperlink } from '../models/Hyperlink.js';
2
- interface Props {
3
+ export interface DropdownProps {
4
+ /** Whether the dropdown is expanded (bindable) */
3
5
  show?: boolean;
6
+ /** Text label for the trigger button */
4
7
  label: string | undefined;
8
+ /** Visual style variant of the trigger button */
9
+ variant?: ButtonVariant;
10
+ /** SVG icon for the trigger button */
5
11
  buttonSvg?: string | undefined;
12
+ /** Navigation links to display (flat array or grouped arrays) */
6
13
  links?: Hyperlink[] | Hyperlink[][];
14
+ /** Additional CSS classes */
7
15
  class?: string;
16
+ /** Horizontal alignment of dropdown content relative to button */
8
17
  alignToButton?: 'left' | 'right';
9
18
  }
10
- declare const Dropdown: import("svelte").Component<Props, {}, "show">;
19
+ declare const Dropdown: import("svelte").Component<DropdownProps, {}, "show">;
11
20
  type Dropdown = ReturnType<typeof Dropdown>;
12
21
  export default Dropdown;
@@ -1,122 +1,224 @@
1
1
  <script lang="ts">
2
- import { toast } from './Toast.svelte.js';
2
+ import type { Snippet } from 'svelte';
3
3
  import Loader from './Loader.svelte';
4
+ import Icon from '../icon/Icon.svelte';
5
+ import iconCloudUpload from '../icon/cloud-upload.svg?raw';
6
+ import iconDownload from '../icon/download.svg?raw';
4
7
 
5
- interface Props {
8
+ export interface DropzoneProps {
9
+ /** Show loading state */
6
10
  loading?: boolean;
11
+ /** Disable the dropzone without showing the loading state */
12
+ disabled?: boolean;
13
+ /** Allow multiple file selection */
7
14
  multiple?: boolean;
15
+ /** Callback when valid files are dropped or selected */
8
16
  onFileSubmit: (files: File[]) => void;
17
+ /** Callback with files rejected by accept, maxSize or maxFiles validation */
18
+ onReject?: (files: File[]) => void;
19
+ /** Accepted file types (e.g., "image/*,.pdf") */
9
20
  accept?: string;
21
+ /** Maximum file size in bytes */
22
+ maxSize?: number;
23
+ /** Maximum number of files per drop or selection (only applies when multiple) */
24
+ maxFiles?: number;
25
+ /** Icon shown above the label (raw SVG string, defaults to the cloud upload icon) */
26
+ icon?: string;
27
+ /** Icon shown while files are dragged over (raw SVG string, defaults to the download icon) */
28
+ dragOverIcon?: string;
29
+ /** Label text (defaults to "Click to upload or drag and drop") */
30
+ label?: string;
31
+ /** Sublabel text under the label (defaults to a hint built from accept/maxSize) */
32
+ sublabel?: string;
33
+ /** Width override (any CSS width value, defaults to 100%) */
34
+ width?: string;
35
+ /** Height override (any CSS height value) */
36
+ height?: string;
37
+ /** Custom content rendered inside the dropzone instead of the label */
38
+ children?: Snippet;
10
39
  }
11
40
 
12
- let { loading = false, multiple = false, onFileSubmit, accept }: Props = $props();
41
+ let {
42
+ loading = false,
43
+ disabled = false,
44
+ multiple = false,
45
+ onFileSubmit,
46
+ onReject,
47
+ accept,
48
+ maxSize,
49
+ maxFiles,
50
+ icon = iconCloudUpload,
51
+ dragOverIcon = iconDownload,
52
+ label,
53
+ sublabel,
54
+ width,
55
+ height,
56
+ children
57
+ }: DropzoneProps = $props();
13
58
 
14
- let dragzoneElement: HTMLElement;
15
59
  let fileInput: HTMLInputElement;
16
- let dragOver: boolean = $state(false);
17
- let labelText = $derived(dragOver ? 'Drop files here' : 'Click here or drag a file');
60
+ // Counter instead of a boolean: dragleave fires when crossing child elements
61
+ let dragDepth = $state(0);
62
+ let dragOver = $derived(dragDepth > 0);
63
+ let labelText = $derived(
64
+ dragOver ? 'Drop files here' : label || 'Click to upload or drag and drop'
65
+ );
66
+ let iconSvg = $derived(dragOver ? dragOverIcon : icon);
67
+ let sublabelText = $derived.by(() => {
68
+ if (sublabel !== undefined) return sublabel;
69
+ const hints = [];
70
+ if (accept) hints.push(accept);
71
+ if (maxSize) hints.push(`up to ${formatBytes(maxSize)}`);
72
+ return hints.length ? hints.join(' · ') : '';
73
+ });
74
+
75
+ function formatBytes(bytes: number): string {
76
+ if (bytes < 1024) return `${bytes} B`;
77
+ const units = ['KB', 'MB', 'GB'];
78
+ const i = Math.min(Math.floor(Math.log(bytes) / Math.log(1024)), units.length);
79
+ return `${parseFloat((bytes / Math.pow(1024, i)).toFixed(1))} ${units[i - 1]}`;
80
+ }
18
81
 
19
- function handleClick(e: Event) {
20
- e.preventDefault();
21
- fileInput!.click();
82
+ function matchesAccept(file: File): boolean {
83
+ if (!accept) return true;
84
+ return accept.split(',').some((token) => {
85
+ const type = token.trim().toLowerCase();
86
+ if (!type) return false;
87
+ if (type === '*/*') return true;
88
+ if (type.startsWith('.')) return file.name.toLowerCase().endsWith(type);
89
+ if (type.endsWith('/*')) return file.type.toLowerCase().startsWith(type.slice(0, -1));
90
+ return file.type.toLowerCase() === type;
91
+ });
22
92
  }
23
93
 
24
- function handleFileSubmission(files: File[]) {
25
- onFileSubmit(files);
94
+ function submitFiles(files: File[]) {
95
+ let accepted = files.filter(
96
+ (file) => matchesAccept(file) && (!maxSize || file.size <= maxSize)
97
+ );
98
+ let rejected = files.filter((file) => !accepted.includes(file));
99
+
100
+ const limit = multiple ? maxFiles : 1;
101
+ if (limit && accepted.length > limit) {
102
+ rejected = [...rejected, ...accepted.slice(limit)];
103
+ accepted = accepted.slice(0, limit);
104
+ }
105
+
106
+ if (accepted.length) onFileSubmit(accepted);
107
+ if (rejected.length) onReject?.(rejected);
26
108
  }
27
109
 
28
- function handleFiles(event: DragEvent) {
110
+ function handleDrop(event: DragEvent) {
29
111
  event.preventDefault();
112
+ dragDepth = 0;
113
+ if (loading || disabled) return;
30
114
  const files = event.dataTransfer?.files;
31
- if (files) {
32
- let array = Array.from(files);
33
-
34
- if (!multiple && array.length > 1) array = [array[0]];
35
-
36
- handleFileSubmission(array);
37
- }
38
- dragOver = false;
115
+ if (files?.length) submitFiles(Array.from(files));
39
116
  }
40
117
 
41
- function handleDragOver(event: DragEvent) {
118
+ function handleDragEnter(event: DragEvent) {
42
119
  event.preventDefault();
43
- dragOver = true;
120
+ dragDepth++;
44
121
  }
45
122
 
46
123
  function handleDragLeave(event: DragEvent) {
47
124
  event.preventDefault();
48
- dragOver = false;
125
+ dragDepth = Math.max(0, dragDepth - 1);
49
126
  }
50
127
 
51
128
  function handleInputChange(event: Event) {
52
- let fileList: FileList | null = (event.currentTarget as HTMLInputElement)?.files;
53
- if (fileList) {
54
- handleFileSubmission(Array.from(fileList));
55
- } else {
56
- toast.error('No file selected');
57
- }
129
+ const input = event.currentTarget as HTMLInputElement;
130
+ if (input.files?.length) submitFiles(Array.from(input.files));
131
+ // Reset so selecting the same file again re-fires change
132
+ input.value = '';
58
133
  }
59
134
  </script>
60
135
 
61
136
  <input
62
137
  type="file"
63
- id="file-input"
64
- multiple
138
+ {multiple}
139
+ {accept}
65
140
  onchange={handleInputChange}
66
141
  bind:this={fileInput}
67
142
  class="hidden"
68
- accept={accept ? accept : '*.*'}
69
143
  />
70
144
  <button
71
- bind:this={dragzoneElement}
145
+ type="button"
72
146
  class="dropzone"
73
147
  class:dragover={dragOver}
74
- disabled={loading}
75
- ondragenter={handleDragOver}
148
+ style:width
149
+ style:height
150
+ disabled={loading || disabled}
151
+ aria-busy={loading}
152
+ aria-label={labelText}
153
+ ondragenter={handleDragEnter}
76
154
  ondragover={(ev) => ev.preventDefault()}
77
155
  ondragleave={handleDragLeave}
78
- ondrop={handleFiles}
79
- onclick={(e) => handleClick(e)}
156
+ ondrop={handleDrop}
157
+ onclick={() => fileInput.click()}
80
158
  >
81
159
  {#if loading}
82
160
  <Loader fullScreen />
161
+ {:else if children}
162
+ {@render children()}
83
163
  {:else}
84
- {labelText}
164
+ <span class="dropzone-content">
165
+ <Icon svg={iconSvg} size="32px" class="dropzone-icon" />
166
+ <span class="dropzone-label">{labelText}</span>
167
+ {#if sublabelText}
168
+ <span class="dropzone-sublabel">{sublabelText}</span>
169
+ {/if}
170
+ </span>
85
171
  {/if}
86
172
  </button>
87
173
 
88
174
  <style>
89
175
  .dropzone {
90
- border: 1px dashed var(--pui-border-default);
91
- padding: var(--pui-spacing-4);
176
+ width: 100%;
177
+ border: 1px dashed var(--border-color);
178
+ padding: 1rem;
92
179
  text-align: center;
93
180
  cursor: pointer;
94
- transition: all var(--pui-transition-fast) var(--pui-ease-in-out);
95
- border-radius: var(--pui-radius-lg);
181
+ transition: all 0.2s ease-in-out;
182
+ border-radius: 6px;
96
183
  flex-grow: 1;
97
- background-color: var(--pui-input-bg);
98
- color: var(--pui-text-primary);
184
+ background: var(--paper-body-bg);
185
+ color: var(--text-color);
99
186
  }
100
187
 
101
- .dropzone:hover,
188
+ .dropzone:hover:not(:disabled),
102
189
  .dropzone.dragover {
103
- background-color: var(--pui-bg-hover);
104
- border-color: var(--pui-color-primary);
190
+ background: var(--page-bg);
105
191
  }
106
192
 
107
- .hidden {
108
- display: none;
193
+ .dropzone:disabled {
194
+ cursor: not-allowed;
195
+ opacity: 0.65;
196
+ }
197
+
198
+ .dropzone-content {
199
+ display: flex;
200
+ flex-direction: column;
201
+ align-items: center;
202
+ justify-content: center;
203
+ gap: 0.25rem;
204
+ height: 100%;
205
+ }
206
+
207
+ .dropzone-content :global(.dropzone-icon) {
208
+ opacity: 0.65;
209
+ margin-bottom: 0.25rem;
109
210
  }
110
211
 
111
- :global(.dark) .dropzone {
112
- background-color: var(--pui-input-bg);
113
- border-color: var(--pui-border-default);
114
- color: var(--pui-text-primary);
212
+ .dropzone-label {
213
+ font-weight: 500;
115
214
  }
116
215
 
117
- :global(.dark) .dropzone:hover,
118
- :global(.dark) .dropzone.dragover {
119
- background-color: var(--pui-bg-hover);
120
- border-color: var(--pui-color-secondary);
216
+ .dropzone-sublabel {
217
+ font-size: 0.8125rem;
218
+ opacity: 0.65;
219
+ }
220
+
221
+ .hidden {
222
+ display: none;
121
223
  }
122
224
  </style>
@@ -1,9 +1,36 @@
1
- interface Props {
1
+ import type { Snippet } from 'svelte';
2
+ export interface DropzoneProps {
3
+ /** Show loading state */
2
4
  loading?: boolean;
5
+ /** Disable the dropzone without showing the loading state */
6
+ disabled?: boolean;
7
+ /** Allow multiple file selection */
3
8
  multiple?: boolean;
9
+ /** Callback when valid files are dropped or selected */
4
10
  onFileSubmit: (files: File[]) => void;
11
+ /** Callback with files rejected by accept, maxSize or maxFiles validation */
12
+ onReject?: (files: File[]) => void;
13
+ /** Accepted file types (e.g., "image/*,.pdf") */
5
14
  accept?: string;
15
+ /** Maximum file size in bytes */
16
+ maxSize?: number;
17
+ /** Maximum number of files per drop or selection (only applies when multiple) */
18
+ maxFiles?: number;
19
+ /** Icon shown above the label (raw SVG string, defaults to the cloud upload icon) */
20
+ icon?: string;
21
+ /** Icon shown while files are dragged over (raw SVG string, defaults to the download icon) */
22
+ dragOverIcon?: string;
23
+ /** Label text (defaults to "Click to upload or drag and drop") */
24
+ label?: string;
25
+ /** Sublabel text under the label (defaults to a hint built from accept/maxSize) */
26
+ sublabel?: string;
27
+ /** Width override (any CSS width value, defaults to 100%) */
28
+ width?: string;
29
+ /** Height override (any CSS height value) */
30
+ height?: string;
31
+ /** Custom content rendered inside the dropzone instead of the label */
32
+ children?: Snippet;
6
33
  }
7
- declare const Dropzone: import("svelte").Component<Props, {}, "">;
34
+ declare const Dropzone: import("svelte").Component<DropzoneProps, {}, "">;
8
35
  type Dropzone = ReturnType<typeof Dropzone>;
9
36
  export default Dropzone;
@@ -1,12 +1,20 @@
1
1
  <script lang="ts">
2
+ import { resolve } from '$app/paths';
3
+ import { interceptLinkClick } from '../util/interceptLinkClick.js';
2
4
  import type { Snippet } from 'svelte';
3
5
 
4
- interface Props {
6
+ export interface LinkProps {
7
+ /** URL to navigate to */
5
8
  href?: string;
9
+ /** Relationship attribute for the link */
6
10
  rel?: string;
11
+ /** Link target attribute */
7
12
  target?: '_self' | '_blank' | undefined;
13
+ /** Additional CSS classes */
8
14
  class?: string;
15
+ /** Link content */
9
16
  children?: Snippet;
17
+ /** Click event handler */
10
18
  onclick?: (event: MouseEvent) => void;
11
19
  }
12
20
 
@@ -17,15 +25,15 @@
17
25
  class: classes = '',
18
26
  children,
19
27
  onclick
20
- }: Props = $props();
28
+ }: LinkProps = $props();
21
29
  </script>
22
30
 
23
31
  <a
24
32
  {target}
25
33
  class={classes}
26
- {href}
34
+ href={href?.startsWith('/') ? resolve(href as any) : href}
27
35
  {rel}
28
- {onclick}
36
+ onclick={(e) => interceptLinkClick(e, { href, target, onclick })}
29
37
  data-sveltekit-preload-data="hover"
30
38
  >
31
39
  {@render children?.()}
@@ -1,12 +1,18 @@
1
1
  import type { Snippet } from 'svelte';
2
- interface Props {
2
+ export interface LinkProps {
3
+ /** URL to navigate to */
3
4
  href?: string;
5
+ /** Relationship attribute for the link */
4
6
  rel?: string;
7
+ /** Link target attribute */
5
8
  target?: '_self' | '_blank' | undefined;
9
+ /** Additional CSS classes */
6
10
  class?: string;
11
+ /** Link content */
7
12
  children?: Snippet;
13
+ /** Click event handler */
8
14
  onclick?: (event: MouseEvent) => void;
9
15
  }
10
- declare const Link: import("svelte").Component<Props, {}, "">;
16
+ declare const Link: import("svelte").Component<LinkProps, {}, "">;
11
17
  type Link = ReturnType<typeof Link>;
12
18
  export default Link;
@@ -2,13 +2,16 @@
2
2
  import Icon from '../icon/Icon.svelte';
3
3
  import loaderSvg from '../icon/loader.svg?raw';
4
4
 
5
- interface Props {
5
+ export interface LoaderProps {
6
+ /** Display as full-screen overlay */
6
7
  fullScreen?: boolean;
8
+ /** Custom size for the loader icon */
7
9
  sizeOverride?: string;
10
+ /** Additional CSS classes */
8
11
  class?: string;
9
12
  }
10
13
 
11
- let { fullScreen = false, sizeOverride = '50px', class: classes = '' }: Props = $props();
14
+ let { fullScreen = false, sizeOverride = '50px', class: classes = '' }: LoaderProps = $props();
12
15
 
13
16
  // export let fullScreen: boolean = false;
14
17
  // export let sizeOverride: string = '50px';
@@ -17,22 +20,11 @@
17
20
  // export { classes as class };
18
21
  </script>
19
22
 
20
- <span class="loader {classes} {fullScreen && 'full-screen'}">
23
+ <span class="loader flex {classes} {fullScreen && 'full-screen'}">
21
24
  <Icon size={sizeOverride} class="spin" svg={loaderSvg} />
22
25
  </span>
23
26
 
24
27
  <style>
25
- .loader {
26
- display: inline-flex;
27
- align-items: center;
28
- justify-content: center;
29
- color: var(--pui-loader-color, var(--pui-color-primary));
30
- }
31
-
32
- :global(.dark) .loader {
33
- color: var(--pui-loader-color, var(--pui-color-secondary));
34
- }
35
-
36
28
  @keyframes -global-ckw {
37
29
  from {
38
30
  transform: rotate(0deg);
@@ -42,24 +34,37 @@
42
34
  }
43
35
  }
44
36
 
37
+ /* loader
38
+ text-accent
39
+ absolute
40
+ top-0
41
+ z-30
42
+ left-0
43
+ w-full
44
+ h-full
45
+ flex
46
+ items-center
47
+ justify-center
48
+ bg-neutral-200/75
49
+ dark:bg-neutral-700/75 */
45
50
  .full-screen {
46
51
  position: absolute;
47
52
  top: 0;
48
53
  left: 0;
49
54
  width: 100%;
50
55
  height: 100%;
51
- z-index: var(--pui-z-sticky);
56
+ z-index: 30;
52
57
  display: flex;
53
58
  align-items: center;
54
59
  justify-content: center;
55
60
  background-color: rgba(255, 255, 255, 0.75);
56
- border-radius: var(--pui-radius-base);
57
- color: var(--pui-color-primary);
61
+ border-radius: 0.25rem;
62
+ color: var(--ui-primary);
58
63
  }
59
64
 
60
65
  :global(.dark) .full-screen {
61
66
  background-color: rgba(0, 0, 0, 0.75);
62
- color: var(--pui-color-secondary);
67
+ color: var(--ui-accent);
63
68
  }
64
69
 
65
70
  :global(span.spin) {
@@ -1,8 +1,11 @@
1
- interface Props {
1
+ export interface LoaderProps {
2
+ /** Display as full-screen overlay */
2
3
  fullScreen?: boolean;
4
+ /** Custom size for the loader icon */
3
5
  sizeOverride?: string;
6
+ /** Additional CSS classes */
4
7
  class?: string;
5
8
  }
6
- declare const Loader: import("svelte").Component<Props, {}, "">;
9
+ declare const Loader: import("svelte").Component<LoaderProps, {}, "">;
7
10
  type Loader = ReturnType<typeof Loader>;
8
11
  export default Loader;
@@ -0,0 +1,37 @@
1
+ <script lang="ts">
2
+ import { themeState } from '../theme.svelte.js';
3
+ import { brandState } from '../uiTheme.svelte.js';
4
+ import Icon from '../icon/Icon.svelte';
5
+ import Link from './Link.svelte';
6
+ import placeholderLogo from '../icon/logo-placeholder.svg?raw';
7
+
8
+ export interface LogoProps {
9
+ /** Additional CSS classes */
10
+ class?: string;
11
+ /** URL to navigate to when logo is clicked */
12
+ href?: string;
13
+ /** Override the logo fill colour (defaults to theme-aware primary/white) */
14
+ fill?: string;
15
+ /**
16
+ * Raw SVG markup to render. Falls back to the logo registered via
17
+ * `applyTheme({ logo })` / `setLogo()`, then to a generic placeholder.
18
+ */
19
+ svg?: string;
20
+ }
21
+
22
+ let {
23
+ class: classes = '',
24
+ href = '/',
25
+ fill: fillOverride = undefined,
26
+ svg = undefined
27
+ }: LogoProps = $props();
28
+
29
+ const fill = $derived(fillOverride ?? (themeState.isDarkMode ? 'white' : 'var(--ui-primary)'));
30
+ const logoSvg = $derived(svg ?? brandState.logo ?? placeholderLogo);
31
+ </script>
32
+
33
+ <Link {href}>
34
+ <span class="logo {classes}">
35
+ <Icon svg={logoSvg} {fill} stroke="none" />
36
+ </span>
37
+ </Link>
@@ -0,0 +1,16 @@
1
+ export interface LogoProps {
2
+ /** Additional CSS classes */
3
+ class?: string;
4
+ /** URL to navigate to when logo is clicked */
5
+ href?: string;
6
+ /** Override the logo fill colour (defaults to theme-aware primary/white) */
7
+ fill?: string;
8
+ /**
9
+ * Raw SVG markup to render. Falls back to the logo registered via
10
+ * `applyTheme({ logo })` / `setLogo()`, then to a generic placeholder.
11
+ */
12
+ svg?: string;
13
+ }
14
+ declare const Logo: import("svelte").Component<LogoProps, {}, "">;
15
+ type Logo = ReturnType<typeof Logo>;
16
+ export default Logo;