@isoftdata/svelte-context-menu 1.5.0 → 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.
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
- ## Events
30
- * open - Fired when the menu is opened
31
- * close - Fired whe nthe menu is closed
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|preventDefault={e => cm.open(e, "target")}
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
- on:click={() => alert("Dropdown Item Clicked!")}
63
+ onclick={() => alert("Dropdown Item Clicked!")}
53
64
  >
54
65
  Click Me!
55
66
  </DropdownItem>
@@ -1,61 +1,87 @@
1
- <script>import { computePosition, flip } from "@floating-ui/dom";
2
- import { createEventDispatcher } from "svelte";
3
- const dispatch = createEventDispatcher();
4
- export let id = "menu";
5
- export let menuItemClickCloses = true;
6
- export let placement = "bottom-start";
7
- let show = false;
8
- let positionLeft = 0;
9
- let positionTop = 0;
10
- let strategy = "absolute";
11
- let menu;
12
- export async function open(event, targetId = "") {
13
- if (menu) {
14
- show = true;
15
- const virtualEl = {
16
- getBoundingClientRect() {
17
- return {
18
- width: 0,
19
- height: 0,
20
- x: event.clientX,
21
- y: event.clientY,
22
- top: event.clientY,
23
- left: event.clientX,
24
- right: event.clientX,
25
- bottom: event.clientY
26
- };
27
- }
28
- };
29
- const reference = targetId && document.getElementById(targetId) || virtualEl;
30
- if (virtualEl && menu) {
31
- const pos = await computePosition(reference, menu, { placement, middleware: [flip()] });
32
- positionLeft = pos.x;
33
- positionTop = pos.y;
34
- strategy = pos.strategy;
35
- }
36
- dispatch("open");
37
- } else {
38
- show = false;
39
- }
40
- }
41
- export function close() {
42
- show = false;
43
- dispatch("close");
44
- }
45
- function menuKeydownHandler(event) {
46
- const target = event.target;
47
- console.log(event.key, event.currentTarget, event.target);
48
- if ((event.key === " " || event.key === "Enter") && target.tagName === "BUTTON") {
49
- target.click();
50
- } else if (target === event.currentTarget && event.key === "Tab") {
51
- target.querySelector("button")?.focus();
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
- on:click={close}
58
- on:keydown={event => event.key === "Escape" && close()}
83
+ onclick={close}
84
+ onkeydown={event => event.key === "Escape" && close()}
59
85
  />
60
86
 
61
87
  <div
@@ -65,9 +91,12 @@ function menuKeydownHandler(event) {
65
91
  style="position: {strategy}; left:{positionLeft}px; top:{positionTop}px;"
66
92
  role="menu"
67
93
  tabindex="0"
68
- on:click|stopPropagation={() => menuItemClickCloses && close()}
69
- on:keydown|preventDefault|stopPropagation={menuKeydownHandler}
94
+ onclick={event => {
95
+ event.stopPropagation()
96
+ menuItemClickCloses && close()
97
+ }}
98
+ onkeydown={event => menuKeydownHandler(event)}
70
99
  bind:this={menu}
71
100
  >
72
- <slot />
101
+ {@render children?.()}
73
102
  </div>
@@ -1,28 +1,16 @@
1
- import { SvelteComponent } from "svelte";
1
+ import type { Snippet } from "svelte";
2
2
  import { type Placement } from "@floating-ui/dom";
3
- declare const __propDef: {
4
- props: {
5
- id?: string | undefined;
6
- menuItemClickCloses?: boolean | undefined;
7
- placement?: Placement | undefined;
8
- open?: ((event: MouseEvent, targetId?: string) => Promise<void>) | undefined;
9
- close?: (() => void) | undefined;
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
- export {};
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>import DropdownItem from "./DropdownItem.svelte";
2
- export let checked = false;
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
- on:click={() => (checked = !checked)}
11
- on:click
12
- ><slot {checked} />
22
+ onclick={event => {
23
+ checked = !checked
24
+ onclick?.(event)
25
+ }}
26
+ >{@render children?.({ checked })}
13
27
  </DropdownItem>
@@ -1,22 +1,11 @@
1
- import { SvelteComponent } from "svelte";
2
- declare const __propDef: {
3
- props: {
4
- checked?: boolean | undefined;
5
- };
6
- events: {
7
- click: MouseEvent;
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
- export {};
9
+ declare const DropdownCheckboxItem: import("svelte").Component<Props, {}, "checked">;
10
+ type DropdownCheckboxItem = ReturnType<typeof DropdownCheckboxItem>;
11
+ export default DropdownCheckboxItem;
@@ -1,30 +1,31 @@
1
- <script>import Icon from "@isoftdata/svelte-icon";
2
- export let icon = null;
3
- function getIconProps(icon2) {
4
- if (typeof icon2 === "string") {
5
- return {
6
- ...defaultIconProps,
7
- icon: icon2
8
- };
9
- } else {
10
- return {
11
- ...defaultIconProps,
12
- ...icon2
13
- };
14
- }
15
- }
16
- const defaultIconProps = {
17
- prefix: "fas",
18
- fixedWidth: true
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
- on:click
28
- {...$$restProps}
29
- >{#if $$props.icon}<span class="mr-1"><Icon {...iconProps} /> </span>{/if}<slot />
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 { SvelteComponent } from "svelte";
2
- import type { ComponentProps } from "svelte";
1
+ import type { ComponentProps, Snippet } from "svelte";
2
+ import type { HTMLButtonAttributes } from "svelte/elements";
3
3
  import Icon from "@isoftdata/svelte-icon";
4
- declare const __propDef: {
5
- props: {
6
- [x: string]: any;
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
- export {};
8
+ declare const DropdownItem: import("svelte").Component<Props, {}, "">;
9
+ type DropdownItem = ReturnType<typeof DropdownItem>;
10
+ export default DropdownItem;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@isoftdata/svelte-context-menu",
3
- "version": "1.5.0",
3
+ "version": "2.0.0",
4
4
  "scripts": {
5
5
  "dev": "vite dev",
6
6
  "build": "vite build && npm run package",
@@ -24,26 +24,30 @@
24
24
  "!dist/**/*.spec.*"
25
25
  ],
26
26
  "peerDependencies": {
27
- "svelte": "^4.0.0 || ^5.1.6"
27
+ "svelte": "^5.23.1"
28
28
  },
29
29
  "devDependencies": {
30
30
  "@isoftdata/prettier-config": "^1.0.1",
31
- "@sveltejs/adapter-auto": "^2.0.0",
32
- "@sveltejs/kit": "^1.20.4",
33
- "@sveltejs/package": "^2.0.0",
31
+ "@isoftdata/svelte-bootstrap-version-switcher": "^1.0.3",
32
+ "@isoftdata/svelte-button": "^2.0.2",
33
+ "@isoftdata/svelte-checkbox": "^2.0.1",
34
+ "@sveltejs/adapter-auto": "^4.0.0",
35
+ "@sveltejs/kit": "^2.5.27",
36
+ "@sveltejs/package": "^2.3.7",
37
+ "@sveltejs/vite-plugin-svelte": "^5.0.3",
34
38
  "@typescript-eslint/eslint-plugin": "^6.0.0",
35
39
  "@typescript-eslint/parser": "^6.0.0",
36
40
  "eslint": "^8.28.0",
37
41
  "eslint-config-prettier": "^8.5.0",
38
- "eslint-plugin-svelte": "^2.30.0",
39
- "prettier": "^2.8.0",
40
- "prettier-plugin-svelte": "^2.10.1",
42
+ "eslint-plugin-svelte": "^2.45.1",
43
+ "prettier": "^3.1.0",
44
+ "prettier-plugin-svelte": "^3.2.6",
41
45
  "publint": "^0.1.9",
42
- "svelte": "^4.0.5",
43
- "svelte-check": "^3.4.3",
46
+ "svelte": "^5.23.1",
47
+ "svelte-check": "^4.0.0",
44
48
  "tslib": "^2.4.1",
45
- "typescript": "^5.0.0",
46
- "vite": "^4.4.2"
49
+ "typescript": "^5.5.0",
50
+ "vite": "^6.2.2"
47
51
  },
48
52
  "svelte": "./dist/index.js",
49
53
  "types": "./dist/index.d.ts",
@@ -51,6 +55,6 @@
51
55
  "prettier": "@isoftdata/prettier-config",
52
56
  "dependencies": {
53
57
  "@floating-ui/dom": "^1.5.3",
54
- "@isoftdata/svelte-icon": "^1.1.0"
58
+ "@isoftdata/svelte-icon": "^1.6.0"
55
59
  }
56
60
  }