@placeholderco/placeholder-ui 1.0.12 → 2.1.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.
- package/LICENSE +1 -1
- package/README.md +134 -130
- package/dist/app.css +178 -0
- package/dist/bootstrap.min.css +3600 -0
- package/dist/cards/GitCard.svelte +115 -0
- package/dist/cards/GitCard.svelte.d.ts +22 -0
- package/dist/display/Avatar.svelte +29 -17
- package/dist/display/Avatar.svelte.d.ts +12 -2
- package/dist/display/LinkCollection.svelte +40 -18
- package/dist/display/LinkCollection.svelte.d.ts +9 -2
- package/dist/display/Paper.svelte +31 -19
- package/dist/display/Paper.svelte.d.ts +12 -3
- package/dist/display/alert/Alert.svelte +129 -0
- package/dist/display/alert/Alert.svelte.d.ts +15 -0
- package/dist/display/alert/AlertVariant.d.ts +1 -0
- package/dist/display/alert/AlertVariant.js +1 -0
- package/dist/display/filetree/FileTree.svelte +259 -0
- package/dist/display/filetree/FileTree.svelte.d.ts +76 -0
- package/dist/display/filetree/FileTreeItem.svelte +396 -0
- package/dist/display/filetree/FileTreeItem.svelte.d.ts +40 -0
- package/dist/display/filetree/FileTreeNode.d.ts +46 -0
- package/dist/display/filetree/FileTreeNode.js +115 -0
- package/dist/display/filetree/index.d.ts +5 -0
- package/dist/display/filetree/index.js +3 -0
- package/dist/editors/CodeMirror.svelte +306 -0
- package/dist/editors/CodeMirror.svelte.d.ts +31 -0
- package/dist/editors/tiptap/LinkActionMenu.svelte +307 -0
- package/dist/editors/tiptap/LinkActionMenu.svelte.d.ts +8 -0
- package/dist/editors/tiptap/TipTap.svelte +856 -0
- package/dist/editors/tiptap/TipTap.svelte.d.ts +54 -0
- package/dist/editors/tiptap/TipTapBubble.svelte +790 -0
- package/dist/editors/tiptap/TipTapBubble.svelte.d.ts +44 -0
- package/dist/editors/tiptap/collaboration.d.ts +55 -0
- package/dist/editors/tiptap/collaboration.js +1 -0
- package/dist/editors/tiptap/extensions/Alert.d.ts +18 -0
- package/dist/editors/tiptap/extensions/Alert.js +72 -0
- package/dist/editors/tiptap/extensions/CodeBlockEditor.d.ts +21 -0
- package/dist/editors/tiptap/extensions/CodeBlockEditor.js +326 -0
- package/dist/editors/tiptap/extensions/Div.d.ts +19 -0
- package/dist/editors/tiptap/extensions/Div.js +89 -0
- package/dist/editors/tiptap/extensions/Span.d.ts +5 -0
- package/dist/editors/tiptap/extensions/Span.js +56 -0
- package/dist/editors/tiptap/extensions/codeBlockEditor.css +236 -0
- package/dist/editors/tiptap/extensions/codeBlockLanguages.d.ts +17 -0
- package/dist/editors/tiptap/extensions/codeBlockLanguages.js +124 -0
- package/dist/editors/tiptap/extensions/index.d.ts +7 -0
- package/dist/editors/tiptap/extensions/index.js +5 -0
- package/dist/editors/tiptap/toolbar/AlertButton.svelte +261 -0
- package/dist/editors/tiptap/toolbar/AlertButton.svelte.d.ts +7 -0
- package/dist/editors/tiptap/toolbar/AlignCenterButton.svelte +18 -0
- package/dist/editors/tiptap/toolbar/AlignCenterButton.svelte.d.ts +7 -0
- package/dist/editors/tiptap/toolbar/AlignJustifyButton.svelte +18 -0
- package/dist/editors/tiptap/toolbar/AlignJustifyButton.svelte.d.ts +7 -0
- package/dist/editors/tiptap/toolbar/AlignLeftButton.svelte +18 -0
- package/dist/editors/tiptap/toolbar/AlignLeftButton.svelte.d.ts +7 -0
- package/dist/editors/tiptap/toolbar/AlignRightButton.svelte +18 -0
- package/dist/editors/tiptap/toolbar/AlignRightButton.svelte.d.ts +7 -0
- package/dist/editors/tiptap/toolbar/BoldButton.svelte +18 -0
- package/dist/editors/tiptap/toolbar/BoldButton.svelte.d.ts +7 -0
- package/dist/editors/tiptap/toolbar/BulletListButton.svelte +18 -0
- package/dist/editors/tiptap/toolbar/BulletListButton.svelte.d.ts +7 -0
- package/dist/editors/tiptap/toolbar/CodeBlockButton.svelte +18 -0
- package/dist/editors/tiptap/toolbar/CodeBlockButton.svelte.d.ts +7 -0
- package/dist/editors/tiptap/toolbar/CodeButton.svelte +18 -0
- package/dist/editors/tiptap/toolbar/CodeButton.svelte.d.ts +7 -0
- package/dist/editors/tiptap/toolbar/ColorButton.svelte +299 -0
- package/dist/editors/tiptap/toolbar/ColorButton.svelte.d.ts +20 -0
- package/dist/editors/tiptap/toolbar/DocumentButton.svelte +58 -0
- package/dist/editors/tiptap/toolbar/DocumentButton.svelte.d.ts +9 -0
- package/dist/editors/tiptap/toolbar/HeadingButton.svelte +30 -0
- package/dist/editors/tiptap/toolbar/HeadingButton.svelte.d.ts +8 -0
- package/dist/editors/tiptap/toolbar/HtmlButton.svelte +68 -0
- package/dist/editors/tiptap/toolbar/HtmlButton.svelte.d.ts +7 -0
- package/dist/editors/tiptap/toolbar/ImageButton.svelte +54 -0
- package/dist/editors/tiptap/toolbar/ImageButton.svelte.d.ts +8 -0
- package/dist/editors/tiptap/toolbar/ItalicButton.svelte +18 -0
- package/dist/editors/tiptap/toolbar/ItalicButton.svelte.d.ts +7 -0
- package/dist/editors/tiptap/toolbar/LinkButton.svelte +257 -0
- package/dist/editors/tiptap/toolbar/LinkButton.svelte.d.ts +7 -0
- package/dist/editors/tiptap/toolbar/OrderedListButton.svelte +18 -0
- package/dist/editors/tiptap/toolbar/OrderedListButton.svelte.d.ts +7 -0
- package/dist/editors/tiptap/toolbar/RedoButton.svelte +18 -0
- package/dist/editors/tiptap/toolbar/RedoButton.svelte.d.ts +7 -0
- package/dist/editors/tiptap/toolbar/StrikethroughButton.svelte +18 -0
- package/dist/editors/tiptap/toolbar/StrikethroughButton.svelte.d.ts +7 -0
- package/dist/editors/tiptap/toolbar/SubscriptButton.svelte +18 -0
- package/dist/editors/tiptap/toolbar/SubscriptButton.svelte.d.ts +7 -0
- package/dist/editors/tiptap/toolbar/SuperscriptButton.svelte +18 -0
- package/dist/editors/tiptap/toolbar/SuperscriptButton.svelte.d.ts +7 -0
- package/dist/editors/tiptap/toolbar/TableButton.svelte +258 -0
- package/dist/editors/tiptap/toolbar/TableButton.svelte.d.ts +7 -0
- package/dist/editors/tiptap/toolbar/ToolbarButton.svelte +84 -0
- package/dist/editors/tiptap/toolbar/ToolbarButton.svelte.d.ts +10 -0
- package/dist/editors/tiptap/toolbar/UnderlineButton.svelte +18 -0
- package/dist/editors/tiptap/toolbar/UnderlineButton.svelte.d.ts +7 -0
- package/dist/editors/tiptap/toolbar/UndoButton.svelte +18 -0
- package/dist/editors/tiptap/toolbar/UndoButton.svelte.d.ts +7 -0
- package/dist/form/Autocomplete.svelte +86 -135
- package/dist/form/Autocomplete.svelte.d.ts +6 -8
- package/dist/form/AutocompleteMulti.svelte +385 -227
- package/dist/form/AutocompleteMulti.svelte.d.ts +59 -10
- package/dist/form/Checkbox.svelte +59 -54
- package/dist/form/Checkbox.svelte.d.ts +14 -5
- package/dist/form/Chips.svelte +29 -13
- package/dist/form/Chips.svelte.d.ts +16 -2
- package/dist/form/ColorPicker.svelte +957 -0
- package/dist/form/ColorPicker.svelte.d.ts +78 -0
- package/dist/form/ComboBox.svelte +30 -20
- package/dist/form/ComboBox.svelte.d.ts +1 -1
- package/dist/form/ComboBoxItemBuilder.svelte +161 -188
- package/dist/form/ComboBoxItemBuilder.svelte.d.ts +19 -2
- package/dist/form/ComboBoxMulti.svelte +38 -24
- package/dist/form/ComboBoxMulti.svelte.d.ts +1 -1
- package/dist/form/CronBuilder.svelte +84 -67
- package/dist/form/CronBuilder.svelte.d.ts +16 -3
- package/dist/form/DatePicker.svelte +148 -195
- package/dist/form/DateRangePicker.svelte +101 -98
- package/dist/form/DateTimePicker.svelte +167 -208
- package/dist/form/DateTimePicker.svelte.d.ts +10 -2
- package/dist/form/FormGroup.svelte +33 -10
- package/dist/form/FormGroup.svelte.d.ts +12 -2
- package/dist/form/Number.svelte +70 -47
- package/dist/form/Number.svelte.d.ts +36 -6
- package/dist/form/Radio.svelte +18 -18
- package/dist/form/RadioGroup.svelte +23 -12
- package/dist/form/RadioGroup.svelte.d.ts +13 -2
- package/dist/form/SegmentedControl.svelte +76 -56
- package/dist/form/SegmentedControl.svelte.d.ts +9 -4
- package/dist/form/Select.svelte +150 -233
- package/dist/form/Select.svelte.d.ts +29 -5
- package/dist/form/SelectMulti.svelte +163 -277
- package/dist/form/SelectMulti.svelte.d.ts +36 -7
- package/dist/form/Slider.svelte +183 -269
- package/dist/form/Slider.svelte.d.ts +32 -17
- package/dist/form/StringArrayBuilder.svelte +249 -0
- package/dist/form/StringArrayBuilder.svelte.d.ts +15 -0
- package/dist/form/Switch.svelte +164 -106
- package/dist/form/Switch.svelte.d.ts +22 -3
- package/dist/form/TextArea.svelte +49 -29
- package/dist/form/TextArea.svelte.d.ts +23 -3
- package/dist/form/Textbox.svelte +69 -68
- package/dist/form/Textbox.svelte.d.ts +32 -4
- package/dist/form/TimePicker.svelte +42 -47
- package/dist/form/TimePicker.svelte.d.ts +12 -2
- package/dist/form/comboBoxCore.svelte.d.ts +99 -0
- package/dist/form/comboBoxCore.svelte.js +243 -0
- package/dist/icon/Icon.svelte +8 -3
- package/dist/icon/Icon.svelte.d.ts +8 -3
- package/dist/icon/file-type-bmp.svg +1 -0
- package/dist/icon/file-type-csv.svg +1 -0
- package/dist/icon/file-type-doc.svg +1 -0
- package/dist/icon/file-type-docx.svg +1 -0
- package/dist/icon/file-type-jpg.svg +1 -0
- package/dist/icon/file-type-pdf.svg +1 -0
- package/dist/icon/file-type-png.svg +1 -0
- package/dist/icon/file-type-ppt.svg +1 -0
- package/dist/icon/file-type-svg.svg +1 -0
- package/dist/icon/file-type-txt.svg +1 -0
- package/dist/icon/file-type-xls.svg +1 -0
- package/dist/icon/file-type-zip.svg +1 -0
- package/dist/icon/index.d.ts +166 -151
- package/dist/icon/index.js +166 -152
- package/dist/icon/logo-placeholder.svg +6 -0
- package/dist/icon/source-code.svg +1 -0
- package/dist/icon/sun-moon.svg +1 -0
- package/dist/icon/table.svg +6 -0
- package/dist/index.d.ts +146 -82
- package/dist/index.js +77 -78
- package/dist/layout/AppShell.svelte +12 -162
- package/dist/layout/AppShell.svelte.d.ts +6 -27
- package/dist/layout/CustomNavbar.svelte +9 -7
- package/dist/layout/CustomNavbar.svelte.d.ts +4 -2
- package/dist/layout/Navbar.svelte +161 -129
- package/dist/layout/Navbar.svelte.d.ts +29 -15
- package/dist/layout/NavbarItemDisplay.svelte +2 -2
- package/dist/layout/NavbarItemView.svelte +182 -0
- package/dist/layout/NavbarItemView.svelte.d.ts +10 -0
- package/dist/layout/Sidenav.svelte +280 -341
- package/dist/layout/Sidenav.svelte.d.ts +36 -4
- package/dist/models/ComboBoxItem.d.ts +2 -0
- package/dist/models/Employee.d.ts +53 -0
- package/dist/models/Employee.js +23 -0
- package/dist/models/Hyperlink.d.ts +3 -1
- package/dist/models/LinkCollectionItem.d.ts +3 -1
- package/dist/models/NavbarItem.d.ts +6 -2
- package/dist/theme.svelte.d.ts +0 -1
- package/dist/theme.svelte.js +46 -58
- package/dist/ui/Accordion.svelte +81 -242
- package/dist/ui/Accordion.svelte.d.ts +37 -14
- package/dist/ui/AccordionItem.svelte +303 -0
- package/dist/ui/AccordionItem.svelte.d.ts +21 -0
- package/dist/ui/ActionIcon.svelte +25 -21
- package/dist/ui/ActionIcon.svelte.d.ts +15 -2
- package/dist/ui/Badge.svelte +163 -171
- package/dist/ui/Badge.svelte.d.ts +13 -5
- package/dist/ui/Button.svelte +178 -205
- package/dist/ui/Button.svelte.d.ts +19 -2
- package/dist/ui/ButtonVariant.d.ts +15 -1
- package/dist/ui/ContextMenu.svelte +146 -0
- package/dist/ui/ContextMenu.svelte.d.ts +37 -0
- package/dist/ui/ContextMenuList.svelte +188 -0
- package/dist/ui/ContextMenuList.svelte.d.ts +12 -0
- package/dist/ui/Dialog.svelte +183 -111
- package/dist/ui/Dialog.svelte.d.ts +21 -4
- package/dist/ui/Drawer.svelte +237 -184
- package/dist/ui/Drawer.svelte.d.ts +20 -2
- package/dist/ui/Dropdown.svelte +24 -14
- package/dist/ui/Dropdown.svelte.d.ts +11 -2
- package/dist/ui/Dropzone.svelte +159 -57
- package/dist/ui/Dropzone.svelte.d.ts +29 -2
- package/dist/ui/Link.svelte +12 -4
- package/dist/ui/Link.svelte.d.ts +8 -2
- package/dist/ui/Loader.svelte +23 -18
- package/dist/ui/Loader.svelte.d.ts +5 -2
- package/dist/ui/Logo.svelte +37 -0
- package/dist/ui/Logo.svelte.d.ts +16 -0
- package/dist/ui/MultiSortable.svelte +287 -0
- package/dist/ui/MultiSortable.svelte.d.ts +50 -0
- package/dist/ui/Pagination.svelte +15 -9
- package/dist/ui/Pagination.svelte.d.ts +6 -2
- package/dist/ui/Popover.svelte +43 -65
- package/dist/ui/Popover.svelte.d.ts +14 -5
- package/dist/ui/ProgressBar.svelte +28 -30
- package/dist/ui/ProgressBarVariant.d.ts +7 -1
- package/dist/ui/SegmentedControl.svelte +0 -0
- package/dist/ui/SegmentedControl.svelte.d.ts +26 -0
- package/dist/ui/Sortable.svelte +163 -0
- package/dist/ui/Sortable.svelte.d.ts +47 -0
- package/dist/ui/Table.svelte +383 -80
- package/dist/ui/Table.svelte.d.ts +43 -2
- package/dist/ui/Tabs.svelte +22 -18
- package/dist/ui/Tabs.svelte.d.ts +6 -2
- package/dist/ui/ThemeSwitcher.svelte +26 -7
- package/dist/ui/ThemeSwitcher.svelte.d.ts +4 -2
- package/dist/ui/Tooltip.svelte +312 -180
- package/dist/ui/Tooltip.svelte.d.ts +4 -3
- package/dist/uiTheme.svelte.d.ts +86 -0
- package/dist/uiTheme.svelte.js +214 -0
- package/dist/util/DateFunctions.js +1 -1
- package/dist/util/DragDrop.d.ts +50 -0
- package/dist/util/DragDrop.js +154 -0
- package/dist/util/NavigateTo.js +2 -2
- package/dist/util/dayjs.d.ts +4 -0
- package/dist/util/dayjs.js +15 -0
- package/package.json +66 -26
- package/dist/display/Alert.svelte +0 -179
- package/dist/display/Alert.svelte.d.ts +0 -13
- package/dist/form/FileInput.svelte +0 -235
- package/dist/form/FileInput.svelte.d.ts +0 -23
- package/dist/form/PasswordInput.svelte +0 -253
- package/dist/form/PasswordInput.svelte.d.ts +0 -34
- package/dist/form/Rating.svelte +0 -235
- package/dist/form/Rating.svelte.d.ts +0 -21
- package/dist/styles/components.css +0 -199
- package/dist/styles/dark.css +0 -146
- package/dist/styles/index.css +0 -116
- package/dist/styles/reset.css +0 -110
- package/dist/styles/semantic.css +0 -86
- package/dist/styles/tokens.css +0 -203
- package/dist/styles/utilities.css +0 -523
- package/dist/ui/Breadcrumbs.svelte +0 -131
- package/dist/ui/Breadcrumbs.svelte.d.ts +0 -16
- package/dist/ui/LoadingOverlay.svelte +0 -53
- package/dist/ui/LoadingOverlay.svelte.d.ts +0 -11
- package/dist/ui/Progress.svelte +0 -191
- package/dist/ui/Progress.svelte.d.ts +0 -15
- package/dist/ui/RingProgress.svelte +0 -141
- package/dist/ui/RingProgress.svelte.d.ts +0 -18
- package/dist/ui/Skeleton.svelte +0 -85
- package/dist/ui/Skeleton.svelte.d.ts +0 -12
- package/dist/ui/Stepper.svelte +0 -355
- package/dist/ui/Stepper.svelte.d.ts +0 -24
- package/dist/ui/Timeline.svelte +0 -225
- package/dist/ui/Timeline.svelte.d.ts +0 -23
- /package/dist/{actions → util}/ClickOutside.d.ts +0 -0
- /package/dist/{actions → util}/ClickOutside.js +0 -0
- /package/dist/{transitions → util}/Transitions.d.ts +0 -0
- /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;
|