@isoftdata/svelte-context-menu 0.0.1 → 0.0.3

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,22 +8,44 @@ npm i @isoftdata/svelte-context-menu
8
8
 
9
9
  ## Props
10
10
  | Name | Type | Description | Default Value |
11
- |------|------|-------------|---------------|
12
- | | | | |
13
- | | | | |
14
- | | | | |
11
+ | --- | --- | --- | --- |
12
+ | `id` | string | The id of the context menu element. | "menu" |
13
+ | `placement` | string | The placement of the context menu relative to the reference element. | "bottom-start" |
15
14
 
16
- ## Slots
17
- * default - A brief description of the slot, if there is one
15
+ ## Usage
18
16
 
19
- ## Events
20
- * exampleEvent - A brief description of the event, if there are any
17
+ The main way you'll interact with this component is through calling the methods below. To access these methods on the component instance, you will have to use the `bind:this` directive on the `ContextMenu` in your template, and assign it to a variable, which you can then call the methods on. See Example for more details.
18
+
19
+ ### Methods
20
+
21
+ * open - Opens the context menu. Arguments below.
22
+ * `event` - the mouse event
23
+ * `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.
24
+ * 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.
25
+ ### Example
21
26
 
22
- ## Example
23
27
  ```html
24
28
  <script lang="ts">
25
29
  import ContextMenu from '@isoftdata/svelte-context-menu'
30
+
31
+ let cm: ContextMenu
26
32
  </script>
27
33
 
