@net-advantage/nabs-ui-dropdownmenu 0.70.0 → 0.72.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/dist/dropdownmenu.model.d.ts +4 -0
- package/dist/dropdownmenu.model.test.d.ts +1 -0
- package/dist/dropdownmenu.utils.d.ts +1 -4
- package/dist/dropdownmenu.view-model.d.ts +35 -0
- package/dist/dropdownmenu.view-model.test.d.ts +1 -0
- package/dist/nabs-ui-dropdownmenu.cjs +1 -1
- package/dist/nabs-ui-dropdownmenu.js +77 -39
- package/package.json +8 -3
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { DropdownMenuActionItem, DropdownMenuDividerItem, DropdownMenuItem } from './DropdownMenu.types';
|
|
2
|
+
export declare function isDividerItem(item: DropdownMenuItem | undefined): item is DropdownMenuDividerItem;
|
|
3
|
+
export declare function getItemLabel(item: DropdownMenuActionItem): string | number | bigint | boolean | import('react').ReactElement<unknown, string | import('react').JSXElementConstructor<any>> | Iterable<import('react').ReactNode> | Promise<string | number | bigint | boolean | import('react').ReactPortal | import('react').ReactElement<unknown, string | import('react').JSXElementConstructor<any>> | Iterable<import('react').ReactNode> | null | undefined>;
|
|
4
|
+
export declare function getItemKey(item: DropdownMenuActionItem, index: number): string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,4 +1 @@
|
|
|
1
|
-
|
|
2
|
-
export declare function isDividerItem(item: DropdownMenuItem | undefined): item is DropdownMenuDividerItem;
|
|
3
|
-
export declare function getItemLabel(item: DropdownMenuActionItem): string | number | bigint | boolean | import('react').ReactElement<unknown, string | import('react').JSXElementConstructor<any>> | Iterable<import('react').ReactNode> | Promise<string | number | bigint | boolean | import('react').ReactPortal | import('react').ReactElement<unknown, string | import('react').JSXElementConstructor<any>> | Iterable<import('react').ReactNode> | null | undefined>;
|
|
4
|
-
export declare function getItemKey(item: DropdownMenuActionItem, index: number): string;
|
|
1
|
+
export { isDividerItem, getItemLabel, getItemKey, } from './dropdownmenu.model';
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { DropdownMenuProps } from './DropdownMenu.types';
|
|
2
|
+
export interface DropdownMenuClassNameOptions {
|
|
3
|
+
className?: string;
|
|
4
|
+
triggerClassName?: string;
|
|
5
|
+
menuClassName?: string;
|
|
6
|
+
itemClassName?: string;
|
|
7
|
+
dividerClassName?: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function buildDropdownMenuId(id: string | undefined, generatedId: string): string;
|
|
10
|
+
export declare function buildDropdownMenuClassNames(options: DropdownMenuClassNameOptions): {
|
|
11
|
+
classes: string;
|
|
12
|
+
triggerClasses: string;
|
|
13
|
+
menuClasses: string;
|
|
14
|
+
itemClasses: string;
|
|
15
|
+
dividerClasses: string;
|
|
16
|
+
};
|
|
17
|
+
export declare function buildDropdownMenuItemClassName(baseClassName: string, state: {
|
|
18
|
+
disabled?: boolean;
|
|
19
|
+
danger?: boolean;
|
|
20
|
+
}): string;
|
|
21
|
+
export declare function computeDropdownMenuPosition({ menuAlign, triggerRect, viewportWidth, viewportPadding, verticalGap, }: {
|
|
22
|
+
menuAlign: DropdownMenuProps["menuAlign"];
|
|
23
|
+
triggerRect: Pick<DOMRect, "left" | "right" | "bottom" | "width">;
|
|
24
|
+
viewportWidth: number;
|
|
25
|
+
viewportPadding?: number;
|
|
26
|
+
verticalGap?: number;
|
|
27
|
+
}): {
|
|
28
|
+
top: number;
|
|
29
|
+
left: number;
|
|
30
|
+
minWidth: number;
|
|
31
|
+
};
|
|
32
|
+
export declare function toDropdownMenuStateLabel({ open, itemCount, }: {
|
|
33
|
+
open: boolean;
|
|
34
|
+
itemCount: number;
|
|
35
|
+
}): string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require("react"),t=require("react-dom"),n=require("react/jsx-runtime");function r(e){return!!(e&&`type`in e&&e.type===`divider`)}function i(e){return e.label??e.children??`Menu item`}function a(e,t){return e.value??e.label??`item-${t}`}function o(
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require("react"),t=require("react-dom"),n=require("react/jsx-runtime");function r(e){return!!(e&&`type`in e&&e.type===`divider`)}function i(e){return e.label??e.children??`Menu item`}function a(e,t){return e.value??e.label??`item-${t}`}function o(e,t){return e||`nabs-ui-dropdownmenu-${t.replace(/:/g,``)}`}function s(e){return{classes:[`nabs-ui-dropdownmenu`,e.className??``].filter(Boolean).join(` `),triggerClasses:[`nabs-ui-dropdownmenu__trigger`,e.triggerClassName??``].filter(Boolean).join(` `),menuClasses:[`nabs-ui-dropdownmenu__menu`,e.menuClassName??``].filter(Boolean).join(` `),itemClasses:[`nabs-ui-dropdownmenu__item`,e.itemClassName??``].filter(Boolean).join(` `),dividerClasses:[`nabs-ui-dropdownmenu__divider`,e.dividerClassName??``].filter(Boolean).join(` `)}}function c(e,t){return[e,t.disabled?`nabs-ui-dropdownmenu__item--disabled`:``,t.danger?`nabs-ui-dropdownmenu__item--danger`:``].filter(Boolean).join(` `)}function l({menuAlign:e,triggerRect:t,viewportWidth:n,viewportPadding:r=8,verticalGap:i=10}){let a=t.width,o=n-a-r,s=e===`end`?t.right-a:t.left,c=Math.max(r,Math.min(s,o));return{top:Math.max(r,t.bottom+i),left:c,minWidth:a}}function u({label:u=`Menu`,triggerContent:d,triggerAriaLabel:f,menuAlign:p=`start`,items:m=[],className:h=``,triggerClassName:g=``,menuClassName:_=``,itemClassName:v=``,dividerClassName:y=``,id:b,onItemSelect:x,...S}){let[C,w]=(0,e.useState)(!1),T=(0,e.useRef)(null),E=(0,e.useRef)(null),D=(0,e.useRef)(null),O=o(b,(0,e.useId)()),{classes:k,triggerClasses:A,menuClasses:j,itemClasses:M,dividerClasses:N}=s({className:h,triggerClassName:g,menuClassName:_,itemClassName:v,dividerClassName:y});(0,e.useEffect)(()=>{let e=e=>{e.target instanceof Node&&T.current&&!T.current.contains(e.target)&&D.current&&!D.current.contains(e.target)&&w(!1)},t=e=>{e.key===`Escape`&&w(!1)};return document.addEventListener(`pointerdown`,e),document.addEventListener(`keydown`,t),()=>{document.removeEventListener(`pointerdown`,e),document.removeEventListener(`keydown`,t)}},[]),(0,e.useLayoutEffect)(()=>{if(!C||!E.current)return;let e=()=>{let e=E.current?.getBoundingClientRect(),t=D.current;if(!e||!t)return;let n=l({menuAlign:p,triggerRect:e,viewportWidth:window.innerWidth});t.style.position=`fixed`,t.style.top=`${n.top}px`,t.style.left=`${n.left}px`,t.style.right=`auto`,t.style.minWidth=`${n.minWidth}px`,t.style.visibility=`visible`};return D.current&&(D.current.style.visibility=`hidden`),e(),window.addEventListener(`resize`,e),window.addEventListener(`scroll`,e,!0),()=>{window.removeEventListener(`resize`,e),window.removeEventListener(`scroll`,e,!0)}},[p,C]);let P=(e,t)=>{if(w(!1),e.disabled){t.preventDefault();return}x?.(e,t),e.onSelect?.(e,t)};return(0,n.jsxs)(`div`,{ref:T,className:k,...S,children:[(0,n.jsx)(`button`,{ref:E,type:`button`,className:A,"aria-haspopup":`menu`,"aria-controls":O,"aria-label":f??u,onClick:()=>w(e=>!e),children:d??(0,n.jsxs)(n.Fragment,{children:[(0,n.jsx)(`span`,{className:`nabs-ui-dropdownmenu__triggerLabel`,children:u}),(0,n.jsx)(`span`,{className:`nabs-ui-dropdownmenu__triggerChevron`,"aria-hidden":`true`,children:`▾`})]})}),C&&typeof document<`u`?(0,t.createPortal)((0,n.jsx)(`div`,{ref:D,id:O,className:j,"aria-label":u,children:m.map((e,t)=>{if(r(e))return(0,n.jsx)(`div`,{className:N,role:`separator`,"aria-hidden":`true`},`divider-${t}`);let o=i(e),s=c(M,{disabled:e.disabled,danger:e.danger}),l={className:s,"aria-disabled":e.disabled||void 0};return e.href?(0,n.jsxs)(`a`,{...l,href:e.disabled?void 0:e.href,onClick:t=>P(e,t),children:[(0,n.jsx)(`span`,{className:`nabs-ui-dropdownmenu__itemLabel`,children:o}),e.description?(0,n.jsx)(`span`,{className:`nabs-ui-dropdownmenu__itemDescription`,children:e.description}):null]},a(e,t)):(0,n.jsxs)(`button`,{type:`button`,className:s,disabled:e.disabled,onClick:t=>P(e,t),children:[(0,n.jsx)(`span`,{className:`nabs-ui-dropdownmenu__itemLabel`,children:o}),e.description?(0,n.jsx)(`span`,{className:`nabs-ui-dropdownmenu__itemDescription`,children:e.description}):null]},a(e,t))})}),document.body):null]})}exports.DropdownMenu=u;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useEffect as e, useId as t, useLayoutEffect as n, useRef as r, useState as i } from "react";
|
|
2
2
|
import { createPortal as a } from "react-dom";
|
|
3
3
|
import { Fragment as o, jsx as s, jsxs as c } from "react/jsx-runtime";
|
|
4
|
-
//#region src/dropdownmenu.
|
|
4
|
+
//#region src/dropdownmenu.model.ts
|
|
5
5
|
function l(e) {
|
|
6
6
|
return !!(e && "type" in e && e.type === "divider");
|
|
7
7
|
}
|
|
@@ -12,80 +12,118 @@ function d(e, t) {
|
|
|
12
12
|
return e.value ?? e.label ?? `item-${t}`;
|
|
13
13
|
}
|
|
14
14
|
//#endregion
|
|
15
|
+
//#region src/dropdownmenu.view-model.ts
|
|
16
|
+
function f(e, t) {
|
|
17
|
+
return e || `nabs-ui-dropdownmenu-${t.replace(/:/g, "")}`;
|
|
18
|
+
}
|
|
19
|
+
function p(e) {
|
|
20
|
+
return {
|
|
21
|
+
classes: ["nabs-ui-dropdownmenu", e.className ?? ""].filter(Boolean).join(" "),
|
|
22
|
+
triggerClasses: ["nabs-ui-dropdownmenu__trigger", e.triggerClassName ?? ""].filter(Boolean).join(" "),
|
|
23
|
+
menuClasses: ["nabs-ui-dropdownmenu__menu", e.menuClassName ?? ""].filter(Boolean).join(" "),
|
|
24
|
+
itemClasses: ["nabs-ui-dropdownmenu__item", e.itemClassName ?? ""].filter(Boolean).join(" "),
|
|
25
|
+
dividerClasses: ["nabs-ui-dropdownmenu__divider", e.dividerClassName ?? ""].filter(Boolean).join(" ")
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function m(e, t) {
|
|
29
|
+
return [
|
|
30
|
+
e,
|
|
31
|
+
t.disabled ? "nabs-ui-dropdownmenu__item--disabled" : "",
|
|
32
|
+
t.danger ? "nabs-ui-dropdownmenu__item--danger" : ""
|
|
33
|
+
].filter(Boolean).join(" ");
|
|
34
|
+
}
|
|
35
|
+
function h({ menuAlign: e, triggerRect: t, viewportWidth: n, viewportPadding: r = 8, verticalGap: i = 10 }) {
|
|
36
|
+
let a = t.width, o = n - a - r, s = e === "end" ? t.right - a : t.left, c = Math.max(r, Math.min(s, o));
|
|
37
|
+
return {
|
|
38
|
+
top: Math.max(r, t.bottom + i),
|
|
39
|
+
left: c,
|
|
40
|
+
minWidth: a
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
//#endregion
|
|
15
44
|
//#region src/DropdownMenu.tsx
|
|
16
|
-
function
|
|
17
|
-
let [
|
|
45
|
+
function g({ label: g = "Menu", triggerContent: _, triggerAriaLabel: v, menuAlign: y = "start", items: b = [], className: x = "", triggerClassName: S = "", menuClassName: C = "", itemClassName: w = "", dividerClassName: T = "", id: E, onItemSelect: D, ...O }) {
|
|
46
|
+
let [k, A] = i(!1), j = r(null), M = r(null), N = r(null), P = f(E, t()), { classes: F, triggerClasses: I, menuClasses: L, itemClasses: R, dividerClasses: z } = p({
|
|
47
|
+
className: x,
|
|
48
|
+
triggerClassName: S,
|
|
49
|
+
menuClassName: C,
|
|
50
|
+
itemClassName: w,
|
|
51
|
+
dividerClassName: T
|
|
52
|
+
});
|
|
18
53
|
e(() => {
|
|
19
54
|
let e = (e) => {
|
|
20
|
-
e.target instanceof Node &&
|
|
55
|
+
e.target instanceof Node && j.current && !j.current.contains(e.target) && N.current && !N.current.contains(e.target) && A(!1);
|
|
21
56
|
}, t = (e) => {
|
|
22
|
-
e.key === "Escape" &&
|
|
57
|
+
e.key === "Escape" && A(!1);
|
|
23
58
|
};
|
|
24
59
|
return document.addEventListener("pointerdown", e), document.addEventListener("keydown", t), () => {
|
|
25
60
|
document.removeEventListener("pointerdown", e), document.removeEventListener("keydown", t);
|
|
26
61
|
};
|
|
27
62
|
}, []), n(() => {
|
|
28
|
-
if (!
|
|
63
|
+
if (!k || !M.current) return;
|
|
29
64
|
let e = () => {
|
|
30
|
-
let e =
|
|
65
|
+
let e = M.current?.getBoundingClientRect(), t = N.current;
|
|
31
66
|
if (!e || !t) return;
|
|
32
|
-
let n =
|
|
33
|
-
|
|
67
|
+
let n = h({
|
|
68
|
+
menuAlign: y,
|
|
69
|
+
triggerRect: e,
|
|
70
|
+
viewportWidth: window.innerWidth
|
|
71
|
+
});
|
|
72
|
+
t.style.position = "fixed", t.style.top = `${n.top}px`, t.style.left = `${n.left}px`, t.style.right = "auto", t.style.minWidth = `${n.minWidth}px`, t.style.visibility = "visible";
|
|
34
73
|
};
|
|
35
|
-
return
|
|
74
|
+
return N.current && (N.current.style.visibility = "hidden"), e(), window.addEventListener("resize", e), window.addEventListener("scroll", e, !0), () => {
|
|
36
75
|
window.removeEventListener("resize", e), window.removeEventListener("scroll", e, !0);
|
|
37
76
|
};
|
|
38
|
-
}, [
|
|
39
|
-
let
|
|
40
|
-
if (
|
|
77
|
+
}, [y, k]);
|
|
78
|
+
let B = (e, t) => {
|
|
79
|
+
if (A(!1), e.disabled) {
|
|
41
80
|
t.preventDefault();
|
|
42
81
|
return;
|
|
43
82
|
}
|
|
44
|
-
|
|
83
|
+
D?.(e, t), e.onSelect?.(e, t);
|
|
45
84
|
};
|
|
46
85
|
return /* @__PURE__ */ c("div", {
|
|
47
|
-
ref:
|
|
48
|
-
className:
|
|
49
|
-
...
|
|
86
|
+
ref: j,
|
|
87
|
+
className: F,
|
|
88
|
+
...O,
|
|
50
89
|
children: [/* @__PURE__ */ s("button", {
|
|
51
|
-
ref:
|
|
90
|
+
ref: M,
|
|
52
91
|
type: "button",
|
|
53
|
-
className:
|
|
92
|
+
className: I,
|
|
54
93
|
"aria-haspopup": "menu",
|
|
55
|
-
"aria-controls":
|
|
56
|
-
"aria-label":
|
|
57
|
-
onClick: () =>
|
|
58
|
-
children:
|
|
94
|
+
"aria-controls": P,
|
|
95
|
+
"aria-label": v ?? g,
|
|
96
|
+
onClick: () => A((e) => !e),
|
|
97
|
+
children: _ ?? /* @__PURE__ */ c(o, { children: [/* @__PURE__ */ s("span", {
|
|
59
98
|
className: "nabs-ui-dropdownmenu__triggerLabel",
|
|
60
|
-
children:
|
|
99
|
+
children: g
|
|
61
100
|
}), /* @__PURE__ */ s("span", {
|
|
62
101
|
className: "nabs-ui-dropdownmenu__triggerChevron",
|
|
63
102
|
"aria-hidden": "true",
|
|
64
103
|
children: "▾"
|
|
65
104
|
})] })
|
|
66
|
-
}),
|
|
67
|
-
ref:
|
|
68
|
-
id:
|
|
69
|
-
className:
|
|
70
|
-
"aria-label":
|
|
71
|
-
children:
|
|
105
|
+
}), k && typeof document < "u" ? a(/* @__PURE__ */ s("div", {
|
|
106
|
+
ref: N,
|
|
107
|
+
id: P,
|
|
108
|
+
className: L,
|
|
109
|
+
"aria-label": g,
|
|
110
|
+
children: b.map((e, t) => {
|
|
72
111
|
if (l(e)) return /* @__PURE__ */ s("div", {
|
|
73
|
-
className:
|
|
112
|
+
className: z,
|
|
74
113
|
role: "separator",
|
|
75
114
|
"aria-hidden": "true"
|
|
76
115
|
}, `divider-${t}`);
|
|
77
|
-
let n = u(e), r =
|
|
78
|
-
|
|
79
|
-
e.
|
|
80
|
-
|
|
81
|
-
].filter(Boolean).join(" "), i = {
|
|
116
|
+
let n = u(e), r = m(R, {
|
|
117
|
+
disabled: e.disabled,
|
|
118
|
+
danger: e.danger
|
|
119
|
+
}), i = {
|
|
82
120
|
className: r,
|
|
83
121
|
"aria-disabled": e.disabled || void 0
|
|
84
122
|
};
|
|
85
123
|
return e.href ? /* @__PURE__ */ c("a", {
|
|
86
124
|
...i,
|
|
87
125
|
href: e.disabled ? void 0 : e.href,
|
|
88
|
-
onClick: (t) =>
|
|
126
|
+
onClick: (t) => B(e, t),
|
|
89
127
|
children: [/* @__PURE__ */ s("span", {
|
|
90
128
|
className: "nabs-ui-dropdownmenu__itemLabel",
|
|
91
129
|
children: n
|
|
@@ -97,7 +135,7 @@ function f({ label: f = "Menu", triggerContent: p, triggerAriaLabel: m, menuAlig
|
|
|
97
135
|
type: "button",
|
|
98
136
|
className: r,
|
|
99
137
|
disabled: e.disabled,
|
|
100
|
-
onClick: (t) =>
|
|
138
|
+
onClick: (t) => B(e, t),
|
|
101
139
|
children: [/* @__PURE__ */ s("span", {
|
|
102
140
|
className: "nabs-ui-dropdownmenu__itemLabel",
|
|
103
141
|
children: n
|
|
@@ -111,4 +149,4 @@ function f({ label: f = "Menu", triggerContent: p, triggerAriaLabel: m, menuAlig
|
|
|
111
149
|
});
|
|
112
150
|
}
|
|
113
151
|
//#endregion
|
|
114
|
-
export {
|
|
152
|
+
export { g as DropdownMenu };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@net-advantage/nabs-ui-dropdownmenu",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.72.0",
|
|
4
4
|
"description": "Reusable dropdown menu control for the Nabs UI library.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/nabs-ui-dropdownmenu.cjs",
|
|
@@ -25,11 +25,16 @@
|
|
|
25
25
|
"@types/react": "^19.0.0",
|
|
26
26
|
"@types/react-dom": "^19.0.0",
|
|
27
27
|
"@vitejs/plugin-react": "^6.0.1",
|
|
28
|
+
"@vitest/coverage-v8": "^3.2.4",
|
|
28
29
|
"react-dom": "^19.2.6",
|
|
29
|
-
"vite": "^8.0.12"
|
|
30
|
+
"vite": "^8.0.12",
|
|
31
|
+
"vitest": "^3.2.4"
|
|
30
32
|
},
|
|
31
33
|
"scripts": {
|
|
32
34
|
"build": "vite build",
|
|
33
|
-
"dev": "vite"
|
|
35
|
+
"dev": "vite",
|
|
36
|
+
"test:unit": "vitest run",
|
|
37
|
+
"test:unit:watch": "vitest",
|
|
38
|
+
"test:unit:coverage": "vitest run --coverage --coverage.reporter=html --coverage.reporter=json-summary --coverage.reporter=text-summary"
|
|
34
39
|
}
|
|
35
40
|
}
|