neko-popup 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 nekomiclub
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,153 @@
1
+ # React Popup
2
+
3
+ <!-- GIFs -->
4
+
5
+ ## 🎉 Installation
6
+ ```bash
7
+ $ npm install neko-popup
8
+ $ pnpm install neko-popup
9
+ $ yarn add neko-popup
10
+ ```
11
+
12
+ ```tsx
13
+ import 'neko-popup/css';
14
+
15
+ import React from 'react';
16
+
17
+ import { PopupButton, PopupLayer, PopupWindow } from './_package';
18
+
19
+ function App() {
20
+ const popupId1 = 'popup-1';
21
+
22
+
23
+
24
+ return <PopupLayer>
25
+ <PopupButton popupId={popupId1}>
26
+ Popup 1
27
+ </PopupButton>
28
+
29
+
30
+
31
+ <PopupWindow
32
+ id={popupId1}
33
+ className="w-[500px] h-[300px] bg-white"
34
+ animation={'fade'}
35
+ >
36
+ <PopupButton popupId={popupId1}>
37
+ Popup 1
38
+ </PopupButton>
39
+ </PopupWindow>
40
+ </PopupLayer>
41
+ }
42
+ ```
43
+
44
+ ## ✨ Features
45
+ - Active popup can be closed by pressing Escape or clicking on the backdrop
46
+ - State can be controlled by passing state/stateSetter from parent
47
+ - Built-in fade/scale popup animations
48
+ - Popups can be stacked, recently opened popup will have larger z-index
49
+ - Popups appears on top of the html stacking context
50
+ - Implements [WAI-ARIA Dialog Pattern](https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/)
51
+ - Does not break sticky elements when hiding overflow
52
+ - Hooks before/after animation start/end
53
+ - Easy open/close disable with state support
54
+ - Support nextjs client environment
55
+
56
+ <!-- ## 👀 Examples
57
+ - [Controlled state with custom popup buttons]()
58
+ - [Disabling popup & show discard changes]() -->
59
+
60
+ ## ⚙️ API
61
+ ```ts
62
+ export type PopupWindowDisabledType = 'onEscape' | 'onLayer';
63
+ export type StateSetter<S = any> = React.Dispatch<React.SetStateAction<S>>
64
+ type PopupWindowAnimationType = 'fade' | 'scale' | null
65
+
66
+ interface IPopupLayerProps {
67
+ children?: ReactNode | ReactNode[]
68
+
69
+ /** @default 10000 */
70
+ baseZIndex?: number
71
+
72
+ /**
73
+ * Disable body scroll when there is at least one open popup
74
+ *
75
+ * @default true
76
+ */
77
+ disableBodyScrollOnActivePopup?: boolean
78
+ }
79
+
80
+ export interface IPopupButtonProps {
81
+ popupId: string
82
+
83
+ /**
84
+ * Element tag
85
+ *
86
+ * @default "button"
87
+ */
88
+ as?: 'button' | 'div'
89
+
90
+ disabled?: boolean
91
+ children?: ReactNode | ReactNode[]
92
+ className?: string
93
+ id?: string
94
+
95
+ onClick?(e: React.MouseEvent): void
96
+ }
97
+
98
+ interface IPopupWindowProps {
99
+ id: string
100
+ children: ReactNode | ReactNode[]
101
+
102
+ isOpen?: boolean
103
+ setIsOpen?: StateSetter<boolean>
104
+
105
+ className?: string
106
+ layerClassName?: string
107
+ disabled?: PopupWindowDisabledType[] | boolean
108
+
109
+ /**
110
+ * Popup dialog animation type
111
+ *
112
+ * @default "fade"
113
+ */
114
+ animation?: 'fade' | 'scale' | null
115
+
116
+ /**
117
+ * Popup animation duration in msec
118
+ *
119
+ * @default 200
120
+ */
121
+ animationDuraionMs?: number
122
+
123
+ /**
124
+ * Fire callback when popup invoked to open
125
+ */
126
+ onBeforeEnter?(): void
127
+
128
+ /**
129
+ * Fire callback when popup open animation fullfilled.
130
+ *
131
+ * @see animationDuration
132
+ */
133
+ onAfterEnter?(): void
134
+
135
+ /**
136
+ * Fire callback when popup invoked to close
137
+ */
138
+ onBeforeExit?(): void
139
+
140
+ /**
141
+ * Fire callback when popup close animation fullfilled.
142
+ *
143
+ * @see animationDuration
144
+ */
145
+ onAfterExit?(): void
146
+ }
147
+ ```
148
+
149
+ ## ☁️ Migration Guides
150
+ - [Migration from @fullkekw/popup](./docs/migration.md#fullkekwpopup)
151
+
152
+ ## ©️ License
153
+ Licensed under MIT ©️ nekomiclub 2026
@@ -0,0 +1,22 @@
1
+ import { default as React, RefObject } from 'react';
2
+ import { default as clsx } from 'clsx';
3
+ export type PopupWindowDisabledType = 'onEscape' | 'onLayer';
4
+ export type StateSetter<S = any> = React.Dispatch<React.SetStateAction<S>>;
5
+ export type RegisterNodeArgs = Pick<IPopupNode, 'id' | 'isOpen' | 'disabled'>;
6
+ /** Get value type from nested object path */
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
+ export interface IPopupNode {
9
+ id: string;
10
+ isOpen: boolean;
11
+ zIndex: number;
12
+ disabled: PopupWindowDisabledType[];
13
+ }
14
+ export interface IPopupContext {
15
+ nodes: IPopupNode[];
16
+ containerRef: RefObject<HTMLDivElement | null>;
17
+ invokePopup(id: string, forceState?: boolean): void;
18
+ registerNode(args: RegisterNodeArgs): void;
19
+ updateNodeProperty<K extends keyof IPopupNode>(id: string, key: K, value: ValueFromPath<IPopupNode, K>): void;
20
+ }
21
+ export declare const PopupContext: React.Context<IPopupContext>;
22
+ export declare const cn: typeof clsx;
@@ -0,0 +1,17 @@
1
+ import { FC, ReactNode } from 'react';
2
+ export interface IPopupButtonProps {
3
+ popupId: string;
4
+ /**
5
+ * Element tag
6
+ *
7
+ * @default "button"
8
+ */
9
+ as?: 'button' | 'div';
10
+ disabled?: boolean;
11
+ children?: ReactNode | ReactNode[];
12
+ className?: string;
13
+ id?: string;
14
+ onClick?(e: React.MouseEvent): void;
15
+ }
16
+ declare const PopupButton: FC<IPopupButtonProps>;
17
+ export default PopupButton;
@@ -0,0 +1,14 @@
1
+ import { FC, ReactNode } from 'react';
2
+ interface IPopupLayerProps {
3
+ children?: ReactNode | ReactNode[];
4
+ /** @default 10000 */
5
+ baseZIndex?: number;
6
+ /**
7
+ * Disable body scroll when there is at least one open popup
8
+ *
9
+ * @default true
10
+ */
11
+ disableBodyScrollOnActivePopup?: boolean;
12
+ }
13
+ declare const PopupLayer: FC<IPopupLayerProps>;
14
+ export default PopupLayer;
@@ -0,0 +1,45 @@
1
+ import { FC, ReactNode } from 'react';
2
+ import { PopupWindowDisabledType, StateSetter } from './Interfaces';
3
+ interface IPopupWindowProps {
4
+ id: string;
5
+ children: ReactNode | ReactNode[];
6
+ isOpen?: boolean;
7
+ setIsOpen?: StateSetter<boolean>;
8
+ className?: string;
9
+ layerClassName?: string;
10
+ disabled?: PopupWindowDisabledType[] | boolean;
11
+ /**
12
+ * Popup dialog animation type
13
+ *
14
+ * @default "fade"
15
+ */
16
+ animation?: 'fade' | 'scale' | null;
17
+ /**
18
+ * Popup animation duration in msec
19
+ *
20
+ * @default 200
21
+ */
22
+ animationDuraionMs?: number;
23
+ /**
24
+ * Fire callback when popup invoked to open
25
+ */
26
+ onBeforeEnter?(): void;
27
+ /**
28
+ * Fire callback when popup open animation fullfilled.
29
+ *
30
+ * @see animationDuration
31
+ */
32
+ onAfterEnter?(): void;
33
+ /**
34
+ * Fire callback when popup invoked to close
35
+ */
36
+ onBeforeExit?(): void;
37
+ /**
38
+ * Fire callback when popup close animation fullfilled.
39
+ *
40
+ * @see animationDuration
41
+ */
42
+ onAfterExit?(): void;
43
+ }
44
+ declare const PopupWindow: FC<IPopupWindowProps>;
45
+ export default PopupWindow;
@@ -0,0 +1,4 @@
1
+ declare class EFKW extends Error {
2
+ constructor(msg: string);
3
+ }
4
+ export default EFKW;
@@ -0,0 +1,3 @@
1
+ export { default as PopupLayer } from './PopupLayer';
2
+ export { default as PopupButton } from './PopupButton';
3
+ export { default as PopupWindow } from './PopupWindow';
@@ -0,0 +1,155 @@
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;
12
+ 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;
17
+ }
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;
21
+ }
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");
41
+ }, [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));
51
+ }
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,
77
+ {
78
+ tabIndex: 0,
79
+ disabled: e.disabled,
80
+ "aria-disabled": e.disabled,
81
+ "aria-haspopup": "dialog",
82
+ id: e.id,
83
+ className: g("neko-popup-button", s && "neko-popup-button--active", e.className),
84
+ onClick: b,
85
+ children: e.children
86
+ }
87
+ );
88
+ };
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 ? [
94
+ e,
95
+ n
96
+ ] : [o, a];
97
+ }
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({
106
+ id: e.id,
107
+ isOpen: !!e.isOpen,
108
+ disabled: f
109
+ }));
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(() => {
120
+ e.onAfterEnter && e.onAfterEnter();
121
+ }, i)) : (e.onBeforeExit && e.onBeforeExit(), setTimeout(() => {
122
+ e.onAfterExit && e.onAfterExit();
123
+ }, i));
124
+ }, [a]);
125
+ function d() {
126
+ f.includes("onLayer") || n.invokePopup(e.id, !1);
127
+ }
128
+ return s && w(/* @__PURE__ */ v(
129
+ "section",
130
+ {
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(
137
+ "article",
138
+ {
139
+ id: e.id,
140
+ className: g("neko-popup", a && "neko-popup--active", k && `neko-popup--animation_${k}`, e.className),
141
+ role: "dialog",
142
+ "aria-modal": !0,
143
+ onClick: (t) => t.stopPropagation(),
144
+ children: e.children
145
+ }
146
+ )
147
+ }
148
+ ), s);
149
+ };
150
+ export {
151
+ T as PopupButton,
152
+ R as PopupLayer,
153
+ Z as PopupWindow
154
+ };
155
+ //# sourceMappingURL=index.es.js.map
@@ -0,0 +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 {\r\n constructor(msg: string) {\r\n super(msg);\r\n\r\n this.name = 'error at [neko-popup]';\r\n }\r\n}\r\n\r\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';\r\nimport clsx from 'clsx';\r\n\r\n\r\n\r\n\r\nexport type PopupWindowDisabledType = 'onEscape' | 'onLayer';\r\nexport type StateSetter<S = any> = React.Dispatch<React.SetStateAction<S>>\r\nexport type RegisterNodeArgs = Pick<IPopupNode, 'id' | 'isOpen' | 'disabled'>\r\n\r\n/** Get value type from nested object path */\r\nexport type ValueFromPath<T, P> =\r\n P extends `${infer K}.${infer R}`\r\n ? K extends keyof T\r\n ? R extends keyof T[K]\r\n ? T[K][R]\r\n : never\r\n : never\r\n : P extends keyof T\r\n ? T[P]\r\n : never;\r\n\r\n\r\n\r\nexport interface IPopupNode {\r\n id: string\r\n isOpen: boolean\r\n zIndex: number\r\n disabled: PopupWindowDisabledType[]\r\n}\r\n\r\nexport interface IPopupContext {\r\n nodes: IPopupNode[]\r\n containerRef: RefObject<HTMLDivElement | null>\r\n\r\n invokePopup(id: string, forceState?: boolean): void\r\n registerNode(args: RegisterNodeArgs): void\r\n updateNodeProperty<K extends keyof IPopupNode>(id: string, key: K, value: ValueFromPath<IPopupNode, K>): void\r\n}\r\n\r\n\r\n\r\nexport const PopupContext = createContext<IPopupContext>({} as IPopupContext);\r\nexport const cn = clsx;","'use client';\r\n\r\nimport { FC, ReactNode, useEffect, useRef, useState } from 'react';\r\n\r\nimport EFKW from './components/ErrorComponents';\r\nimport { IPopupNode, PopupContext, RegisterNodeArgs, ValueFromPath } from './Interfaces';\r\n\r\n\r\n\r\ninterface IPopupLayerProps {\r\n children?: ReactNode | ReactNode[]\r\n\r\n /** @default 10000 */\r\n baseZIndex?: number\r\n\r\n /**\r\n * Disable body scroll when there is at least one open popup\r\n * \r\n * @default true\r\n */\r\n disableBodyScrollOnActivePopup?: boolean\r\n}\r\n\r\n\r\n\r\nconst PopupLayer: FC<IPopupLayerProps> = (props) => {\r\n const [nodes, setNodes] = useState<IPopupNode[]>([]);\r\n const [isScrollDisabled, setIsScrollDisabled] = useState(false);\r\n\r\n const containerRef = useRef<HTMLDivElement>(null);\r\n\r\n const baseZIndex = props.baseZIndex ?? 10000;\r\n const disableBodyScrollOnActivePopup = props.disableBodyScrollOnActivePopup ?? true;\r\n\r\n\r\n\r\n // Handle close closest to user popup on escape & scroll\r\n useEffect(() => {\r\n // === Handle close closest popup on escape\r\n const controller = new AbortController();\r\n\r\n window.addEventListener('keydown', e => {\r\n const key = e.key;\r\n\r\n if (key === 'Escape') {\r\n const maxZIndex = Math.max(...nodes.filter(el => el.isOpen).map(el => el.zIndex));\r\n const node = nodes.find(el => el.zIndex === maxZIndex);\r\n if (!node || node.disabled.includes('onEscape')) return;\r\n\r\n // eslint-disable-next-line\r\n invokePopup(node.id, false);\r\n }\r\n }, { signal: controller.signal });\r\n\r\n\r\n\r\n // === Disable body scroll on active popup\r\n if (disableBodyScrollOnActivePopup) {\r\n let anyOpenNode = false;\r\n nodes.forEach(el => el.isOpen ? anyOpenNode = true : null);\r\n\r\n setIsScrollDisabled(anyOpenNode);\r\n }\r\n\r\n\r\n\r\n return () => {\r\n controller.abort();\r\n };\r\n }, [nodes]);\r\n\r\n // Handle body scroll\r\n useEffect(() => {\r\n if (isScrollDisabled) document.body.classList.add('neko-popup--noScroll');\r\n else document.body.classList.remove('neko-popup--noScroll');\r\n }, [isScrollDisabled]);\r\n\r\n\r\n\r\n /** Toggle popup state */\r\n function invokePopup(entityOrId: string | IPopupNode, forceState?: boolean) {\r\n const node = typeof entityOrId === 'string' ? nodes.find(el => el.id === entityOrId) : entityOrId;\r\n if (!node) throw new EFKW(typeof entityOrId === 'string' ? `Cannot find popup node with id #${entityOrId}` : `Entity is not assigned to the node`);\r\n\r\n const newState = forceState ?? !node.isOpen;\r\n\r\n // === Update node\r\n node.isOpen = newState;\r\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\r\n\r\n _updateNodeInNodes(node);\r\n }\r\n\r\n /** Update node property */\r\n function updateNodeProperty<K extends keyof IPopupNode>(id: string, key: K, value: ValueFromPath<IPopupNode, K>) {\r\n const node = nodes.find(el => el.id === id);\r\n if (!node) return;\r\n\r\n node[key] = value;\r\n\r\n _updateNodeInNodes(node);\r\n }\r\n\r\n /** Update node in nodes array */\r\n function _updateNodeInNodes(node: IPopupNode) {\r\n setNodes(prev => [...prev.filter(el => el.id !== node.id), node]);\r\n }\r\n\r\n /** Add new popup window to state */\r\n const registerNode = ({ id, isOpen, disabled }: RegisterNodeArgs) => invokePopup({ id, isOpen: false, disabled, zIndex: -1 }, isOpen);\r\n\r\n\r\n\r\n return <PopupContext.Provider value={{\r\n nodes,\r\n containerRef,\r\n invokePopup,\r\n registerNode,\r\n updateNodeProperty\r\n }}>\r\n {props.children}\r\n\r\n <section style={{ zIndex: baseZIndex }} ref={containerRef} />\r\n </PopupContext.Provider>;\r\n};\r\n\r\nexport default PopupLayer;","'use client';\r\n\r\nimport { FC, JSX, ReactNode, useContext, useEffect, useState } from 'react';\r\n\r\nimport { cn, PopupContext } from './Interfaces';\r\n\r\n\r\n\r\nexport interface IPopupButtonProps {\r\n popupId: string\r\n\r\n /** \r\n * Element tag\r\n * \r\n * @default \"button\"\r\n */\r\n as?: 'button' | 'div'\r\n\r\n disabled?: boolean\r\n children?: ReactNode | ReactNode[]\r\n className?: string\r\n id?: string\r\n\r\n onClick?(e: React.MouseEvent): void\r\n}\r\n\r\n\r\n\r\nconst PopupButton: FC<IPopupButtonProps> = (props) => {\r\n const ctx = useContext(PopupContext);\r\n\r\n const [isActive, setIsActive] = useState(false);\r\n\r\n const Tag: keyof JSX.IntrinsicElements = props.as ?? 'button';\r\n\r\n\r\n\r\n // Handle isActive on context change\r\n useEffect(() => {\r\n const node = ctx.nodes.find(el => el.id === props.popupId);\r\n if (!node) return;\r\n\r\n setIsActive(node.isOpen);\r\n }, [ctx]);\r\n\r\n\r\n\r\n function invokePopup(e: React.MouseEvent) {\r\n ctx.invokePopup(props.popupId);\r\n\r\n if (props.onClick) props.onClick(e);\r\n }\r\n\r\n\r\n\r\n return <Tag\r\n tabIndex={0}\r\n disabled={props.disabled}\r\n aria-disabled={props.disabled}\r\n aria-haspopup={'dialog'}\r\n id={props.id}\r\n className={cn(`neko-popup-button`, isActive && 'neko-popup-button--active', props.className)}\r\n onClick={invokePopup}\r\n >\r\n {props.children}\r\n </Tag>;\r\n};\r\n\r\nexport default PopupButton;","'use client';\r\n\r\nimport { useEffect, useMemo, useState } from 'react';\r\n\r\n\r\n\r\n\r\ntype StateSetter<S> = React.Dispatch<React.SetStateAction<S>>;\r\ntype InitialState<S> = S | (() => S);\r\n\r\nexport default function useMixedState<S = undefined>(): [S | undefined, StateSetter<S | undefined>];\r\nexport default function useMixedState<S>(initialState: InitialState<S>): [S, StateSetter<S>];\r\nexport default function useMixedState<S>(state: S, setter?: StateSetter<S>): [S, StateSetter<S>];\r\n\r\n\r\n\r\n/** \r\n * Use mixed state hook\r\n * \r\n * @param initialStateOrValue Initial state, can be undefined, value or function. If externalSetter not specified, will return default state\r\n * @param externalSetter External state setter. If specified will return external state\r\n */\r\nexport default function useMixedState<S>(initialStateOrValue?: InitialState<S>, externalSetter?: StateSetter<S>) {\r\n const isControlled = useMemo(() => arguments.length === 2 && externalSetter !== undefined, []);\r\n\r\n const [state, setter] = useState<S | undefined>(initialStateOrValue);\r\n\r\n\r\n\r\n // Propagate state update on external state update even if external setter is not provided\r\n useEffect(() => {\r\n if (!isControlled) {\r\n \r\n setter(initialStateOrValue);\r\n }\r\n }, [initialStateOrValue]);\r\n\r\n\r\n\r\n if (isControlled) {\r\n return [\r\n initialStateOrValue as S,\r\n externalSetter as StateSetter<S>\r\n ];\r\n }\r\n\r\n return [state, setter];\r\n}","'use client'\r\n\r\nimport { FC, ReactNode, useContext, useEffect, useLayoutEffect, useRef, useState } from 'react';\r\nimport { createPortal } from 'react-dom';\r\n\r\nimport useMixedState from './hooks/useMixedState';\r\nimport { cn, PopupContext, PopupWindowDisabledType, StateSetter } from './Interfaces';\r\n\r\n\r\n\r\ntype PopupWindowAnimationType = 'fade' | 'scale' | null\r\n\r\n\r\n\r\ninterface IPopupWindowProps {\r\n id: string\r\n children: ReactNode | ReactNode[]\r\n\r\n isOpen?: boolean\r\n setIsOpen?: StateSetter<boolean>\r\n\r\n className?: string\r\n layerClassName?: string\r\n disabled?: PopupWindowDisabledType[] | boolean\r\n\r\n /** \r\n * Popup dialog animation type\r\n * \r\n * @default \"fade\"\r\n */\r\n animation?: 'fade' | 'scale' | null\r\n\r\n /**\r\n * Popup animation duration in msec\r\n * \r\n * @default 200\r\n */\r\n animationDuraionMs?: number\r\n\r\n /** \r\n * Fire callback when popup invoked to open\r\n */\r\n onBeforeEnter?(): void\r\n\r\n /** \r\n * Fire callback when popup open animation fullfilled. \r\n * \r\n * @see animationDuration\r\n */\r\n onAfterEnter?(): void\r\n\r\n /** \r\n * Fire callback when popup invoked to close\r\n */\r\n onBeforeExit?(): void\r\n\r\n /** \r\n * Fire callback when popup close animation fullfilled. \r\n * \r\n * @see animationDuration\r\n */\r\n onAfterExit?(): void\r\n}\r\n\r\n\r\n\r\nconst PopupWindow: FC<IPopupWindowProps> = (props) => {\r\n const ctx = useContext(PopupContext);\r\n\r\n const [container, setContainer] = useState<HTMLDivElement | null>(null);\r\n const [isOpen, setIsOpen] = useMixedState(props.isOpen ?? false, props.setIsOpen);\r\n const [zIndex, setZIndex] = useState(-1);\r\n const [disabled, setDisabled] = useState<PopupWindowDisabledType[]>([]);\r\n\r\n const layerRef = useRef<HTMLDivElement>(null);\r\n\r\n const animation: PopupWindowAnimationType = props.animation !== undefined ? props.animation : 'fade';\r\n const animationDuration = props.animationDuraionMs ?? 200;\r\n\r\n\r\n\r\n // Handle disabled\r\n useLayoutEffect(() => {\r\n let disabled: PopupWindowDisabledType[] = [];\r\n\r\n if (typeof props.disabled === 'boolean') disabled = props.disabled ? ['onEscape', 'onLayer'] : [];\r\n else disabled = props.disabled ?? [];\r\n\r\n ctx.updateNodeProperty(props.id, 'disabled', disabled);\r\n }, [props.disabled]);\r\n\r\n\r\n\r\n // Mount & register node\r\n useEffect(() => {\r\n const container = ctx.containerRef.current;\r\n if (!container) return;\r\n\r\n setContainer(container);\r\n\r\n ctx.registerNode({\r\n id: props.id,\r\n isOpen: Boolean(props.isOpen),\r\n disabled\r\n });\r\n }, []);\r\n\r\n // Handle node sync with context\r\n useEffect(() => {\r\n const node = ctx.nodes.find(el => el.id === props.id);\r\n if (!node) return;\r\n\r\n setIsOpen(node.isOpen);\r\n setZIndex(node.zIndex);\r\n setDisabled(node.disabled);\r\n }, [ctx.nodes]);\r\n\r\n // Handle layer z-index change\r\n useEffect(() => {\r\n const layer = layerRef.current;\r\n if (!layer) return;\r\n\r\n if (isOpen) layer.style.zIndex = `${zIndex}`;\r\n else setTimeout(() => {\r\n layer.style.zIndex = `${-1}`;\r\n }, animationDuration);\r\n }, [isOpen]);\r\n\r\n // Handle events (onBeforeEnter, etc)\r\n useEffect(() => {\r\n if (isOpen) {\r\n if (props.onBeforeEnter) props.onBeforeEnter();\r\n\r\n setTimeout(() => {\r\n if (props.onAfterEnter) props.onAfterEnter();\r\n }, animationDuration);\r\n } else {\r\n if (props.onBeforeExit) props.onBeforeExit();\r\n\r\n setTimeout(() => {\r\n if (props.onAfterExit) props.onAfterExit();\r\n }, animationDuration);\r\n }\r\n }, [isOpen]);\r\n\r\n\r\n\r\n function layerOnClick() {\r\n if (disabled.includes('onLayer')) return;\r\n\r\n ctx.invokePopup(props.id, false);\r\n }\r\n\r\n\r\n\r\n return container && createPortal(<section\r\n className={cn(`neko-popup-backdrop`, isOpen && 'neko-popup-backdrop--active', props.layerClassName)}\r\n aria-hidden={!isOpen}\r\n style={{ transition: `${animationDuration}ms ease-in-out`, cursor: disabled.includes('onLayer') ? 'default' : 'pointer' }}\r\n onClick={layerOnClick}\r\n ref={layerRef}\r\n >\r\n <article\r\n id={props.id}\r\n className={cn(`neko-popup`, isOpen && 'neko-popup--active', animation && `neko-popup--animation_${animation}`, props.className)}\r\n role=\"dialog\"\r\n aria-modal\r\n onClick={e => e.stopPropagation()}\r\n >\r\n {props.children}\r\n </article>\r\n </section>, container);\r\n};\r\n\r\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]}
@@ -0,0 +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"})}));
2
+ //# sourceMappingURL=index.umd.js.map
@@ -0,0 +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 {\r\n constructor(msg: string) {\r\n super(msg);\r\n\r\n this.name = 'error at [neko-popup]';\r\n }\r\n}\r\n\r\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';\r\nimport clsx from 'clsx';\r\n\r\n\r\n\r\n\r\nexport type PopupWindowDisabledType = 'onEscape' | 'onLayer';\r\nexport type StateSetter<S = any> = React.Dispatch<React.SetStateAction<S>>\r\nexport type RegisterNodeArgs = Pick<IPopupNode, 'id' | 'isOpen' | 'disabled'>\r\n\r\n/** Get value type from nested object path */\r\nexport type ValueFromPath<T, P> =\r\n P extends `${infer K}.${infer R}`\r\n ? K extends keyof T\r\n ? R extends keyof T[K]\r\n ? T[K][R]\r\n : never\r\n : never\r\n : P extends keyof T\r\n ? T[P]\r\n : never;\r\n\r\n\r\n\r\nexport interface IPopupNode {\r\n id: string\r\n isOpen: boolean\r\n zIndex: number\r\n disabled: PopupWindowDisabledType[]\r\n}\r\n\r\nexport interface IPopupContext {\r\n nodes: IPopupNode[]\r\n containerRef: RefObject<HTMLDivElement | null>\r\n\r\n invokePopup(id: string, forceState?: boolean): void\r\n registerNode(args: RegisterNodeArgs): void\r\n updateNodeProperty<K extends keyof IPopupNode>(id: string, key: K, value: ValueFromPath<IPopupNode, K>): void\r\n}\r\n\r\n\r\n\r\nexport const PopupContext = createContext<IPopupContext>({} as IPopupContext);\r\nexport const cn = clsx;","'use client';\r\n\r\nimport { FC, ReactNode, useEffect, useRef, useState } from 'react';\r\n\r\nimport EFKW from './components/ErrorComponents';\r\nimport { IPopupNode, PopupContext, RegisterNodeArgs, ValueFromPath } from './Interfaces';\r\n\r\n\r\n\r\ninterface IPopupLayerProps {\r\n children?: ReactNode | ReactNode[]\r\n\r\n /** @default 10000 */\r\n baseZIndex?: number\r\n\r\n /**\r\n * Disable body scroll when there is at least one open popup\r\n * \r\n * @default true\r\n */\r\n disableBodyScrollOnActivePopup?: boolean\r\n}\r\n\r\n\r\n\r\nconst PopupLayer: FC<IPopupLayerProps> = (props) => {\r\n const [nodes, setNodes] = useState<IPopupNode[]>([]);\r\n const [isScrollDisabled, setIsScrollDisabled] = useState(false);\r\n\r\n const containerRef = useRef<HTMLDivElement>(null);\r\n\r\n const baseZIndex = props.baseZIndex ?? 10000;\r\n const disableBodyScrollOnActivePopup = props.disableBodyScrollOnActivePopup ?? true;\r\n\r\n\r\n\r\n // Handle close closest to user popup on escape & scroll\r\n useEffect(() => {\r\n // === Handle close closest popup on escape\r\n const controller = new AbortController();\r\n\r\n window.addEventListener('keydown', e => {\r\n const key = e.key;\r\n\r\n if (key === 'Escape') {\r\n const maxZIndex = Math.max(...nodes.filter(el => el.isOpen).map(el => el.zIndex));\r\n const node = nodes.find(el => el.zIndex === maxZIndex);\r\n if (!node || node.disabled.includes('onEscape')) return;\r\n\r\n // eslint-disable-next-line\r\n invokePopup(node.id, false);\r\n }\r\n }, { signal: controller.signal });\r\n\r\n\r\n\r\n // === Disable body scroll on active popup\r\n if (disableBodyScrollOnActivePopup) {\r\n let anyOpenNode = false;\r\n nodes.forEach(el => el.isOpen ? anyOpenNode = true : null);\r\n\r\n setIsScrollDisabled(anyOpenNode);\r\n }\r\n\r\n\r\n\r\n return () => {\r\n controller.abort();\r\n };\r\n }, [nodes]);\r\n\r\n // Handle body scroll\r\n useEffect(() => {\r\n if (isScrollDisabled) document.body.classList.add('neko-popup--noScroll');\r\n else document.body.classList.remove('neko-popup--noScroll');\r\n }, [isScrollDisabled]);\r\n\r\n\r\n\r\n /** Toggle popup state */\r\n function invokePopup(entityOrId: string | IPopupNode, forceState?: boolean) {\r\n const node = typeof entityOrId === 'string' ? nodes.find(el => el.id === entityOrId) : entityOrId;\r\n if (!node) throw new EFKW(typeof entityOrId === 'string' ? `Cannot find popup node with id #${entityOrId}` : `Entity is not assigned to the node`);\r\n\r\n const newState = forceState ?? !node.isOpen;\r\n\r\n // === Update node\r\n node.isOpen = newState;\r\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\r\n\r\n _updateNodeInNodes(node);\r\n }\r\n\r\n /** Update node property */\r\n function updateNodeProperty<K extends keyof IPopupNode>(id: string, key: K, value: ValueFromPath<IPopupNode, K>) {\r\n const node = nodes.find(el => el.id === id);\r\n if (!node) return;\r\n\r\n node[key] = value;\r\n\r\n _updateNodeInNodes(node);\r\n }\r\n\r\n /** Update node in nodes array */\r\n function _updateNodeInNodes(node: IPopupNode) {\r\n setNodes(prev => [...prev.filter(el => el.id !== node.id), node]);\r\n }\r\n\r\n /** Add new popup window to state */\r\n const registerNode = ({ id, isOpen, disabled }: RegisterNodeArgs) => invokePopup({ id, isOpen: false, disabled, zIndex: -1 }, isOpen);\r\n\r\n\r\n\r\n return <PopupContext.Provider value={{\r\n nodes,\r\n containerRef,\r\n invokePopup,\r\n registerNode,\r\n updateNodeProperty\r\n }}>\r\n {props.children}\r\n\r\n <section style={{ zIndex: baseZIndex }} ref={containerRef} />\r\n </PopupContext.Provider>;\r\n};\r\n\r\nexport default PopupLayer;","'use client';\r\n\r\nimport { FC, JSX, ReactNode, useContext, useEffect, useState } from 'react';\r\n\r\nimport { cn, PopupContext } from './Interfaces';\r\n\r\n\r\n\r\nexport interface IPopupButtonProps {\r\n popupId: string\r\n\r\n /** \r\n * Element tag\r\n * \r\n * @default \"button\"\r\n */\r\n as?: 'button' | 'div'\r\n\r\n disabled?: boolean\r\n children?: ReactNode | ReactNode[]\r\n className?: string\r\n id?: string\r\n\r\n onClick?(e: React.MouseEvent): void\r\n}\r\n\r\n\r\n\r\nconst PopupButton: FC<IPopupButtonProps> = (props) => {\r\n const ctx = useContext(PopupContext);\r\n\r\n const [isActive, setIsActive] = useState(false);\r\n\r\n const Tag: keyof JSX.IntrinsicElements = props.as ?? 'button';\r\n\r\n\r\n\r\n // Handle isActive on context change\r\n useEffect(() => {\r\n const node = ctx.nodes.find(el => el.id === props.popupId);\r\n if (!node) return;\r\n\r\n setIsActive(node.isOpen);\r\n }, [ctx]);\r\n\r\n\r\n\r\n function invokePopup(e: React.MouseEvent) {\r\n ctx.invokePopup(props.popupId);\r\n\r\n if (props.onClick) props.onClick(e);\r\n }\r\n\r\n\r\n\r\n return <Tag\r\n tabIndex={0}\r\n disabled={props.disabled}\r\n aria-disabled={props.disabled}\r\n aria-haspopup={'dialog'}\r\n id={props.id}\r\n className={cn(`neko-popup-button`, isActive && 'neko-popup-button--active', props.className)}\r\n onClick={invokePopup}\r\n >\r\n {props.children}\r\n </Tag>;\r\n};\r\n\r\nexport default PopupButton;","'use client';\r\n\r\nimport { useEffect, useMemo, useState } from 'react';\r\n\r\n\r\n\r\n\r\ntype StateSetter<S> = React.Dispatch<React.SetStateAction<S>>;\r\ntype InitialState<S> = S | (() => S);\r\n\r\nexport default function useMixedState<S = undefined>(): [S | undefined, StateSetter<S | undefined>];\r\nexport default function useMixedState<S>(initialState: InitialState<S>): [S, StateSetter<S>];\r\nexport default function useMixedState<S>(state: S, setter?: StateSetter<S>): [S, StateSetter<S>];\r\n\r\n\r\n\r\n/** \r\n * Use mixed state hook\r\n * \r\n * @param initialStateOrValue Initial state, can be undefined, value or function. If externalSetter not specified, will return default state\r\n * @param externalSetter External state setter. If specified will return external state\r\n */\r\nexport default function useMixedState<S>(initialStateOrValue?: InitialState<S>, externalSetter?: StateSetter<S>) {\r\n const isControlled = useMemo(() => arguments.length === 2 && externalSetter !== undefined, []);\r\n\r\n const [state, setter] = useState<S | undefined>(initialStateOrValue);\r\n\r\n\r\n\r\n // Propagate state update on external state update even if external setter is not provided\r\n useEffect(() => {\r\n if (!isControlled) {\r\n \r\n setter(initialStateOrValue);\r\n }\r\n }, [initialStateOrValue]);\r\n\r\n\r\n\r\n if (isControlled) {\r\n return [\r\n initialStateOrValue as S,\r\n externalSetter as StateSetter<S>\r\n ];\r\n }\r\n\r\n return [state, setter];\r\n}","'use client'\r\n\r\nimport { FC, ReactNode, useContext, useEffect, useLayoutEffect, useRef, useState } from 'react';\r\nimport { createPortal } from 'react-dom';\r\n\r\nimport useMixedState from './hooks/useMixedState';\r\nimport { cn, PopupContext, PopupWindowDisabledType, StateSetter } from './Interfaces';\r\n\r\n\r\n\r\ntype PopupWindowAnimationType = 'fade' | 'scale' | null\r\n\r\n\r\n\r\ninterface IPopupWindowProps {\r\n id: string\r\n children: ReactNode | ReactNode[]\r\n\r\n isOpen?: boolean\r\n setIsOpen?: StateSetter<boolean>\r\n\r\n className?: string\r\n layerClassName?: string\r\n disabled?: PopupWindowDisabledType[] | boolean\r\n\r\n /** \r\n * Popup dialog animation type\r\n * \r\n * @default \"fade\"\r\n */\r\n animation?: 'fade' | 'scale' | null\r\n\r\n /**\r\n * Popup animation duration in msec\r\n * \r\n * @default 200\r\n */\r\n animationDuraionMs?: number\r\n\r\n /** \r\n * Fire callback when popup invoked to open\r\n */\r\n onBeforeEnter?(): void\r\n\r\n /** \r\n * Fire callback when popup open animation fullfilled. \r\n * \r\n * @see animationDuration\r\n */\r\n onAfterEnter?(): void\r\n\r\n /** \r\n * Fire callback when popup invoked to close\r\n */\r\n onBeforeExit?(): void\r\n\r\n /** \r\n * Fire callback when popup close animation fullfilled. \r\n * \r\n * @see animationDuration\r\n */\r\n onAfterExit?(): void\r\n}\r\n\r\n\r\n\r\nconst PopupWindow: FC<IPopupWindowProps> = (props) => {\r\n const ctx = useContext(PopupContext);\r\n\r\n const [container, setContainer] = useState<HTMLDivElement | null>(null);\r\n const [isOpen, setIsOpen] = useMixedState(props.isOpen ?? false, props.setIsOpen);\r\n const [zIndex, setZIndex] = useState(-1);\r\n const [disabled, setDisabled] = useState<PopupWindowDisabledType[]>([]);\r\n\r\n const layerRef = useRef<HTMLDivElement>(null);\r\n\r\n const animation: PopupWindowAnimationType = props.animation !== undefined ? props.animation : 'fade';\r\n const animationDuration = props.animationDuraionMs ?? 200;\r\n\r\n\r\n\r\n // Handle disabled\r\n useLayoutEffect(() => {\r\n let disabled: PopupWindowDisabledType[] = [];\r\n\r\n if (typeof props.disabled === 'boolean') disabled = props.disabled ? ['onEscape', 'onLayer'] : [];\r\n else disabled = props.disabled ?? [];\r\n\r\n ctx.updateNodeProperty(props.id, 'disabled', disabled);\r\n }, [props.disabled]);\r\n\r\n\r\n\r\n // Mount & register node\r\n useEffect(() => {\r\n const container = ctx.containerRef.current;\r\n if (!container) return;\r\n\r\n setContainer(container);\r\n\r\n ctx.registerNode({\r\n id: props.id,\r\n isOpen: Boolean(props.isOpen),\r\n disabled\r\n });\r\n }, []);\r\n\r\n // Handle node sync with context\r\n useEffect(() => {\r\n const node = ctx.nodes.find(el => el.id === props.id);\r\n if (!node) return;\r\n\r\n setIsOpen(node.isOpen);\r\n setZIndex(node.zIndex);\r\n setDisabled(node.disabled);\r\n }, [ctx.nodes]);\r\n\r\n // Handle layer z-index change\r\n useEffect(() => {\r\n const layer = layerRef.current;\r\n if (!layer) return;\r\n\r\n if (isOpen) layer.style.zIndex = `${zIndex}`;\r\n else setTimeout(() => {\r\n layer.style.zIndex = `${-1}`;\r\n }, animationDuration);\r\n }, [isOpen]);\r\n\r\n // Handle events (onBeforeEnter, etc)\r\n useEffect(() => {\r\n if (isOpen) {\r\n if (props.onBeforeEnter) props.onBeforeEnter();\r\n\r\n setTimeout(() => {\r\n if (props.onAfterEnter) props.onAfterEnter();\r\n }, animationDuration);\r\n } else {\r\n if (props.onBeforeExit) props.onBeforeExit();\r\n\r\n setTimeout(() => {\r\n if (props.onAfterExit) props.onAfterExit();\r\n }, animationDuration);\r\n }\r\n }, [isOpen]);\r\n\r\n\r\n\r\n function layerOnClick() {\r\n if (disabled.includes('onLayer')) return;\r\n\r\n ctx.invokePopup(props.id, false);\r\n }\r\n\r\n\r\n\r\n return container && createPortal(<section\r\n className={cn(`neko-popup-backdrop`, isOpen && 'neko-popup-backdrop--active', props.layerClassName)}\r\n aria-hidden={!isOpen}\r\n style={{ transition: `${animationDuration}ms ease-in-out`, cursor: disabled.includes('onLayer') ? 'default' : 'pointer' }}\r\n onClick={layerOnClick}\r\n ref={layerRef}\r\n >\r\n <article\r\n id={props.id}\r\n className={cn(`neko-popup`, isOpen && 'neko-popup--active', animation && `neko-popup--animation_${animation}`, props.className)}\r\n role=\"dialog\"\r\n aria-modal\r\n onClick={e => e.stopPropagation()}\r\n >\r\n {props.children}\r\n </article>\r\n </section>, container);\r\n};\r\n\r\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]}
package/package.json ADDED
@@ -0,0 +1,71 @@
1
+ {
2
+ "name": "neko-popup",
3
+ "version": "3.0.0",
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
+ "type": "module",
16
+ "main": "./dist/index.umd.js",
17
+ "module": "./dist/index.es.js",
18
+ "types": "./dist/_package/index.d.ts",
19
+ "exports": {
20
+ ".": {
21
+ "types": "./dist/index.d.ts",
22
+ "import": "./dist/index.es.js",
23
+ "require": "./dist/index.umd.js"
24
+ },
25
+ "./css": "./dist/styles.css"
26
+ },
27
+ "files": [
28
+ "dist/"
29
+ ],
30
+ "scripts": {
31
+ "dev": "npx eslint . --fix --ext .ts,.tsx && npx vite",
32
+ "build": "npx rimraf dist && npx tsc && npx vite build"
33
+ },
34
+ "publishConfig": {
35
+ "access": "public"
36
+ },
37
+ "homepage": "https://github.com/nekomiclub/neko-popup#readme",
38
+ "bugs": {
39
+ "url": "https://github.com/nekomiclub/neko-popup/issues"
40
+ },
41
+ "repository": {
42
+ "type": "git",
43
+ "url": "git+https://github.com/nekomiclub/neko-popup.git"
44
+ },
45
+ "devDependencies": {
46
+ "@eslint/js": "^9.32.0",
47
+ "@tailwindcss/vite": "^4.1.16",
48
+ "@types/react": "^19.1.9",
49
+ "@types/react-dom": "^19.1.7",
50
+ "@typescript-eslint/eslint-plugin": "^8.56.1",
51
+ "@vitejs/plugin-react": "^4.7.0",
52
+ "clsx": "^2.1.1",
53
+ "eslint": "^9.32.0",
54
+ "eslint-plugin-import": "^2.32.0",
55
+ "eslint-plugin-path": "^2.0.3",
56
+ "eslint-plugin-react-hooks": "^7.0.1",
57
+ "eslint-plugin-react-refresh": "^0.5.2",
58
+ "eslint-plugin-simple-import-sort": "^12.1.1",
59
+ "eslint-plugin-unused-imports": "^4.1.4",
60
+ "husky": "^9.1.7",
61
+ "react": "^19.2.4",
62
+ "react-dom": "^19.2.4",
63
+ "rimraf": "^6.0.1",
64
+ "sass": "^1.97.3",
65
+ "tailwindcss": "^4.1.16",
66
+ "typescript": "^5.9.3",
67
+ "typescript-eslint": "^8.56.1",
68
+ "vite": "^7.3.1",
69
+ "vite-plugin-dts": "^4.5.4"
70
+ }
71
+ }