neko-popup 3.0.4 → 3.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -64,7 +64,7 @@ export type StateSetter<S = any> = React.Dispatch<React.SetStateAction<S>>
64
64
  type PopupWindowAnimationType = 'fade' | 'scale' | null
65
65
 
66
66
  interface IPopupLayerProps {
67
- children?: ReactNode | ReactNode[]
67
+ children?: ReactNode
68
68
 
69
69
  /** @default 10000 */
70
70
  baseZIndex?: number
@@ -88,16 +88,16 @@ export interface IPopupButtonProps {
88
88
  as?: 'button' | 'div'
89
89
 
90
90
  disabled?: boolean
91
- children?: ReactNode | ReactNode[]
91
+ children?: ReactNode
92
92
  className?: string
93
93
  id?: string
94
94
 
95
95
  onClick?(e: React.MouseEvent): void
96
96
  }
97
97
 
98
- interface IPopupWindowProps {
98
+ export interface IPopupWindowProps {
99
99
  id: string
100
- children: ReactNode | ReactNode[]
100
+ children: ReactNode
101
101
 
102
102
  isOpen?: boolean
103
103
  setIsOpen?: StateSetter<boolean>
@@ -113,34 +113,23 @@ interface IPopupWindowProps {
113
113
  */
114
114
  animation?: 'fade' | 'scale' | null
115
115
 
116
- /**
117
- * Popup animation duration in msec
118
- *
119
- * @default 200
120
- */
121
- animationDuraionMs?: number
122
-
123
116
  /**
124
- * Fire callback when popup invoked to open
117
+ * Fire callback when popup is mounting
125
118
  */
126
119
  onBeforeEnter?(): void
127
120
 
128
121
  /**
129
- * Fire callback when popup open animation fullfilled.
130
- *
131
- * @see animationDuration
122
+ * Fire callback when popup appear animation finished
132
123
  */
133
124
  onAfterEnter?(): void
134
125
 
135
126
  /**
136
- * Fire callback when popup invoked to close
127
+ * Fire callback when popup started hide animation
137
128
  */
138
129
  onBeforeExit?(): void
139
130
 
140
131
  /**
141
- * Fire callback when popup close animation fullfilled.
142
- *
143
- * @see animationDuration
132
+ * Fire callback when popup become unmount
144
133
  */
145
134
  onAfterExit?(): void
146
135
  }
@@ -8,7 +8,7 @@ export interface IPopupButtonProps {
8
8
  */
9
9
  as?: 'button' | 'div';
10
10
  disabled?: boolean;
11
- children?: ReactNode | ReactNode[];
11
+ children?: ReactNode;
12
12
  className?: string;
13
13
  id?: string;
14
14
  onClick?(e: React.MouseEvent): void;
@@ -1,6 +1,6 @@
1
1
  import { FC, ReactNode } from 'react';
2
2
  export interface IPopupLayerProps {
3
- children?: ReactNode | ReactNode[];
3
+ children?: ReactNode;
4
4
  /** @default 10000 */
5
5
  baseZIndex?: number;
6
6
  /**
@@ -3,7 +3,7 @@ import { PopupWindowDisabledType, StateSetter } from './Interfaces';
3
3
  export type PopupWindowAnimationType = 'fade' | 'scale' | null;
4
4
  export interface IPopupWindowProps {
5
5
  id: string;
6
- children: ReactNode | ReactNode[];
6
+ children: ReactNode;
7
7
  isOpen?: boolean;
8
8
  setIsOpen?: StateSetter<boolean>;
9
9
  className?: string;
@@ -16,29 +16,19 @@ export interface IPopupWindowProps {
16
16
  */
17
17
  animation?: 'fade' | 'scale' | null;
18
18
  /**
19
- * Popup animation duration in msec
20
- *
21
- * @default 200
22
- */
23
- animationDuraionMs?: number;
24
- /**
25
- * Fire callback when popup invoked to open
19
+ * Fire callback when popup is mounting
26
20
  */
27
21
  onBeforeEnter?(): void;
28
22
  /**
29
- * Fire callback when popup open animation fullfilled.
30
- *
31
- * @see animationDuration
23
+ * Fire callback when popup appear animation finished
32
24
  */
33
25
  onAfterEnter?(): void;
34
26
  /**
35
- * Fire callback when popup invoked to close
27
+ * Fire callback when popup started hide animation
36
28
  */
37
29
  onBeforeExit?(): void;
38
30
  /**
39
- * Fire callback when popup close animation fullfilled.
40
- *
41
- * @see animationDuration
31
+ * Fire callback when popup become unmount
42
32
  */
43
33
  onAfterExit?(): void;
44
34
  }
package/dist/index.es.js CHANGED
@@ -1,160 +1,161 @@
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";
1
+ import { jsx as P, jsxs as w } from "react/jsx-runtime";
2
+ import { createContext as C, useContext as O, useState as u, useEffect as c, useRef as I, useMemo as B, useLayoutEffect as L } from "react";
3
+ import { createPortal as M } from "react-dom";
4
4
  function N(e) {
5
- var o, i, t = "";
6
- if (typeof e == "string" || typeof e == "number") t += e;
5
+ var t, i, n = "";
6
+ if (typeof e == "string" || typeof e == "number") n += e;
7
7
  else if (typeof e == "object") if (Array.isArray(e)) {
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;
8
+ var d = e.length;
9
+ for (t = 0; t < d; t++) e[t] && (i = N(e[t])) && (n && (n += " "), n += i);
10
+ } else for (i in e) e[i] && (n && (n += " "), n += i);
11
+ return n;
12
12
  }
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;
13
+ function z() {
14
+ for (var e, t, i = 0, n = "", d = arguments.length; i < d; i++) (e = arguments[i]) && (t = N(e)) && (n && (n += " "), n += t);
15
+ return n;
16
16
  }
17
- const g = z({}), I = D, Z = (e) => {
18
- const o = A(g), [i, t] = f(!1), s = e.as ?? "button";
17
+ const A = C({}), h = z, T = (e) => {
18
+ const t = O(A), [i, n] = u(!1), d = e.as ?? "button";
19
19
  c(() => {
20
- const r = o.nodes.find((y) => y.id === e.popupId);
21
- r && t(r.isOpen);
22
- }, [o]);
23
- function p(r) {
24
- o.invokePopup(e.popupId), e.onClick && e.onClick(r);
20
+ const l = t.nodes.find((m) => m.id === e.popupId);
21
+ l && n(l.isOpen);
22
+ }, [t]);
23
+ function p(l) {
24
+ t.invokePopup(e.popupId), e.onClick && e.onClick(l);
25
25
  }
26
- return /* @__PURE__ */ E(
27
- s,
26
+ return /* @__PURE__ */ P(
27
+ d,
28
28
  {
29
29
  tabIndex: 0,
30
30
  disabled: e.disabled,
31
31
  "aria-disabled": e.disabled,
32
32
  "aria-haspopup": "dialog",
33
33
  id: e.id,
34
- className: I("neko-popup-button", i && "neko-popup-button--active", e.className),
34
+ className: h("neko-popup-button", i && "neko-popup-button--active", e.className),
35
35
  onClick: p,
36
36
  children: e.children
37
37
  }
38
38
  );
39
39
  };
40
- class M extends Error {
41
- constructor(o) {
42
- super(o), this.name = "error at [neko-popup]";
40
+ class D extends Error {
41
+ constructor(t) {
42
+ super(t), this.name = "error at [neko-popup]";
43
43
  }
44
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);
45
+ const Z = (e) => {
46
+ const t = e.baseZIndex ?? 1e4, i = e.disableBodyScrollOnActivePopup ?? !0, [n, d] = u([]), [p, l] = u(!1), [m, b] = u(!1), y = I(null);
47
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);
48
+ const s = new AbortController();
49
+ if (window.addEventListener("keydown", (o) => {
50
+ if (o.key === "Escape") {
51
+ const f = Math.max(...n.filter((v) => v.isOpen).map((v) => v.zIndex)), r = n.find((v) => v.zIndex === f);
52
+ if (!r || r.disabled.includes("onEscape")) return;
53
+ k(r.id, !1);
54
54
  }
55
- }, { signal: n.signal }), i) {
56
- let a = !1;
57
- t.forEach((d) => d.isOpen ? a = !0 : null), r(a);
55
+ }, { signal: s.signal }), i) {
56
+ let o = !1;
57
+ n.forEach((a) => a.isOpen ? o = !0 : null), l(o);
58
58
  }
59
59
  return () => {
60
- n.abort();
60
+ s.abort();
61
61
  };
62
- }, [t]), c(() => {
63
- x(t.some((n) => n.isOpen));
64
- }, [t]), c(() => {
62
+ }, [n]), c(() => {
63
+ b(n.some((s) => s.isOpen));
64
+ }, [n]), c(() => {
65
65
  p ? document.body.classList.add("neko-popup--noScroll") : document.body.classList.remove("neko-popup--noScroll");
66
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);
67
+ function k(s, o) {
68
+ const a = typeof s == "string" ? n.find((r) => r.id === s) : s;
69
+ if (!a) throw new D(typeof s == "string" ? `Cannot find popup node with id #${s}` : "Entity is not assigned to the node");
70
+ const f = o ?? !a.isOpen;
71
+ a.isOpen = f, a.zIndex = f ? Math.max(...n.map((r) => r.zIndex), 0) + 1 : -1, x(a);
72
72
  }
73
- function h(n, a, d) {
74
- const u = t.find((l) => l.id === n);
75
- u && (u[a] = d, m(u));
73
+ function g(s, o, a) {
74
+ const f = n.find((r) => r.id === s);
75
+ f && (f[o] = a, x(f));
76
76
  }
77
- function m(n) {
78
- s((a) => [...a.filter((d) => d.id !== n.id), n]);
77
+ function x(s) {
78
+ d((o) => [...o.filter((a) => a.id !== s.id), s]);
79
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
80
+ const E = ({ id: s, isOpen: o, disabled: a }) => k({ id: s, isOpen: !1, disabled: a, zIndex: -1 }, o);
81
+ return /* @__PURE__ */ w(A.Provider, { value: {
82
+ nodes: n,
83
+ containerRef: y,
84
+ invokePopup: k,
85
+ mountNode: E,
86
+ updateNodeProperty: g
87
87
  }, children: [
88
88
  e.children,
89
- /* @__PURE__ */ E("section", { className: I("neko-popup-layer", y && "neko-popup-layer--active"), style: { zIndex: o }, ref: v })
89
+ /* @__PURE__ */ P("section", { className: h("neko-popup-layer", m && "neko-popup-layer--active"), style: { zIndex: t }, ref: y })
90
90
  ] });
91
91
  };
92
- function S(e, o) {
93
- const i = w(() => arguments.length === 2 && o !== void 0, []), [t, s] = f(e);
92
+ function S(e, t) {
93
+ const i = B(() => arguments.length === 2 && t !== void 0, []), [n, d] = u(e);
94
94
  return c(() => {
95
- i || s(e);
95
+ i || d(e);
96
96
  }, [e]), i ? [
97
97
  e,
98
- o
99
- ] : [t, s];
98
+ t
99
+ ] : [n, d];
100
100
  }
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);
101
+ const F = (e) => {
102
+ const t = O(A), [i, n] = u(null), [d, p] = S(e.isOpen ?? !1, e.setIsOpen), [l, m] = u(!!e.isOpen), [b, y] = u(!!e.isOpen), [k, g] = u([]), x = I(null), E = e.animation !== void 0 ? e.animation : "fade";
103
+ L(() => {
104
+ let o = [];
105
+ typeof e.disabled == "boolean" ? o = e.disabled ? ["onEscape", "onLayer"] : [] : o = e.disabled ?? [], t.updateNodeProperty(e.id, "disabled", o);
106
106
  }, [e.disabled]), c(() => {
107
- const n = o.containerRef.current;
108
- n && (t(n), o.mountNode({
107
+ const o = t.containerRef.current;
108
+ if (!o) return console.warn("[neko-popup]: Popup layer container is not found in DOM");
109
+ n(o), t.mountNode({
109
110
  id: e.id,
110
111
  isOpen: !!e.isOpen,
111
- disabled: x
112
- }));
112
+ disabled: k
113
+ });
113
114
  }, []), c(() => {
114
- o.nodes.some((n) => n.id === e.id) && o.invokePopup(e.id, e.isOpen);
115
+ t.nodes.some((o) => o.id === e.id) && t.invokePopup(e.id, e.isOpen);
115
116
  }, [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(() => {
125
- e.onAfterEnter && e.onAfterEnter();
126
- }, m)) : (e.onBeforeExit && e.onBeforeExit(), setTimeout(() => {
127
- e.onAfterExit && e.onAfterExit();
128
- }, m));
129
- }, [s]);
130
- function P() {
131
- x.includes("onLayer") || o.invokePopup(e.id, !1);
117
+ const o = t.nodes.find((a) => a.id === e.id);
118
+ if (!o) return console.warn(`[neko-popup]: Popup node (#${e.id}) is not exist`);
119
+ p(o.isOpen), g(o.disabled);
120
+ }, [t.nodes]), c(() => {
121
+ d ? (e.onBeforeEnter && e.onBeforeEnter(), m(!0), requestAnimationFrame(() => {
122
+ y(!0), e.onAfterEnter && e.onAfterEnter();
123
+ })) : (y(!1), e.onBeforeExit && e.onBeforeExit());
124
+ }, [d]), c(() => {
125
+ const o = x.current;
126
+ if (!o) return console.warn(`[neko-popup]: Popup (#${e.id}) layer is not found in DOM`);
127
+ o.addEventListener("mousedown", (a) => {
128
+ a.target.closest(".neko-popup") || t.invokePopup(e.id, !1);
129
+ });
130
+ }, [l]);
131
+ function s() {
132
+ b || (m(!1), e.onAfterExit && e.onAfterExit());
132
133
  }
133
- return i && L(/* @__PURE__ */ E(
134
+ return l ? i && M(/* @__PURE__ */ P(
134
135
  "section",
135
136
  {
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
+ className: h("neko-popup-backdrop", b && "neko-popup-backdrop--active", e.layerClassName),
138
+ "aria-hidden": !b,
139
+ style: { cursor: k.includes("onLayer") ? "default" : "pointer" },
140
+ onTransitionEnd: s,
141
+ ref: x,
142
+ children: /* @__PURE__ */ P(
142
143
  "article",
143
144
  {
144
145
  id: e.id,
145
- className: I("neko-popup", s && "neko-popup--active", h && `neko-popup--animation_${h}`, e.className),
146
+ className: h("neko-popup", b && "neko-popup--active", E && `neko-popup--animation_${E}`, e.className),
146
147
  role: "dialog",
147
148
  "aria-modal": !0,
148
- onClick: (n) => n.stopPropagation(),
149
+ onClick: (o) => o.stopPropagation(),
149
150
  children: e.children
150
151
  }
151
152
  )
152
153
  }
153
- ), i);
154
+ ), i) : null;
154
155
  };
155
156
  export {
156
- Z as PopupButton,
157
- j as PopupLayer,
158
- W as PopupWindow
157
+ T as PopupButton,
158
+ Z as PopupLayer,
159
+ F as PopupWindow
159
160
  };
160
161
  //# sourceMappingURL=index.es.js.map
@@ -1 +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]}
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\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\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\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\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 * Fire callback when popup is mounting\n */\n onBeforeEnter?(): void\n\n /** \n * Fire callback when popup appear animation finished\n */\n onAfterEnter?(): void\n\n /** \n * Fire callback when popup started hide animation\n */\n onBeforeExit?(): void\n\n /** \n * Fire callback when popup become unmount\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 [isMounted, setIsMounted] = useState(Boolean(props.isOpen));\n const [isVisible, setIsVisible] = useState(Boolean(props.isOpen));\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\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 console.warn(`[neko-popup]: Popup layer container is not found in DOM`);;\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 console.warn(`[neko-popup]: Popup node (#${props.id}) is not exist`);\n\n setIsOpen(node.isOpen);\n setDisabled(node.disabled);\n }, [ctx.nodes]);\n\n // Handle events (onBeforeEnter, etc)\n useEffect(() => {\n if (isOpen) {\n if (props.onBeforeEnter) props.onBeforeEnter();\n\n setIsMounted(true);\n\n requestAnimationFrame(() => {\n setIsVisible(true);\n\n if (props.onAfterEnter) props.onAfterEnter();\n });\n } else {\n setIsVisible(false);\n\n if (props.onBeforeExit) props.onBeforeExit();\n }\n }, [isOpen]);\n\n // Handle layer clicks\n useEffect(() => {\n const layer = layerRef.current;\n if (!layer) return console.warn(`[neko-popup]: Popup (#${props.id}) layer is not found in DOM`);\n\n layer.addEventListener('mousedown', ev => {\n // Pass inbound clicks\n if ((ev.target as HTMLElement).closest('.neko-popup')) return;\n\n ctx.invokePopup(props.id, false);\n });\n }, [isMounted]);\n\n\n\n function layerOnClick() {\n if (disabled.includes('onLayer')) return;\n\n ctx.invokePopup(props.id, false);\n }\n\n function handleTransitionEnd() {\n if (!isVisible) {\n setIsMounted(false);\n\n if (props.onAfterExit) props.onAfterExit();\n }\n }\n\n\n\n if (!isMounted) return null;\n\n return container && createPortal(<section\n className={cn(`neko-popup-backdrop`, isVisible && 'neko-popup-backdrop--active', props.layerClassName)}\n aria-hidden={!isVisible}\n style={{ cursor: disabled.includes('onLayer') ? 'default' : 'pointer' }}\n onTransitionEnd={handleTransitionEnd}\n ref={layerRef}\n >\n <article\n id={props.id}\n className={cn(`neko-popup`, isVisible && '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","f","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","isMounted","setIsMounted","isVisible","setIsVisible","setDisabled","layerRef","animation","useLayoutEffect","layer","ev","handleTransitionEnd","createPortal"],"mappings":"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","x_google_ignoreList":[0]}
package/dist/index.umd.js CHANGED
@@ -1,2 +1,2 @@
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"})}));
1
+ (function(f,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):(f=typeof globalThis<"u"?globalThis:f||self,l(f["neko-popup"]={},f.jsxRuntime,f.React,f.ReactDOM))})(this,(function(f,l,i,I){"use strict";function A(e){var t,s,n="";if(typeof e=="string"||typeof e=="number")n+=e;else if(typeof e=="object")if(Array.isArray(e)){var a=e.length;for(t=0;t<a;t++)e[t]&&(s=A(e[t]))&&(n&&(n+=" "),n+=s)}else for(s in e)e[s]&&(n&&(n+=" "),n+=s);return n}function S(){for(var e,t,s=0,n="",a=arguments.length;s<a;s++)(e=arguments[s])&&(t=A(e))&&(n&&(n+=" "),n+=t);return n}const O=i.createContext({}),P=S,w=e=>{const t=i.useContext(O),[s,n]=i.useState(!1),a=e.as??"button";i.useEffect(()=>{const c=t.nodes.find(y=>y.id===e.popupId);c&&n(c.isOpen)},[t]);function m(c){t.invokePopup(e.popupId),e.onClick&&e.onClick(c)}return l.jsx(a,{tabIndex:0,disabled:e.disabled,"aria-disabled":e.disabled,"aria-haspopup":"dialog",id:e.id,className:P("neko-popup-button",s&&"neko-popup-button--active",e.className),onClick:m,children:e.children})};class N extends Error{constructor(t){super(t),this.name="error at [neko-popup]"}}const C=e=>{const t=e.baseZIndex??1e4,s=e.disableBodyScrollOnActivePopup??!0,[n,a]=i.useState([]),[m,c]=i.useState(!1),[y,b]=i.useState(!1),x=i.useRef(null);i.useEffect(()=>{const d=new AbortController;if(window.addEventListener("keydown",o=>{if(o.key==="Escape"){const p=Math.max(...n.filter(v=>v.isOpen).map(v=>v.zIndex)),r=n.find(v=>v.zIndex===p);if(!r||r.disabled.includes("onEscape"))return;k(r.id,!1)}},{signal:d.signal}),s){let o=!1;n.forEach(u=>u.isOpen?o=!0:null),c(o)}return()=>{d.abort()}},[n]),i.useEffect(()=>{b(n.some(d=>d.isOpen))},[n]),i.useEffect(()=>{m?document.body.classList.add("neko-popup--noScroll"):document.body.classList.remove("neko-popup--noScroll")},[m]);function k(d,o){const u=typeof d=="string"?n.find(r=>r.id===d):d;if(!u)throw new N(typeof d=="string"?`Cannot find popup node with id #${d}`:"Entity is not assigned to the node");const p=o??!u.isOpen;u.isOpen=p,u.zIndex=p?Math.max(...n.map(r=>r.zIndex),0)+1:-1,E(u)}function g(d,o,u){const p=n.find(r=>r.id===d);p&&(p[o]=u,E(p))}function E(d){a(o=>[...o.filter(u=>u.id!==d.id),d])}const h=({id:d,isOpen:o,disabled:u})=>k({id:d,isOpen:!1,disabled:u,zIndex:-1},o);return l.jsxs(O.Provider,{value:{nodes:n,containerRef:x,invokePopup:k,mountNode:h,updateNodeProperty:g},children:[e.children,l.jsx("section",{className:P("neko-popup-layer",y&&"neko-popup-layer--active"),style:{zIndex:t},ref:x})]})};function B(e,t){const s=i.useMemo(()=>arguments.length===2&&t!==void 0,[]),[n,a]=i.useState(e);return i.useEffect(()=>{s||a(e)},[e]),s?[e,t]:[n,a]}const M=e=>{const t=i.useContext(O),[s,n]=i.useState(null),[a,m]=B(e.isOpen??!1,e.setIsOpen),[c,y]=i.useState(!!e.isOpen),[b,x]=i.useState(!!e.isOpen),[k,g]=i.useState([]),E=i.useRef(null),h=e.animation!==void 0?e.animation:"fade";i.useLayoutEffect(()=>{let o=[];typeof e.disabled=="boolean"?o=e.disabled?["onEscape","onLayer"]:[]:o=e.disabled??[],t.updateNodeProperty(e.id,"disabled",o)},[e.disabled]),i.useEffect(()=>{const o=t.containerRef.current;if(!o)return console.warn("[neko-popup]: Popup layer container is not found in DOM");n(o),t.mountNode({id:e.id,isOpen:!!e.isOpen,disabled:k})},[]),i.useEffect(()=>{t.nodes.some(o=>o.id===e.id)&&t.invokePopup(e.id,e.isOpen)},[e.isOpen]),i.useEffect(()=>{const o=t.nodes.find(u=>u.id===e.id);if(!o)return console.warn(`[neko-popup]: Popup node (#${e.id}) is not exist`);m(o.isOpen),g(o.disabled)},[t.nodes]),i.useEffect(()=>{a?(e.onBeforeEnter&&e.onBeforeEnter(),y(!0),requestAnimationFrame(()=>{x(!0),e.onAfterEnter&&e.onAfterEnter()})):(x(!1),e.onBeforeExit&&e.onBeforeExit())},[a]),i.useEffect(()=>{const o=E.current;if(!o)return console.warn(`[neko-popup]: Popup (#${e.id}) layer is not found in DOM`);o.addEventListener("mousedown",u=>{u.target.closest(".neko-popup")||t.invokePopup(e.id,!1)})},[c]);function d(){b||(y(!1),e.onAfterExit&&e.onAfterExit())}return c?s&&I.createPortal(l.jsx("section",{className:P("neko-popup-backdrop",b&&"neko-popup-backdrop--active",e.layerClassName),"aria-hidden":!b,style:{cursor:k.includes("onLayer")?"default":"pointer"},onTransitionEnd:d,ref:E,children:l.jsx("article",{id:e.id,className:P("neko-popup",b&&"neko-popup--active",h&&`neko-popup--animation_${h}`,e.className),role:"dialog","aria-modal":!0,onClick:o=>o.stopPropagation(),children:e.children})}),s):null};f.PopupButton=w,f.PopupLayer=C,f.PopupWindow=M,Object.defineProperty(f,Symbol.toStringTag,{value:"Module"})}));
2
2
  //# sourceMappingURL=index.umd.js.map
@@ -1 +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]}
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\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\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\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\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 * Fire callback when popup is mounting\n */\n onBeforeEnter?(): void\n\n /** \n * Fire callback when popup appear animation finished\n */\n onAfterEnter?(): void\n\n /** \n * Fire callback when popup started hide animation\n */\n onBeforeExit?(): void\n\n /** \n * Fire callback when popup become unmount\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 [isMounted, setIsMounted] = useState(Boolean(props.isOpen));\n const [isVisible, setIsVisible] = useState(Boolean(props.isOpen));\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\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 console.warn(`[neko-popup]: Popup layer container is not found in DOM`);;\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 console.warn(`[neko-popup]: Popup node (#${props.id}) is not exist`);\n\n setIsOpen(node.isOpen);\n setDisabled(node.disabled);\n }, [ctx.nodes]);\n\n // Handle events (onBeforeEnter, etc)\n useEffect(() => {\n if (isOpen) {\n if (props.onBeforeEnter) props.onBeforeEnter();\n\n setIsMounted(true);\n\n requestAnimationFrame(() => {\n setIsVisible(true);\n\n if (props.onAfterEnter) props.onAfterEnter();\n });\n } else {\n setIsVisible(false);\n\n if (props.onBeforeExit) props.onBeforeExit();\n }\n }, [isOpen]);\n\n // Handle layer clicks\n useEffect(() => {\n const layer = layerRef.current;\n if (!layer) return console.warn(`[neko-popup]: Popup (#${props.id}) layer is not found in DOM`);\n\n layer.addEventListener('mousedown', ev => {\n // Pass inbound clicks\n if ((ev.target as HTMLElement).closest('.neko-popup')) return;\n\n ctx.invokePopup(props.id, false);\n });\n }, [isMounted]);\n\n\n\n function layerOnClick() {\n if (disabled.includes('onLayer')) return;\n\n ctx.invokePopup(props.id, false);\n }\n\n function handleTransitionEnd() {\n if (!isVisible) {\n setIsMounted(false);\n\n if (props.onAfterExit) props.onAfterExit();\n }\n }\n\n\n\n if (!isMounted) return null;\n\n return container && createPortal(<section\n className={cn(`neko-popup-backdrop`, isVisible && 'neko-popup-backdrop--active', props.layerClassName)}\n aria-hidden={!isVisible}\n style={{ cursor: disabled.includes('onLayer') ? 'default' : 'pointer' }}\n onTransitionEnd={handleTransitionEnd}\n ref={layerRef}\n >\n <article\n id={props.id}\n className={cn(`neko-popup`, isVisible && '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","f","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","isMounted","setIsMounted","isVisible","setIsVisible","setDisabled","layerRef","animation","useLayoutEffect","layer","ev","handleTransitionEnd","createPortal"],"mappings":"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","x_google_ignoreList":[0]}
package/dist/styles.css CHANGED
@@ -1 +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}
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;display:flex;align-items:center;justify-content:center;transition:.2s ease-in-out}.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,21 +1,12 @@
1
1
  {
2
2
  "name": "neko-popup",
3
- "version": "3.0.4",
3
+ "version": "3.0.6",
4
4
  "description": "Simple and cool react popup package 🚀",
5
- "keywords": [
6
- "popup",
7
- "aria popup",
8
- "dialog",
9
- "modal",
10
- "react popup",
11
- "nextjs popup",
12
- "react-component"
13
- ],
14
- "license": "MIT",
15
5
  "type": "module",
16
6
  "main": "./dist/index.umd.js",
17
7
  "module": "./dist/index.es.js",
18
- "types": "./dist/_package/index.d.ts",
8
+ "types": "./dist/index.d.ts",
9
+ "license": "MIT",
19
10
  "exports": {
20
11
  ".": {
21
12
  "types": "./dist/index.d.ts",
@@ -27,16 +18,25 @@
27
18
  "files": [
28
19
  "dist/"
29
20
  ],
21
+ "keywords": [
22
+ "popup",
23
+ "aria popup",
24
+ "dialog",
25
+ "modal",
26
+ "react popup",
27
+ "nextjs popup",
28
+ "react-component"
29
+ ],
30
30
  "scripts": {
31
31
  "dev": "npm run lint && npx vite",
32
32
  "build": "npx rimraf dist && npm run lint && npx tsc && npx vite build",
33
33
  "test": "echo Testing ...",
34
34
  "lint": "npx eslint . --fix"
35
35
  },
36
+ "homepage": "https://github.com/nekomiclub/neko-popup#readme",
36
37
  "publishConfig": {
37
38
  "access": "public"
38
39
  },
39
- "homepage": "https://github.com/nekomiclub/neko-popup#readme",
40
40
  "bugs": {
41
41
  "url": "https://github.com/nekomiclub/neko-popup/issues"
42
42
  },
@@ -47,8 +47,6 @@
47
47
  "devDependencies": {
48
48
  "@eslint/js": "^9.32.0",
49
49
  "@tailwindcss/vite": "^4.1.16",
50
- "@types/react": "^19.1.9",
51
- "@types/react-dom": "^19.1.7",
52
50
  "@typescript-eslint/eslint-plugin": "^8.56.1",
53
51
  "@vitejs/plugin-react": "^4.7.0",
54
52
  "clsx": "^2.1.1",
@@ -60,14 +58,20 @@
60
58
  "eslint-plugin-simple-import-sort": "^12.1.1",
61
59
  "eslint-plugin-unused-imports": "^4.1.4",
62
60
  "husky": "^9.1.7",
63
- "react": "^19.2.4",
64
- "react-dom": "^19.2.4",
65
61
  "rimraf": "^6.0.1",
66
62
  "sass": "^1.97.3",
67
63
  "tailwindcss": "^4.1.16",
68
64
  "typescript": "^5.9.3",
69
65
  "typescript-eslint": "^8.56.1",
70
66
  "vite": "^7.3.1",
71
- "vite-plugin-dts": "^4.5.4"
67
+ "vite-plugin-dts": "^4.5.4",
68
+ "@types/react": "19.1.9",
69
+ "@types/react-dom": "19.1.7",
70
+ "react": "19.2.4",
71
+ "react-dom": "19.2.4"
72
+ },
73
+ "peerDependencies": {
74
+ "react": ">=19",
75
+ "react-dom": ">=19"
72
76
  }
73
77
  }