@isoftdata/svelte-context-menu 1.5.0 → 2.0.1
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/README.md +16 -5
- package/dist/ContextMenu.svelte +88 -59
- package/dist/ContextMenu.svelte.d.ts +14 -26
- package/dist/DropdownCheckboxItem.svelte +19 -5
- package/dist/DropdownCheckboxItem.svelte.d.ts +10 -21
- package/dist/DropdownItem.svelte +25 -24
- package/dist/DropdownItem.svelte.d.ts +8 -21
- package/package.json +62 -55
package/README.md
CHANGED
|
@@ -8,7 +8,15 @@ A Context/Dropdown menu component, filled with [DropdownItem](DropdownItem.md)s
|
|
|
8
8
|
npm i @isoftdata/svelte-context-menu
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
+
## Breaking changes
|
|
12
|
+
|
|
13
|
+
### 2.0.0
|
|
14
|
+
- Require Svelte 5
|
|
15
|
+
- Slots -> Snippets
|
|
16
|
+
- Events -> Callbacks
|
|
17
|
+
|
|
11
18
|
## Props
|
|
19
|
+
|
|
12
20
|
| Name | Type | Description | Default Value |
|
|
13
21
|
| --- | --- | --- | --- |
|
|
14
22
|
| id | `string` | The id of the context menu element. | "menu" |
|
|
@@ -26,9 +34,9 @@ The main way you'll interact with this component is through calling the methods
|
|
|
26
34
|
* `targetId` (optional) the id of an element. If supplied, the menu will be positioned relative to this element. Otherwise, it will be positioned relative to the cursor.
|
|
27
35
|
* close - Closes the context menu. Normally you'd close the context menu by clicking on an item within it, but this is also an option.
|
|
28
36
|
|
|
29
|
-
##
|
|
30
|
-
* open -
|
|
31
|
-
* close -
|
|
37
|
+
## Callbacks
|
|
38
|
+
* open - Called when the menu is opened
|
|
39
|
+
* close - Called when the menu is closed
|
|
32
40
|
|
|
33
41
|
### Example
|
|
34
42
|
|
|
@@ -42,14 +50,17 @@ The main way you'll interact with this component is through calling the methods
|
|
|
42
50
|
<button
|
|
43
51
|
id="target"
|
|
44
52
|
class="btn btn-primary btn-block"
|
|
45
|
-
on:contextmenu
|
|
53
|
+
on:contextmenu={e => {
|
|
54
|
+
event.preventDefault()
|
|
55
|
+
cm.open(e, "target")
|
|
56
|
+
}}
|
|
46
57
|
>
|
|
47
58
|
Right Click to Open Menu
|
|
48
59
|
</button>
|
|
49
60
|
<ContextMenu bind:this={cm}>
|
|
50
61
|
<DropdownItem
|
|
51
62
|
icon="mouse"
|
|
52
|
-
|
|
63
|
+
onclick={() => alert("Dropdown Item Clicked!")}
|
|
53
64
|
>
|
|
54
65
|
Click Me!
|
|
55
66
|
</DropdownItem>
|
package/dist/ContextMenu.svelte
CHANGED
|
@@ -1,73 +1,102 @@
|
|
|
1
|
-
<script
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from "svelte"
|
|
3
|
+
import { computePosition, flip, type Placement } from "@floating-ui/dom"
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
id?: string
|
|
7
|
+
menuItemClickCloses?: boolean
|
|
8
|
+
placement?: Placement
|
|
9
|
+
children?: Snippet
|
|
10
|
+
open?: () => void
|
|
11
|
+
close?: () => void
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const uniqueId = $props.id()
|
|
15
|
+
|
|
16
|
+
let {
|
|
17
|
+
//
|
|
18
|
+
id = `dropdown-menu-${uniqueId}`,
|
|
19
|
+
menuItemClickCloses = true,
|
|
20
|
+
placement = "bottom-start",
|
|
21
|
+
children,
|
|
22
|
+
open: openCb,
|
|
23
|
+
close: closeCb,
|
|
24
|
+
}: Props = $props()
|
|
25
|
+
|
|
26
|
+
let show = $state(false)
|
|
27
|
+
let positionLeft = $state(0)
|
|
28
|
+
let positionTop = $state(0)
|
|
29
|
+
let strategy = $state("absolute")
|
|
30
|
+
|
|
31
|
+
let menu: HTMLElement | undefined = $state()
|
|
32
|
+
|
|
33
|
+
export async function open(event: MouseEvent, targetId: string = "") {
|
|
34
|
+
event.stopPropagation()
|
|
35
|
+
if (menu) {
|
|
36
|
+
show = true
|
|
37
|
+
const virtualEl = {
|
|
38
|
+
getBoundingClientRect() {
|
|
39
|
+
return {
|
|
40
|
+
width: 0,
|
|
41
|
+
height: 0,
|
|
42
|
+
x: event.clientX,
|
|
43
|
+
y: event.clientY,
|
|
44
|
+
top: event.clientY,
|
|
45
|
+
left: event.clientX,
|
|
46
|
+
right: event.clientX,
|
|
47
|
+
bottom: event.clientY,
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
}
|
|
51
|
+
const reference = (targetId && document.getElementById(targetId)) || virtualEl
|
|
52
|
+
|
|
53
|
+
if (virtualEl && menu) {
|
|
54
|
+
const pos = await computePosition(reference, menu, { placement, middleware: [flip()] })
|
|
55
|
+
positionLeft = pos.x
|
|
56
|
+
positionTop = pos.y
|
|
57
|
+
strategy = pos.strategy
|
|
58
|
+
}
|
|
59
|
+
openCb?.()
|
|
60
|
+
} else {
|
|
61
|
+
show = false
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function close() {
|
|
66
|
+
show = false
|
|
67
|
+
closeCb?.()
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function menuKeydownHandler(event: KeyboardEvent) {
|
|
71
|
+
event.stopPropagation()
|
|
72
|
+
const target = event.target as HTMLElement
|
|
73
|
+
console.log(event.key, event.currentTarget, event.target)
|
|
74
|
+
if ((event.key === " " || event.key === "Enter") && target.tagName === "BUTTON") {
|
|
75
|
+
target.click()
|
|
76
|
+
} else if (target === event.currentTarget && event.key === "Tab") {
|
|
77
|
+
target.querySelector("button")?.focus()
|
|
78
|
+
}
|
|
79
|
+
}
|
|
54
80
|
</script>
|
|
55
81
|
|
|
56
82
|
<svelte:window
|
|
57
|
-
|
|
58
|
-
|
|
83
|
+
onclick={close}
|
|
84
|
+
onkeydown={event => event.key === "Escape" && close()}
|
|
59
85
|
/>
|
|
60
86
|
|
|
61
87
|
<div
|
|
62
88
|
{id}
|
|
63
|
-
class="dropdown-menu shadow
|
|
89
|
+
class="dropdown-menu shadow rounded"
|
|
64
90
|
class:show
|
|
65
91
|
style="position: {strategy}; left:{positionLeft}px; top:{positionTop}px;"
|
|
66
92
|
role="menu"
|
|
67
93
|
tabindex="0"
|
|
68
|
-
|
|
69
|
-
|
|
94
|
+
onclick={event => {
|
|
95
|
+
event.stopPropagation()
|
|
96
|
+
menuItemClickCloses && close()
|
|
97
|
+
}}
|
|
98
|
+
onkeydown={event => menuKeydownHandler(event)}
|
|
70
99
|
bind:this={menu}
|
|
71
100
|
>
|
|
72
|
-
|
|
101
|
+
{@render children?.()}
|
|
73
102
|
</div>
|
|
@@ -1,28 +1,16 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { Snippet } from "svelte";
|
|
2
2
|
import { type Placement } from "@floating-ui/dom";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
};
|
|
11
|
-
events: {
|
|
12
|
-
open: CustomEvent<any>;
|
|
13
|
-
close: CustomEvent<any>;
|
|
14
|
-
} & {
|
|
15
|
-
[evt: string]: CustomEvent<any>;
|
|
16
|
-
};
|
|
17
|
-
slots: {
|
|
18
|
-
default: {};
|
|
19
|
-
};
|
|
20
|
-
};
|
|
21
|
-
export type ContextMenuProps = typeof __propDef.props;
|
|
22
|
-
export type ContextMenuEvents = typeof __propDef.events;
|
|
23
|
-
export type ContextMenuSlots = typeof __propDef.slots;
|
|
24
|
-
export default class ContextMenu extends SvelteComponent<ContextMenuProps, ContextMenuEvents, ContextMenuSlots> {
|
|
25
|
-
get open(): (event: MouseEvent, targetId?: string) => Promise<void>;
|
|
26
|
-
get close(): () => void;
|
|
3
|
+
interface Props {
|
|
4
|
+
id?: string;
|
|
5
|
+
menuItemClickCloses?: boolean;
|
|
6
|
+
placement?: Placement;
|
|
7
|
+
children?: Snippet;
|
|
8
|
+
open?: () => void;
|
|
9
|
+
close?: () => void;
|
|
27
10
|
}
|
|
28
|
-
|
|
11
|
+
declare const ContextMenu: import("svelte").Component<Props, {
|
|
12
|
+
open: (event: MouseEvent, targetId?: string) => Promise<void>;
|
|
13
|
+
close: () => void;
|
|
14
|
+
}, "">;
|
|
15
|
+
type ContextMenu = ReturnType<typeof ContextMenu>;
|
|
16
|
+
export default ContextMenu;
|
|
@@ -1,5 +1,17 @@
|
|
|
1
|
-
<script
|
|
2
|
-
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { ComponentProps, Snippet } from "svelte"
|
|
3
|
+
import DropdownItem from "./DropdownItem.svelte"
|
|
4
|
+
interface Props extends Omit<ComponentProps<typeof DropdownItem>, "children"> {
|
|
5
|
+
checked?: boolean
|
|
6
|
+
children?: Snippet<[{ checked: boolean }]>
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
let {
|
|
10
|
+
//
|
|
11
|
+
checked = $bindable(false),
|
|
12
|
+
onclick,
|
|
13
|
+
children,
|
|
14
|
+
}: Props = $props()
|
|
3
15
|
</script>
|
|
4
16
|
|
|
5
17
|
<DropdownItem
|
|
@@ -7,7 +19,9 @@ export let checked = false;
|
|
|
7
19
|
icon: checked ? "check-square" : "square",
|
|
8
20
|
prefix: "far",
|
|
9
21
|
}}
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
22
|
+
onclick={event => {
|
|
23
|
+
checked = !checked
|
|
24
|
+
onclick?.(event)
|
|
25
|
+
}}
|
|
26
|
+
>{@render children?.({ checked })}
|
|
13
27
|
</DropdownItem>
|
|
@@ -1,22 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
} & {
|
|
9
|
-
[evt: string]: CustomEvent<any>;
|
|
10
|
-
};
|
|
11
|
-
slots: {
|
|
12
|
-
default: {
|
|
13
|
-
checked: boolean;
|
|
14
|
-
};
|
|
15
|
-
};
|
|
16
|
-
};
|
|
17
|
-
export type DropdownCheckboxItemProps = typeof __propDef.props;
|
|
18
|
-
export type DropdownCheckboxItemEvents = typeof __propDef.events;
|
|
19
|
-
export type DropdownCheckboxItemSlots = typeof __propDef.slots;
|
|
20
|
-
export default class DropdownCheckboxItem extends SvelteComponent<DropdownCheckboxItemProps, DropdownCheckboxItemEvents, DropdownCheckboxItemSlots> {
|
|
1
|
+
import type { ComponentProps, Snippet } from "svelte";
|
|
2
|
+
import DropdownItem from "./DropdownItem.svelte";
|
|
3
|
+
interface Props extends Omit<ComponentProps<typeof DropdownItem>, "children"> {
|
|
4
|
+
checked?: boolean;
|
|
5
|
+
children?: Snippet<[{
|
|
6
|
+
checked: boolean;
|
|
7
|
+
}]>;
|
|
21
8
|
}
|
|
22
|
-
|
|
9
|
+
declare const DropdownCheckboxItem: import("svelte").Component<Props, {}, "checked">;
|
|
10
|
+
type DropdownCheckboxItem = ReturnType<typeof DropdownCheckboxItem>;
|
|
11
|
+
export default DropdownCheckboxItem;
|
package/dist/DropdownItem.svelte
CHANGED
|
@@ -1,30 +1,31 @@
|
|
|
1
|
-
<script
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
};
|
|
20
|
-
$:
|
|
21
|
-
iconProps = getIconProps(icon);
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { ComponentProps, Snippet } from "svelte"
|
|
3
|
+
import type { HTMLButtonAttributes } from "svelte/elements"
|
|
4
|
+
import Icon, { getIconProps } from "@isoftdata/svelte-icon"
|
|
5
|
+
|
|
6
|
+
interface Props extends Omit<HTMLButtonAttributes, `on:${string}` | `bind:${string}` | `aria-${string}`> {
|
|
7
|
+
icon?: ComponentProps<Icon>["icon"] | ComponentProps<Icon>
|
|
8
|
+
children?: Snippet
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
let {
|
|
12
|
+
//
|
|
13
|
+
icon,
|
|
14
|
+
children,
|
|
15
|
+
...rest
|
|
16
|
+
}: Props = $props()
|
|
17
|
+
|
|
18
|
+
const iconProps = $derived(getIconProps(icon))
|
|
22
19
|
</script>
|
|
23
20
|
|
|
24
21
|
<button
|
|
25
22
|
class="dropdown-item"
|
|
26
23
|
type="button"
|
|
27
|
-
|
|
28
|
-
{
|
|
29
|
-
|
|
24
|
+
{...rest}
|
|
25
|
+
>{#if icon}<span class="mr-1"
|
|
26
|
+
><Icon
|
|
27
|
+
fixedWidth
|
|
28
|
+
{...iconProps}
|
|
29
|
+
/>
|
|
30
|
+
</span>{/if}{@render children?.()}
|
|
30
31
|
</button>
|
|
@@ -1,23 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import type {
|
|
1
|
+
import type { ComponentProps, Snippet } from "svelte";
|
|
2
|
+
import type { HTMLButtonAttributes } from "svelte/elements";
|
|
3
3
|
import Icon from "@isoftdata/svelte-icon";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
icon?: ComponentProps<Icon>["icon"] | ComponentProps<Icon>;
|
|
8
|
-
};
|
|
9
|
-
events: {
|
|
10
|
-
click: MouseEvent;
|
|
11
|
-
} & {
|
|
12
|
-
[evt: string]: CustomEvent<any>;
|
|
13
|
-
};
|
|
14
|
-
slots: {
|
|
15
|
-
default: {};
|
|
16
|
-
};
|
|
17
|
-
};
|
|
18
|
-
export type DropdownItemProps = typeof __propDef.props;
|
|
19
|
-
export type DropdownItemEvents = typeof __propDef.events;
|
|
20
|
-
export type DropdownItemSlots = typeof __propDef.slots;
|
|
21
|
-
export default class DropdownItem extends SvelteComponent<DropdownItemProps, DropdownItemEvents, DropdownItemSlots> {
|
|
4
|
+
interface Props extends Omit<HTMLButtonAttributes, `on:${string}` | `bind:${string}` | `aria-${string}`> {
|
|
5
|
+
icon?: ComponentProps<Icon>["icon"] | ComponentProps<Icon>;
|
|
6
|
+
children?: Snippet;
|
|
22
7
|
}
|
|
23
|
-
|
|
8
|
+
declare const DropdownItem: import("svelte").Component<Props, {}, "">;
|
|
9
|
+
type DropdownItem = ReturnType<typeof DropdownItem>;
|
|
10
|
+
export default DropdownItem;
|
package/package.json
CHANGED
|
@@ -1,56 +1,63 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
2
|
+
"name": "@isoftdata/svelte-context-menu",
|
|
3
|
+
"version": "2.0.1",
|
|
4
|
+
"exports": {
|
|
5
|
+
".": {
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"svelte": "./dist/index.js"
|
|
8
|
+
}
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"!dist/**/*.test.*",
|
|
13
|
+
"!dist/**/*.spec.*"
|
|
14
|
+
],
|
|
15
|
+
"peerDependencies": {
|
|
16
|
+
"svelte": "^5.23.2"
|
|
17
|
+
},
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"@isoftdata/prettier-config": "^2.0.4",
|
|
20
|
+
"@isoftdata/svelte-bootstrap-version-switcher": "^1.1.2",
|
|
21
|
+
"@isoftdata/svelte-button": "^2.1.0",
|
|
22
|
+
"@isoftdata/svelte-checkbox": "^2.1.1",
|
|
23
|
+
"@sveltejs/adapter-auto": "^6.0.0",
|
|
24
|
+
"@sveltejs/kit": "^2.5.27",
|
|
25
|
+
"@sveltejs/package": "^2.3.7",
|
|
26
|
+
"@sveltejs/vite-plugin-svelte": "^5.0.3",
|
|
27
|
+
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
|
28
|
+
"@typescript-eslint/parser": "^6.0.0",
|
|
29
|
+
"eslint": "^8.28.0",
|
|
30
|
+
"eslint-config-prettier": "^8.5.0",
|
|
31
|
+
"eslint-plugin-svelte": "^2.45.1",
|
|
32
|
+
"prettier": "^3.1.0",
|
|
33
|
+
"prettier-plugin-svelte": "^3.2.6",
|
|
34
|
+
"publint": "^0.1.9",
|
|
35
|
+
"svelte": "^5.23.1",
|
|
36
|
+
"svelte-check": "^4.0.0",
|
|
37
|
+
"tslib": "^2.4.1",
|
|
38
|
+
"typescript": "^5.5.0",
|
|
39
|
+
"vite": "^6.3.3"
|
|
40
|
+
},
|
|
41
|
+
"svelte": "./dist/index.js",
|
|
42
|
+
"types": "./dist/index.d.ts",
|
|
43
|
+
"type": "module",
|
|
44
|
+
"prettier": "@isoftdata/prettier-config",
|
|
45
|
+
"dependencies": {
|
|
46
|
+
"@floating-ui/dom": "^1.5.3",
|
|
47
|
+
"@isoftdata/svelte-icon": "^2.0.0",
|
|
48
|
+
"svelte": "^5.23.2"
|
|
49
|
+
},
|
|
50
|
+
"engines": {
|
|
51
|
+
"pnpm": "10.x"
|
|
52
|
+
},
|
|
53
|
+
"scripts": {
|
|
54
|
+
"dev": "vite dev",
|
|
55
|
+
"build": "vite build && npm run package",
|
|
56
|
+
"preview": "vite preview",
|
|
57
|
+
"package": "svelte-kit sync && svelte-package && publint",
|
|
58
|
+
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
|
59
|
+
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
|
60
|
+
"lint": "prettier --plugin-search-dir . --check . && eslint .",
|
|
61
|
+
"format": "prettier --plugin-search-dir . --write ."
|
|
62
|
+
}
|
|
63
|
+
}
|