@asup/context-menu 1.0.4 → 1.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/README.md CHANGED
@@ -30,18 +30,29 @@ import { ContextMenuProvider, iMenuItem } from '@asup/context-menu';
30
30
 
31
31
  <ContextMenuHandler
32
32
  menuItems={[
33
- { label: 'Item 1', action: item1Function },
34
- { label: 'Item 2', action: item2Function, group: [
35
- { label: 'Subitem 2.1', action: item21Function }
36
- ...
37
- ]
33
+ {
34
+ label: 'Item 1',
35
+ action: () => {
36
+ console.log('Item 1 function run');
37
+ },
38
+ },
39
+ {
40
+ label: 'Item 2',
41
+ action: () => console.log('Item 2 function run'),
42
+ group: [
43
+ { label: 'Subitem 2.1', action: () => console.log('Item 2.1 function run') },
44
+ ],
45
+ },
46
+ {
47
+ label: 'Item 3',
48
+ action: () => console.log('Item 3 function run'),
49
+ disabled: true,
38
50
  },
39
- { label: 'Item 3', action: item3Function, disabeld: true },
40
- ...
41
51
  ]}
42
52
  >
43
53
  <Chilren
44
54
  where the context menu is applied...
45
55
  />
46
- </ContextMenuProvider>
56
+ </ContextMenuHandler>
57
+
47
58
  ```
package/dist/cjs/main.css CHANGED
@@ -61,4 +61,8 @@
61
61
  position: relative;
62
62
  }
63
63
 
64
+ .context-menu-item-label {
65
+ flex-grow: 1;
66
+ }
67
+
64
68
  /*# sourceMappingURL=main.css.map */
@@ -1 +1 @@
1
- {"mappings":"AAAA;;;;;;;;;;AAWA;;;;AAIA;;;;;;;;;;;;;AAaA;;;;;AAKA;;;;AAIA;;;;AAIA;;;;AAIA;;;;;AAKA;;;;;AAKA;;;;AAIA","sources":["src/components/ContextMenu.scss"],"sourcesContent":[".context-menu {\n position: absolute;\n visibility: hidden;\n border: 1px solid;\n border-color: rgb(17, 20, 24);\n border-radius: 8px;\n opacity: 1;\n background-color: rgb(251, 253, 246);\n z-index: 10000;\n}\n\n.context-menu.visible {\n visibility: inherit;\n}\n\n.context-menu-item {\n color: rgb(17, 20, 24);\n cursor: pointer;\n padding: 0 4px;\n min-width: 80px;\n height: 21px;\n position: relative;\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n white-space: nowrap;\n}\n\n.context-menu-item.disabled {\n background-color: rgba(0, 0, 0, 0.2);\n cursor: not-allowed;\n}\n\n.context-menu-item:first-child {\n padding-top: 4px;\n}\n\n.context-menu-item:last-child {\n padding-bottom: 4px;\n}\n\n.context-menu-item:not(.disabled):hover {\n background-color: rgb(251, 233, 230);\n}\n\n.context-menu-item:hover:first-child {\n border-top-left-radius: 6px;\n border-top-right-radius: 6px;\n}\n\n.context-menu-item:hover:last-child {\n border-bottom-left-radius: 6px;\n border-bottom-right-radius: 6px;\n}\n\n.context-menu-item .caret-holder {\n align-self: flex-end;\n}\n\n.context-menu-item .caret-holder .sub-menu {\n z-index: 1;\n position: relative;\n}\n"],"names":[],"version":3,"file":"main.css.map"}
1
+ {"mappings":"AAAA;;;;;;;;;;AAWA;;;;AAIA;;;;;;;;;;;;;AAaA;;;;;AAKA;;;;AAIA;;;;AAIA;;;;AAIA;;;;;AAKA;;;;;AAKA;;;;AAIA;;;;;AAKA","sources":["src/components/ContextMenu.css"],"sourcesContent":[".context-menu {\n position: absolute;\n visibility: hidden;\n border: 1px solid;\n border-color: rgb(17, 20, 24);\n border-radius: 8px;\n opacity: 1;\n background-color: rgb(251, 253, 246);\n z-index: 10000;\n}\n\n.context-menu.visible {\n visibility: inherit;\n}\n\n.context-menu-item {\n color: rgb(17, 20, 24);\n cursor: pointer;\n padding: 0 4px;\n min-width: 80px;\n height: 21px;\n position: relative;\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n white-space: nowrap;\n}\n\n.context-menu-item.disabled {\n background-color: rgba(0, 0, 0, 0.2);\n cursor: not-allowed;\n}\n\n.context-menu-item:first-child {\n padding-top: 4px;\n}\n\n.context-menu-item:last-child {\n padding-bottom: 4px;\n}\n\n.context-menu-item:not(.disabled):hover {\n background-color: rgb(251, 233, 230);\n}\n\n.context-menu-item:hover:first-child {\n border-top-left-radius: 6px;\n border-top-right-radius: 6px;\n}\n\n.context-menu-item:hover:last-child {\n border-bottom-left-radius: 6px;\n border-bottom-right-radius: 6px;\n}\n\n.context-menu-item .caret-holder {\n align-self: flex-end;\n}\n\n.context-menu-item .caret-holder .sub-menu {\n z-index: 1;\n position: relative;\n}\n\n.context-menu-item-label {\n flex-grow: 1;\n}\n"],"names":[],"version":3,"file":"main.css.map"}
package/dist/cjs/main.js CHANGED
@@ -98,6 +98,7 @@ var $5150b66b01c99189$export$8dc6765e8be191c7 = /*#__PURE__*/ (0, ($parcel$inter
98
98
  },
99
99
  children: [
100
100
  /*#__PURE__*/ (0, $gTuX4$reactjsxruntime.jsx)("span", {
101
+ className: "context-menu-item-label",
101
102
  children: e.label
102
103
  }),
103
104
  e.group && /*#__PURE__*/ (0, $gTuX4$reactjsxruntime.jsx)((0, $2275c4645c0c5e94$export$a9c522a6d7beb830), {
@@ -1 +1 @@
1
- {"mappings":"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","sources":["src/main.ts","src/components/index.ts","src/components/ContextMenuHandler.tsx","src/components/ContextMenu.tsx","src/components/SubMenu.tsx"],"sourcesContent":["export * from './components';\n","import { ContextMenuHandler } from './ContextMenuHandler';\nimport { iMenuItem } from './interface';\n\nexport { ContextMenuHandler };\nexport type { iMenuItem };\n","import { MouseEvent, useCallback, useEffect, useRef, useState } from 'react';\nimport { createPortal } from 'react-dom';\nimport { ContextMenu } from './ContextMenu';\nimport { iMenuItem } from './interface';\nimport './ContextMenu.scss';\n\nexport const ContextMenuHandler = ({\n children,\n menuItems,\n style = {\n height: 'fit-content',\n width: 'fit-content',\n },\n}: {\n children: JSX.Element[] | JSX.Element;\n menuItems: iMenuItem[];\n style?: React.CSSProperties;\n}): JSX.Element => {\n // Menu resources\n const divRef = useRef<HTMLDivElement | null>(null);\n const menuRef = useRef<HTMLDivElement | null>(null);\n const [menuXPos, setMenuXPos] = useState<number>(0);\n const [menuYPos, setMenuYPos] = useState<number>(0);\n const [menuVisible, setMenuVisible] = useState<boolean>(false);\n const [target, setTarget] = useState<Range | null>(null);\n\n // Show menu when context is requested\n const showMenu = (e: MouseEvent<HTMLDivElement>) => {\n const sel = window.getSelection();\n setTarget(sel && sel.rangeCount > 0 ? sel.getRangeAt(0) : null);\n e.preventDefault();\n e.stopPropagation();\n setMenuVisible(true);\n setMenuXPos(e.pageX);\n setMenuYPos(e.pageY);\n };\n\n // Handle click off the menu\n const handleClick = useCallback((e: globalThis.MouseEvent) => {\n if (\n menuRef.current &&\n ((e.target instanceof Element && !menuRef.current?.contains(e.target)) ||\n !(e.target instanceof Element))\n ) {\n setMenuVisible(false);\n }\n }, []);\n\n // Update the document click handler\n useEffect(() => {\n if (menuVisible) document.addEventListener('mousedown', handleClick);\n else document.removeEventListener('mousedown', handleClick);\n return () => {\n document.removeEventListener('mousedown', handleClick);\n };\n }, [handleClick, menuVisible]);\n\n return (\n <>\n <div\n onContextMenu={showMenu}\n className='context-menu-handler'\n style={style}\n >\n {children}\n </div>\n {menuVisible &&\n createPortal(\n <div\n style={{ position: 'absolute', top: 0, left: 0 }}\n ref={divRef}\n >\n <ContextMenu\n visible={true}\n ref={menuRef}\n entries={menuItems}\n xPos={menuXPos}\n yPos={menuYPos}\n target={target}\n toClose={() => setMenuVisible(false)}\n />\n </div>,\n document.body,\n )}\n </>\n );\n};\n","import React from 'react';\nimport { iMenuItem } from './interface';\nimport { SubMenu } from './SubMenu';\n\nexport interface contextMenuProps {\n visible: boolean;\n entries: iMenuItem[];\n target: Range | null;\n xPos: number;\n yPos: number;\n toClose: () => void;\n}\n\nexport const ContextMenu = React.forwardRef<HTMLDivElement, contextMenuProps>(\n ({ visible, entries, target, xPos, yPos, toClose }, ref): JSX.Element => {\n ContextMenu.displayName = 'ContextMenu';\n\n return (\n <div\n ref={ref}\n className={`context-menu${visible ? ' visible' : ''}`}\n style={{\n top: `${yPos}px`,\n left: `${xPos}px`,\n }}\n >\n {entries.map((e, i) => (\n <div\n key={i}\n className={`context-menu-item${e.disabled ? ' disabled' : ''}`}\n onClick={(ev) => {\n ev.preventDefault();\n ev.stopPropagation();\n e.action && !e.disabled && e.action(target);\n !e.disabled && toClose();\n }}\n >\n <span>{e.label}</span>\n {e.group && (\n <SubMenu\n toClose={toClose}\n entries={e.group}\n target={target}\n />\n )}\n </div>\n ))}\n </div>\n );\n },\n);\n","import { useState } from 'react';\nimport { ContextMenu } from './ContextMenu';\nimport { iMenuItem } from './interface';\n\nexport interface subMenuProps {\n entries: iMenuItem[];\n target: Range | null;\n toClose: () => void;\n}\n\nexport const SubMenu = ({ entries, target, toClose }: subMenuProps): JSX.Element => {\n const [visible, setVisible] = useState<boolean>(false);\n\n return (\n <span\n className='caret-holder'\n onMouseEnter={() => {\n setVisible(true);\n }}\n onMouseLeave={() => {\n setVisible(false);\n }}\n >\n <svg\n xmlns='http://www.w3.org/2000/svg'\n width='16'\n height='16'\n fill='currentColor'\n viewBox='0 0 16 16'\n >\n <path d='m12.14 8.753-5.482 4.796c-.646.566-1.658.106-1.658-.753V3.204a1 1 0 0 1 1.659-.753l5.48 4.796a1 1 0 0 1 0 1.506z' />\n </svg>\n <div className='sub-menu'>\n <ContextMenu\n visible={visible}\n entries={entries}\n target={target}\n xPos={14}\n yPos={-21}\n toClose={toClose}\n />\n </div>\n </span>\n );\n};\n"],"names":[],"version":3,"file":"main.js.map"}
1
+ {"mappings":"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","sources":["src/main.ts","src/components/index.ts","src/components/ContextMenuHandler.tsx","src/components/ContextMenu.tsx","src/components/SubMenu.tsx"],"sourcesContent":["export * from './components';\n","import { ContextMenuHandler } from './ContextMenuHandler';\nimport { iMenuItem } from './interface';\n\nexport { ContextMenuHandler };\nexport type { iMenuItem };\n","import { MouseEvent, useCallback, useEffect, useRef, useState } from 'react';\nimport { createPortal } from 'react-dom';\nimport { ContextMenu } from './ContextMenu';\nimport { iMenuItem } from './interface';\nimport './ContextMenu.scss';\n\nexport const ContextMenuHandler = ({\n children,\n menuItems,\n style = {\n height: 'fit-content',\n width: 'fit-content',\n },\n}: {\n children: JSX.Element[] | JSX.Element;\n menuItems: iMenuItem[];\n style?: React.CSSProperties;\n}): JSX.Element => {\n // Menu resources\n const divRef = useRef<HTMLDivElement | null>(null);\n const menuRef = useRef<HTMLDivElement | null>(null);\n const [menuXPos, setMenuXPos] = useState<number>(0);\n const [menuYPos, setMenuYPos] = useState<number>(0);\n const [menuVisible, setMenuVisible] = useState<boolean>(false);\n const [target, setTarget] = useState<Range | null>(null);\n\n // Show menu when context is requested\n const showMenu = (e: MouseEvent<HTMLDivElement>) => {\n const sel = window.getSelection();\n setTarget(sel && sel.rangeCount > 0 ? sel.getRangeAt(0) : null);\n e.preventDefault();\n e.stopPropagation();\n setMenuVisible(true);\n setMenuXPos(e.pageX);\n setMenuYPos(e.pageY);\n };\n\n // Handle click off the menu\n const handleClick = useCallback((e: globalThis.MouseEvent) => {\n if (\n menuRef.current &&\n ((e.target instanceof Element && !menuRef.current?.contains(e.target)) ||\n !(e.target instanceof Element))\n ) {\n setMenuVisible(false);\n }\n }, []);\n\n // Update the document click handler\n useEffect(() => {\n if (menuVisible) document.addEventListener('mousedown', handleClick);\n else document.removeEventListener('mousedown', handleClick);\n return () => {\n document.removeEventListener('mousedown', handleClick);\n };\n }, [handleClick, menuVisible]);\n\n return (\n <>\n <div\n onContextMenu={showMenu}\n className='context-menu-handler'\n style={style}\n >\n {children}\n </div>\n {menuVisible &&\n createPortal(\n <div\n style={{ position: 'absolute', top: 0, left: 0 }}\n ref={divRef}\n >\n <ContextMenu\n visible={true}\n ref={menuRef}\n entries={menuItems}\n xPos={menuXPos}\n yPos={menuYPos}\n target={target}\n toClose={() => setMenuVisible(false)}\n />\n </div>,\n document.body,\n )}\n </>\n );\n};\n","import React from 'react';\nimport { iMenuItem } from './interface';\nimport { SubMenu } from './SubMenu';\n\nexport interface contextMenuProps {\n visible: boolean;\n entries: iMenuItem[];\n target: Range | null;\n xPos: number;\n yPos: number;\n toClose: () => void;\n}\n\nexport const ContextMenu = React.forwardRef<HTMLDivElement, contextMenuProps>(\n ({ visible, entries, target, xPos, yPos, toClose }, ref): JSX.Element => {\n ContextMenu.displayName = 'ContextMenu';\n\n return (\n <div\n ref={ref}\n className={`context-menu${visible ? ' visible' : ''}`}\n style={{\n top: `${yPos}px`,\n left: `${xPos}px`,\n }}\n >\n {entries.map((e, i) => (\n <div\n key={i}\n className={`context-menu-item${e.disabled ? ' disabled' : ''}`}\n onClick={(ev) => {\n ev.preventDefault();\n ev.stopPropagation();\n e.action && !e.disabled && e.action(target);\n !e.disabled && toClose();\n }}\n >\n <span className='context-menu-item-label'>{e.label}</span>\n {e.group && (\n <SubMenu\n toClose={toClose}\n entries={e.group}\n target={target}\n />\n )}\n </div>\n ))}\n </div>\n );\n },\n);\n","import { useState } from 'react';\nimport { ContextMenu } from './ContextMenu';\nimport { iMenuItem } from './interface';\n\nexport interface subMenuProps {\n entries: iMenuItem[];\n target: Range | null;\n toClose: () => void;\n}\n\nexport const SubMenu = ({ entries, target, toClose }: subMenuProps): JSX.Element => {\n const [visible, setVisible] = useState<boolean>(false);\n\n return (\n <span\n className='caret-holder'\n onMouseEnter={() => {\n setVisible(true);\n }}\n onMouseLeave={() => {\n setVisible(false);\n }}\n >\n <svg\n xmlns='http://www.w3.org/2000/svg'\n width='16'\n height='16'\n fill='currentColor'\n viewBox='0 0 16 16'\n >\n <path d='m12.14 8.753-5.482 4.796c-.646.566-1.658.106-1.658-.753V3.204a1 1 0 0 1 1.659-.753l5.48 4.796a1 1 0 0 1 0 1.506z' />\n </svg>\n <div className='sub-menu'>\n <ContextMenu\n visible={visible}\n entries={entries}\n target={target}\n xPos={14}\n yPos={-21}\n toClose={toClose}\n />\n </div>\n </span>\n );\n};\n"],"names":[],"version":3,"file":"main.js.map"}
@@ -1,13 +1,13 @@
1
- export interface iMenuItem {
2
- label: string;
3
- disabled?: boolean;
4
- action?: (target?: Range | null) => void;
5
- group?: iMenuItem[];
6
- }
7
- export const ContextMenuHandler: ({ children, menuItems, style, }: {
8
- children: JSX.Element[] | JSX.Element;
9
- menuItems: iMenuItem[];
10
- style?: import("react").CSSProperties | undefined;
11
- }) => JSX.Element;
1
+ export interface iMenuItem {
2
+ label: string;
3
+ disabled?: boolean;
4
+ action?: (target?: Range | null) => void;
5
+ group?: iMenuItem[];
6
+ }
7
+ export const ContextMenuHandler: ({ children, menuItems, style, }: {
8
+ children: JSX.Element[] | JSX.Element;
9
+ menuItems: iMenuItem[];
10
+ style?: import("react").CSSProperties | undefined;
11
+ }) => JSX.Element;
12
12
 
13
13
  //# sourceMappingURL=context-menu.d.ts.map
package/dist/main.css CHANGED
@@ -61,4 +61,8 @@
61
61
  position: relative;
62
62
  }
63
63
 
64
+ .context-menu-item-label {
65
+ flex-grow: 1;
66
+ }
67
+
64
68
  /*# sourceMappingURL=main.css.map */
package/dist/main.css.map CHANGED
@@ -1 +1 @@
1
- {"mappings":"AAAA;;;;;;;;;;AAWA;;;;AAIA;;;;;;;;;;;;;AAaA;;;;;AAKA;;;;AAIA;;;;AAIA;;;;AAIA;;;;;AAKA;;;;;AAKA;;;;AAIA","sources":["src/components/ContextMenu.scss"],"sourcesContent":[".context-menu {\n position: absolute;\n visibility: hidden;\n border: 1px solid;\n border-color: rgb(17, 20, 24);\n border-radius: 8px;\n opacity: 1;\n background-color: rgb(251, 253, 246);\n z-index: 10000;\n}\n\n.context-menu.visible {\n visibility: inherit;\n}\n\n.context-menu-item {\n color: rgb(17, 20, 24);\n cursor: pointer;\n padding: 0 4px;\n min-width: 80px;\n height: 21px;\n position: relative;\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n white-space: nowrap;\n}\n\n.context-menu-item.disabled {\n background-color: rgba(0, 0, 0, 0.2);\n cursor: not-allowed;\n}\n\n.context-menu-item:first-child {\n padding-top: 4px;\n}\n\n.context-menu-item:last-child {\n padding-bottom: 4px;\n}\n\n.context-menu-item:not(.disabled):hover {\n background-color: rgb(251, 233, 230);\n}\n\n.context-menu-item:hover:first-child {\n border-top-left-radius: 6px;\n border-top-right-radius: 6px;\n}\n\n.context-menu-item:hover:last-child {\n border-bottom-left-radius: 6px;\n border-bottom-right-radius: 6px;\n}\n\n.context-menu-item .caret-holder {\n align-self: flex-end;\n}\n\n.context-menu-item .caret-holder .sub-menu {\n z-index: 1;\n position: relative;\n}\n"],"names":[],"version":3,"file":"main.css.map"}
1
+ {"mappings":"AAAA;;;;;;;;;;AAWA;;;;AAIA;;;;;;;;;;;;;AAaA;;;;;AAKA;;;;AAIA;;;;AAIA;;;;AAIA;;;;;AAKA;;;;;AAKA;;;;AAIA;;;;;AAKA","sources":["src/components/ContextMenu.css"],"sourcesContent":[".context-menu {\n position: absolute;\n visibility: hidden;\n border: 1px solid;\n border-color: rgb(17, 20, 24);\n border-radius: 8px;\n opacity: 1;\n background-color: rgb(251, 253, 246);\n z-index: 10000;\n}\n\n.context-menu.visible {\n visibility: inherit;\n}\n\n.context-menu-item {\n color: rgb(17, 20, 24);\n cursor: pointer;\n padding: 0 4px;\n min-width: 80px;\n height: 21px;\n position: relative;\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n white-space: nowrap;\n}\n\n.context-menu-item.disabled {\n background-color: rgba(0, 0, 0, 0.2);\n cursor: not-allowed;\n}\n\n.context-menu-item:first-child {\n padding-top: 4px;\n}\n\n.context-menu-item:last-child {\n padding-bottom: 4px;\n}\n\n.context-menu-item:not(.disabled):hover {\n background-color: rgb(251, 233, 230);\n}\n\n.context-menu-item:hover:first-child {\n border-top-left-radius: 6px;\n border-top-right-radius: 6px;\n}\n\n.context-menu-item:hover:last-child {\n border-bottom-left-radius: 6px;\n border-bottom-right-radius: 6px;\n}\n\n.context-menu-item .caret-holder {\n align-self: flex-end;\n}\n\n.context-menu-item .caret-holder .sub-menu {\n z-index: 1;\n position: relative;\n}\n\n.context-menu-item-label {\n flex-grow: 1;\n}\n"],"names":[],"version":3,"file":"main.css.map"}
package/dist/main.js CHANGED
@@ -73,6 +73,7 @@ const $567ed433af94513f$export$8dc6765e8be191c7 = /*#__PURE__*/ (0, $duWW8$react
73
73
  },
74
74
  children: [
75
75
  /*#__PURE__*/ (0, $duWW8$jsx)("span", {
76
+ className: "context-menu-item-label",
76
77
  children: e.label
77
78
  }),
78
79
  e.group && /*#__PURE__*/ (0, $duWW8$jsx)((0, $885a1576aecbe1c6$export$a9c522a6d7beb830), {
package/dist/main.js.map CHANGED
@@ -1 +1 @@
1
- {"mappings":";;;;;;;;;;;AEAA;;;ACAA;;ACAA;;;AAUO,MAAM,4CAAU,CAAC,WAAE,QAAO,UAAE,OAAM,WAAE,QAAO,EAAgB,GAAkB;IAClF,MAAM,CAAC,SAAS,WAAW,GAAG,CAAA,GAAA,eAAO,EAAW,KAAK;IAErD,qBACE,iBAAC;QACC,WAAU;QACV,cAAc,IAAM;YAClB,WAAW,IAAI;QACjB;QACA,cAAc,IAAM;YAClB,WAAW,KAAK;QAClB;;0BAEA,gBAAC;gBACC,OAAM;gBACN,OAAM;gBACN,QAAO;gBACP,MAAK;gBACL,SAAQ;0BAER,cAAA,gBAAC;oBAAK,GAAE;;;0BAEV,gBAAC;gBAAI,WAAU;0BACb,cAAA,gBAAC,CAAA,GAAA,yCAAW,AAAD;oBACT,SAAS;oBACT,SAAS;oBACT,QAAQ;oBACR,MAAM;oBACN,MAAM;oBACN,SAAS;;;;;AAKnB;;;AD/BO,MAAM,0DAAc,CAAA,GAAA,YAAI,EAAE,UAAU,CACzC,CAAC,WAAE,QAAO,WAAE,QAAO,UAAE,OAAM,QAAE,KAAI,QAAE,KAAI,WAAE,QAAO,EAAE,EAAE,MAAqB;IACvE,0CAAY,WAAW,GAAG;IAE1B,qBACE,gBAAC;QACC,KAAK;QACL,WAAW,CAAC,YAAY,EAAE,UAAU,aAAa,EAAE,CAAC,CAAC;QACrD,OAAO;YACL,KAAK,CAAC,EAAE,KAAK,EAAE,CAAC;YAChB,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC;QACnB;kBAEC,QAAQ,GAAG,CAAC,CAAC,GAAG,kBACf,iBAAC;gBAEC,WAAW,CAAC,iBAAiB,EAAE,EAAE,QAAQ,GAAG,cAAc,EAAE,CAAC,CAAC;gBAC9D,SAAS,CAAC,KAAO;oBACf,GAAG,cAAc;oBACjB,GAAG,eAAe;oBAClB,EAAE,MAAM,IAAI,CAAC,EAAE,QAAQ,IAAI,EAAE,MAAM,CAAC;oBACpC,CAAC,EAAE,QAAQ,IAAI;gBACjB;;kCAEA,gBAAC;kCAAM,EAAE,KAAK;;oBACb,EAAE,KAAK,kBACN,gBAAC,CAAA,GAAA,yCAAO,AAAD;wBACL,SAAS;wBACT,SAAS,EAAE,KAAK;wBAChB,QAAQ;;;eAdP;;AAqBf;;;;AD3CK,MAAM,4CAAqB,CAAC,YACjC,SAAQ,aACR,UAAS,SACT,QAAQ;IACN,QAAQ;IACR,OAAO;AACT,IAKD,GAAkB;IACjB,iBAAiB;IACjB,MAAM,SAAS,CAAA,GAAA,aAAK,EAAyB,IAAI;IACjD,MAAM,UAAU,CAAA,GAAA,aAAK,EAAyB,IAAI;IAClD,MAAM,CAAC,UAAU,YAAY,GAAG,CAAA,GAAA,eAAQ,AAAD,EAAU;IACjD,MAAM,CAAC,UAAU,YAAY,GAAG,CAAA,GAAA,eAAQ,AAAD,EAAU;IACjD,MAAM,CAAC,aAAa,eAAe,GAAG,CAAA,GAAA,eAAO,EAAW,KAAK;IAC7D,MAAM,CAAC,QAAQ,UAAU,GAAG,CAAA,GAAA,eAAO,EAAgB,IAAI;IAEvD,sCAAsC;IACtC,MAAM,WAAW,CAAC,IAAkC;QAClD,MAAM,MAAM,OAAO,YAAY;QAC/B,UAAU,OAAO,IAAI,UAAU,GAAG,IAAI,IAAI,UAAU,CAAC,KAAK,IAAI;QAC9D,EAAE,cAAc;QAChB,EAAE,eAAe;QACjB,eAAe,IAAI;QACnB,YAAY,EAAE,KAAK;QACnB,YAAY,EAAE,KAAK;IACrB;IAEA,4BAA4B;IAC5B,MAAM,cAAc,CAAA,GAAA,kBAAW,AAAD,EAAE,CAAC,IAA6B;YAGxB;QAFpC,IACE,QAAQ,OAAO,IACd,CAAA,AAAC,EAAE,MAAM,YAAY,WAAW,EAAC,CAAA,mBAAA,QAAQ,OAAO,cAAf,8BAAA,KAAA,IAAA,iBAAiB,SAAS,EAAE,MAAM,MAClE,CAAE,CAAA,EAAE,MAAM,YAAY,OAAM,CAAC,GAE/B,eAAe,KAAK;IAExB,GAAG,EAAE;IAEL,oCAAoC;IACpC,CAAA,GAAA,gBAAS,AAAD,EAAE,IAAM;QACd,IAAI,aAAa,SAAS,gBAAgB,CAAC,aAAa;aACnD,SAAS,mBAAmB,CAAC,aAAa;QAC/C,OAAO,IAAM;YACX,SAAS,mBAAmB,CAAC,aAAa;QAC5C;IACF,GAAG;QAAC;QAAa;KAAY;IAE7B,qBACE;;0BACE,gBAAC;gBACC,eAAe;gBACf,WAAU;gBACV,OAAO;0BAEN;;YAEF,6BACC,CAAA,GAAA,mBAAY,AAAD,gBACT,gBAAC;gBACC,OAAO;oBAAE,UAAU;oBAAY,KAAK;oBAAG,MAAM;gBAAE;gBAC/C,KAAK;0BAEL,cAAA,gBAAC,CAAA,GAAA,yCAAW,AAAD;oBACT,SAAS,IAAI;oBACb,KAAK;oBACL,SAAS;oBACT,MAAM;oBACN,MAAM;oBACN,QAAQ;oBACR,SAAS,IAAM,eAAe,KAAK;;gBAGvC,SAAS,IAAI;;;AAIvB;;ADtFA;;ADAA","sources":["src/main.ts","src/components/index.ts","src/components/ContextMenuHandler.tsx","src/components/ContextMenu.tsx","src/components/SubMenu.tsx"],"sourcesContent":["export * from './components';\n","import { ContextMenuHandler } from './ContextMenuHandler';\nimport { iMenuItem } from './interface';\n\nexport { ContextMenuHandler };\nexport type { iMenuItem };\n","import { MouseEvent, useCallback, useEffect, useRef, useState } from 'react';\nimport { createPortal } from 'react-dom';\nimport { ContextMenu } from './ContextMenu';\nimport { iMenuItem } from './interface';\nimport './ContextMenu.scss';\n\nexport const ContextMenuHandler = ({\n children,\n menuItems,\n style = {\n height: 'fit-content',\n width: 'fit-content',\n },\n}: {\n children: JSX.Element[] | JSX.Element;\n menuItems: iMenuItem[];\n style?: React.CSSProperties;\n}): JSX.Element => {\n // Menu resources\n const divRef = useRef<HTMLDivElement | null>(null);\n const menuRef = useRef<HTMLDivElement | null>(null);\n const [menuXPos, setMenuXPos] = useState<number>(0);\n const [menuYPos, setMenuYPos] = useState<number>(0);\n const [menuVisible, setMenuVisible] = useState<boolean>(false);\n const [target, setTarget] = useState<Range | null>(null);\n\n // Show menu when context is requested\n const showMenu = (e: MouseEvent<HTMLDivElement>) => {\n const sel = window.getSelection();\n setTarget(sel && sel.rangeCount > 0 ? sel.getRangeAt(0) : null);\n e.preventDefault();\n e.stopPropagation();\n setMenuVisible(true);\n setMenuXPos(e.pageX);\n setMenuYPos(e.pageY);\n };\n\n // Handle click off the menu\n const handleClick = useCallback((e: globalThis.MouseEvent) => {\n if (\n menuRef.current &&\n ((e.target instanceof Element && !menuRef.current?.contains(e.target)) ||\n !(e.target instanceof Element))\n ) {\n setMenuVisible(false);\n }\n }, []);\n\n // Update the document click handler\n useEffect(() => {\n if (menuVisible) document.addEventListener('mousedown', handleClick);\n else document.removeEventListener('mousedown', handleClick);\n return () => {\n document.removeEventListener('mousedown', handleClick);\n };\n }, [handleClick, menuVisible]);\n\n return (\n <>\n <div\n onContextMenu={showMenu}\n className='context-menu-handler'\n style={style}\n >\n {children}\n </div>\n {menuVisible &&\n createPortal(\n <div\n style={{ position: 'absolute', top: 0, left: 0 }}\n ref={divRef}\n >\n <ContextMenu\n visible={true}\n ref={menuRef}\n entries={menuItems}\n xPos={menuXPos}\n yPos={menuYPos}\n target={target}\n toClose={() => setMenuVisible(false)}\n />\n </div>,\n document.body,\n )}\n </>\n );\n};\n","import React from 'react';\nimport { iMenuItem } from './interface';\nimport { SubMenu } from './SubMenu';\n\nexport interface contextMenuProps {\n visible: boolean;\n entries: iMenuItem[];\n target: Range | null;\n xPos: number;\n yPos: number;\n toClose: () => void;\n}\n\nexport const ContextMenu = React.forwardRef<HTMLDivElement, contextMenuProps>(\n ({ visible, entries, target, xPos, yPos, toClose }, ref): JSX.Element => {\n ContextMenu.displayName = 'ContextMenu';\n\n return (\n <div\n ref={ref}\n className={`context-menu${visible ? ' visible' : ''}`}\n style={{\n top: `${yPos}px`,\n left: `${xPos}px`,\n }}\n >\n {entries.map((e, i) => (\n <div\n key={i}\n className={`context-menu-item${e.disabled ? ' disabled' : ''}`}\n onClick={(ev) => {\n ev.preventDefault();\n ev.stopPropagation();\n e.action && !e.disabled && e.action(target);\n !e.disabled && toClose();\n }}\n >\n <span>{e.label}</span>\n {e.group && (\n <SubMenu\n toClose={toClose}\n entries={e.group}\n target={target}\n />\n )}\n </div>\n ))}\n </div>\n );\n },\n);\n","import { useState } from 'react';\nimport { ContextMenu } from './ContextMenu';\nimport { iMenuItem } from './interface';\n\nexport interface subMenuProps {\n entries: iMenuItem[];\n target: Range | null;\n toClose: () => void;\n}\n\nexport const SubMenu = ({ entries, target, toClose }: subMenuProps): JSX.Element => {\n const [visible, setVisible] = useState<boolean>(false);\n\n return (\n <span\n className='caret-holder'\n onMouseEnter={() => {\n setVisible(true);\n }}\n onMouseLeave={() => {\n setVisible(false);\n }}\n >\n <svg\n xmlns='http://www.w3.org/2000/svg'\n width='16'\n height='16'\n fill='currentColor'\n viewBox='0 0 16 16'\n >\n <path d='m12.14 8.753-5.482 4.796c-.646.566-1.658.106-1.658-.753V3.204a1 1 0 0 1 1.659-.753l5.48 4.796a1 1 0 0 1 0 1.506z' />\n </svg>\n <div className='sub-menu'>\n <ContextMenu\n visible={visible}\n entries={entries}\n target={target}\n xPos={14}\n yPos={-21}\n toClose={toClose}\n />\n </div>\n </span>\n );\n};\n"],"names":[],"version":3,"file":"main.js.map"}
1
+ {"mappings":"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","sources":["src/main.ts","src/components/index.ts","src/components/ContextMenuHandler.tsx","src/components/ContextMenu.tsx","src/components/SubMenu.tsx"],"sourcesContent":["export * from './components';\n","import { ContextMenuHandler } from './ContextMenuHandler';\nimport { iMenuItem } from './interface';\n\nexport { ContextMenuHandler };\nexport type { iMenuItem };\n","import { MouseEvent, useCallback, useEffect, useRef, useState } from 'react';\nimport { createPortal } from 'react-dom';\nimport { ContextMenu } from './ContextMenu';\nimport { iMenuItem } from './interface';\nimport './ContextMenu.scss';\n\nexport const ContextMenuHandler = ({\n children,\n menuItems,\n style = {\n height: 'fit-content',\n width: 'fit-content',\n },\n}: {\n children: JSX.Element[] | JSX.Element;\n menuItems: iMenuItem[];\n style?: React.CSSProperties;\n}): JSX.Element => {\n // Menu resources\n const divRef = useRef<HTMLDivElement | null>(null);\n const menuRef = useRef<HTMLDivElement | null>(null);\n const [menuXPos, setMenuXPos] = useState<number>(0);\n const [menuYPos, setMenuYPos] = useState<number>(0);\n const [menuVisible, setMenuVisible] = useState<boolean>(false);\n const [target, setTarget] = useState<Range | null>(null);\n\n // Show menu when context is requested\n const showMenu = (e: MouseEvent<HTMLDivElement>) => {\n const sel = window.getSelection();\n setTarget(sel && sel.rangeCount > 0 ? sel.getRangeAt(0) : null);\n e.preventDefault();\n e.stopPropagation();\n setMenuVisible(true);\n setMenuXPos(e.pageX);\n setMenuYPos(e.pageY);\n };\n\n // Handle click off the menu\n const handleClick = useCallback((e: globalThis.MouseEvent) => {\n if (\n menuRef.current &&\n ((e.target instanceof Element && !menuRef.current?.contains(e.target)) ||\n !(e.target instanceof Element))\n ) {\n setMenuVisible(false);\n }\n }, []);\n\n // Update the document click handler\n useEffect(() => {\n if (menuVisible) document.addEventListener('mousedown', handleClick);\n else document.removeEventListener('mousedown', handleClick);\n return () => {\n document.removeEventListener('mousedown', handleClick);\n };\n }, [handleClick, menuVisible]);\n\n return (\n <>\n <div\n onContextMenu={showMenu}\n className='context-menu-handler'\n style={style}\n >\n {children}\n </div>\n {menuVisible &&\n createPortal(\n <div\n style={{ position: 'absolute', top: 0, left: 0 }}\n ref={divRef}\n >\n <ContextMenu\n visible={true}\n ref={menuRef}\n entries={menuItems}\n xPos={menuXPos}\n yPos={menuYPos}\n target={target}\n toClose={() => setMenuVisible(false)}\n />\n </div>,\n document.body,\n )}\n </>\n );\n};\n","import React from 'react';\nimport { iMenuItem } from './interface';\nimport { SubMenu } from './SubMenu';\n\nexport interface contextMenuProps {\n visible: boolean;\n entries: iMenuItem[];\n target: Range | null;\n xPos: number;\n yPos: number;\n toClose: () => void;\n}\n\nexport const ContextMenu = React.forwardRef<HTMLDivElement, contextMenuProps>(\n ({ visible, entries, target, xPos, yPos, toClose }, ref): JSX.Element => {\n ContextMenu.displayName = 'ContextMenu';\n\n return (\n <div\n ref={ref}\n className={`context-menu${visible ? ' visible' : ''}`}\n style={{\n top: `${yPos}px`,\n left: `${xPos}px`,\n }}\n >\n {entries.map((e, i) => (\n <div\n key={i}\n className={`context-menu-item${e.disabled ? ' disabled' : ''}`}\n onClick={(ev) => {\n ev.preventDefault();\n ev.stopPropagation();\n e.action && !e.disabled && e.action(target);\n !e.disabled && toClose();\n }}\n >\n <span className='context-menu-item-label'>{e.label}</span>\n {e.group && (\n <SubMenu\n toClose={toClose}\n entries={e.group}\n target={target}\n />\n )}\n </div>\n ))}\n </div>\n );\n },\n);\n","import { useState } from 'react';\nimport { ContextMenu } from './ContextMenu';\nimport { iMenuItem } from './interface';\n\nexport interface subMenuProps {\n entries: iMenuItem[];\n target: Range | null;\n toClose: () => void;\n}\n\nexport const SubMenu = ({ entries, target, toClose }: subMenuProps): JSX.Element => {\n const [visible, setVisible] = useState<boolean>(false);\n\n return (\n <span\n className='caret-holder'\n onMouseEnter={() => {\n setVisible(true);\n }}\n onMouseLeave={() => {\n setVisible(false);\n }}\n >\n <svg\n xmlns='http://www.w3.org/2000/svg'\n width='16'\n height='16'\n fill='currentColor'\n viewBox='0 0 16 16'\n >\n <path d='m12.14 8.753-5.482 4.796c-.646.566-1.658.106-1.658-.753V3.204a1 1 0 0 1 1.659-.753l5.48 4.796a1 1 0 0 1 0 1.506z' />\n </svg>\n <div className='sub-menu'>\n <ContextMenu\n visible={visible}\n entries={entries}\n target={target}\n xPos={14}\n yPos={-21}\n toClose={toClose}\n />\n </div>\n </span>\n );\n};\n"],"names":[],"version":3,"file":"main.js.map"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@asup/context-menu",
3
- "version": "1.0.4",
3
+ "version": "1.1.0",
4
4
  "description": "REACT Typescript Context menu component",
5
5
  "author": "Paul Thomas <@PaulDThomas>",
6
6
  "private": false,
@@ -57,7 +57,7 @@
57
57
  "process": "^0.11.10",
58
58
  "ts-jest": "^29.0.3",
59
59
  "ts-node": "^10.9.1",
60
- "typescript": "^4.9.3"
60
+ "typescript": "^5.0.4"
61
61
  },
62
62
  "lint-staged": {
63
63
  "**/*": "prettier --write --ignore-unknown"