@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
@@ -0,0 +1,129 @@
1
+ <script lang="ts">
2
+ import Icon from '../../icon/Icon.svelte';
3
+
4
+ import warningSvg from '../../icon/alert-triangle.svg?raw';
5
+ import dangerSvg from '../../icon/alert-octagon.svg?raw';
6
+ import type { AlertVariant } from './AlertVariant.js';
7
+ import type { Snippet } from 'svelte';
8
+
9
+ export interface AlertProps {
10
+ /** Title text displayed in the alert header */
11
+ title?: string;
12
+ /** Visual style variant of the alert */
13
+ variant?: AlertVariant;
14
+ /** Additional CSS classes */
15
+ class?: string;
16
+ /** Alert content */
17
+ children: Snippet;
18
+ }
19
+
20
+ let { title = '', variant = 'default', class: classes = '', children }: AlertProps = $props();
21
+
22
+ let iconSvg: string | undefined = $state(undefined);
23
+
24
+ $effect(() => {
25
+ if (variant === 'warning') iconSvg = warningSvg;
26
+ else if (variant === 'danger') iconSvg = dangerSvg;
27
+ });
28
+ </script>
29
+
30
+ <div
31
+ class="alert {classes}
32
+ {variant === 'warning' && 'warning'}
33
+ {variant === 'danger' && 'danger'}"
34
+ >
35
+ {#if iconSvg}
36
+ <div class="icon">
37
+ <Icon svg={iconSvg} size="1.25rem" />
38
+ </div>
39
+ {/if}
40
+ <div>
41
+ {#if title}
42
+ <div class="alert-title">{title}</div>
43
+ {/if}
44
+ {@render children?.()}
45
+ </div>
46
+ </div>
47
+
48
+ <style>
49
+ .alert {
50
+ display: flex;
51
+ border-radius: 0.25rem;
52
+ padding: 0.5rem;
53
+ background: rgba(var(--ui-accent-rgbc), 0.3);
54
+ border: 1px solid var(--ui-accent);
55
+ color: var(--text-color);
56
+
57
+ .icon {
58
+ width: 1.25rem;
59
+ padding-top: 0.15rem;
60
+ margin-right: 0.75rem;
61
+ }
62
+
63
+ .alert-title {
64
+ margin-bottom: 0.25rem;
65
+ font-weight: 700;
66
+ }
67
+
68
+ .alert-title,
69
+ .icon {
70
+ color: var(--ui-primary);
71
+ }
72
+ }
73
+
74
+ .alert.danger {
75
+ background: #f8d7da;
76
+ color: #58151c;
77
+ border: 1px solid #f1aeb5;
78
+
79
+ .alert-title,
80
+ .icon {
81
+ color: #58151c;
82
+ }
83
+ }
84
+
85
+ .alert.warning {
86
+ background: #fff3cd;
87
+ color: #664d03;
88
+ border: 1px solid #ffe69c;
89
+
90
+ .alert-title,
91
+ .icon {
92
+ color: #664d03;
93
+ }
94
+ }
95
+
96
+ :global(.dark) {
97
+ .alert {
98
+ background: rgba(var(--ui-primary-rgbc), 0.3);
99
+ border-color: var(--ui-primary);
100
+
101
+ .alert-title,
102
+ .icon {
103
+ color: var(--white);
104
+ }
105
+ }
106
+
107
+ .alert.danger {
108
+ background: #2c0b0e;
109
+ color: #ea868f;
110
+ border-color: #842029;
111
+
112
+ .alert-title,
113
+ .icon {
114
+ color: #ea868f;
115
+ }
116
+ }
117
+
118
+ .alert.warning {
119
+ background: #332701;
120
+ color: #fff3cd;
121
+ border-color: #997404;
122
+
123
+ .alert-title,
124
+ .icon {
125
+ color: #fff3cd;
126
+ }
127
+ }
128
+ }
129
+ </style>
@@ -0,0 +1,15 @@
1
+ import type { AlertVariant } from './AlertVariant.js';
2
+ import type { Snippet } from 'svelte';
3
+ export interface AlertProps {
4
+ /** Title text displayed in the alert header */
5
+ title?: string;
6
+ /** Visual style variant of the alert */
7
+ variant?: AlertVariant;
8
+ /** Additional CSS classes */
9
+ class?: string;
10
+ /** Alert content */
11
+ children: Snippet;
12
+ }
13
+ declare const Alert: import("svelte").Component<AlertProps, {}, "">;
14
+ type Alert = ReturnType<typeof Alert>;
15
+ export default Alert;
@@ -0,0 +1 @@
1
+ export type AlertVariant = 'default' | 'warning' | 'danger';
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,259 @@
1
+ <script lang="ts" module>
2
+ import type { Snippet } from 'svelte';
3
+ import type { FileTreeNode } from './FileTreeNode.js';
4
+ import type { ContextMenuItem } from '../../ui/ContextMenu.svelte';
5
+
6
+ export interface FileTreeDropEvent {
7
+ /** Id of the node that was dragged */
8
+ sourceId: string;
9
+ /** Id of the destination container (folder). Null when dropped at the tree root. */
10
+ targetContainerId: string | null;
11
+ /** Insertion position within the destination container */
12
+ index: number;
13
+ }
14
+
15
+ export interface FileTreeProps {
16
+ /** Tree data — bindable so drag/drop reorders write back */
17
+ nodes: FileTreeNode[];
18
+ /**
19
+ * Currently selected node id (bindable). Setting it programmatically expands
20
+ * every ancestor folder so the selected row is visible.
21
+ */
22
+ selectedId?: string;
23
+ /** Custom drag group — distinct trees with different groups won't move items between each other */
24
+ group?: string;
25
+ /** Enable drag-and-drop reordering. Default true. */
26
+ isDraggable?: boolean;
27
+ /** Renders the label portion of a row. Receives the node. */
28
+ nodeContent?: Snippet<[FileTreeNode]>;
29
+ /**
30
+ * Renders a trailing action bar for a row (e.g. View / Edit / Add buttons).
31
+ * Called with the node so actions can vary per node/type.
32
+ * Rendered as a real sibling of the row button — so it may safely contain
33
+ * interactive elements (links, buttons) and clicks won't select the row.
34
+ */
35
+ nodeActions?: Snippet<[FileTreeNode]>;
36
+ /**
37
+ * When the action bar appears: `'selected'` (default), `'hover'`, or `'always'`.
38
+ * The bar is hidden automatically while a drag is in progress.
39
+ */
40
+ nodeActionsTrigger?: 'selected' | 'hover' | 'always';
41
+ /**
42
+ * Where the action bar sits within the row:
43
+ * `'end'` (default) pins it to the row's right edge;
44
+ * `'beside'` places it immediately after the label — useful for
45
+ * full-width trees where the right edge is far from the name.
46
+ */
47
+ nodeActionsPlacement?: 'end' | 'beside';
48
+ /**
49
+ * When `false`, folders cannot be collapsed — they always render expanded
50
+ * and clicking a folder row selects it without toggling. Default `true`.
51
+ */
52
+ collapsible?: boolean;
53
+ /**
54
+ * When `true`, each row's label gets a `title` attribute with the node name,
55
+ * so truncated names show a native tooltip on hover. Default `false`.
56
+ */
57
+ showTitles?: boolean;
58
+ /**
59
+ * Returns the context-menu items shown when a node is right-clicked.
60
+ * Return `[]` (or omit the prop) to leave the native browser menu in place.
61
+ * Called with the right-clicked node, so items can vary per node/type.
62
+ */
63
+ menuItems?: (node: FileTreeNode) => ContextMenuItem[];
64
+ /** Minimum width of the context menu. */
65
+ menuMinWidth?: string;
66
+ onSelect?: (node: FileTreeNode) => void;
67
+ /** Fires when a node is double-clicked (e.g. to open a file). A double-click also fires `onSelect` first. */
68
+ onDoubleClick?: (node: FileTreeNode) => void;
69
+ /** Fires when a node is right-clicked, before the menu opens. Call `preventDefault()` yourself only if you also omit `menuItems`. */
70
+ onContextMenu?: (node: FileTreeNode, event: MouseEvent) => void;
71
+ /** Fires after a successful drag/drop. Receives the moved id and the destination container id. */
72
+ onDrop?: (event: FileTreeDropEvent) => void;
73
+ /** Fires after any change to the tree (currently only drag/drop). Receives the new tree. */
74
+ onChange?: (nodes: FileTreeNode[]) => void;
75
+ class?: string;
76
+ }
77
+ </script>
78
+
79
+ <script lang="ts">
80
+ import { untrack } from 'svelte';
81
+ import FileTreeItem from './FileTreeItem.svelte';
82
+ import { expandTo, findNode, isAncestor, moveNode } from './FileTreeNode.js';
83
+ import { droppable, type DragContext } from '../../util/DragDrop.js';
84
+ import ContextMenu from '../../ui/ContextMenu.svelte';
85
+
86
+ let {
87
+ nodes = $bindable(),
88
+ selectedId = $bindable(undefined),
89
+ group = 'default',
90
+ isDraggable = true,
91
+ nodeContent,
92
+ nodeActions,
93
+ nodeActionsTrigger = 'selected',
94
+ nodeActionsPlacement = 'end',
95
+ collapsible = true,
96
+ showTitles = false,
97
+ menuItems,
98
+ menuMinWidth = '180px',
99
+ onSelect,
100
+ onDoubleClick,
101
+ onContextMenu,
102
+ onDrop,
103
+ onChange,
104
+ class: classes = ''
105
+ }: FileTreeProps = $props();
106
+
107
+ let menuOpen = $state(false);
108
+ /** True while any row is being dragged — used to hide inline node actions. */
109
+ let dragging = $state(false);
110
+ let menuX = $state(0);
111
+ let menuY = $state(0);
112
+ let menuList = $state<ContextMenuItem[]>([]);
113
+
114
+ function handleContextMenu(node: FileTreeNode, event: MouseEvent) {
115
+ onContextMenu?.(node, event);
116
+ if (!menuItems) return;
117
+ const list = menuItems(node);
118
+ if (!list || list.length === 0) return;
119
+ event.preventDefault();
120
+ // Highlight the row that owns the menu.
121
+ selectedId = node.id;
122
+ menuList = list;
123
+ menuX = event.clientX;
124
+ menuY = event.clientY;
125
+ menuOpen = true;
126
+ }
127
+
128
+ // Reveal the selection: whenever selectedId changes (e.g. set programmatically by
129
+ // the caller), expand every ancestor folder so the selected row is visible.
130
+ // Only selectedId is tracked — collapsing a parent of the current selection
131
+ // afterwards is respected until the selection changes again.
132
+ $effect(() => {
133
+ const id = selectedId;
134
+ if (id === undefined) return;
135
+ untrack(() => {
136
+ if (expandTo(nodes, id)) nodes = nodes;
137
+ });
138
+ });
139
+
140
+ const dragType = $derived(`pui-filetree:${group}`);
141
+
142
+ function handleSelect(node: FileTreeNode) {
143
+ selectedId = node.id;
144
+ onSelect?.(node);
145
+ }
146
+
147
+ function handleToggle(node: FileTreeNode) {
148
+ // Mutate in place — caller's binding sees the change because we reassign the array.
149
+ const found = findNode(nodes, node.id);
150
+ if (!found) return;
151
+ found.node.expanded = !found.node.expanded;
152
+ nodes = nodes;
153
+ }
154
+
155
+ function handleMove(sourceId: string, targetId: string, position: 'before' | 'inside' | 'after') {
156
+ if (!isDraggable) return;
157
+ if (sourceId === targetId) return;
158
+ if (isAncestor(nodes, sourceId, targetId)) return;
159
+
160
+ const targetFound = findNode(nodes, targetId);
161
+ if (!targetFound) return;
162
+
163
+ let parentId: string | null;
164
+ let index: number;
165
+
166
+ if (position === 'inside') {
167
+ if (targetFound.node.type !== 'folder') return;
168
+ parentId = targetId;
169
+ index = -1;
170
+ if (!targetFound.node.expanded) targetFound.node.expanded = true;
171
+ } else {
172
+ // before / after: insert as sibling of the target
173
+ const targetParent = findParentOf(nodes, targetId);
174
+ parentId = targetParent ? targetParent.id : null;
175
+ index = targetFound.index + (position === 'after' ? 1 : 0);
176
+ }
177
+
178
+ const next = moveNode(nodes, sourceId, { parentId, index });
179
+ if (next !== nodes) {
180
+ nodes = next;
181
+ onDrop?.({ sourceId, targetContainerId: parentId, index });
182
+ onChange?.(next);
183
+ }
184
+ }
185
+
186
+ function findParentOf(tree: FileTreeNode[], id: string): FileTreeNode | null {
187
+ for (const n of tree) {
188
+ if (n.children?.some((c) => c.id === id)) return n;
189
+ if (n.children) {
190
+ const found = findParentOf(n.children, id);
191
+ if (found) return found;
192
+ }
193
+ }
194
+ return null;
195
+ }
196
+
197
+ function handleRootDrop(ctx: DragContext<{ sourceId: string }>) {
198
+ if (!isDraggable) return;
199
+ const sourceId = ctx.payload.sourceId;
200
+ const next = moveNode(nodes, sourceId, { parentId: null, index: -1 });
201
+ if (next !== nodes) {
202
+ nodes = next;
203
+ onDrop?.({ sourceId, targetContainerId: null, index: next.length - 1 });
204
+ onChange?.(next);
205
+ }
206
+ }
207
+ </script>
208
+
209
+ <div
210
+ class="pui-filetree {classes}"
211
+ use:droppable={{
212
+ accepts: isDraggable ? dragType : '__never__',
213
+ onDrop: (ctx) => handleRootDrop(ctx as DragContext<{ sourceId: string }>)
214
+ }}
215
+ >
216
+ {#each nodes as node (node.id)}
217
+ <FileTreeItem
218
+ {node}
219
+ depth={0}
220
+ {dragType}
221
+ {selectedId}
222
+ {isDraggable}
223
+ onSelect={handleSelect}
224
+ {onDoubleClick}
225
+ onToggle={handleToggle}
226
+ onMove={handleMove}
227
+ onContextMenu={handleContextMenu}
228
+ {nodeContent}
229
+ {nodeActions}
230
+ {nodeActionsTrigger}
231
+ {nodeActionsPlacement}
232
+ {collapsible}
233
+ {showTitles}
234
+ {dragging}
235
+ onDragState={(d) => (dragging = d)}
236
+ />
237
+ {/each}
238
+ {#if nodes.length === 0}
239
+ <div class="ft-empty">Empty</div>
240
+ {/if}
241
+ </div>
242
+
243
+ <ContextMenu bind:open={menuOpen} x={menuX} y={menuY} items={menuList} minWidth={menuMinWidth} />
244
+
245
+ <style>
246
+ .pui-filetree {
247
+ display: flex;
248
+ flex-direction: column;
249
+ min-height: 80px;
250
+ }
251
+
252
+ .ft-empty {
253
+ padding: 1rem;
254
+ text-align: center;
255
+ color: var(--text-muted, #888);
256
+ font-size: 0.875rem;
257
+ font-style: italic;
258
+ }
259
+ </style>
@@ -0,0 +1,76 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { FileTreeNode } from './FileTreeNode.js';
3
+ import type { ContextMenuItem } from '../../ui/ContextMenu.svelte';
4
+ export interface FileTreeDropEvent {
5
+ /** Id of the node that was dragged */
6
+ sourceId: string;
7
+ /** Id of the destination container (folder). Null when dropped at the tree root. */
8
+ targetContainerId: string | null;
9
+ /** Insertion position within the destination container */
10
+ index: number;
11
+ }
12
+ export interface FileTreeProps {
13
+ /** Tree data — bindable so drag/drop reorders write back */
14
+ nodes: FileTreeNode[];
15
+ /**
16
+ * Currently selected node id (bindable). Setting it programmatically expands
17
+ * every ancestor folder so the selected row is visible.
18
+ */
19
+ selectedId?: string;
20
+ /** Custom drag group — distinct trees with different groups won't move items between each other */
21
+ group?: string;
22
+ /** Enable drag-and-drop reordering. Default true. */
23
+ isDraggable?: boolean;
24
+ /** Renders the label portion of a row. Receives the node. */
25
+ nodeContent?: Snippet<[FileTreeNode]>;
26
+ /**
27
+ * Renders a trailing action bar for a row (e.g. View / Edit / Add buttons).
28
+ * Called with the node so actions can vary per node/type.
29
+ * Rendered as a real sibling of the row button — so it may safely contain
30
+ * interactive elements (links, buttons) and clicks won't select the row.
31
+ */
32
+ nodeActions?: Snippet<[FileTreeNode]>;
33
+ /**
34
+ * When the action bar appears: `'selected'` (default), `'hover'`, or `'always'`.
35
+ * The bar is hidden automatically while a drag is in progress.
36
+ */
37
+ nodeActionsTrigger?: 'selected' | 'hover' | 'always';
38
+ /**
39
+ * Where the action bar sits within the row:
40
+ * `'end'` (default) pins it to the row's right edge;
41
+ * `'beside'` places it immediately after the label — useful for
42
+ * full-width trees where the right edge is far from the name.
43
+ */
44
+ nodeActionsPlacement?: 'end' | 'beside';
45
+ /**
46
+ * When `false`, folders cannot be collapsed — they always render expanded
47
+ * and clicking a folder row selects it without toggling. Default `true`.
48
+ */
49
+ collapsible?: boolean;
50
+ /**
51
+ * When `true`, each row's label gets a `title` attribute with the node name,
52
+ * so truncated names show a native tooltip on hover. Default `false`.
53
+ */
54
+ showTitles?: boolean;
55
+ /**
56
+ * Returns the context-menu items shown when a node is right-clicked.
57
+ * Return `[]` (or omit the prop) to leave the native browser menu in place.
58
+ * Called with the right-clicked node, so items can vary per node/type.
59
+ */
60
+ menuItems?: (node: FileTreeNode) => ContextMenuItem[];
61
+ /** Minimum width of the context menu. */
62
+ menuMinWidth?: string;
63
+ onSelect?: (node: FileTreeNode) => void;
64
+ /** Fires when a node is double-clicked (e.g. to open a file). A double-click also fires `onSelect` first. */
65
+ onDoubleClick?: (node: FileTreeNode) => void;
66
+ /** Fires when a node is right-clicked, before the menu opens. Call `preventDefault()` yourself only if you also omit `menuItems`. */
67
+ onContextMenu?: (node: FileTreeNode, event: MouseEvent) => void;
68
+ /** Fires after a successful drag/drop. Receives the moved id and the destination container id. */
69
+ onDrop?: (event: FileTreeDropEvent) => void;
70
+ /** Fires after any change to the tree (currently only drag/drop). Receives the new tree. */
71
+ onChange?: (nodes: FileTreeNode[]) => void;
72
+ class?: string;
73
+ }
74
+ declare const FileTree: import("svelte").Component<FileTreeProps, {}, "selectedId" | "nodes">;
75
+ type FileTree = ReturnType<typeof FileTree>;
76
+ export default FileTree;