28
- <ContextMenu prop='foo' />
34
+ <button
35
+ id="target"
36
+ class="btn btn-primary btn-block"
37
+ on:contextmenu|preventDefault={e => cm.open(e, "target")}
38
+ >
39
+ Right Click to Open Menu
40
+ </button>
41
+ <ContextMenu bind:this={cm}>
42
+ <button
43
+ class="dropdown-item"
44
+ type="button"
45
+ on:click={() => cmClicks++}
46
+ >Click Me!
47
+ </button>
48
+ <div class="downdown-divider" />
49
+ <h6 class="dropdown-header">Clicked {cmClicks} time(s)</h6>
50
+ </ContextMenu>
29
51
  ```
@@ -1,29 +1,13 @@
1
1
  <script>import { onMount } from "svelte";
2
+ import { computePosition, flip } from "@floating-ui/dom";
2
3
  export let id = "menu";
3
- export let show = false;
4
- export let positionLeft = 0;
5
- export let positionTop = 0;
4
+ export let placement = "bottom-start";
5
+ let show = false;
6
+ let positionLeft = 0;
7
+ let positionTop = 0;
8
+ let strategy = "absolute";
6
9
  let mounted = false;
7
- function getPosition(e) {
8
- let posX = 0;
9
- let posY = 0;
10
- if (e.pageX || e.pageY) {
11
- posX = e.pageX;
12
- posY = e.pageY;
13
- } else if (e.clientX || e.clientY) {
14
- posX = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
15
- posY = e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
16
- }
17
- return {
18
- x: posX,
19
- y: posY
20
- };
21
- }
22
- function clickInsideElement(event, className) {
23
- const el = event.target.closest(`.${className}`);
24
- return el || false;
25
- return false;
26
- }
10
+ let menu;
27
11
  function handleMenuClick(event) {
28
12
  if (id && mounted) {
29
13
  const element = document.getElementById(id);
@@ -32,36 +16,37 @@ function handleMenuClick(event) {
32
16
  }
33
17
  }
34
18
  }
35
- onMount(() => {
36
- mounted = true;
37
- });
38
- export function contextMenu(event, property) {
39
- let menu;
40
- if (mounted) {
41
- menu = document.getElementById(id);
42
- }
43
- if (property && menu && property === menu.id) {
44
- event.preventDefault();
45
- const { x, y } = getPosition(event);
46
- const menuWidth = menu.offsetWidth + 4;
47
- const menuHeight = menu.offsetHeight + 4;
48
- const windowWidth = window.innerWidth;
49
- const windowHeight = window.innerHeight;
50
- if (windowWidth - x < menuWidth) {
51
- positionLeft = windowWidth - menuWidth;
52
- } else {
53
- positionLeft = x;
54
- }
55
- if (windowHeight - y < menuHeight) {
56
- positionTop = menuHeight * -1;
57
- } else {
58
- positionTop = 40;
59
- }
19
+ export async function open(event, targetId = "") {
20
+ if (menu) {
60
21
  show = true;
22
+ const virtualEl = {
23
+ getBoundingClientRect() {
24
+ return {
25
+ width: 0,
26
+ height: 0,
27
+ x: event.clientX,
28
+ y: event.clientY,
29
+ top: event.clientY,
30
+ left: event.clientX,
31
+ right: event.clientX,
32
+ bottom: event.clientY
33
+ };
34
+ }
35
+ };
36
+ const reference = targetId && document.getElementById(targetId) || virtualEl;
37
+ if (virtualEl && menu) {
38
+ const pos = await computePosition(reference, menu, { placement, middleware: [flip()] });
39
+ positionLeft = pos.x;
40
+ positionTop = pos.y;
41
+ strategy = pos.strategy;
42
+ }
61
43
  } else {
62
44
  show = false;
63
45
  }
64
46
  }
47
+ export function close() {
48
+ show = false;
49
+ }
65
50
  </script>
66
51
 
67
52
  <svelte:window on:click={event => handleMenuClick(event)} />
@@ -70,15 +55,16 @@ export function contextMenu(event, property) {
70
55
  {id}
71
56
  class="dropdown-menu shadow bg-white rounded"
72
57
  class:show
73
- style="position: absolute; left:{{ positionLeft }}px; top:{{ positionTop }}px;"
74
- on:click|stopPropagation={() => (show = false)}
58
+ style="position: {strategy}; left:{positionLeft}px; top:{positionTop}px;"
75
59
  role="menu"
76
60
  tabindex="0"
61
+ on:click|stopPropagation={() => (show = false)}
77
62
  on:keydown|preventDefault|stopPropagation={e => {
78
63
  if (e.key === "Escape") {
79
64
  show = false
80
65
  }
81
66
  }}
67
+ bind:this={menu}
82
68
  >
83
69
  <slot />
84
70
  </div>
@@ -1,11 +1,11 @@
1
1
  import { SvelteComponent } from "svelte";
2
+ import { type Placement } from "@floating-ui/dom";
2
3
  declare const __propDef: {
3
4
  props: {
4
5
  id?: string | undefined;
5
- show?: boolean | undefined;
6
- positionLeft?: number | undefined;
7
- positionTop?: number | undefined;
8
- contextMenu?: ((event: MouseEvent, property: string) => void) | undefined;
6
+ placement?: Placement | undefined;
7
+ open?: ((event: MouseEvent, targetId?: string) => Promise<void>) | undefined;
8
+ close?: (() => void) | undefined;
9
9
  };
10
10
  events: {
11
11
  [evt: string]: CustomEvent<any>;
@@ -18,6 +18,7 @@ export type ContextMenuProps = typeof __propDef.props;
18
18
  export type ContextMenuEvents = typeof __propDef.events;
19
19
  export type ContextMenuSlots = typeof __propDef.slots;
20
20
  export default class ContextMenu extends SvelteComponent<ContextMenuProps, ContextMenuEvents, ContextMenuSlots> {
21
- get contextMenu(): (event: MouseEvent, property: string) => void;
21
+ get open(): (event: MouseEvent, targetId?: string) => Promise<void>;
22
+ get close(): () => void;
22
23
  }
23
24
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@isoftdata/svelte-context-menu",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "scripts": {
5
5
  "dev": "vite dev",
6
6
  "build": "vite build && npm run package",
@@ -27,6 +27,7 @@
27
27
  "svelte": "^4.0.0"
28
28
  },
29
29
  "devDependencies": {
30
+ "@floating-ui/dom": "^1.5.3",
30
31
  "@isoftdata/prettier-config": "^1.0.1",
31
32
  "@sveltejs/adapter-auto": "^2.0.0",
32
33
  "@sveltejs/kit": "^1.20.4",