neko-popup 3.0.8 → 3.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.es.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { jsx as E, jsxs as w } from "react/jsx-runtime";
2
- import { createContext as B, useContext as O, useState as f, useEffect as l, useRef as g, useMemo as L, useLayoutEffect as N } from "react";
2
+ import { createContext as B, useContext as O, useState as f, useEffect as l, useRef as P, useMemo as L, useLayoutEffect as N } from "react";
3
3
  import { createPortal as z } from "react-dom";
4
4
  function C(e) {
5
5
  var o, s, t = "";
@@ -14,13 +14,13 @@ function M() {
14
14
  for (var e, o, s = 0, t = "", a = arguments.length; s < a; s++) (e = arguments[s]) && (o = C(e)) && (t && (t += " "), t += o);
15
15
  return t;
16
16
  }
17
- const I = B({}), A = M, T = (e) => {
17
+ const I = B({}), g = M, T = (e) => {
18
18
  const o = O(I), [s, t] = f(!1), a = e.as ?? "button";
19
19
  l(() => {
20
- const c = o.nodes.find((y) => y.id === e.popupId);
20
+ const c = o.nodes.find((b) => b.id === e.popupId);
21
21
  c && t(c.isOpen);
22
22
  }, [o]);
23
- function b(c) {
23
+ function m(c) {
24
24
  o.invokePopup(e.popupId), e.onClick && e.onClick(c);
25
25
  }
26
26
  return /* @__PURE__ */ E(
@@ -31,8 +31,8 @@ const I = B({}), A = M, T = (e) => {
31
31
  "aria-disabled": e.disabled,
32
32
  "aria-haspopup": "dialog",
33
33
  id: e.id,
34
- className: A("neko-popup-button", s && "neko-popup-button--active", e.className),
35
- onClick: b,
34
+ className: g("neko-popup-button", s && "neko-popup-button--active", e.className),
35
+ onClick: m,
36
36
  children: e.children
37
37
  }
38
38
  );
@@ -43,7 +43,7 @@ class S extends Error {
43
43
  }
44
44
  }
45
45
  const Z = (e) => {
46
- const o = e.baseZIndex ?? 1e4, s = e.disableBodyScrollOnActivePopup ?? !0, [t, a] = f([]), [b, c] = f(!1), [y, m] = f(!1), x = g(null);
46
+ const o = e.baseZIndex ?? 1e4, s = e.disableBodyScrollOnActivePopup ?? !0, [t, a] = f([]), [m, c] = f(!1), [b, p] = f(!1), x = P(null);
47
47
  l(() => {
48
48
  const i = new AbortController();
49
49
  if (window.addEventListener("keydown", (d) => {
@@ -60,33 +60,33 @@ const Z = (e) => {
60
60
  i.abort();
61
61
  };
62
62
  }, [t]), l(() => {
63
- m(t.some((i) => i.isOpen));
63
+ p(t.some((i) => i.isOpen));
64
64
  }, [t]), l(() => {
65
- b ? document.body.classList.add("neko-popup--noScroll") : document.body.classList.remove("neko-popup--noScroll");
66
- }, [b]);
65
+ m ? document.body.classList.add("neko-popup--noScroll") : document.body.classList.remove("neko-popup--noScroll");
66
+ }, [m]);
67
67
  function k(i, d) {
68
68
  const n = typeof i == "string" ? t.find((u) => u.id === i) : i;
69
69
  if (!n) throw new S(typeof i == "string" ? `Cannot find popup node with id #${i}` : "Entity is not assigned to the node");
70
70
  const r = d ?? !n.isOpen;
71
71
  n.isOpen = r, n.zIndex = r ? Math.max(...t.map((u) => u.zIndex), 0) + 1 : -1, v(n);
72
72
  }
73
- function P(i, d, n) {
73
+ function A(i, d, n) {
74
74
  const r = t.find((u) => u.id === i);
75
75
  r && (r[d] = n, v(r));
76
76
  }
77
77
  function v(i) {
78
78
  a((d) => [...d.filter((n) => n.id !== i.id), i]);
79
79
  }
80
- const p = ({ id: i, isOpen: d, disabled: n }) => k({ id: i, isOpen: !1, disabled: n, zIndex: -1 }, d);
80
+ const y = ({ id: i, isOpen: d, disabled: n }) => k({ id: i, isOpen: !1, disabled: n, zIndex: -1 }, d);
81
81
  return /* @__PURE__ */ w(I.Provider, { value: {
82
82
  nodes: t,
83
83
  containerRef: x,
84
84
  invokePopup: k,
85
- mountNode: p,
86
- updateNodeProperty: P
85
+ mountNode: y,
86
+ updateNodeProperty: A
87
87
  }, children: [
88
88
  e.children,
89
- /* @__PURE__ */ E("section", { className: A("neko-popup-layer", y && "neko-popup-layer--active"), style: { zIndex: o }, ref: x })
89
+ /* @__PURE__ */ E("section", { className: g("neko-popup-layer", b && "neko-popup-layer--active"), style: { zIndex: o }, ref: x })
90
90
  ] });
91
91
  };
92
92
  function F(e, o) {
@@ -99,7 +99,7 @@ function F(e, o) {
99
99
  ] : [t, a];
100
100
  }
101
101
  const W = (e) => {
102
- const o = O(I), [s, t] = f(null), [a, b] = F(e.isOpen ?? !1, e.setIsOpen), [c, y] = f(!!e.isOpen), [m, x] = f(!!e.isOpen), [k, P] = f([]), v = g(null), p = g(!0), i = e.animation !== void 0 ? e.animation : "fade";
102
+ const o = O(I), [s, t] = f(null), [a, m] = F(e.isOpen ?? !1, e.setIsOpen), [c, b] = f(!!e.isOpen), [p, x] = f(!!e.isOpen), [k, A] = f([]), v = P(null), y = P(!0), i = e.animation !== void 0 ? e.animation : "fade";
103
103
  N(() => {
104
104
  let n = [];
105
105
  typeof e.disabled == "boolean" ? n = e.disabled ? ["onEscape", "onLayer"] : [] : n = e.disabled ?? [], o.updateNodeProperty(e.id, "disabled", n);
@@ -114,9 +114,9 @@ const W = (e) => {
114
114
  o.nodes.some((n) => n.id === e.id) && o.invokePopup(e.id, e.isOpen);
115
115
  }, [e.isOpen]), l(() => {
116
116
  const n = o.nodes.find((r) => r.id === e.id);
117
- n && (b(n.isOpen), P(n.disabled));
117
+ n && (m(n.isOpen), A(n.disabled));
118
118
  }, [o.nodes]), l(() => {
119
- p.current = !1, a ? (e.onBeforeEnter && e.onBeforeEnter(), y(!0)) : (e.onBeforeExit && e.onBeforeExit(), x(!1));
119
+ y.current = !1, a ? (e.onBeforeEnter && e.onBeforeEnter(), b(!0)) : (e.onBeforeExit && e.onBeforeExit(), x(!1));
120
120
  }, [a]), l(() => {
121
121
  const n = v.current;
122
122
  n && n.addEventListener("mousedown", (r) => {
@@ -130,13 +130,13 @@ const W = (e) => {
130
130
  return () => cancelAnimationFrame(n);
131
131
  }, [c, a]);
132
132
  function d(n) {
133
- m && a && (e.onAfterEnter && !p.current && e.onAfterEnter(n), p.current = !0), !m && !a && (e.onAfterExit && !p.current && e.onAfterExit(n), y(!1), p.current = !0);
133
+ n.target.className.includes("neko-popup-backdrop") && (p && a && e.onAfterEnter && !y.current && e.onAfterEnter(n), !p && !a && (e.onAfterExit && !y.current && e.onAfterExit(n), b(!1)), y.current = !0);
134
134
  }
135
135
  return c ? s && z(/* @__PURE__ */ E(
136
136
  "section",
137
137
  {
138
- className: A("neko-popup-backdrop", m && "neko-popup-backdrop--active", e.layerClassName),
139
- "aria-hidden": !m,
138
+ className: g("neko-popup-backdrop", p && "neko-popup-backdrop--active", e.layerClassName),
139
+ "aria-hidden": !p,
140
140
  style: { cursor: k.includes("onLayer") ? "default" : "pointer" },
141
141
  onTransitionEnd: d,
142
142
  ref: v,
@@ -144,7 +144,7 @@ const W = (e) => {
144
144
  "article",
145
145
  {
146
146
  id: e.id,
147
- className: A("neko-popup", m && "neko-popup--active", i && `neko-popup--animation_${i}`, e.className),
147
+ className: g("neko-popup", p && "neko-popup--active", i && `neko-popup--animation_${i}`, e.className),
148
148
  role: "dialog",
149
149
  "aria-modal": !0,
150
150
  onClick: (n) => n.stopPropagation(),
@@ -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\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?(ev: React.TransitionEvent): void\n\n /** \n * Fire callback when popup started hide animation or at initial mount\n */\n onBeforeExit?(): void\n\n /** \n * Fire callback when popup become unmount\n */\n onAfterExit?(ev: React.TransitionEvent): 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 const isAnimationFinished = useRef(true);\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;\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 setDisabled(node.disabled);\n }, [ctx.nodes]);\n\n // Handle events on open moun and on close animation exit\n useEffect(() => {\n isAnimationFinished.current = false;\n\n if (isOpen) {\n // On Before Enter\n if (props.onBeforeEnter) props.onBeforeEnter();\n\n setIsMounted(true);\n } else {\n // On Before Exit\n if (props.onBeforeExit) props.onBeforeExit();\n\n setIsVisible(false);\n }\n }, [isOpen]);\n\n // Handle layer clicks\n useEffect(() => {\n const layer = layerRef.current;\n if (!layer) return;\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 useLayoutEffect(() => {\n if (!isMounted || !isOpen) return;\n\n const id = requestAnimationFrame(() => {\n setIsVisible(true);\n });\n\n return () => cancelAnimationFrame(id);\n }, [isMounted, isOpen]);\n\n\n\n function handleTransitionEnd(ev: React.TransitionEvent) {\n // On After Enter\n if (isVisible && isOpen) {\n if (props.onAfterEnter && !isAnimationFinished.current) props.onAfterEnter(ev);\n\n isAnimationFinished.current = true;\n }\n\n // On After Exit\n if (!isVisible && !isOpen) {\n if (props.onAfterExit && !isAnimationFinished.current) props.onAfterExit(ev);\n\n setIsMounted(false);\n\n isAnimationFinished.current = true;\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","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","isMounted","setIsMounted","isVisible","setIsVisible","setDisabled","layerRef","isAnimationFinished","animation","useLayoutEffect","layer","ev","handleTransitionEnd","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?(ev: React.TransitionEvent): void\n\n /** \n * Fire callback when popup started hide animation or at initial mount\n */\n onBeforeExit?(): void\n\n /** \n * Fire callback when popup become unmount\n */\n onAfterExit?(ev: React.TransitionEvent): 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 const isAnimationFinished = useRef(true);\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;\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 setDisabled(node.disabled);\n }, [ctx.nodes]);\n\n // Handle events on open moun and on close animation exit\n useEffect(() => {\n isAnimationFinished.current = false;\n\n if (isOpen) {\n // On Before Enter\n if (props.onBeforeEnter) props.onBeforeEnter();\n\n setIsMounted(true);\n } else {\n // On Before Exit\n if (props.onBeforeExit) props.onBeforeExit();\n\n setIsVisible(false);\n }\n }, [isOpen]);\n\n // Handle layer clicks\n useEffect(() => {\n const layer = layerRef.current;\n if (!layer) return;\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 useLayoutEffect(() => {\n if (!isMounted || !isOpen) return;\n\n const id = requestAnimationFrame(() => {\n setIsVisible(true);\n });\n\n return () => cancelAnimationFrame(id);\n }, [isMounted, isOpen]);\n\n\n\n function handleTransitionEnd(ev: React.TransitionEvent) {\n // Skip buble events\n if (!(ev.target as HTMLElement).className.includes('neko-popup-backdrop')) return;\n\n // On After Enter\n if (isVisible && isOpen) {\n if (props.onAfterEnter && !isAnimationFinished.current) props.onAfterEnter(ev);\n }\n\n // On After Exit\n if (!isVisible && !isOpen) {\n if (props.onAfterExit && !isAnimationFinished.current) props.onAfterExit(ev);\n\n setIsMounted(false);\n }\n\n isAnimationFinished.current = true;\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","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","isMounted","setIsMounted","isVisible","setIsVisible","setDisabled","layerRef","isAnimationFinished","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,r){typeof exports=="object"&&typeof module<"u"?r(exports,require("react/jsx-runtime"),require("react"),require("react-dom")):typeof define=="function"&&define.amd?define(["exports","react/jsx-runtime","react","react-dom"],r):(c=typeof globalThis<"u"?globalThis:c||self,r(c["neko-popup"]={},c.jsxRuntime,c.React,c.ReactDOM))})(this,(function(c,r,o,I){"use strict";function O(e){var i,d,t="";if(typeof e=="string"||typeof e=="number")t+=e;else if(typeof e=="object")if(Array.isArray(e)){var u=e.length;for(i=0;i<u;i++)e[i]&&(d=O(e[i]))&&(t&&(t+=" "),t+=d)}else for(d in e)e[d]&&(t&&(t+=" "),t+=d);return t}function S(){for(var e,i,d=0,t="",u=arguments.length;d<u;d++)(e=arguments[d])&&(i=O(e))&&(t&&(t+=" "),t+=i);return t}const A=o.createContext({}),P=S,N=e=>{const i=o.useContext(A),[d,t]=o.useState(!1),u=e.as??"button";o.useEffect(()=>{const l=i.nodes.find(E=>E.id===e.popupId);l&&t(l.isOpen)},[i]);function y(l){i.invokePopup(e.popupId),e.onClick&&e.onClick(l)}return r.jsx(u,{tabIndex:0,disabled:e.disabled,"aria-disabled":e.disabled,"aria-haspopup":"dialog",id:e.id,className:P("neko-popup-button",d&&"neko-popup-button--active",e.className),onClick:y,children:e.children})};class C extends Error{constructor(i){super(i),this.name="error at [neko-popup]"}}const w=e=>{const i=e.baseZIndex??1e4,d=e.disableBodyScrollOnActivePopup??!0,[t,u]=o.useState([]),[y,l]=o.useState(!1),[E,m]=o.useState(!1),x=o.useRef(null);o.useEffect(()=>{const s=new AbortController;if(window.addEventListener("keydown",a=>{if(a.key==="Escape"){const f=Math.max(...t.filter(h=>h.isOpen).map(h=>h.zIndex)),p=t.find(h=>h.zIndex===f);if(!p||p.disabled.includes("onEscape"))return;k(p.id,!1)}},{signal:s.signal}),d){let a=!1;t.forEach(n=>n.isOpen?a=!0:null),l(a)}return()=>{s.abort()}},[t]),o.useEffect(()=>{m(t.some(s=>s.isOpen))},[t]),o.useEffect(()=>{y?document.body.classList.add("neko-popup--noScroll"):document.body.classList.remove("neko-popup--noScroll")},[y]);function k(s,a){const n=typeof s=="string"?t.find(p=>p.id===s):s;if(!n)throw new C(typeof s=="string"?`Cannot find popup node with id #${s}`:"Entity is not assigned to the node");const f=a??!n.isOpen;n.isOpen=f,n.zIndex=f?Math.max(...t.map(p=>p.zIndex),0)+1:-1,v(n)}function g(s,a,n){const f=t.find(p=>p.id===s);f&&(f[a]=n,v(f))}function v(s){u(a=>[...a.filter(n=>n.id!==s.id),s])}const b=({id:s,isOpen:a,disabled:n})=>k({id:s,isOpen:!1,disabled:n,zIndex:-1},a);return r.jsxs(A.Provider,{value:{nodes:t,containerRef:x,invokePopup:k,mountNode:b,updateNodeProperty:g},children:[e.children,r.jsx("section",{className:P("neko-popup-layer",E&&"neko-popup-layer--active"),style:{zIndex:i},ref:x})]})};function B(e,i){const d=o.useMemo(()=>arguments.length===2&&i!==void 0,[]),[t,u]=o.useState(e);return o.useEffect(()=>{d||u(e)},[e]),d?[e,i]:[t,u]}const L=e=>{const i=o.useContext(A),[d,t]=o.useState(null),[u,y]=B(e.isOpen??!1,e.setIsOpen),[l,E]=o.useState(!!e.isOpen),[m,x]=o.useState(!!e.isOpen),[k,g]=o.useState([]),v=o.useRef(null),b=o.useRef(!0),s=e.animation!==void 0?e.animation:"fade";o.useLayoutEffect(()=>{let n=[];typeof e.disabled=="boolean"?n=e.disabled?["onEscape","onLayer"]:[]:n=e.disabled??[],i.updateNodeProperty(e.id,"disabled",n)},[e.disabled]),o.useEffect(()=>{const n=i.containerRef.current;n&&(t(n),i.mountNode({id:e.id,isOpen:!!e.isOpen,disabled:k}))},[]),o.useEffect(()=>{i.nodes.some(n=>n.id===e.id)&&i.invokePopup(e.id,e.isOpen)},[e.isOpen]),o.useEffect(()=>{const n=i.nodes.find(f=>f.id===e.id);n&&(y(n.isOpen),g(n.disabled))},[i.nodes]),o.useEffect(()=>{b.current=!1,u?(e.onBeforeEnter&&e.onBeforeEnter(),E(!0)):(e.onBeforeExit&&e.onBeforeExit(),x(!1))},[u]),o.useEffect(()=>{const n=v.current;n&&n.addEventListener("mousedown",f=>{f.target.closest(".neko-popup")||i.invokePopup(e.id,!1)})},[l]),o.useLayoutEffect(()=>{if(!l||!u)return;const n=requestAnimationFrame(()=>{x(!0)});return()=>cancelAnimationFrame(n)},[l,u]);function a(n){m&&u&&(e.onAfterEnter&&!b.current&&e.onAfterEnter(n),b.current=!0),!m&&!u&&(e.onAfterExit&&!b.current&&e.onAfterExit(n),E(!1),b.current=!0)}return l?d&&I.createPortal(r.jsx("section",{className:P("neko-popup-backdrop",m&&"neko-popup-backdrop--active",e.layerClassName),"aria-hidden":!m,style:{cursor:k.includes("onLayer")?"default":"pointer"},onTransitionEnd:a,ref:v,children:r.jsx("article",{id:e.id,className:P("neko-popup",m&&"neko-popup--active",s&&`neko-popup--animation_${s}`,e.className),role:"dialog","aria-modal":!0,onClick:n=>n.stopPropagation(),children:e.children})}),d):null};c.PopupButton=N,c.PopupLayer=w,c.PopupWindow=L,Object.defineProperty(c,Symbol.toStringTag,{value:"Module"})}));
1
+ (function(f,r){typeof exports=="object"&&typeof module<"u"?r(exports,require("react/jsx-runtime"),require("react"),require("react-dom")):typeof define=="function"&&define.amd?define(["exports","react/jsx-runtime","react","react-dom"],r):(f=typeof globalThis<"u"?globalThis:f||self,r(f["neko-popup"]={},f.jsxRuntime,f.React,f.ReactDOM))})(this,(function(f,r,o,I){"use strict";function O(e){var i,d,t="";if(typeof e=="string"||typeof e=="number")t+=e;else if(typeof e=="object")if(Array.isArray(e)){var u=e.length;for(i=0;i<u;i++)e[i]&&(d=O(e[i]))&&(t&&(t+=" "),t+=d)}else for(d in e)e[d]&&(t&&(t+=" "),t+=d);return t}function N(){for(var e,i,d=0,t="",u=arguments.length;d<u;d++)(e=arguments[d])&&(i=O(e))&&(t&&(t+=" "),t+=i);return t}const A=o.createContext({}),P=N,S=e=>{const i=o.useContext(A),[d,t]=o.useState(!1),u=e.as??"button";o.useEffect(()=>{const l=i.nodes.find(y=>y.id===e.popupId);l&&t(l.isOpen)},[i]);function b(l){i.invokePopup(e.popupId),e.onClick&&e.onClick(l)}return r.jsx(u,{tabIndex:0,disabled:e.disabled,"aria-disabled":e.disabled,"aria-haspopup":"dialog",id:e.id,className:P("neko-popup-button",d&&"neko-popup-button--active",e.className),onClick:b,children:e.children})};class C extends Error{constructor(i){super(i),this.name="error at [neko-popup]"}}const w=e=>{const i=e.baseZIndex??1e4,d=e.disableBodyScrollOnActivePopup??!0,[t,u]=o.useState([]),[b,l]=o.useState(!1),[y,m]=o.useState(!1),x=o.useRef(null);o.useEffect(()=>{const s=new AbortController;if(window.addEventListener("keydown",a=>{if(a.key==="Escape"){const c=Math.max(...t.filter(v=>v.isOpen).map(v=>v.zIndex)),p=t.find(v=>v.zIndex===c);if(!p||p.disabled.includes("onEscape"))return;k(p.id,!1)}},{signal:s.signal}),d){let a=!1;t.forEach(n=>n.isOpen?a=!0:null),l(a)}return()=>{s.abort()}},[t]),o.useEffect(()=>{m(t.some(s=>s.isOpen))},[t]),o.useEffect(()=>{b?document.body.classList.add("neko-popup--noScroll"):document.body.classList.remove("neko-popup--noScroll")},[b]);function k(s,a){const n=typeof s=="string"?t.find(p=>p.id===s):s;if(!n)throw new C(typeof s=="string"?`Cannot find popup node with id #${s}`:"Entity is not assigned to the node");const c=a??!n.isOpen;n.isOpen=c,n.zIndex=c?Math.max(...t.map(p=>p.zIndex),0)+1:-1,h(n)}function g(s,a,n){const c=t.find(p=>p.id===s);c&&(c[a]=n,h(c))}function h(s){u(a=>[...a.filter(n=>n.id!==s.id),s])}const E=({id:s,isOpen:a,disabled:n})=>k({id:s,isOpen:!1,disabled:n,zIndex:-1},a);return r.jsxs(A.Provider,{value:{nodes:t,containerRef:x,invokePopup:k,mountNode:E,updateNodeProperty:g},children:[e.children,r.jsx("section",{className:P("neko-popup-layer",y&&"neko-popup-layer--active"),style:{zIndex:i},ref:x})]})};function B(e,i){const d=o.useMemo(()=>arguments.length===2&&i!==void 0,[]),[t,u]=o.useState(e);return o.useEffect(()=>{d||u(e)},[e]),d?[e,i]:[t,u]}const L=e=>{const i=o.useContext(A),[d,t]=o.useState(null),[u,b]=B(e.isOpen??!1,e.setIsOpen),[l,y]=o.useState(!!e.isOpen),[m,x]=o.useState(!!e.isOpen),[k,g]=o.useState([]),h=o.useRef(null),E=o.useRef(!0),s=e.animation!==void 0?e.animation:"fade";o.useLayoutEffect(()=>{let n=[];typeof e.disabled=="boolean"?n=e.disabled?["onEscape","onLayer"]:[]:n=e.disabled??[],i.updateNodeProperty(e.id,"disabled",n)},[e.disabled]),o.useEffect(()=>{const n=i.containerRef.current;n&&(t(n),i.mountNode({id:e.id,isOpen:!!e.isOpen,disabled:k}))},[]),o.useEffect(()=>{i.nodes.some(n=>n.id===e.id)&&i.invokePopup(e.id,e.isOpen)},[e.isOpen]),o.useEffect(()=>{const n=i.nodes.find(c=>c.id===e.id);n&&(b(n.isOpen),g(n.disabled))},[i.nodes]),o.useEffect(()=>{E.current=!1,u?(e.onBeforeEnter&&e.onBeforeEnter(),y(!0)):(e.onBeforeExit&&e.onBeforeExit(),x(!1))},[u]),o.useEffect(()=>{const n=h.current;n&&n.addEventListener("mousedown",c=>{c.target.closest(".neko-popup")||i.invokePopup(e.id,!1)})},[l]),o.useLayoutEffect(()=>{if(!l||!u)return;const n=requestAnimationFrame(()=>{x(!0)});return()=>cancelAnimationFrame(n)},[l,u]);function a(n){n.target.className.includes("neko-popup-backdrop")&&(m&&u&&e.onAfterEnter&&!E.current&&e.onAfterEnter(n),!m&&!u&&(e.onAfterExit&&!E.current&&e.onAfterExit(n),y(!1)),E.current=!0)}return l?d&&I.createPortal(r.jsx("section",{className:P("neko-popup-backdrop",m&&"neko-popup-backdrop--active",e.layerClassName),"aria-hidden":!m,style:{cursor:k.includes("onLayer")?"default":"pointer"},onTransitionEnd:a,ref:h,children:r.jsx("article",{id:e.id,className:P("neko-popup",m&&"neko-popup--active",s&&`neko-popup--animation_${s}`,e.className),role:"dialog","aria-modal":!0,onClick:n=>n.stopPropagation(),children:e.children})}),d):null};f.PopupButton=S,f.PopupLayer=w,f.PopupWindow=L,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\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?(ev: React.TransitionEvent): void\n\n /** \n * Fire callback when popup started hide animation or at initial mount\n */\n onBeforeExit?(): void\n\n /** \n * Fire callback when popup become unmount\n */\n onAfterExit?(ev: React.TransitionEvent): 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 const isAnimationFinished = useRef(true);\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;\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 setDisabled(node.disabled);\n }, [ctx.nodes]);\n\n // Handle events on open moun and on close animation exit\n useEffect(() => {\n isAnimationFinished.current = false;\n\n if (isOpen) {\n // On Before Enter\n if (props.onBeforeEnter) props.onBeforeEnter();\n\n setIsMounted(true);\n } else {\n // On Before Exit\n if (props.onBeforeExit) props.onBeforeExit();\n\n setIsVisible(false);\n }\n }, [isOpen]);\n\n // Handle layer clicks\n useEffect(() => {\n const layer = layerRef.current;\n if (!layer) return;\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 useLayoutEffect(() => {\n if (!isMounted || !isOpen) return;\n\n const id = requestAnimationFrame(() => {\n setIsVisible(true);\n });\n\n return () => cancelAnimationFrame(id);\n }, [isMounted, isOpen]);\n\n\n\n function handleTransitionEnd(ev: React.TransitionEvent) {\n // On After Enter\n if (isVisible && isOpen) {\n if (props.onAfterEnter && !isAnimationFinished.current) props.onAfterEnter(ev);\n\n isAnimationFinished.current = true;\n }\n\n // On After Exit\n if (!isVisible && !isOpen) {\n if (props.onAfterExit && !isAnimationFinished.current) props.onAfterExit(ev);\n\n setIsMounted(false);\n\n isAnimationFinished.current = true;\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","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","isMounted","setIsMounted","isVisible","setIsVisible","setDisabled","layerRef","isAnimationFinished","animation","useLayoutEffect","layer","ev","handleTransitionEnd","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?(ev: React.TransitionEvent): void\n\n /** \n * Fire callback when popup started hide animation or at initial mount\n */\n onBeforeExit?(): void\n\n /** \n * Fire callback when popup become unmount\n */\n onAfterExit?(ev: React.TransitionEvent): 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 const isAnimationFinished = useRef(true);\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;\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 setDisabled(node.disabled);\n }, [ctx.nodes]);\n\n // Handle events on open moun and on close animation exit\n useEffect(() => {\n isAnimationFinished.current = false;\n\n if (isOpen) {\n // On Before Enter\n if (props.onBeforeEnter) props.onBeforeEnter();\n\n setIsMounted(true);\n } else {\n // On Before Exit\n if (props.onBeforeExit) props.onBeforeExit();\n\n setIsVisible(false);\n }\n }, [isOpen]);\n\n // Handle layer clicks\n useEffect(() => {\n const layer = layerRef.current;\n if (!layer) return;\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 useLayoutEffect(() => {\n if (!isMounted || !isOpen) return;\n\n const id = requestAnimationFrame(() => {\n setIsVisible(true);\n });\n\n return () => cancelAnimationFrame(id);\n }, [isMounted, isOpen]);\n\n\n\n function handleTransitionEnd(ev: React.TransitionEvent) {\n // Skip buble events\n if (!(ev.target as HTMLElement).className.includes('neko-popup-backdrop')) return;\n\n // On After Enter\n if (isVisible && isOpen) {\n if (props.onAfterEnter && !isAnimationFinished.current) props.onAfterEnter(ev);\n }\n\n // On After Exit\n if (!isVisible && !isOpen) {\n if (props.onAfterExit && !isAnimationFinished.current) props.onAfterExit(ev);\n\n setIsMounted(false);\n }\n\n isAnimationFinished.current = true;\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","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","isMounted","setIsMounted","isVisible","setIsVisible","setDisabled","layerRef","isAnimationFinished","animation","useLayoutEffect","layer","ev","handleTransitionEnd","createPortal"],"mappings":"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","x_google_ignoreList":[0]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neko-popup",
3
- "version": "3.0.8",
3
+ "version": "3.0.9",
4
4
  "description": "Simple and cool react popup package 🚀",
5
5
  "type": "module",
6
6
  "main": "./dist/index.umd.js",