neko-popup 3.0.2 → 3.0.4

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.
@@ -2,7 +2,7 @@ import { default as React, RefObject } from 'react';
2
2
  import { default as clsx } from 'clsx';
3
3
  export type PopupWindowDisabledType = 'onEscape' | 'onLayer';
4
4
  export type StateSetter<S = any> = React.Dispatch<React.SetStateAction<S>>;
5
- export type RegisterNodeArgs = Pick<IPopupNode, 'id' | 'isOpen' | 'disabled'>;
5
+ export type MountNodeArgs = Pick<IPopupNode, 'id' | 'isOpen' | 'disabled'>;
6
6
  /** Get value type from nested object path */
7
7
  export type ValueFromPath<T, P> = P extends `${infer K}.${infer R}` ? K extends keyof T ? R extends keyof T[K] ? T[K][R] : never : never : P extends keyof T ? T[P] : never;
8
8
  export interface IPopupNode {
@@ -15,7 +15,7 @@ export interface IPopupContext {
15
15
  nodes: IPopupNode[];
16
16
  containerRef: RefObject<HTMLDivElement | null>;
17
17
  invokePopup(id: string, forceState?: boolean): void;
18
- registerNode(args: RegisterNodeArgs): void;
18
+ mountNode(args: MountNodeArgs): void;
19
19
  updateNodeProperty<K extends keyof IPopupNode>(id: string, key: K, value: ValueFromPath<IPopupNode, K>): void;
20
20
  }
21
21
  export declare const PopupContext: React.Context<IPopupContext>;
@@ -13,5 +13,4 @@ export interface IPopupButtonProps {
13
13
  id?: string;
14
14
  onClick?(e: React.MouseEvent): void;
15
15
  }
16
- declare const PopupButton: FC<IPopupButtonProps>;
17
- export default PopupButton;
16
+ export declare const PopupButton: FC<IPopupButtonProps>;
@@ -1,5 +1,5 @@
1
1
  import { FC, ReactNode } from 'react';
2
- interface IPopupLayerProps {
2
+ export interface IPopupLayerProps {
3
3
  children?: ReactNode | ReactNode[];
4
4
  /** @default 10000 */
5
5
  baseZIndex?: number;
@@ -10,5 +10,4 @@ interface IPopupLayerProps {
10
10
  */
11
11
  disableBodyScrollOnActivePopup?: boolean;
12
12
  }
13
- declare const PopupLayer: FC<IPopupLayerProps>;
14
- export default PopupLayer;
13
+ export declare const PopupLayer: FC<IPopupLayerProps>;
@@ -1,6 +1,7 @@
1
1
  import { FC, ReactNode } from 'react';
2
2
  import { PopupWindowDisabledType, StateSetter } from './Interfaces';
3
- interface IPopupWindowProps {
3
+ export type PopupWindowAnimationType = 'fade' | 'scale' | null;
4
+ export interface IPopupWindowProps {
4
5
  id: string;
5
6
  children: ReactNode | ReactNode[];
6
7
  isOpen?: boolean;
@@ -41,5 +42,4 @@ interface IPopupWindowProps {
41
42
  */
42
43
  onAfterExit?(): void;
43
44
  }
44
- declare const PopupWindow: FC<IPopupWindowProps>;
45
- export default PopupWindow;
45
+ export declare const PopupWindow: FC<IPopupWindowProps>;
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
- export { default as PopupLayer } from './PopupLayer';
2
- export { default as PopupButton } from './PopupButton';
3
- export { default as PopupWindow } from './PopupWindow';
1
+ export * from './PopupButton';
2
+ export * from './PopupLayer';
3
+ export * from './PopupWindow';
4
+ export type { IPopupContext, IPopupNode } from './Interfaces';
package/dist/index.es.js CHANGED
@@ -1,155 +1,160 @@
1
- import { jsxs as N, jsx as v } from "react/jsx-runtime";
2
- import { createContext as A, useState as m, useRef as I, useEffect as u, useContext as P, useMemo as z, useLayoutEffect as O } from "react";
3
- import { createPortal as w } from "react-dom";
4
- class B extends Error {
5
- constructor(n) {
6
- super(n), this.name = "error at [neko-popup]";
7
- }
8
- }
9
- function C(e) {
10
- var n, s, o = "";
11
- if (typeof e == "string" || typeof e == "number") o += e;
1
+ import { jsx as E, jsxs as O } from "react/jsx-runtime";
2
+ import { createContext as z, useContext as A, useState as f, useEffect as c, useRef as C, useMemo as w, useLayoutEffect as B } from "react";
3
+ import { createPortal as L } from "react-dom";
4
+ function N(e) {
5
+ var o, i, t = "";
6
+ if (typeof e == "string" || typeof e == "number") t += e;
12
7
  else if (typeof e == "object") if (Array.isArray(e)) {
13
- var a = e.length;
14
- for (n = 0; n < a; n++) e[n] && (s = C(e[n])) && (o && (o += " "), o += s);
15
- } else for (s in e) e[s] && (o && (o += " "), o += s);
16
- return o;
8
+ var s = e.length;
9
+ for (o = 0; o < s; o++) e[o] && (i = N(e[o])) && (t && (t += " "), t += i);
10
+ } else for (i in e) e[i] && (t && (t += " "), t += i);
11
+ return t;
17
12
  }
18
- function L() {
19
- for (var e, n, s = 0, o = "", a = arguments.length; s < a; s++) (e = arguments[s]) && (n = C(e)) && (o && (o += " "), o += n);
20
- return o;
13
+ function D() {
14
+ for (var e, o, i = 0, t = "", s = arguments.length; i < s; i++) (e = arguments[i]) && (o = N(e)) && (t && (t += " "), t += o);
15
+ return t;
21
16
  }
22
- const E = A({}), g = L, R = (e) => {
23
- const [n, s] = m([]), [o, a] = m(!1), b = I(null), l = e.baseZIndex ?? 1e4, x = e.disableBodyScrollOnActivePopup ?? !0;
24
- u(() => {
25
- const i = new AbortController();
26
- if (window.addEventListener("keydown", (d) => {
27
- if (d.key === "Escape") {
28
- const c = Math.max(...n.filter((y) => y.isOpen).map((y) => y.zIndex)), r = n.find((y) => y.zIndex === c);
29
- if (!r || r.disabled.includes("onEscape")) return;
30
- f(r.id, !1);
31
- }
32
- }, { signal: i.signal }), x) {
33
- let d = !1;
34
- n.forEach((t) => t.isOpen ? d = !0 : null), a(d);
35
- }
36
- return () => {
37
- i.abort();
38
- };
39
- }, [n]), u(() => {
40
- o ? document.body.classList.add("neko-popup--noScroll") : document.body.classList.remove("neko-popup--noScroll");
17
+ const g = z({}), I = D, Z = (e) => {
18
+ const o = A(g), [i, t] = f(!1), s = e.as ?? "button";
19
+ c(() => {
20
+ const r = o.nodes.find((y) => y.id === e.popupId);
21
+ r && t(r.isOpen);
41
22
  }, [o]);
42
- function f(i, d) {
43
- const t = typeof i == "string" ? n.find((r) => r.id === i) : i;
44
- if (!t) throw new B(typeof i == "string" ? `Cannot find popup node with id #${i}` : "Entity is not assigned to the node");
45
- const c = d ?? !t.isOpen;
46
- t.isOpen = c, t.zIndex = c ? Math.max(...n.map((r) => r.zIndex), 0) + 1 : -1, p(t);
47
- }
48
- function h(i, d, t) {
49
- const c = n.find((r) => r.id === i);
50
- c && (c[d] = t, p(c));
23
+ function p(r) {
24
+ o.invokePopup(e.popupId), e.onClick && e.onClick(r);
51
25
  }
52
- function p(i) {
53
- s((d) => [...d.filter((t) => t.id !== i.id), i]);
54
- }
55
- const k = ({ id: i, isOpen: d, disabled: t }) => f({ id: i, isOpen: !1, disabled: t, zIndex: -1 }, d);
56
- return /* @__PURE__ */ N(E.Provider, { value: {
57
- nodes: n,
58
- containerRef: b,
59
- invokePopup: f,
60
- registerNode: k,
61
- updateNodeProperty: h
62
- }, children: [
63
- e.children,
64
- /* @__PURE__ */ v("section", { style: { zIndex: l }, ref: b })
65
- ] });
66
- }, T = (e) => {
67
- const n = P(E), [s, o] = m(!1), a = e.as ?? "button";
68
- u(() => {
69
- const l = n.nodes.find((x) => x.id === e.popupId);
70
- l && o(l.isOpen);
71
- }, [n]);
72
- function b(l) {
73
- n.invokePopup(e.popupId), e.onClick && e.onClick(l);
74
- }
75
- return /* @__PURE__ */ v(
76
- a,
26
+ return /* @__PURE__ */ E(
27
+ s,
77
28
  {
78
29
  tabIndex: 0,
79
30
  disabled: e.disabled,
80
31
  "aria-disabled": e.disabled,
81
32
  "aria-haspopup": "dialog",
82
33
  id: e.id,
83
- className: g("neko-popup-button", s && "neko-popup-button--active", e.className),
84
- onClick: b,
34
+ className: I("neko-popup-button", i && "neko-popup-button--active", e.className),
35
+ onClick: p,
85
36
  children: e.children
86
37
  }
87
38
  );
88
39
  };
89
- function D(e, n) {
90
- const s = z(() => arguments.length === 2 && n !== void 0, []), [o, a] = m(e);
91
- return u(() => {
92
- s || a(e);
93
- }, [e]), s ? [
40
+ class M extends Error {
41
+ constructor(o) {
42
+ super(o), this.name = "error at [neko-popup]";
43
+ }
44
+ }
45
+ const j = (e) => {
46
+ const o = e.baseZIndex ?? 1e4, i = e.disableBodyScrollOnActivePopup ?? !0, [t, s] = f([]), [p, r] = f(!1), [y, x] = f(!1), v = C(null);
47
+ c(() => {
48
+ const n = new AbortController();
49
+ if (window.addEventListener("keydown", (a) => {
50
+ if (a.key === "Escape") {
51
+ const u = Math.max(...t.filter((k) => k.isOpen).map((k) => k.zIndex)), l = t.find((k) => k.zIndex === u);
52
+ if (!l || l.disabled.includes("onEscape")) return;
53
+ b(l.id, !1);
54
+ }
55
+ }, { signal: n.signal }), i) {
56
+ let a = !1;
57
+ t.forEach((d) => d.isOpen ? a = !0 : null), r(a);
58
+ }
59
+ return () => {
60
+ n.abort();
61
+ };
62
+ }, [t]), c(() => {
63
+ x(t.some((n) => n.isOpen));
64
+ }, [t]), c(() => {
65
+ p ? document.body.classList.add("neko-popup--noScroll") : document.body.classList.remove("neko-popup--noScroll");
66
+ }, [p]);
67
+ function b(n, a) {
68
+ const d = typeof n == "string" ? t.find((l) => l.id === n) : n;
69
+ if (!d) throw new M(typeof n == "string" ? `Cannot find popup node with id #${n}` : "Entity is not assigned to the node");
70
+ const u = a ?? !d.isOpen;
71
+ d.isOpen = u, d.zIndex = u ? Math.max(...t.map((l) => l.zIndex), 0) + 1 : -1, m(d);
72
+ }
73
+ function h(n, a, d) {
74
+ const u = t.find((l) => l.id === n);
75
+ u && (u[a] = d, m(u));
76
+ }
77
+ function m(n) {
78
+ s((a) => [...a.filter((d) => d.id !== n.id), n]);
79
+ }
80
+ const P = ({ id: n, isOpen: a, disabled: d }) => b({ id: n, isOpen: !1, disabled: d, zIndex: -1 }, a);
81
+ return /* @__PURE__ */ O(g.Provider, { value: {
82
+ nodes: t,
83
+ containerRef: v,
84
+ invokePopup: b,
85
+ mountNode: P,
86
+ updateNodeProperty: h
87
+ }, children: [
88
+ e.children,
89
+ /* @__PURE__ */ E("section", { className: I("neko-popup-layer", y && "neko-popup-layer--active"), style: { zIndex: o }, ref: v })
90
+ ] });
91
+ };
92
+ function S(e, o) {
93
+ const i = w(() => arguments.length === 2 && o !== void 0, []), [t, s] = f(e);
94
+ return c(() => {
95
+ i || s(e);
96
+ }, [e]), i ? [
94
97
  e,
95
- n
96
- ] : [o, a];
98
+ o
99
+ ] : [t, s];
97
100
  }
98
- const Z = (e) => {
99
- const n = P(E), [s, o] = m(null), [a, b] = D(e.isOpen ?? !1, e.setIsOpen), [l, x] = m(-1), [f, h] = m([]), p = I(null), k = e.animation !== void 0 ? e.animation : "fade", i = e.animationDuraionMs ?? 200;
100
- O(() => {
101
- let t = [];
102
- typeof e.disabled == "boolean" ? t = e.disabled ? ["onEscape", "onLayer"] : [] : t = e.disabled ?? [], n.updateNodeProperty(e.id, "disabled", t);
103
- }, [e.disabled]), u(() => {
104
- const t = n.containerRef.current;
105
- t && (o(t), n.registerNode({
101
+ const W = (e) => {
102
+ const o = A(g), [i, t] = f(null), [s, p] = S(e.isOpen ?? !1, e.setIsOpen), [r, y] = f(-1), [x, v] = f([]), b = C(null), h = e.animation !== void 0 ? e.animation : "fade", m = e.animationDuraionMs ?? 200;
103
+ B(() => {
104
+ let n = [];
105
+ typeof e.disabled == "boolean" ? n = e.disabled ? ["onEscape", "onLayer"] : [] : n = e.disabled ?? [], o.updateNodeProperty(e.id, "disabled", n);
106
+ }, [e.disabled]), c(() => {
107
+ const n = o.containerRef.current;
108
+ n && (t(n), o.mountNode({
106
109
  id: e.id,
107
110
  isOpen: !!e.isOpen,
108
- disabled: f
111
+ disabled: x
109
112
  }));
110
- }, []), u(() => {
111
- const t = n.nodes.find((c) => c.id === e.id);
112
- t && (b(t.isOpen), x(t.zIndex), h(t.disabled));
113
- }, [n.nodes]), u(() => {
114
- const t = p.current;
115
- t && (a ? t.style.zIndex = `${l}` : setTimeout(() => {
116
- t.style.zIndex = "-1";
117
- }, i));
118
- }, [a]), u(() => {
119
- a ? (e.onBeforeEnter && e.onBeforeEnter(), setTimeout(() => {
113
+ }, []), c(() => {
114
+ o.nodes.some((n) => n.id === e.id) && o.invokePopup(e.id, e.isOpen);
115
+ }, [e.isOpen]), c(() => {
116
+ const n = o.nodes.find((a) => a.id === e.id);
117
+ n && (p(n.isOpen), y(n.zIndex), v(n.disabled));
118
+ }, [o.nodes]), c(() => {
119
+ const n = b.current;
120
+ n && (s ? n.style.zIndex = `${r}` : setTimeout(() => {
121
+ n.style.zIndex = "-1";
122
+ }, m));
123
+ }, [s]), c(() => {
124
+ s ? (e.onBeforeEnter && e.onBeforeEnter(), setTimeout(() => {
120
125
  e.onAfterEnter && e.onAfterEnter();
121
- }, i)) : (e.onBeforeExit && e.onBeforeExit(), setTimeout(() => {
126
+ }, m)) : (e.onBeforeExit && e.onBeforeExit(), setTimeout(() => {
122
127
  e.onAfterExit && e.onAfterExit();
123
- }, i));
124
- }, [a]);
125
- function d() {
126
- f.includes("onLayer") || n.invokePopup(e.id, !1);
128
+ }, m));
129
+ }, [s]);
130
+ function P() {
131
+ x.includes("onLayer") || o.invokePopup(e.id, !1);
127
132
  }
128
- return s && w(/* @__PURE__ */ v(
133
+ return i && L(/* @__PURE__ */ E(
129
134
  "section",
130
135
  {
131
- className: g("neko-popup-backdrop", a && "neko-popup-backdrop--active", e.layerClassName),
132
- "aria-hidden": !a,
133
- style: { transition: `${i}ms ease-in-out`, cursor: f.includes("onLayer") ? "default" : "pointer" },
134
- onClick: d,
135
- ref: p,
136
- children: /* @__PURE__ */ v(
136
+ className: I("neko-popup-backdrop", s && "neko-popup-backdrop--active", e.layerClassName),
137
+ "aria-hidden": !s,
138
+ style: { transition: `${m}ms ease-in-out`, cursor: x.includes("onLayer") ? "default" : "pointer" },
139
+ onClick: P,
140
+ ref: b,
141
+ children: /* @__PURE__ */ E(
137
142
  "article",
138
143
  {
139
144
  id: e.id,
140
- className: g("neko-popup", a && "neko-popup--active", k && `neko-popup--animation_${k}`, e.className),
145
+ className: I("neko-popup", s && "neko-popup--active", h && `neko-popup--animation_${h}`, e.className),
141
146
  role: "dialog",
142
147
  "aria-modal": !0,
143
- onClick: (t) => t.stopPropagation(),
148
+ onClick: (n) => n.stopPropagation(),
144
149
  children: e.children
145
150
  }
146
151
  )
147
152
  }
148
- ), s);
153
+ ), i);
149
154
  };
150
155
  export {
151
- T as PopupButton,
152
- R as PopupLayer,
153
- Z as PopupWindow
156
+ Z as PopupButton,
157
+ j as PopupLayer,
158
+ W as PopupWindow
154
159
  };
155
160
  //# sourceMappingURL=index.es.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.es.js","sources":["../src/_package/components/ErrorComponents.ts","../node_modules/clsx/dist/clsx.mjs","../src/_package/Interfaces.ts","../src/_package/PopupLayer.tsx","../src/_package/PopupButton.tsx","../src/_package/hooks/useMixedState.ts","../src/_package/PopupWindow.tsx"],"sourcesContent":["class EFKW extends Error {\n constructor(msg: string) {\n super(msg);\n\n this.name = 'error at [neko-popup]';\n }\n}\n\nexport default EFKW;","function r(e){var t,f,n=\"\";if(\"string\"==typeof e||\"number\"==typeof e)n+=e;else if(\"object\"==typeof e)if(Array.isArray(e)){var o=e.length;for(t=0;t<o;t++)e[t]&&(f=r(e[t]))&&(n&&(n+=\" \"),n+=f)}else for(f in e)e[f]&&(n&&(n+=\" \"),n+=f);return n}export function clsx(){for(var e,t,f=0,n=\"\",o=arguments.length;f<o;f++)(e=arguments[f])&&(t=r(e))&&(n&&(n+=\" \"),n+=t);return n}export default clsx;","import React, { createContext, RefObject } from 'react';\nimport clsx from 'clsx';\n\n\n\n\nexport type PopupWindowDisabledType = 'onEscape' | 'onLayer';\nexport type StateSetter<S = any> = React.Dispatch<React.SetStateAction<S>>\nexport type RegisterNodeArgs = Pick<IPopupNode, 'id' | 'isOpen' | 'disabled'>\n\n/** Get value type from nested object path */\nexport type ValueFromPath<T, P> =\n P extends `${infer K}.${infer R}`\n ? K extends keyof T\n ? R extends keyof T[K]\n ? T[K][R]\n : never\n : never\n : P extends keyof T\n ? T[P]\n : never;\n\n\n\nexport interface IPopupNode {\n id: string\n isOpen: boolean\n zIndex: number\n disabled: PopupWindowDisabledType[]\n}\n\nexport interface IPopupContext {\n nodes: IPopupNode[]\n containerRef: RefObject<HTMLDivElement | null>\n\n invokePopup(id: string, forceState?: boolean): void\n registerNode(args: RegisterNodeArgs): void\n updateNodeProperty<K extends keyof IPopupNode>(id: string, key: K, value: ValueFromPath<IPopupNode, K>): void\n}\n\n\n\nexport const PopupContext = createContext<IPopupContext>({} as IPopupContext);\nexport const cn = clsx;","'use client';\n\nimport { FC, ReactNode, useEffect, useRef, useState } from 'react';\n\nimport EFKW from './components/ErrorComponents';\nimport { IPopupNode, PopupContext, RegisterNodeArgs, ValueFromPath } from './Interfaces';\n\n\n\ninterface IPopupLayerProps {\n children?: ReactNode | ReactNode[]\n\n /** @default 10000 */\n baseZIndex?: number\n\n /**\n * Disable body scroll when there is at least one open popup\n * \n * @default true\n */\n disableBodyScrollOnActivePopup?: boolean\n}\n\n\n\nconst PopupLayer: FC<IPopupLayerProps> = (props) => {\n const [nodes, setNodes] = useState<IPopupNode[]>([]);\n const [isScrollDisabled, setIsScrollDisabled] = useState(false);\n\n const containerRef = useRef<HTMLDivElement>(null);\n\n const baseZIndex = props.baseZIndex ?? 10000;\n const disableBodyScrollOnActivePopup = props.disableBodyScrollOnActivePopup ?? true;\n\n\n\n // Handle close closest to user popup on escape & scroll\n useEffect(() => {\n // === Handle close closest popup on escape\n const controller = new AbortController();\n\n window.addEventListener('keydown', e => {\n const key = e.key;\n\n if (key === 'Escape') {\n const maxZIndex = Math.max(...nodes.filter(el => el.isOpen).map(el => el.zIndex));\n const node = nodes.find(el => el.zIndex === maxZIndex);\n if (!node || node.disabled.includes('onEscape')) return;\n\n // eslint-disable-next-line\n invokePopup(node.id, false);\n }\n }, { signal: controller.signal });\n\n\n\n // === Disable body scroll on active popup\n if (disableBodyScrollOnActivePopup) {\n let anyOpenNode = false;\n nodes.forEach(el => el.isOpen ? anyOpenNode = true : null);\n\n setIsScrollDisabled(anyOpenNode);\n }\n\n\n\n return () => {\n controller.abort();\n };\n }, [nodes]);\n\n // Handle body scroll\n useEffect(() => {\n if (isScrollDisabled) document.body.classList.add('neko-popup--noScroll');\n else document.body.classList.remove('neko-popup--noScroll');\n }, [isScrollDisabled]);\n\n\n\n /** Toggle popup state */\n function invokePopup(entityOrId: string | IPopupNode, forceState?: boolean) {\n const node = typeof entityOrId === 'string' ? nodes.find(el => el.id === entityOrId) : entityOrId;\n if (!node) throw new EFKW(typeof entityOrId === 'string' ? `Cannot find popup node with id #${entityOrId}` : `Entity is not assigned to the node`);\n\n const newState = forceState ?? !node.isOpen;\n\n // === Update node\n node.isOpen = newState;\n node.zIndex = newState ? Math.max(...nodes.map(el => el.zIndex), 0) + 1 : -1; // Make new popup invocation closer to user using larger z-index\n\n _updateNodeInNodes(node);\n }\n\n /** Update node property */\n function updateNodeProperty<K extends keyof IPopupNode>(id: string, key: K, value: ValueFromPath<IPopupNode, K>) {\n const node = nodes.find(el => el.id === id);\n if (!node) return;\n\n node[key] = value;\n\n _updateNodeInNodes(node);\n }\n\n /** Update node in nodes array */\n function _updateNodeInNodes(node: IPopupNode) {\n setNodes(prev => [...prev.filter(el => el.id !== node.id), node]);\n }\n\n /** Add new popup window to state */\n const registerNode = ({ id, isOpen, disabled }: RegisterNodeArgs) => invokePopup({ id, isOpen: false, disabled, zIndex: -1 }, isOpen);\n\n\n\n return <PopupContext.Provider value={{\n nodes,\n containerRef,\n invokePopup,\n registerNode,\n updateNodeProperty\n }}>\n {props.children}\n\n <section style={{ zIndex: baseZIndex }} ref={containerRef} />\n </PopupContext.Provider>;\n};\n\nexport default PopupLayer;","'use client';\n\nimport { FC, JSX, ReactNode, useContext, useEffect, useState } from 'react';\n\nimport { cn, PopupContext } from './Interfaces';\n\n\n\nexport interface IPopupButtonProps {\n popupId: string\n\n /** \n * Element tag\n * \n * @default \"button\"\n */\n as?: 'button' | 'div'\n\n disabled?: boolean\n children?: ReactNode | ReactNode[]\n className?: string\n id?: string\n\n onClick?(e: React.MouseEvent): void\n}\n\n\n\nconst PopupButton: FC<IPopupButtonProps> = (props) => {\n const ctx = useContext(PopupContext);\n\n const [isActive, setIsActive] = useState(false);\n\n const Tag: keyof JSX.IntrinsicElements = props.as ?? 'button';\n\n\n\n // Handle isActive on context change\n useEffect(() => {\n const node = ctx.nodes.find(el => el.id === props.popupId);\n if (!node) return;\n\n setIsActive(node.isOpen);\n }, [ctx]);\n\n\n\n function invokePopup(e: React.MouseEvent) {\n ctx.invokePopup(props.popupId);\n\n if (props.onClick) props.onClick(e);\n }\n\n\n\n return <Tag\n tabIndex={0}\n disabled={props.disabled}\n aria-disabled={props.disabled}\n aria-haspopup={'dialog'}\n id={props.id}\n className={cn(`neko-popup-button`, isActive && 'neko-popup-button--active', props.className)}\n onClick={invokePopup}\n >\n {props.children}\n </Tag>;\n};\n\nexport default PopupButton;","'use client';\n\nimport { useEffect, useMemo, useState } from 'react';\n\n\n\n\ntype StateSetter<S> = React.Dispatch<React.SetStateAction<S>>;\ntype InitialState<S> = S | (() => S);\n\nexport default function useMixedState<S = undefined>(): [S | undefined, StateSetter<S | undefined>];\nexport default function useMixedState<S>(initialState: InitialState<S>): [S, StateSetter<S>];\nexport default function useMixedState<S>(state: S, setter?: StateSetter<S>): [S, StateSetter<S>];\n\n\n\n/** \n * Use mixed state hook\n * \n * @param initialStateOrValue Initial state, can be undefined, value or function. If externalSetter not specified, will return default state\n * @param externalSetter External state setter. If specified will return external state\n */\nexport default function useMixedState<S>(initialStateOrValue?: InitialState<S>, externalSetter?: StateSetter<S>) {\n const isControlled = useMemo(() => arguments.length === 2 && externalSetter !== undefined, []);\n\n const [state, setter] = useState<S | undefined>(initialStateOrValue);\n\n\n\n // Propagate state update on external state update even if external setter is not provided\n useEffect(() => {\n if (!isControlled) {\n \n setter(initialStateOrValue);\n }\n }, [initialStateOrValue]);\n\n\n\n if (isControlled) {\n return [\n initialStateOrValue as S,\n externalSetter as StateSetter<S>\n ];\n }\n\n return [state, setter];\n}","'use client';\n\nimport { FC, ReactNode, useContext, useEffect, useLayoutEffect, useRef, useState } from 'react';\nimport { createPortal } from 'react-dom';\n\nimport useMixedState from './hooks/useMixedState';\nimport { cn, PopupContext, PopupWindowDisabledType, StateSetter } from './Interfaces';\n\n\n\ntype PopupWindowAnimationType = 'fade' | 'scale' | null\n\n\n\ninterface IPopupWindowProps {\n id: string\n children: ReactNode | ReactNode[]\n\n isOpen?: boolean\n setIsOpen?: StateSetter<boolean>\n\n className?: string\n layerClassName?: string\n disabled?: PopupWindowDisabledType[] | boolean\n\n /** \n * Popup dialog animation type\n * \n * @default \"fade\"\n */\n animation?: 'fade' | 'scale' | null\n\n /**\n * Popup animation duration in msec\n * \n * @default 200\n */\n animationDuraionMs?: number\n\n /** \n * Fire callback when popup invoked to open\n */\n onBeforeEnter?(): void\n\n /** \n * Fire callback when popup open animation fullfilled. \n * \n * @see animationDuration\n */\n onAfterEnter?(): void\n\n /** \n * Fire callback when popup invoked to close\n */\n onBeforeExit?(): void\n\n /** \n * Fire callback when popup close animation fullfilled. \n * \n * @see animationDuration\n */\n onAfterExit?(): void\n}\n\n\n\nconst PopupWindow: FC<IPopupWindowProps> = (props) => {\n const ctx = useContext(PopupContext);\n\n const [container, setContainer] = useState<HTMLDivElement | null>(null);\n const [isOpen, setIsOpen] = useMixedState(props.isOpen ?? false, props.setIsOpen);\n const [zIndex, setZIndex] = useState(-1);\n const [disabled, setDisabled] = useState<PopupWindowDisabledType[]>([]);\n\n const layerRef = useRef<HTMLDivElement>(null);\n\n const animation: PopupWindowAnimationType = props.animation !== undefined ? props.animation : 'fade';\n const animationDuration = props.animationDuraionMs ?? 200;\n\n\n\n // Handle disabled\n useLayoutEffect(() => {\n let disabled: PopupWindowDisabledType[] = [];\n\n if (typeof props.disabled === 'boolean') disabled = props.disabled ? ['onEscape', 'onLayer'] : [];\n else disabled = props.disabled ?? [];\n\n ctx.updateNodeProperty(props.id, 'disabled', disabled);\n }, [props.disabled]);\n\n\n\n // Mount & register node\n useEffect(() => {\n const container = ctx.containerRef.current;\n if (!container) return;\n\n setContainer(container);\n\n ctx.registerNode({\n id: props.id,\n isOpen: Boolean(props.isOpen),\n disabled\n });\n }, []);\n\n // Handle node sync with context\n useEffect(() => {\n const node = ctx.nodes.find(el => el.id === props.id);\n if (!node) return;\n\n setIsOpen(node.isOpen);\n setZIndex(node.zIndex);\n setDisabled(node.disabled);\n }, [ctx.nodes]);\n\n // Handle layer z-index change\n useEffect(() => {\n const layer = layerRef.current;\n if (!layer) return;\n\n if (isOpen) layer.style.zIndex = `${zIndex}`;\n else setTimeout(() => {\n layer.style.zIndex = `${-1}`;\n }, animationDuration);\n }, [isOpen]);\n\n // Handle events (onBeforeEnter, etc)\n useEffect(() => {\n if (isOpen) {\n if (props.onBeforeEnter) props.onBeforeEnter();\n\n setTimeout(() => {\n if (props.onAfterEnter) props.onAfterEnter();\n }, animationDuration);\n } else {\n if (props.onBeforeExit) props.onBeforeExit();\n\n setTimeout(() => {\n if (props.onAfterExit) props.onAfterExit();\n }, animationDuration);\n }\n }, [isOpen]);\n\n\n\n function layerOnClick() {\n if (disabled.includes('onLayer')) return;\n\n ctx.invokePopup(props.id, false);\n }\n\n\n\n return container && createPortal(<section\n className={cn(`neko-popup-backdrop`, isOpen && 'neko-popup-backdrop--active', props.layerClassName)}\n aria-hidden={!isOpen}\n style={{ transition: `${animationDuration}ms ease-in-out`, cursor: disabled.includes('onLayer') ? 'default' : 'pointer' }}\n onClick={layerOnClick}\n ref={layerRef}\n >\n <article\n id={props.id}\n className={cn(`neko-popup`, isOpen && 'neko-popup--active', animation && `neko-popup--animation_${animation}`, props.className)}\n role=\"dialog\"\n aria-modal\n onClick={e => e.stopPropagation()}\n >\n {props.children}\n </article>\n </section>, container);\n};\n\nexport default PopupWindow;"],"names":["EFKW","msg","r","t","f","n","o","clsx","PopupContext","createContext","cn","PopupLayer","props","nodes","setNodes","useState","isScrollDisabled","setIsScrollDisabled","containerRef","useRef","baseZIndex","disableBodyScrollOnActivePopup","useEffect","controller","e","maxZIndex","el","node","invokePopup","anyOpenNode","entityOrId","forceState","newState","_updateNodeInNodes","updateNodeProperty","id","key","value","prev","registerNode","isOpen","disabled","jsxs","jsx","PopupButton","ctx","useContext","isActive","setIsActive","Tag","useMixedState","initialStateOrValue","externalSetter","isControlled","useMemo","state","setter","PopupWindow","container","setContainer","setIsOpen","zIndex","setZIndex","setDisabled","layerRef","animation","animationDuration","useLayoutEffect","layer","layerOnClick","createPortal"],"mappings":"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","x_google_ignoreList":[1]}
1
+ {"version":3,"file":"index.es.js","sources":["../node_modules/clsx/dist/clsx.mjs","../src/_package/Interfaces.ts","../src/_package/PopupButton.tsx","../src/_package/components/ErrorComponents.ts","../src/_package/PopupLayer.tsx","../src/_package/hooks/useMixedState.ts","../src/_package/PopupWindow.tsx"],"sourcesContent":["function r(e){var t,f,n=\"\";if(\"string\"==typeof e||\"number\"==typeof e)n+=e;else if(\"object\"==typeof e)if(Array.isArray(e)){var o=e.length;for(t=0;t<o;t++)e[t]&&(f=r(e[t]))&&(n&&(n+=\" \"),n+=f)}else for(f in e)e[f]&&(n&&(n+=\" \"),n+=f);return n}export function clsx(){for(var e,t,f=0,n=\"\",o=arguments.length;f<o;f++)(e=arguments[f])&&(t=r(e))&&(n&&(n+=\" \"),n+=t);return n}export default clsx;","import React, { createContext, RefObject } from 'react';\nimport clsx from 'clsx';\n\n\n\n\nexport type PopupWindowDisabledType = 'onEscape' | 'onLayer';\nexport type StateSetter<S = any> = React.Dispatch<React.SetStateAction<S>>\nexport type MountNodeArgs = Pick<IPopupNode, 'id' | 'isOpen' | 'disabled'>\n\n/** Get value type from nested object path */\nexport type ValueFromPath<T, P> =\n P extends `${infer K}.${infer R}`\n ? K extends keyof T\n ? R extends keyof T[K]\n ? T[K][R]\n : never\n : never\n : P extends keyof T\n ? T[P]\n : never;\n\n\n\nexport interface IPopupNode {\n id: string\n isOpen: boolean\n zIndex: number\n disabled: PopupWindowDisabledType[]\n}\n\nexport interface IPopupContext {\n nodes: IPopupNode[]\n containerRef: RefObject<HTMLDivElement | null>\n\n invokePopup(id: string, forceState?: boolean): void\n mountNode(args: MountNodeArgs): void\n updateNodeProperty<K extends keyof IPopupNode>(id: string, key: K, value: ValueFromPath<IPopupNode, K>): void\n}\n\n\n\nexport const PopupContext = createContext<IPopupContext>({} as IPopupContext);\nexport const cn = clsx;","'use client';\n\nimport { FC, JSX, ReactNode, useContext, useEffect, useState } from 'react';\n\nimport { cn, PopupContext } from './Interfaces';\n\n\n\nexport interface IPopupButtonProps {\n popupId: string\n\n /** \n * Element tag\n * \n * @default \"button\"\n */\n as?: 'button' | 'div'\n\n disabled?: boolean\n children?: ReactNode | ReactNode[]\n className?: string\n id?: string\n\n onClick?(e: React.MouseEvent): void\n}\n\n\n\nexport const PopupButton: FC<IPopupButtonProps> = (props) => {\n const ctx = useContext(PopupContext);\n\n const [isActive, setIsActive] = useState(false);\n\n const Tag: keyof JSX.IntrinsicElements = props.as ?? 'button';\n\n\n\n // Handle isActive on context change\n useEffect(() => {\n const node = ctx.nodes.find(el => el.id === props.popupId);\n if (!node) return;\n\n setIsActive(node.isOpen);\n }, [ctx]);\n\n\n\n function invokePopup(e: React.MouseEvent) {\n ctx.invokePopup(props.popupId);\n\n if (props.onClick) props.onClick(e);\n }\n\n\n\n return <Tag\n tabIndex={0}\n disabled={props.disabled}\n aria-disabled={props.disabled}\n aria-haspopup={'dialog'}\n id={props.id}\n className={cn(`neko-popup-button`, isActive && 'neko-popup-button--active', props.className)}\n onClick={invokePopup}\n >\n {props.children}\n </Tag>;\n};","class EFKW extends Error {\n constructor(msg: string) {\n super(msg);\n\n this.name = 'error at [neko-popup]';\n }\n}\n\nexport default EFKW;","'use client';\n\nimport { FC, ReactNode, useEffect, useRef, useState } from 'react';\n\nimport EFKW from './components/ErrorComponents';\nimport { cn, IPopupNode, MountNodeArgs, PopupContext, ValueFromPath } from './Interfaces';\n\n\n\nexport interface IPopupLayerProps {\n children?: ReactNode | ReactNode[]\n\n /** @default 10000 */\n baseZIndex?: number\n\n /**\n * Disable body scroll when there is at least one open popup\n * \n * @default true\n */\n disableBodyScrollOnActivePopup?: boolean\n}\n\n\n\nexport const PopupLayer: FC<IPopupLayerProps> = (props) => {\n const baseZIndex = props.baseZIndex ?? 10000;\n const disableBodyScrollOnActivePopup = props.disableBodyScrollOnActivePopup ?? true;\n\n const [nodes, setNodes] = useState<IPopupNode[]>([]);\n const [isScrollDisabled, setIsScrollDisabled] = useState(false);\n const [isAnyPopupActive, setIsAnyPopupActive] = useState(false);\n\n const containerRef = useRef<HTMLDivElement>(null);\n\n\n\n // Handle close closest to user popup on escape & scroll\n useEffect(() => {\n // === Handle close closest popup on escape\n const controller = new AbortController();\n\n window.addEventListener('keydown', e => {\n const key = e.key;\n\n if (key === 'Escape') {\n const maxZIndex = Math.max(...nodes.filter(el => el.isOpen).map(el => el.zIndex));\n const node = nodes.find(el => el.zIndex === maxZIndex);\n if (!node || node.disabled.includes('onEscape')) return;\n\n // eslint-disable-next-line\n invokePopup(node.id, false);\n }\n }, { signal: controller.signal });\n\n\n\n // === Disable body scroll on active popup\n if (disableBodyScrollOnActivePopup) {\n let anyOpenNode = false;\n nodes.forEach(el => el.isOpen ? anyOpenNode = true : null);\n\n setIsScrollDisabled(anyOpenNode);\n }\n\n\n\n return () => {\n controller.abort();\n };\n }, [nodes]);\n\n // Handle isAnyPopupActive\n useEffect(() => {\n setIsAnyPopupActive(nodes.some(el => el.isOpen));\n }, [nodes]);\n\n // Handle body scroll\n useEffect(() => {\n if (isScrollDisabled) document.body.classList.add('neko-popup--noScroll');\n else document.body.classList.remove('neko-popup--noScroll');\n }, [isScrollDisabled]);\n\n\n\n /** Toggle popup state */\n function invokePopup(entityOrId: string | IPopupNode, forceState?: boolean) {\n const node = typeof entityOrId === 'string' ? nodes.find(el => el.id === entityOrId) : entityOrId;\n if (!node) throw new EFKW(typeof entityOrId === 'string' ? `Cannot find popup node with id #${entityOrId}` : `Entity is not assigned to the node`);\n\n const newState = forceState ?? !node.isOpen;\n\n // === Update node\n node.isOpen = newState;\n node.zIndex = newState ? Math.max(...nodes.map(el => el.zIndex), 0) + 1 : -1; // Make new popup invocation closer to user using larger z-index\n\n _updateNode(node);\n }\n\n /** Update node property */\n function updateNodeProperty<K extends keyof IPopupNode>(id: string, key: K, value: ValueFromPath<IPopupNode, K>) {\n const node = nodes.find(el => el.id === id);\n if (!node) return;\n\n node[key] = value;\n\n _updateNode(node);\n }\n\n /** Update node in nodes array */\n function _updateNode(node: IPopupNode) {\n setNodes(prev => [...prev.filter(el => el.id !== node.id), node]);\n }\n\n /** Add new popup window to state */\n const mountNode = ({ id, isOpen, disabled }: MountNodeArgs) => invokePopup({ id, isOpen: false, disabled, zIndex: -1 }, isOpen);\n\n\n\n return <PopupContext.Provider value={{\n nodes,\n containerRef,\n invokePopup,\n mountNode,\n updateNodeProperty\n }}>\n {props.children}\n\n <section className={cn(`neko-popup-layer`, isAnyPopupActive && 'neko-popup-layer--active')} style={{ zIndex: baseZIndex }} ref={containerRef} />\n </PopupContext.Provider>;\n};","'use client';\n\nimport { useEffect, useMemo, useState } from 'react';\n\n\n\n\ntype StateSetter<S> = React.Dispatch<React.SetStateAction<S>>;\ntype InitialState<S> = S | (() => S);\n\nexport default function useMixedState<S = undefined>(): [S | undefined, StateSetter<S | undefined>];\nexport default function useMixedState<S>(initialState: InitialState<S>): [S, StateSetter<S>];\nexport default function useMixedState<S>(state: S, setter?: StateSetter<S>): [S, StateSetter<S>];\n\n\n\n/** \n * Use mixed state hook\n * \n * @param initialStateOrValue Initial state, can be undefined, value or function. If externalSetter not specified, will return default state\n * @param externalSetter External state setter. If specified will return external state\n */\nexport default function useMixedState<S>(initialStateOrValue?: InitialState<S>, externalSetter?: StateSetter<S>) {\n const isControlled = useMemo(() => arguments.length === 2 && externalSetter !== undefined, []);\n\n const [state, setter] = useState<S | undefined>(initialStateOrValue);\n\n\n\n // Propagate state update on external state update even if external setter is not provided\n useEffect(() => {\n if (!isControlled) {\n \n setter(initialStateOrValue);\n }\n }, [initialStateOrValue]);\n\n\n\n if (isControlled) {\n return [\n initialStateOrValue as S,\n externalSetter as StateSetter<S>\n ];\n }\n\n return [state, setter];\n}","'use client';\n\nimport { FC, ReactNode, useContext, useEffect, useLayoutEffect, useRef, useState } from 'react';\nimport { createPortal } from 'react-dom';\n\nimport useMixedState from './hooks/useMixedState';\nimport { cn, PopupContext, PopupWindowDisabledType, StateSetter } from './Interfaces';\n\n\n\nexport type PopupWindowAnimationType = 'fade' | 'scale' | null\n\n\n\nexport interface IPopupWindowProps {\n id: string\n children: ReactNode | ReactNode[]\n\n isOpen?: boolean\n setIsOpen?: StateSetter<boolean>\n\n className?: string\n layerClassName?: string\n disabled?: PopupWindowDisabledType[] | boolean\n\n /** \n * Popup dialog animation type\n * \n * @default \"fade\"\n */\n animation?: 'fade' | 'scale' | null\n\n /**\n * Popup animation duration in msec\n * \n * @default 200\n */\n animationDuraionMs?: number\n\n /** \n * Fire callback when popup invoked to open\n */\n onBeforeEnter?(): void\n\n /** \n * Fire callback when popup open animation fullfilled. \n * \n * @see animationDuration\n */\n onAfterEnter?(): void\n\n /** \n * Fire callback when popup invoked to close\n */\n onBeforeExit?(): void\n\n /** \n * Fire callback when popup close animation fullfilled. \n * \n * @see animationDuration\n */\n onAfterExit?(): void\n}\n\n\n\nexport const PopupWindow: FC<IPopupWindowProps> = (props) => {\n const ctx = useContext(PopupContext);\n\n const [container, setContainer] = useState<HTMLDivElement | null>(null);\n const [isOpen, setIsOpen] = useMixedState(props.isOpen ?? false, props.setIsOpen);\n const [zIndex, setZIndex] = useState(-1);\n const [disabled, setDisabled] = useState<PopupWindowDisabledType[]>([]);\n\n const layerRef = useRef<HTMLDivElement>(null);\n\n const animation: PopupWindowAnimationType = props.animation !== undefined ? props.animation : 'fade';\n const animationDuration = props.animationDuraionMs ?? 200;\n\n\n\n // Handle disabled\n useLayoutEffect(() => {\n let disabled: PopupWindowDisabledType[] = [];\n\n if (typeof props.disabled === 'boolean') disabled = props.disabled ? ['onEscape', 'onLayer'] : [];\n else disabled = props.disabled ?? [];\n\n ctx.updateNodeProperty(props.id, 'disabled', disabled);\n }, [props.disabled]);\n\n\n\n // Mount\n useEffect(() => {\n const container = ctx.containerRef.current;\n if (!container) return;\n\n setContainer(container);\n\n ctx.mountNode({\n id: props.id,\n isOpen: Boolean(props.isOpen),\n disabled\n });\n }, []);\n\n // Handle toggle node on external isOpen update\n useEffect(() => {\n // Skip if node is not mounted yet\n if (!ctx.nodes.some(el => el.id === props.id)) return;\n\n ctx.invokePopup(props.id, props.isOpen);\n }, [props.isOpen]);\n\n // Handle node sync with context\n useEffect(() => {\n const node = ctx.nodes.find(el => el.id === props.id);\n if (!node) return;\n\n setIsOpen(node.isOpen);\n setZIndex(node.zIndex);\n setDisabled(node.disabled);\n }, [ctx.nodes]);\n\n // Handle layer z-index change\n useEffect(() => {\n const layer = layerRef.current;\n if (!layer) return;\n\n if (isOpen) layer.style.zIndex = `${zIndex}`;\n else setTimeout(() => {\n layer.style.zIndex = `${-1}`;\n }, animationDuration);\n }, [isOpen]);\n\n // Handle events (onBeforeEnter, etc)\n useEffect(() => {\n if (isOpen) {\n if (props.onBeforeEnter) props.onBeforeEnter();\n\n setTimeout(() => {\n if (props.onAfterEnter) props.onAfterEnter();\n }, animationDuration);\n } else {\n if (props.onBeforeExit) props.onBeforeExit();\n\n setTimeout(() => {\n if (props.onAfterExit) props.onAfterExit();\n }, animationDuration);\n }\n }, [isOpen]);\n\n\n\n function layerOnClick() {\n if (disabled.includes('onLayer')) return;\n\n ctx.invokePopup(props.id, false);\n }\n\n\n\n return container && createPortal(<section\n className={cn(`neko-popup-backdrop`, isOpen && 'neko-popup-backdrop--active', props.layerClassName)}\n aria-hidden={!isOpen}\n style={{ transition: `${animationDuration}ms ease-in-out`, cursor: disabled.includes('onLayer') ? 'default' : 'pointer' }}\n onClick={layerOnClick}\n ref={layerRef}\n >\n <article\n id={props.id}\n className={cn(`neko-popup`, isOpen && 'neko-popup--active', animation && `neko-popup--animation_${animation}`, props.className)}\n role=\"dialog\"\n aria-modal\n onClick={e => e.stopPropagation()}\n >\n {props.children}\n </article>\n </section>, container);\n};"],"names":["r","t","f","n","o","clsx","PopupContext","createContext","cn","PopupButton","props","ctx","useContext","isActive","setIsActive","useState","Tag","useEffect","node","el","invokePopup","e","jsx","EFKW","msg","PopupLayer","baseZIndex","disableBodyScrollOnActivePopup","nodes","setNodes","isScrollDisabled","setIsScrollDisabled","isAnyPopupActive","setIsAnyPopupActive","containerRef","useRef","controller","maxZIndex","anyOpenNode","entityOrId","forceState","newState","_updateNode","updateNodeProperty","id","key","value","prev","mountNode","isOpen","disabled","jsxs","useMixedState","initialStateOrValue","externalSetter","isControlled","useMemo","state","setter","PopupWindow","container","setContainer","setIsOpen","zIndex","setZIndex","setDisabled","layerRef","animation","animationDuration","useLayoutEffect","layer","layerOnClick","createPortal"],"mappings":"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","x_google_ignoreList":[0]}
package/dist/index.umd.js CHANGED
@@ -1,2 +1,2 @@
1
- (function(c,l){typeof exports=="object"&&typeof module<"u"?l(exports,require("react/jsx-runtime"),require("react"),require("react-dom")):typeof define=="function"&&define.amd?define(["exports","react/jsx-runtime","react","react-dom"],l):(c=typeof globalThis<"u"?globalThis:c||self,l(c["neko-popup"]={},c.jsxRuntime,c.React,c.ReactDOM))})(this,(function(c,l,o,g){"use strict";class C extends Error{constructor(n){super(n),this.name="error at [neko-popup]"}}function I(e){var n,d,i="";if(typeof e=="string"||typeof e=="number")i+=e;else if(typeof e=="object")if(Array.isArray(e)){var a=e.length;for(n=0;n<a;n++)e[n]&&(d=I(e[n]))&&(i&&(i+=" "),i+=d)}else for(d in e)e[d]&&(i&&(i+=" "),i+=d);return i}function N(){for(var e,n,d=0,i="",a=arguments.length;d<a;d++)(e=arguments[d])&&(n=I(e))&&(i&&(i+=" "),i+=n);return i}const h=o.createContext({}),v=N,S=e=>{const[n,d]=o.useState([]),[i,a]=o.useState(!1),x=o.useRef(null),p=e.baseZIndex??1e4,y=e.disableBodyScrollOnActivePopup??!0;o.useEffect(()=>{const s=new AbortController;if(window.addEventListener("keydown",u=>{if(u.key==="Escape"){const f=Math.max(...n.filter(k=>k.isOpen).map(k=>k.zIndex)),r=n.find(k=>k.zIndex===f);if(!r||r.disabled.includes("onEscape"))return;m(r.id,!1)}},{signal:s.signal}),y){let u=!1;n.forEach(t=>t.isOpen?u=!0:null),a(u)}return()=>{s.abort()}},[n]),o.useEffect(()=>{i?document.body.classList.add("neko-popup--noScroll"):document.body.classList.remove("neko-popup--noScroll")},[i]);function m(s,u){const t=typeof s=="string"?n.find(r=>r.id===s):s;if(!t)throw new C(typeof s=="string"?`Cannot find popup node with id #${s}`:"Entity is not assigned to the node");const f=u??!t.isOpen;t.isOpen=f,t.zIndex=f?Math.max(...n.map(r=>r.zIndex),0)+1:-1,b(t)}function P(s,u,t){const f=n.find(r=>r.id===s);f&&(f[u]=t,b(f))}function b(s){d(u=>[...u.filter(t=>t.id!==s.id),s])}const E=({id:s,isOpen:u,disabled:t})=>m({id:s,isOpen:!1,disabled:t,zIndex:-1},u);return l.jsxs(h.Provider,{value:{nodes:n,containerRef:x,invokePopup:m,registerNode:E,updateNodeProperty:P},children:[e.children,l.jsx("section",{style:{zIndex:p},ref:x})]})},O=e=>{const n=o.useContext(h),[d,i]=o.useState(!1),a=e.as??"button";o.useEffect(()=>{const p=n.nodes.find(y=>y.id===e.popupId);p&&i(p.isOpen)},[n]);function x(p){n.invokePopup(e.popupId),e.onClick&&e.onClick(p)}return l.jsx(a,{tabIndex:0,disabled:e.disabled,"aria-disabled":e.disabled,"aria-haspopup":"dialog",id:e.id,className:v("neko-popup-button",d&&"neko-popup-button--active",e.className),onClick:x,children:e.children})};function A(e,n){const d=o.useMemo(()=>arguments.length===2&&n!==void 0,[]),[i,a]=o.useState(e);return o.useEffect(()=>{d||a(e)},[e]),d?[e,n]:[i,a]}const w=e=>{const n=o.useContext(h),[d,i]=o.useState(null),[a,x]=A(e.isOpen??!1,e.setIsOpen),[p,y]=o.useState(-1),[m,P]=o.useState([]),b=o.useRef(null),E=e.animation!==void 0?e.animation:"fade",s=e.animationDuraionMs??200;o.useLayoutEffect(()=>{let t=[];typeof e.disabled=="boolean"?t=e.disabled?["onEscape","onLayer"]:[]:t=e.disabled??[],n.updateNodeProperty(e.id,"disabled",t)},[e.disabled]),o.useEffect(()=>{const t=n.containerRef.current;t&&(i(t),n.registerNode({id:e.id,isOpen:!!e.isOpen,disabled:m}))},[]),o.useEffect(()=>{const t=n.nodes.find(f=>f.id===e.id);t&&(x(t.isOpen),y(t.zIndex),P(t.disabled))},[n.nodes]),o.useEffect(()=>{const t=b.current;t&&(a?t.style.zIndex=`${p}`:setTimeout(()=>{t.style.zIndex="-1"},s))},[a]),o.useEffect(()=>{a?(e.onBeforeEnter&&e.onBeforeEnter(),setTimeout(()=>{e.onAfterEnter&&e.onAfterEnter()},s)):(e.onBeforeExit&&e.onBeforeExit(),setTimeout(()=>{e.onAfterExit&&e.onAfterExit()},s))},[a]);function u(){m.includes("onLayer")||n.invokePopup(e.id,!1)}return d&&g.createPortal(l.jsx("section",{className:v("neko-popup-backdrop",a&&"neko-popup-backdrop--active",e.layerClassName),"aria-hidden":!a,style:{transition:`${s}ms ease-in-out`,cursor:m.includes("onLayer")?"default":"pointer"},onClick:u,ref:b,children:l.jsx("article",{id:e.id,className:v("neko-popup",a&&"neko-popup--active",E&&`neko-popup--animation_${E}`,e.className),role:"dialog","aria-modal":!0,onClick:t=>t.stopPropagation(),children:e.children})}),d)};c.PopupButton=O,c.PopupLayer=S,c.PopupWindow=w,Object.defineProperty(c,Symbol.toStringTag,{value:"Module"})}));
1
+ (function(c,f){typeof exports=="object"&&typeof module<"u"?f(exports,require("react/jsx-runtime"),require("react"),require("react-dom")):typeof define=="function"&&define.amd?define(["exports","react/jsx-runtime","react","react-dom"],f):(c=typeof globalThis<"u"?globalThis:c||self,f(c["neko-popup"]={},c.jsxRuntime,c.React,c.ReactDOM))})(this,(function(c,f,i,C){"use strict";function A(e){var o,s,t="";if(typeof e=="string"||typeof e=="number")t+=e;else if(typeof e=="object")if(Array.isArray(e)){var d=e.length;for(o=0;o<d;o++)e[o]&&(s=A(e[o]))&&(t&&(t+=" "),t+=s)}else for(s in e)e[s]&&(t&&(t+=" "),t+=s);return t}function O(){for(var e,o,s=0,t="",d=arguments.length;s<d;s++)(e=arguments[s])&&(o=A(e))&&(t&&(t+=" "),t+=o);return t}const I=i.createContext({}),v=O,N=e=>{const o=i.useContext(I),[s,t]=i.useState(!1),d=e.as??"button";i.useEffect(()=>{const r=o.nodes.find(b=>b.id===e.popupId);r&&t(r.isOpen)},[o]);function y(r){o.invokePopup(e.popupId),e.onClick&&e.onClick(r)}return f.jsx(d,{tabIndex:0,disabled:e.disabled,"aria-disabled":e.disabled,"aria-haspopup":"dialog",id:e.id,className:v("neko-popup-button",s&&"neko-popup-button--active",e.className),onClick:y,children:e.children})};class S extends Error{constructor(o){super(o),this.name="error at [neko-popup]"}}const w=e=>{const o=e.baseZIndex??1e4,s=e.disableBodyScrollOnActivePopup??!0,[t,d]=i.useState([]),[y,r]=i.useState(!1),[b,k]=i.useState(!1),h=i.useRef(null);i.useEffect(()=>{const n=new AbortController;if(window.addEventListener("keydown",u=>{if(u.key==="Escape"){const p=Math.max(...t.filter(E=>E.isOpen).map(E=>E.zIndex)),l=t.find(E=>E.zIndex===p);if(!l||l.disabled.includes("onEscape"))return;x(l.id,!1)}},{signal:n.signal}),s){let u=!1;t.forEach(a=>a.isOpen?u=!0:null),r(u)}return()=>{n.abort()}},[t]),i.useEffect(()=>{k(t.some(n=>n.isOpen))},[t]),i.useEffect(()=>{y?document.body.classList.add("neko-popup--noScroll"):document.body.classList.remove("neko-popup--noScroll")},[y]);function x(n,u){const a=typeof n=="string"?t.find(l=>l.id===n):n;if(!a)throw new S(typeof n=="string"?`Cannot find popup node with id #${n}`:"Entity is not assigned to the node");const p=u??!a.isOpen;a.isOpen=p,a.zIndex=p?Math.max(...t.map(l=>l.zIndex),0)+1:-1,m(a)}function P(n,u,a){const p=t.find(l=>l.id===n);p&&(p[u]=a,m(p))}function m(n){d(u=>[...u.filter(a=>a.id!==n.id),n])}const g=({id:n,isOpen:u,disabled:a})=>x({id:n,isOpen:!1,disabled:a,zIndex:-1},u);return f.jsxs(I.Provider,{value:{nodes:t,containerRef:h,invokePopup:x,mountNode:g,updateNodeProperty:P},children:[e.children,f.jsx("section",{className:v("neko-popup-layer",b&&"neko-popup-layer--active"),style:{zIndex:o},ref:h})]})};function z(e,o){const s=i.useMemo(()=>arguments.length===2&&o!==void 0,[]),[t,d]=i.useState(e);return i.useEffect(()=>{s||d(e)},[e]),s?[e,o]:[t,d]}const B=e=>{const o=i.useContext(I),[s,t]=i.useState(null),[d,y]=z(e.isOpen??!1,e.setIsOpen),[r,b]=i.useState(-1),[k,h]=i.useState([]),x=i.useRef(null),P=e.animation!==void 0?e.animation:"fade",m=e.animationDuraionMs??200;i.useLayoutEffect(()=>{let n=[];typeof e.disabled=="boolean"?n=e.disabled?["onEscape","onLayer"]:[]:n=e.disabled??[],o.updateNodeProperty(e.id,"disabled",n)},[e.disabled]),i.useEffect(()=>{const n=o.containerRef.current;n&&(t(n),o.mountNode({id:e.id,isOpen:!!e.isOpen,disabled:k}))},[]),i.useEffect(()=>{o.nodes.some(n=>n.id===e.id)&&o.invokePopup(e.id,e.isOpen)},[e.isOpen]),i.useEffect(()=>{const n=o.nodes.find(u=>u.id===e.id);n&&(y(n.isOpen),b(n.zIndex),h(n.disabled))},[o.nodes]),i.useEffect(()=>{const n=x.current;n&&(d?n.style.zIndex=`${r}`:setTimeout(()=>{n.style.zIndex="-1"},m))},[d]),i.useEffect(()=>{d?(e.onBeforeEnter&&e.onBeforeEnter(),setTimeout(()=>{e.onAfterEnter&&e.onAfterEnter()},m)):(e.onBeforeExit&&e.onBeforeExit(),setTimeout(()=>{e.onAfterExit&&e.onAfterExit()},m))},[d]);function g(){k.includes("onLayer")||o.invokePopup(e.id,!1)}return s&&C.createPortal(f.jsx("section",{className:v("neko-popup-backdrop",d&&"neko-popup-backdrop--active",e.layerClassName),"aria-hidden":!d,style:{transition:`${m}ms ease-in-out`,cursor:k.includes("onLayer")?"default":"pointer"},onClick:g,ref:x,children:f.jsx("article",{id:e.id,className:v("neko-popup",d&&"neko-popup--active",P&&`neko-popup--animation_${P}`,e.className),role:"dialog","aria-modal":!0,onClick:n=>n.stopPropagation(),children:e.children})}),s)};c.PopupButton=N,c.PopupLayer=w,c.PopupWindow=B,Object.defineProperty(c,Symbol.toStringTag,{value:"Module"})}));
2
2
  //# sourceMappingURL=index.umd.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.umd.js","sources":["../src/_package/components/ErrorComponents.ts","../node_modules/clsx/dist/clsx.mjs","../src/_package/Interfaces.ts","../src/_package/PopupLayer.tsx","../src/_package/PopupButton.tsx","../src/_package/hooks/useMixedState.ts","../src/_package/PopupWindow.tsx"],"sourcesContent":["class EFKW extends Error {\n constructor(msg: string) {\n super(msg);\n\n this.name = 'error at [neko-popup]';\n }\n}\n\nexport default EFKW;","function r(e){var t,f,n=\"\";if(\"string\"==typeof e||\"number\"==typeof e)n+=e;else if(\"object\"==typeof e)if(Array.isArray(e)){var o=e.length;for(t=0;t<o;t++)e[t]&&(f=r(e[t]))&&(n&&(n+=\" \"),n+=f)}else for(f in e)e[f]&&(n&&(n+=\" \"),n+=f);return n}export function clsx(){for(var e,t,f=0,n=\"\",o=arguments.length;f<o;f++)(e=arguments[f])&&(t=r(e))&&(n&&(n+=\" \"),n+=t);return n}export default clsx;","import React, { createContext, RefObject } from 'react';\nimport clsx from 'clsx';\n\n\n\n\nexport type PopupWindowDisabledType = 'onEscape' | 'onLayer';\nexport type StateSetter<S = any> = React.Dispatch<React.SetStateAction<S>>\nexport type RegisterNodeArgs = Pick<IPopupNode, 'id' | 'isOpen' | 'disabled'>\n\n/** Get value type from nested object path */\nexport type ValueFromPath<T, P> =\n P extends `${infer K}.${infer R}`\n ? K extends keyof T\n ? R extends keyof T[K]\n ? T[K][R]\n : never\n : never\n : P extends keyof T\n ? T[P]\n : never;\n\n\n\nexport interface IPopupNode {\n id: string\n isOpen: boolean\n zIndex: number\n disabled: PopupWindowDisabledType[]\n}\n\nexport interface IPopupContext {\n nodes: IPopupNode[]\n containerRef: RefObject<HTMLDivElement | null>\n\n invokePopup(id: string, forceState?: boolean): void\n registerNode(args: RegisterNodeArgs): void\n updateNodeProperty<K extends keyof IPopupNode>(id: string, key: K, value: ValueFromPath<IPopupNode, K>): void\n}\n\n\n\nexport const PopupContext = createContext<IPopupContext>({} as IPopupContext);\nexport const cn = clsx;","'use client';\n\nimport { FC, ReactNode, useEffect, useRef, useState } from 'react';\n\nimport EFKW from './components/ErrorComponents';\nimport { IPopupNode, PopupContext, RegisterNodeArgs, ValueFromPath } from './Interfaces';\n\n\n\ninterface IPopupLayerProps {\n children?: ReactNode | ReactNode[]\n\n /** @default 10000 */\n baseZIndex?: number\n\n /**\n * Disable body scroll when there is at least one open popup\n * \n * @default true\n */\n disableBodyScrollOnActivePopup?: boolean\n}\n\n\n\nconst PopupLayer: FC<IPopupLayerProps> = (props) => {\n const [nodes, setNodes] = useState<IPopupNode[]>([]);\n const [isScrollDisabled, setIsScrollDisabled] = useState(false);\n\n const containerRef = useRef<HTMLDivElement>(null);\n\n const baseZIndex = props.baseZIndex ?? 10000;\n const disableBodyScrollOnActivePopup = props.disableBodyScrollOnActivePopup ?? true;\n\n\n\n // Handle close closest to user popup on escape & scroll\n useEffect(() => {\n // === Handle close closest popup on escape\n const controller = new AbortController();\n\n window.addEventListener('keydown', e => {\n const key = e.key;\n\n if (key === 'Escape') {\n const maxZIndex = Math.max(...nodes.filter(el => el.isOpen).map(el => el.zIndex));\n const node = nodes.find(el => el.zIndex === maxZIndex);\n if (!node || node.disabled.includes('onEscape')) return;\n\n // eslint-disable-next-line\n invokePopup(node.id, false);\n }\n }, { signal: controller.signal });\n\n\n\n // === Disable body scroll on active popup\n if (disableBodyScrollOnActivePopup) {\n let anyOpenNode = false;\n nodes.forEach(el => el.isOpen ? anyOpenNode = true : null);\n\n setIsScrollDisabled(anyOpenNode);\n }\n\n\n\n return () => {\n controller.abort();\n };\n }, [nodes]);\n\n // Handle body scroll\n useEffect(() => {\n if (isScrollDisabled) document.body.classList.add('neko-popup--noScroll');\n else document.body.classList.remove('neko-popup--noScroll');\n }, [isScrollDisabled]);\n\n\n\n /** Toggle popup state */\n function invokePopup(entityOrId: string | IPopupNode, forceState?: boolean) {\n const node = typeof entityOrId === 'string' ? nodes.find(el => el.id === entityOrId) : entityOrId;\n if (!node) throw new EFKW(typeof entityOrId === 'string' ? `Cannot find popup node with id #${entityOrId}` : `Entity is not assigned to the node`);\n\n const newState = forceState ?? !node.isOpen;\n\n // === Update node\n node.isOpen = newState;\n node.zIndex = newState ? Math.max(...nodes.map(el => el.zIndex), 0) + 1 : -1; // Make new popup invocation closer to user using larger z-index\n\n _updateNodeInNodes(node);\n }\n\n /** Update node property */\n function updateNodeProperty<K extends keyof IPopupNode>(id: string, key: K, value: ValueFromPath<IPopupNode, K>) {\n const node = nodes.find(el => el.id === id);\n if (!node) return;\n\n node[key] = value;\n\n _updateNodeInNodes(node);\n }\n\n /** Update node in nodes array */\n function _updateNodeInNodes(node: IPopupNode) {\n setNodes(prev => [...prev.filter(el => el.id !== node.id), node]);\n }\n\n /** Add new popup window to state */\n const registerNode = ({ id, isOpen, disabled }: RegisterNodeArgs) => invokePopup({ id, isOpen: false, disabled, zIndex: -1 }, isOpen);\n\n\n\n return <PopupContext.Provider value={{\n nodes,\n containerRef,\n invokePopup,\n registerNode,\n updateNodeProperty\n }}>\n {props.children}\n\n <section style={{ zIndex: baseZIndex }} ref={containerRef} />\n </PopupContext.Provider>;\n};\n\nexport default PopupLayer;","'use client';\n\nimport { FC, JSX, ReactNode, useContext, useEffect, useState } from 'react';\n\nimport { cn, PopupContext } from './Interfaces';\n\n\n\nexport interface IPopupButtonProps {\n popupId: string\n\n /** \n * Element tag\n * \n * @default \"button\"\n */\n as?: 'button' | 'div'\n\n disabled?: boolean\n children?: ReactNode | ReactNode[]\n className?: string\n id?: string\n\n onClick?(e: React.MouseEvent): void\n}\n\n\n\nconst PopupButton: FC<IPopupButtonProps> = (props) => {\n const ctx = useContext(PopupContext);\n\n const [isActive, setIsActive] = useState(false);\n\n const Tag: keyof JSX.IntrinsicElements = props.as ?? 'button';\n\n\n\n // Handle isActive on context change\n useEffect(() => {\n const node = ctx.nodes.find(el => el.id === props.popupId);\n if (!node) return;\n\n setIsActive(node.isOpen);\n }, [ctx]);\n\n\n\n function invokePopup(e: React.MouseEvent) {\n ctx.invokePopup(props.popupId);\n\n if (props.onClick) props.onClick(e);\n }\n\n\n\n return <Tag\n tabIndex={0}\n disabled={props.disabled}\n aria-disabled={props.disabled}\n aria-haspopup={'dialog'}\n id={props.id}\n className={cn(`neko-popup-button`, isActive && 'neko-popup-button--active', props.className)}\n onClick={invokePopup}\n >\n {props.children}\n </Tag>;\n};\n\nexport default PopupButton;","'use client';\n\nimport { useEffect, useMemo, useState } from 'react';\n\n\n\n\ntype StateSetter<S> = React.Dispatch<React.SetStateAction<S>>;\ntype InitialState<S> = S | (() => S);\n\nexport default function useMixedState<S = undefined>(): [S | undefined, StateSetter<S | undefined>];\nexport default function useMixedState<S>(initialState: InitialState<S>): [S, StateSetter<S>];\nexport default function useMixedState<S>(state: S, setter?: StateSetter<S>): [S, StateSetter<S>];\n\n\n\n/** \n * Use mixed state hook\n * \n * @param initialStateOrValue Initial state, can be undefined, value or function. If externalSetter not specified, will return default state\n * @param externalSetter External state setter. If specified will return external state\n */\nexport default function useMixedState<S>(initialStateOrValue?: InitialState<S>, externalSetter?: StateSetter<S>) {\n const isControlled = useMemo(() => arguments.length === 2 && externalSetter !== undefined, []);\n\n const [state, setter] = useState<S | undefined>(initialStateOrValue);\n\n\n\n // Propagate state update on external state update even if external setter is not provided\n useEffect(() => {\n if (!isControlled) {\n \n setter(initialStateOrValue);\n }\n }, [initialStateOrValue]);\n\n\n\n if (isControlled) {\n return [\n initialStateOrValue as S,\n externalSetter as StateSetter<S>\n ];\n }\n\n return [state, setter];\n}","'use client';\n\nimport { FC, ReactNode, useContext, useEffect, useLayoutEffect, useRef, useState } from 'react';\nimport { createPortal } from 'react-dom';\n\nimport useMixedState from './hooks/useMixedState';\nimport { cn, PopupContext, PopupWindowDisabledType, StateSetter } from './Interfaces';\n\n\n\ntype PopupWindowAnimationType = 'fade' | 'scale' | null\n\n\n\ninterface IPopupWindowProps {\n id: string\n children: ReactNode | ReactNode[]\n\n isOpen?: boolean\n setIsOpen?: StateSetter<boolean>\n\n className?: string\n layerClassName?: string\n disabled?: PopupWindowDisabledType[] | boolean\n\n /** \n * Popup dialog animation type\n * \n * @default \"fade\"\n */\n animation?: 'fade' | 'scale' | null\n\n /**\n * Popup animation duration in msec\n * \n * @default 200\n */\n animationDuraionMs?: number\n\n /** \n * Fire callback when popup invoked to open\n */\n onBeforeEnter?(): void\n\n /** \n * Fire callback when popup open animation fullfilled. \n * \n * @see animationDuration\n */\n onAfterEnter?(): void\n\n /** \n * Fire callback when popup invoked to close\n */\n onBeforeExit?(): void\n\n /** \n * Fire callback when popup close animation fullfilled. \n * \n * @see animationDuration\n */\n onAfterExit?(): void\n}\n\n\n\nconst PopupWindow: FC<IPopupWindowProps> = (props) => {\n const ctx = useContext(PopupContext);\n\n const [container, setContainer] = useState<HTMLDivElement | null>(null);\n const [isOpen, setIsOpen] = useMixedState(props.isOpen ?? false, props.setIsOpen);\n const [zIndex, setZIndex] = useState(-1);\n const [disabled, setDisabled] = useState<PopupWindowDisabledType[]>([]);\n\n const layerRef = useRef<HTMLDivElement>(null);\n\n const animation: PopupWindowAnimationType = props.animation !== undefined ? props.animation : 'fade';\n const animationDuration = props.animationDuraionMs ?? 200;\n\n\n\n // Handle disabled\n useLayoutEffect(() => {\n let disabled: PopupWindowDisabledType[] = [];\n\n if (typeof props.disabled === 'boolean') disabled = props.disabled ? ['onEscape', 'onLayer'] : [];\n else disabled = props.disabled ?? [];\n\n ctx.updateNodeProperty(props.id, 'disabled', disabled);\n }, [props.disabled]);\n\n\n\n // Mount & register node\n useEffect(() => {\n const container = ctx.containerRef.current;\n if (!container) return;\n\n setContainer(container);\n\n ctx.registerNode({\n id: props.id,\n isOpen: Boolean(props.isOpen),\n disabled\n });\n }, []);\n\n // Handle node sync with context\n useEffect(() => {\n const node = ctx.nodes.find(el => el.id === props.id);\n if (!node) return;\n\n setIsOpen(node.isOpen);\n setZIndex(node.zIndex);\n setDisabled(node.disabled);\n }, [ctx.nodes]);\n\n // Handle layer z-index change\n useEffect(() => {\n const layer = layerRef.current;\n if (!layer) return;\n\n if (isOpen) layer.style.zIndex = `${zIndex}`;\n else setTimeout(() => {\n layer.style.zIndex = `${-1}`;\n }, animationDuration);\n }, [isOpen]);\n\n // Handle events (onBeforeEnter, etc)\n useEffect(() => {\n if (isOpen) {\n if (props.onBeforeEnter) props.onBeforeEnter();\n\n setTimeout(() => {\n if (props.onAfterEnter) props.onAfterEnter();\n }, animationDuration);\n } else {\n if (props.onBeforeExit) props.onBeforeExit();\n\n setTimeout(() => {\n if (props.onAfterExit) props.onAfterExit();\n }, animationDuration);\n }\n }, [isOpen]);\n\n\n\n function layerOnClick() {\n if (disabled.includes('onLayer')) return;\n\n ctx.invokePopup(props.id, false);\n }\n\n\n\n return container && createPortal(<section\n className={cn(`neko-popup-backdrop`, isOpen && 'neko-popup-backdrop--active', props.layerClassName)}\n aria-hidden={!isOpen}\n style={{ transition: `${animationDuration}ms ease-in-out`, cursor: disabled.includes('onLayer') ? 'default' : 'pointer' }}\n onClick={layerOnClick}\n ref={layerRef}\n >\n <article\n id={props.id}\n className={cn(`neko-popup`, isOpen && 'neko-popup--active', animation && `neko-popup--animation_${animation}`, props.className)}\n role=\"dialog\"\n aria-modal\n onClick={e => e.stopPropagation()}\n >\n {props.children}\n </article>\n </section>, container);\n};\n\nexport default PopupWindow;"],"names":["EFKW","msg","r","t","f","n","o","clsx","PopupContext","createContext","cn","PopupLayer","props","nodes","setNodes","useState","isScrollDisabled","setIsScrollDisabled","containerRef","useRef","baseZIndex","disableBodyScrollOnActivePopup","useEffect","controller","e","maxZIndex","el","node","invokePopup","anyOpenNode","entityOrId","forceState","newState","_updateNodeInNodes","updateNodeProperty","id","key","value","prev","registerNode","isOpen","disabled","jsxs","jsx","PopupButton","ctx","useContext","isActive","setIsActive","Tag","useMixedState","initialStateOrValue","externalSetter","isControlled","useMemo","state","setter","PopupWindow","container","setContainer","setIsOpen","zIndex","setZIndex","setDisabled","layerRef","animation","animationDuration","useLayoutEffect","layer","layerOnClick","createPortal"],"mappings":"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","x_google_ignoreList":[1]}
1
+ {"version":3,"file":"index.umd.js","sources":["../node_modules/clsx/dist/clsx.mjs","../src/_package/Interfaces.ts","../src/_package/PopupButton.tsx","../src/_package/components/ErrorComponents.ts","../src/_package/PopupLayer.tsx","../src/_package/hooks/useMixedState.ts","../src/_package/PopupWindow.tsx"],"sourcesContent":["function r(e){var t,f,n=\"\";if(\"string\"==typeof e||\"number\"==typeof e)n+=e;else if(\"object\"==typeof e)if(Array.isArray(e)){var o=e.length;for(t=0;t<o;t++)e[t]&&(f=r(e[t]))&&(n&&(n+=\" \"),n+=f)}else for(f in e)e[f]&&(n&&(n+=\" \"),n+=f);return n}export function clsx(){for(var e,t,f=0,n=\"\",o=arguments.length;f<o;f++)(e=arguments[f])&&(t=r(e))&&(n&&(n+=\" \"),n+=t);return n}export default clsx;","import React, { createContext, RefObject } from 'react';\nimport clsx from 'clsx';\n\n\n\n\nexport type PopupWindowDisabledType = 'onEscape' | 'onLayer';\nexport type StateSetter<S = any> = React.Dispatch<React.SetStateAction<S>>\nexport type MountNodeArgs = Pick<IPopupNode, 'id' | 'isOpen' | 'disabled'>\n\n/** Get value type from nested object path */\nexport type ValueFromPath<T, P> =\n P extends `${infer K}.${infer R}`\n ? K extends keyof T\n ? R extends keyof T[K]\n ? T[K][R]\n : never\n : never\n : P extends keyof T\n ? T[P]\n : never;\n\n\n\nexport interface IPopupNode {\n id: string\n isOpen: boolean\n zIndex: number\n disabled: PopupWindowDisabledType[]\n}\n\nexport interface IPopupContext {\n nodes: IPopupNode[]\n containerRef: RefObject<HTMLDivElement | null>\n\n invokePopup(id: string, forceState?: boolean): void\n mountNode(args: MountNodeArgs): void\n updateNodeProperty<K extends keyof IPopupNode>(id: string, key: K, value: ValueFromPath<IPopupNode, K>): void\n}\n\n\n\nexport const PopupContext = createContext<IPopupContext>({} as IPopupContext);\nexport const cn = clsx;","'use client';\n\nimport { FC, JSX, ReactNode, useContext, useEffect, useState } from 'react';\n\nimport { cn, PopupContext } from './Interfaces';\n\n\n\nexport interface IPopupButtonProps {\n popupId: string\n\n /** \n * Element tag\n * \n * @default \"button\"\n */\n as?: 'button' | 'div'\n\n disabled?: boolean\n children?: ReactNode | ReactNode[]\n className?: string\n id?: string\n\n onClick?(e: React.MouseEvent): void\n}\n\n\n\nexport const PopupButton: FC<IPopupButtonProps> = (props) => {\n const ctx = useContext(PopupContext);\n\n const [isActive, setIsActive] = useState(false);\n\n const Tag: keyof JSX.IntrinsicElements = props.as ?? 'button';\n\n\n\n // Handle isActive on context change\n useEffect(() => {\n const node = ctx.nodes.find(el => el.id === props.popupId);\n if (!node) return;\n\n setIsActive(node.isOpen);\n }, [ctx]);\n\n\n\n function invokePopup(e: React.MouseEvent) {\n ctx.invokePopup(props.popupId);\n\n if (props.onClick) props.onClick(e);\n }\n\n\n\n return <Tag\n tabIndex={0}\n disabled={props.disabled}\n aria-disabled={props.disabled}\n aria-haspopup={'dialog'}\n id={props.id}\n className={cn(`neko-popup-button`, isActive && 'neko-popup-button--active', props.className)}\n onClick={invokePopup}\n >\n {props.children}\n </Tag>;\n};","class EFKW extends Error {\n constructor(msg: string) {\n super(msg);\n\n this.name = 'error at [neko-popup]';\n }\n}\n\nexport default EFKW;","'use client';\n\nimport { FC, ReactNode, useEffect, useRef, useState } from 'react';\n\nimport EFKW from './components/ErrorComponents';\nimport { cn, IPopupNode, MountNodeArgs, PopupContext, ValueFromPath } from './Interfaces';\n\n\n\nexport interface IPopupLayerProps {\n children?: ReactNode | ReactNode[]\n\n /** @default 10000 */\n baseZIndex?: number\n\n /**\n * Disable body scroll when there is at least one open popup\n * \n * @default true\n */\n disableBodyScrollOnActivePopup?: boolean\n}\n\n\n\nexport const PopupLayer: FC<IPopupLayerProps> = (props) => {\n const baseZIndex = props.baseZIndex ?? 10000;\n const disableBodyScrollOnActivePopup = props.disableBodyScrollOnActivePopup ?? true;\n\n const [nodes, setNodes] = useState<IPopupNode[]>([]);\n const [isScrollDisabled, setIsScrollDisabled] = useState(false);\n const [isAnyPopupActive, setIsAnyPopupActive] = useState(false);\n\n const containerRef = useRef<HTMLDivElement>(null);\n\n\n\n // Handle close closest to user popup on escape & scroll\n useEffect(() => {\n // === Handle close closest popup on escape\n const controller = new AbortController();\n\n window.addEventListener('keydown', e => {\n const key = e.key;\n\n if (key === 'Escape') {\n const maxZIndex = Math.max(...nodes.filter(el => el.isOpen).map(el => el.zIndex));\n const node = nodes.find(el => el.zIndex === maxZIndex);\n if (!node || node.disabled.includes('onEscape')) return;\n\n // eslint-disable-next-line\n invokePopup(node.id, false);\n }\n }, { signal: controller.signal });\n\n\n\n // === Disable body scroll on active popup\n if (disableBodyScrollOnActivePopup) {\n let anyOpenNode = false;\n nodes.forEach(el => el.isOpen ? anyOpenNode = true : null);\n\n setIsScrollDisabled(anyOpenNode);\n }\n\n\n\n return () => {\n controller.abort();\n };\n }, [nodes]);\n\n // Handle isAnyPopupActive\n useEffect(() => {\n setIsAnyPopupActive(nodes.some(el => el.isOpen));\n }, [nodes]);\n\n // Handle body scroll\n useEffect(() => {\n if (isScrollDisabled) document.body.classList.add('neko-popup--noScroll');\n else document.body.classList.remove('neko-popup--noScroll');\n }, [isScrollDisabled]);\n\n\n\n /** Toggle popup state */\n function invokePopup(entityOrId: string | IPopupNode, forceState?: boolean) {\n const node = typeof entityOrId === 'string' ? nodes.find(el => el.id === entityOrId) : entityOrId;\n if (!node) throw new EFKW(typeof entityOrId === 'string' ? `Cannot find popup node with id #${entityOrId}` : `Entity is not assigned to the node`);\n\n const newState = forceState ?? !node.isOpen;\n\n // === Update node\n node.isOpen = newState;\n node.zIndex = newState ? Math.max(...nodes.map(el => el.zIndex), 0) + 1 : -1; // Make new popup invocation closer to user using larger z-index\n\n _updateNode(node);\n }\n\n /** Update node property */\n function updateNodeProperty<K extends keyof IPopupNode>(id: string, key: K, value: ValueFromPath<IPopupNode, K>) {\n const node = nodes.find(el => el.id === id);\n if (!node) return;\n\n node[key] = value;\n\n _updateNode(node);\n }\n\n /** Update node in nodes array */\n function _updateNode(node: IPopupNode) {\n setNodes(prev => [...prev.filter(el => el.id !== node.id), node]);\n }\n\n /** Add new popup window to state */\n const mountNode = ({ id, isOpen, disabled }: MountNodeArgs) => invokePopup({ id, isOpen: false, disabled, zIndex: -1 }, isOpen);\n\n\n\n return <PopupContext.Provider value={{\n nodes,\n containerRef,\n invokePopup,\n mountNode,\n updateNodeProperty\n }}>\n {props.children}\n\n <section className={cn(`neko-popup-layer`, isAnyPopupActive && 'neko-popup-layer--active')} style={{ zIndex: baseZIndex }} ref={containerRef} />\n </PopupContext.Provider>;\n};","'use client';\n\nimport { useEffect, useMemo, useState } from 'react';\n\n\n\n\ntype StateSetter<S> = React.Dispatch<React.SetStateAction<S>>;\ntype InitialState<S> = S | (() => S);\n\nexport default function useMixedState<S = undefined>(): [S | undefined, StateSetter<S | undefined>];\nexport default function useMixedState<S>(initialState: InitialState<S>): [S, StateSetter<S>];\nexport default function useMixedState<S>(state: S, setter?: StateSetter<S>): [S, StateSetter<S>];\n\n\n\n/** \n * Use mixed state hook\n * \n * @param initialStateOrValue Initial state, can be undefined, value or function. If externalSetter not specified, will return default state\n * @param externalSetter External state setter. If specified will return external state\n */\nexport default function useMixedState<S>(initialStateOrValue?: InitialState<S>, externalSetter?: StateSetter<S>) {\n const isControlled = useMemo(() => arguments.length === 2 && externalSetter !== undefined, []);\n\n const [state, setter] = useState<S | undefined>(initialStateOrValue);\n\n\n\n // Propagate state update on external state update even if external setter is not provided\n useEffect(() => {\n if (!isControlled) {\n \n setter(initialStateOrValue);\n }\n }, [initialStateOrValue]);\n\n\n\n if (isControlled) {\n return [\n initialStateOrValue as S,\n externalSetter as StateSetter<S>\n ];\n }\n\n return [state, setter];\n}","'use client';\n\nimport { FC, ReactNode, useContext, useEffect, useLayoutEffect, useRef, useState } from 'react';\nimport { createPortal } from 'react-dom';\n\nimport useMixedState from './hooks/useMixedState';\nimport { cn, PopupContext, PopupWindowDisabledType, StateSetter } from './Interfaces';\n\n\n\nexport type PopupWindowAnimationType = 'fade' | 'scale' | null\n\n\n\nexport interface IPopupWindowProps {\n id: string\n children: ReactNode | ReactNode[]\n\n isOpen?: boolean\n setIsOpen?: StateSetter<boolean>\n\n className?: string\n layerClassName?: string\n disabled?: PopupWindowDisabledType[] | boolean\n\n /** \n * Popup dialog animation type\n * \n * @default \"fade\"\n */\n animation?: 'fade' | 'scale' | null\n\n /**\n * Popup animation duration in msec\n * \n * @default 200\n */\n animationDuraionMs?: number\n\n /** \n * Fire callback when popup invoked to open\n */\n onBeforeEnter?(): void\n\n /** \n * Fire callback when popup open animation fullfilled. \n * \n * @see animationDuration\n */\n onAfterEnter?(): void\n\n /** \n * Fire callback when popup invoked to close\n */\n onBeforeExit?(): void\n\n /** \n * Fire callback when popup close animation fullfilled. \n * \n * @see animationDuration\n */\n onAfterExit?(): void\n}\n\n\n\nexport const PopupWindow: FC<IPopupWindowProps> = (props) => {\n const ctx = useContext(PopupContext);\n\n const [container, setContainer] = useState<HTMLDivElement | null>(null);\n const [isOpen, setIsOpen] = useMixedState(props.isOpen ?? false, props.setIsOpen);\n const [zIndex, setZIndex] = useState(-1);\n const [disabled, setDisabled] = useState<PopupWindowDisabledType[]>([]);\n\n const layerRef = useRef<HTMLDivElement>(null);\n\n const animation: PopupWindowAnimationType = props.animation !== undefined ? props.animation : 'fade';\n const animationDuration = props.animationDuraionMs ?? 200;\n\n\n\n // Handle disabled\n useLayoutEffect(() => {\n let disabled: PopupWindowDisabledType[] = [];\n\n if (typeof props.disabled === 'boolean') disabled = props.disabled ? ['onEscape', 'onLayer'] : [];\n else disabled = props.disabled ?? [];\n\n ctx.updateNodeProperty(props.id, 'disabled', disabled);\n }, [props.disabled]);\n\n\n\n // Mount\n useEffect(() => {\n const container = ctx.containerRef.current;\n if (!container) return;\n\n setContainer(container);\n\n ctx.mountNode({\n id: props.id,\n isOpen: Boolean(props.isOpen),\n disabled\n });\n }, []);\n\n // Handle toggle node on external isOpen update\n useEffect(() => {\n // Skip if node is not mounted yet\n if (!ctx.nodes.some(el => el.id === props.id)) return;\n\n ctx.invokePopup(props.id, props.isOpen);\n }, [props.isOpen]);\n\n // Handle node sync with context\n useEffect(() => {\n const node = ctx.nodes.find(el => el.id === props.id);\n if (!node) return;\n\n setIsOpen(node.isOpen);\n setZIndex(node.zIndex);\n setDisabled(node.disabled);\n }, [ctx.nodes]);\n\n // Handle layer z-index change\n useEffect(() => {\n const layer = layerRef.current;\n if (!layer) return;\n\n if (isOpen) layer.style.zIndex = `${zIndex}`;\n else setTimeout(() => {\n layer.style.zIndex = `${-1}`;\n }, animationDuration);\n }, [isOpen]);\n\n // Handle events (onBeforeEnter, etc)\n useEffect(() => {\n if (isOpen) {\n if (props.onBeforeEnter) props.onBeforeEnter();\n\n setTimeout(() => {\n if (props.onAfterEnter) props.onAfterEnter();\n }, animationDuration);\n } else {\n if (props.onBeforeExit) props.onBeforeExit();\n\n setTimeout(() => {\n if (props.onAfterExit) props.onAfterExit();\n }, animationDuration);\n }\n }, [isOpen]);\n\n\n\n function layerOnClick() {\n if (disabled.includes('onLayer')) return;\n\n ctx.invokePopup(props.id, false);\n }\n\n\n\n return container && createPortal(<section\n className={cn(`neko-popup-backdrop`, isOpen && 'neko-popup-backdrop--active', props.layerClassName)}\n aria-hidden={!isOpen}\n style={{ transition: `${animationDuration}ms ease-in-out`, cursor: disabled.includes('onLayer') ? 'default' : 'pointer' }}\n onClick={layerOnClick}\n ref={layerRef}\n >\n <article\n id={props.id}\n className={cn(`neko-popup`, isOpen && 'neko-popup--active', animation && `neko-popup--animation_${animation}`, props.className)}\n role=\"dialog\"\n aria-modal\n onClick={e => e.stopPropagation()}\n >\n {props.children}\n </article>\n </section>, container);\n};"],"names":["r","t","f","n","o","clsx","PopupContext","createContext","cn","PopupButton","props","ctx","useContext","isActive","setIsActive","useState","Tag","useEffect","node","el","invokePopup","e","jsx","EFKW","msg","PopupLayer","baseZIndex","disableBodyScrollOnActivePopup","nodes","setNodes","isScrollDisabled","setIsScrollDisabled","isAnyPopupActive","setIsAnyPopupActive","containerRef","useRef","controller","maxZIndex","anyOpenNode","entityOrId","forceState","newState","_updateNode","updateNodeProperty","id","key","value","prev","mountNode","isOpen","disabled","jsxs","useMixedState","initialStateOrValue","externalSetter","isControlled","useMemo","state","setter","PopupWindow","container","setContainer","setIsOpen","zIndex","setZIndex","setDisabled","layerRef","animation","animationDuration","useLayoutEffect","layer","layerOnClick","createPortal"],"mappings":"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","x_google_ignoreList":[0]}
@@ -0,0 +1 @@
1
+ .neko-popup{cursor:auto;max-height:100%;overflow-y:auto}.neko-popup--animation_fade{transition:inherit}.neko-popup--animation_scale{transition:inherit;scale:.8}.neko-popup--animation_scale.neko-popup--active{scale:1}.neko-popup-layer{position:fixed;inset:0;visibility:hidden}.neko-popup-layer--active{visibility:visible}.neko-popup-backdrop{position:fixed;inset:0;width:100%;height:100%;max-height:100%;background:#0003;-webkit-tap-highlight-color:transparent;contain:paint;opacity:0;visibility:hidden;touch-action:none;pointer-events:none;display:flex;align-items:center;justify-content:center}.neko-popup-backdrop--active{opacity:1;visibility:visible;touch-action:auto;pointer-events:auto}.neko-popup-button{cursor:pointer}.neko-popup--noScroll{overflow:hidden;scrollbar-gutter:stable}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neko-popup",
3
- "version": "3.0.2",
3
+ "version": "3.0.4",
4
4
  "description": "Simple and cool react popup package 🚀",
5
5
  "keywords": [
6
6
  "popup",