@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
@@ -0,0 +1,146 @@
1
+ <script lang="ts" module>
2
+ export interface ContextMenuItem {
3
+ /** Text label. Omit when `separator` is set. */
4
+ label?: string;
5
+ /** Optional leading icon (raw SVG string, e.g. `iconEdit`). */
6
+ icon?: string;
7
+ /** Invoked when the item is clicked. Ignored for separators/parents. */
8
+ action?: () => void;
9
+ /** Non-interactive, dimmed. */
10
+ disabled?: boolean;
11
+ /** Render in the destructive/danger colour. */
12
+ danger?: boolean;
13
+ /** Render a divider instead of a clickable row. */
14
+ separator?: boolean;
15
+ /** Right-aligned shortcut hint (e.g. `⌘C`, `Del`). Purely visual. */
16
+ shortcut?: string;
17
+ /** Nested submenu items — opens on hover. */
18
+ children?: ContextMenuItem[];
19
+ }
20
+
21
+ export interface ContextMenuProps {
22
+ /** Whether the menu is open (bindable). */
23
+ open?: boolean;
24
+ /** Viewport X coordinate to anchor at (usually `event.clientX`). */
25
+ x?: number;
26
+ /** Viewport Y coordinate to anchor at (usually `event.clientY`). */
27
+ y?: number;
28
+ /** The items to display. */
29
+ items?: ContextMenuItem[];
30
+ /** Minimum width of the (top-level) menu. */
31
+ minWidth?: string;
32
+ /** Additional CSS classes on the menu container. */
33
+ class?: string;
34
+ /** Fires whenever the menu closes (dismissed or item chosen). */
35
+ onClose?: () => void;
36
+ }
37
+ </script>
38
+
39
+ <script lang="ts">
40
+ import ContextMenuList from './ContextMenuList.svelte';
41
+
42
+ let {
43
+ open = $bindable(false),
44
+ x = 0,
45
+ y = 0,
46
+ items = [],
47
+ minWidth = '180px',
48
+ class: classes = '',
49
+ onClose
50
+ }: ContextMenuProps = $props();
51
+
52
+ let menuEl = $state<HTMLElement | null>(null);
53
+ let posX = $state(0);
54
+ let posY = $state(0);
55
+ let placed = $state(false);
56
+
57
+ /** Move the menu to <body> so it can't be clipped by overflow/transform ancestors. */
58
+ function portal(node: HTMLElement) {
59
+ document.body.appendChild(node);
60
+ return {
61
+ destroy() {
62
+ node.remove();
63
+ }
64
+ };
65
+ }
66
+
67
+ function close() {
68
+ if (!open) return;
69
+ open = false;
70
+ onClose?.();
71
+ }
72
+
73
+ function handleSelect(item: ContextMenuItem) {
74
+ item.action?.();
75
+ close();
76
+ }
77
+
78
+ // Clamp the menu into the viewport once it's measured. Hidden until placed to avoid a flash.
79
+ $effect(() => {
80
+ if (!open) {
81
+ placed = false;
82
+ return;
83
+ }
84
+ if (!menuEl) return;
85
+ const anchorX = x;
86
+ const anchorY = y;
87
+ const rect = menuEl.getBoundingClientRect();
88
+ const vw = window.innerWidth;
89
+ const vh = window.innerHeight;
90
+ let nx = anchorX;
91
+ let ny = anchorY;
92
+ if (nx + rect.width > vw - 8) nx = Math.max(8, vw - rect.width - 8);
93
+ if (ny + rect.height > vh - 8) ny = Math.max(8, vh - rect.height - 8);
94
+ posX = nx;
95
+ posY = ny;
96
+ placed = true;
97
+ });
98
+
99
+ // Dismiss the menu on outside interaction while it's open.
100
+ $effect(() => {
101
+ if (!open) return;
102
+ const onDown = (e: MouseEvent) => {
103
+ if (menuEl && !menuEl.contains(e.target as Node)) close();
104
+ };
105
+ const onKey = (e: KeyboardEvent) => {
106
+ if (e.key === 'Escape') {
107
+ e.preventDefault();
108
+ close();
109
+ }
110
+ };
111
+ const onScroll = (e: Event) => {
112
+ if (!menuEl || !menuEl.contains(e.target as Node)) close();
113
+ };
114
+ const onResize = () => close();
115
+ document.addEventListener('mousedown', onDown, true);
116
+ document.addEventListener('keydown', onKey, true);
117
+ window.addEventListener('scroll', onScroll, true);
118
+ window.addEventListener('resize', onResize);
119
+ return () => {
120
+ document.removeEventListener('mousedown', onDown, true);
121
+ document.removeEventListener('keydown', onKey, true);
122
+ window.removeEventListener('scroll', onScroll, true);
123
+ window.removeEventListener('resize', onResize);
124
+ };
125
+ });
126
+ </script>
127
+
128
+ {#if open}
129
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
130
+ <div
131
+ use:portal
132
+ bind:this={menuEl}
133
+ class="pui-ctxmenu {classes}"
134
+ style="left: {posX}px; top: {posY}px; visibility: {placed ? 'visible' : 'hidden'};"
135
+ oncontextmenu={(e) => e.preventDefault()}
136
+ >
137
+ <ContextMenuList {items} onSelect={handleSelect} {minWidth} depth={0} />
138
+ </div>
139
+ {/if}
140
+
141
+ <style>
142
+ .pui-ctxmenu {
143
+ position: fixed;
144
+ z-index: 2000;
145
+ }
146
+ </style>
@@ -0,0 +1,37 @@
1
+ export interface ContextMenuItem {
2
+ /** Text label. Omit when `separator` is set. */
3
+ label?: string;
4
+ /** Optional leading icon (raw SVG string, e.g. `iconEdit`). */
5
+ icon?: string;
6
+ /** Invoked when the item is clicked. Ignored for separators/parents. */
7
+ action?: () => void;
8
+ /** Non-interactive, dimmed. */
9
+ disabled?: boolean;
10
+ /** Render in the destructive/danger colour. */
11
+ danger?: boolean;
12
+ /** Render a divider instead of a clickable row. */
13
+ separator?: boolean;
14
+ /** Right-aligned shortcut hint (e.g. `⌘C`, `Del`). Purely visual. */
15
+ shortcut?: string;
16
+ /** Nested submenu items — opens on hover. */
17
+ children?: ContextMenuItem[];
18
+ }
19
+ export interface ContextMenuProps {
20
+ /** Whether the menu is open (bindable). */
21
+ open?: boolean;
22
+ /** Viewport X coordinate to anchor at (usually `event.clientX`). */
23
+ x?: number;
24
+ /** Viewport Y coordinate to anchor at (usually `event.clientY`). */
25
+ y?: number;
26
+ /** The items to display. */
27
+ items?: ContextMenuItem[];
28
+ /** Minimum width of the (top-level) menu. */
29
+ minWidth?: string;
30
+ /** Additional CSS classes on the menu container. */
31
+ class?: string;
32
+ /** Fires whenever the menu closes (dismissed or item chosen). */
33
+ onClose?: () => void;
34
+ }
35
+ declare const ContextMenu: import("svelte").Component<ContextMenuProps, {}, "open">;
36
+ type ContextMenu = ReturnType<typeof ContextMenu>;
37
+ export default ContextMenu;
@@ -0,0 +1,188 @@
1
+ <script lang="ts" module>
2
+ import type { ContextMenuItem } from './ContextMenu.svelte';
3
+ </script>
4
+
5
+ <script lang="ts">
6
+ import Icon from '../icon/Icon.svelte';
7
+ import { iconChevronRight } from '../icon/index.js';
8
+ import Self from './ContextMenuList.svelte';
9
+
10
+ interface Props {
11
+ items: ContextMenuItem[];
12
+ /** Bubbles a chosen (leaf) item up to the root ContextMenu. */
13
+ onSelect: (item: ContextMenuItem) => void;
14
+ /** Nesting level — only the top level applies `minWidth`. */
15
+ depth?: number;
16
+ minWidth?: string;
17
+ }
18
+
19
+ let { items, onSelect, depth = 0, minWidth = '' }: Props = $props();
20
+
21
+ // Only one submenu per list can be open at a time (the hovered item).
22
+ let hoverIndex = $state<number | null>(null);
23
+ let submenuEl = $state<HTMLElement | null>(null);
24
+ let flipLeft = $state(false);
25
+
26
+ function onItemEnter(i: number, item: ContextMenuItem) {
27
+ hoverIndex = item.children?.length ? i : null;
28
+ }
29
+
30
+ function handleClick(item: ContextMenuItem) {
31
+ if (item.disabled || item.separator || item.children?.length) return;
32
+ onSelect(item);
33
+ }
34
+
35
+ // Decide whether the open submenu should flip to the left edge of its parent.
36
+ // Measured from the parent <li> right edge so the result is stable once flipped.
37
+ $effect(() => {
38
+ if (hoverIndex === null || !submenuEl) return;
39
+ const parent = submenuEl.parentElement;
40
+ if (!parent) return;
41
+ const parentRect = parent.getBoundingClientRect();
42
+ const width = submenuEl.offsetWidth;
43
+ flipLeft = parentRect.right + width > window.innerWidth - 4;
44
+ });
45
+ </script>
46
+
47
+ <ul
48
+ class="pui-ctxmenu-list"
49
+ role="menu"
50
+ style={depth === 0 && minWidth ? `min-width: ${minWidth};` : ''}
51
+ >
52
+ {#each items as item, i (i)}
53
+ {#if item.separator}
54
+ <li class="pui-ctxmenu-sep" role="separator"></li>
55
+ {:else}
56
+ <!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
57
+ <li class="pui-ctxmenu-li" role="none" onmouseenter={() => onItemEnter(i, item)}>
58
+ <button
59
+ type="button"
60
+ role="menuitem"
61
+ class="pui-ctxmenu-item"
62
+ class:danger={item.danger}
63
+ disabled={item.disabled}
64
+ onclick={() => handleClick(item)}
65
+ >
66
+ <span class="pui-ctxmenu-ico">
67
+ {#if item.icon}<Icon svg={item.icon} size="16px" />{/if}
68
+ </span>
69
+ <span class="pui-ctxmenu-label">{item.label}</span>
70
+ {#if item.shortcut}
71
+ <span class="pui-ctxmenu-shortcut">{item.shortcut}</span>
72
+ {/if}
73
+ {#if item.children?.length}
74
+ <span class="pui-ctxmenu-caret"><Icon svg={iconChevronRight} size="14px" /></span>
75
+ {/if}
76
+ </button>
77
+
78
+ {#if item.children?.length && hoverIndex === i}
79
+ <div class="pui-ctxmenu-submenu" class:flip-left={flipLeft} bind:this={submenuEl}>
80
+ <Self items={item.children} {onSelect} depth={depth + 1} />
81
+ </div>
82
+ {/if}
83
+ </li>
84
+ {/if}
85
+ {/each}
86
+ </ul>
87
+
88
+ <style>
89
+ .pui-ctxmenu-list {
90
+ list-style: none;
91
+ margin: 0;
92
+ padding: 0.25rem;
93
+ background: var(--paper-body-bg, #fff);
94
+ border: 1px solid var(--border-color);
95
+ border-radius: 6px;
96
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);
97
+ color: var(--text-color);
98
+ font-size: 0.875rem;
99
+ user-select: none;
100
+ }
101
+
102
+ .pui-ctxmenu-li {
103
+ position: relative;
104
+ }
105
+
106
+ .pui-ctxmenu-item {
107
+ display: flex;
108
+ align-items: center;
109
+ gap: 0.5rem;
110
+ width: 100%;
111
+ padding: 0.375rem 0.6rem;
112
+ background: none;
113
+ border: 0;
114
+ border-radius: 4px;
115
+ color: inherit;
116
+ font: inherit;
117
+ text-align: left;
118
+ white-space: nowrap;
119
+ cursor: pointer;
120
+ }
121
+
122
+ .pui-ctxmenu-item:hover:not(:disabled),
123
+ .pui-ctxmenu-item:focus-visible {
124
+ background: rgba(var(--ui-primary-rgbc), 0.1);
125
+ outline: none;
126
+ }
127
+
128
+ :global(.dark) .pui-ctxmenu-item:hover:not(:disabled),
129
+ :global(.dark) .pui-ctxmenu-item:focus-visible {
130
+ background: rgba(var(--ui-primary-rgbc), 0.28);
131
+ }
132
+
133
+ .pui-ctxmenu-item:disabled {
134
+ opacity: 0.45;
135
+ cursor: default;
136
+ }
137
+
138
+ .pui-ctxmenu-item.danger {
139
+ color: var(--danger-text, #e03131);
140
+ }
141
+
142
+ .pui-ctxmenu-item.danger:hover:not(:disabled) {
143
+ background: var(--danger-bg-subtle, rgba(224, 49, 49, 0.12));
144
+ }
145
+
146
+ .pui-ctxmenu-ico {
147
+ display: inline-flex;
148
+ width: 16px;
149
+ flex-shrink: 0;
150
+ }
151
+
152
+ .pui-ctxmenu-label {
153
+ flex: 1;
154
+ }
155
+
156
+ .pui-ctxmenu-shortcut {
157
+ padding-left: 1.25rem;
158
+ font-size: 0.75rem;
159
+ color: var(--label-color, #888);
160
+ }
161
+
162
+ .pui-ctxmenu-caret {
163
+ display: inline-flex;
164
+ margin-left: auto;
165
+ opacity: 0.7;
166
+ }
167
+
168
+ .pui-ctxmenu-sep {
169
+ height: 1px;
170
+ margin: 0.25rem 0.3rem;
171
+ background: var(--border-color);
172
+ }
173
+
174
+ .pui-ctxmenu-submenu {
175
+ position: absolute;
176
+ top: -0.25rem;
177
+ left: 100%;
178
+ z-index: 1;
179
+ padding-left: 2px;
180
+ }
181
+
182
+ .pui-ctxmenu-submenu.flip-left {
183
+ left: auto;
184
+ right: 100%;
185
+ padding-left: 0;
186
+ padding-right: 2px;
187
+ }
188
+ </style>
@@ -0,0 +1,12 @@
1
+ import type { ContextMenuItem } from './ContextMenu.svelte';
2
+ interface Props {
3
+ items: ContextMenuItem[];
4
+ /** Bubbles a chosen (leaf) item up to the root ContextMenu. */
5
+ onSelect: (item: ContextMenuItem) => void;
6
+ /** Nesting level — only the top level applies `minWidth`. */
7
+ depth?: number;
8
+ minWidth?: string;
9
+ }
10
+ declare const ContextMenuList: import("svelte").Component<Props, {}, "">;
11
+ type ContextMenuList = ReturnType<typeof ContextMenuList>;
12
+ export default ContextMenuList;