@mshafiqyajid/react-modal 0.2.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -101,3 +101,11 @@ Set `data-theme="dark"` on any ancestor element — no `prefers-color-scheme` qu
101
101
  ## License
102
102
 
103
103
  MIT
104
+
105
+ ## What's new in 0.3.0
106
+
107
+ - **Stacked modals** — depth-aware z-index + behind-scale-down effect. Open a second modal and the first scales/translates back automatically.
108
+ - **Transition variants** — `transition: "fade" | "zoom" | "slide-up" | "slide-down"` (default `"fade"`). Drawers keep their slide-from-edge transition.
109
+ - **`confirm()` programmatic utility** — `import { confirm } from "@mshafiqyajid/react-modal/styled"`. Self-mounts a one-off modal with `Cancel` / `Confirm` buttons (with `danger` variant). Returns `Promise<boolean>`.
110
+ - **Swipe-to-dismiss** — `swipeToDismiss?: boolean` (default `true` for `drawer-bottom`). Touch-drag down past 120 px to dismiss.
111
+ - **`closeOnSubmit?: boolean`** — when the modal contains a `<form>` and it submits successfully, auto-close (skipped if the consumer's onSubmit calls `e.preventDefault()`).
package/dist/styled.cjs CHANGED
@@ -3,6 +3,7 @@
3
3
  var react = require('react');
4
4
  var reactDom = require('react-dom');
5
5
  var jsxRuntime = require('react/jsx-runtime');
6
+ var client = require('react-dom/client');
6
7
 
7
8
  // src/styled/ModalStyled.tsx
8
9
  var FOCUSABLE_SELECTORS = [
@@ -64,6 +65,12 @@ function useFocusTrap() {
64
65
  }, []);
65
66
  return { activate, deactivate, handleKeyDown };
66
67
  }
68
+ var _openModals = [];
69
+ var _modalIdCounter = 0;
70
+ var _stackListeners = /* @__PURE__ */ new Set();
71
+ function _notifyStack() {
72
+ _stackListeners.forEach((fn) => fn());
73
+ }
67
74
  var ModalStyled = react.forwardRef(
68
75
  function ModalStyled2({
69
76
  open,
@@ -89,8 +96,16 @@ var ModalStyled = react.forwardRef(
89
96
  onAfterClose,
90
97
  preventClose,
91
98
  lockBodyScroll = true,
92
- container
99
+ container,
100
+ transition = "fade",
101
+ swipeToDismiss,
102
+ closeOnSubmit = false
93
103
  }, ref) {
104
+ const swipeEnabled = swipeToDismiss ?? variant === "drawer-bottom";
105
+ const myStackIdRef = react.useRef(-1);
106
+ const [stackDepth, setStackDepth] = react.useState(0);
107
+ const [stackPosition, setStackPosition] = react.useState(0);
108
+ const [swipeY, setSwipeY] = react.useState(0);
94
109
  const titleId = react.useId();
95
110
  const descId = react.useId();
96
111
  const isActuallyOpen = open ?? isOpen ?? false;
@@ -159,6 +174,53 @@ var ModalStyled = react.forwardRef(
159
174
  }
160
175
  };
161
176
  }, [lockBodyScroll]);
177
+ react.useEffect(() => {
178
+ if (!isActuallyOpen) return;
179
+ const id = ++_modalIdCounter;
180
+ myStackIdRef.current = id;
181
+ _openModals.push({ id });
182
+ _notifyStack();
183
+ const sync = () => {
184
+ const idx = _openModals.findIndex((m) => m.id === id);
185
+ const total = _openModals.length;
186
+ setStackDepth(total);
187
+ setStackPosition(idx === -1 ? 0 : total - 1 - idx);
188
+ };
189
+ sync();
190
+ _stackListeners.add(sync);
191
+ return () => {
192
+ _stackListeners.delete(sync);
193
+ const idx = _openModals.findIndex((m) => m.id === id);
194
+ if (idx !== -1) _openModals.splice(idx, 1);
195
+ _notifyStack();
196
+ };
197
+ }, [isActuallyOpen]);
198
+ react.useEffect(() => {
199
+ if (!closeOnSubmit || !isActuallyOpen || !panelRef.current) return;
200
+ const panel = panelRef.current;
201
+ const onSubmit = (e) => {
202
+ if (e.defaultPrevented) return;
203
+ setTimeout(() => requestClose("submit"), 0);
204
+ };
205
+ panel.addEventListener("submit", onSubmit);
206
+ return () => panel.removeEventListener("submit", onSubmit);
207
+ }, [closeOnSubmit, isActuallyOpen, requestClose]);
208
+ const swipeStartYRef = react.useRef(null);
209
+ const handleSwipePointerDown = (e) => {
210
+ if (!swipeEnabled || e.pointerType !== "touch") return;
211
+ swipeStartYRef.current = e.clientY;
212
+ };
213
+ const handleSwipePointerMove = (e) => {
214
+ if (swipeStartYRef.current === null) return;
215
+ const dy = Math.max(0, e.clientY - swipeStartYRef.current);
216
+ setSwipeY(dy);
217
+ };
218
+ const handleSwipePointerUp = () => {
219
+ if (swipeStartYRef.current === null) return;
220
+ if (swipeY > 120) requestClose("swipe");
221
+ setSwipeY(0);
222
+ swipeStartYRef.current = null;
223
+ };
162
224
  const previouslyFocusedRef = react.useRef(null);
163
225
  react.useEffect(() => {
164
226
  if (isActuallyOpen) {
@@ -209,6 +271,19 @@ var ModalStyled = react.forwardRef(
209
271
  if (!mounted || !rendered) return null;
210
272
  const hasHeader = title !== void 0 || showCloseButton;
211
273
  const portalTarget = container ?? document.body;
274
+ const overlayStyle = {
275
+ ...overlayColor ? { "--rmod-overlay-bg": overlayColor } : {},
276
+ // Higher stack depth → lower modals scale down + fade slightly behind the topmost.
277
+ zIndex: 1e3 + stackDepth * 10
278
+ };
279
+ const panelStyle = {
280
+ // Stack-position effects: only applied when this isn't the topmost modal.
281
+ ...stackPosition > 0 ? {
282
+ transform: `translateY(${-stackPosition * 8}px) scale(${1 - stackPosition * 0.03})`,
283
+ opacity: Math.max(0.6, 1 - stackPosition * 0.18)
284
+ } : {},
285
+ ...swipeY > 0 ? { transform: `translateY(${swipeY}px)`, transition: "none" } : {}
286
+ };
212
287
  return reactDom.createPortal(
213
288
  /* @__PURE__ */ jsxRuntime.jsx(
214
289
  "div",
@@ -219,8 +294,10 @@ var ModalStyled = react.forwardRef(
219
294
  ].filter(Boolean).join(" "),
220
295
  "data-variant": variant,
221
296
  "data-blur": blur,
297
+ "data-transition": transition,
298
+ "data-stack-position": stackPosition || void 0,
222
299
  "data-state": visible ? "open" : "closed",
223
- style: overlayColor ? { "--rmod-overlay-bg": overlayColor } : void 0,
300
+ style: overlayStyle,
224
301
  onClick: handleOverlayClick,
225
302
  onKeyDown,
226
303
  children: /* @__PURE__ */ jsxRuntime.jsxs(
@@ -241,7 +318,14 @@ var ModalStyled = react.forwardRef(
241
318
  "data-variant": variant,
242
319
  "data-padding": padding,
243
320
  "data-scrollable": scrollable ? "true" : void 0,
321
+ "data-transition": transition,
244
322
  "data-state": visible ? "open" : "closed",
323
+ "data-swiping": swipeY > 0 ? "true" : void 0,
324
+ style: panelStyle,
325
+ onPointerDown: swipeEnabled ? handleSwipePointerDown : void 0,
326
+ onPointerMove: swipeEnabled ? handleSwipePointerMove : void 0,
327
+ onPointerUp: swipeEnabled ? handleSwipePointerUp : void 0,
328
+ onPointerCancel: swipeEnabled ? handleSwipePointerUp : void 0,
245
329
  children: [
246
330
  hasHeader && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rmod-header", children: [
247
331
  title ? /* @__PURE__ */ jsxRuntime.jsx("h2", { id: titleId, className: "rmod-title", children: title }) : /* @__PURE__ */ jsxRuntime.jsx("span", {}),
@@ -268,7 +352,70 @@ var ModalStyled = react.forwardRef(
268
352
  );
269
353
  }
270
354
  );
355
+ function confirm(opts = {}) {
356
+ return new Promise((resolve) => {
357
+ if (typeof window === "undefined" || typeof document === "undefined") {
358
+ resolve(false);
359
+ return;
360
+ }
361
+ const host = document.createElement("div");
362
+ document.body.appendChild(host);
363
+ const root = client.createRoot(host);
364
+ const cleanup = () => {
365
+ setTimeout(() => {
366
+ try {
367
+ root.unmount();
368
+ } catch {
369
+ }
370
+ if (host.parentNode) host.parentNode.removeChild(host);
371
+ }, 320);
372
+ };
373
+ function ConfirmShell() {
374
+ const [open, setOpen] = react.useState(true);
375
+ const finish = (value) => {
376
+ setOpen(false);
377
+ cleanup();
378
+ resolve(value);
379
+ };
380
+ return /* @__PURE__ */ jsxRuntime.jsx(
381
+ ModalStyled,
382
+ {
383
+ open,
384
+ onClose: () => finish(false),
385
+ title: opts.title ?? "Are you sure?",
386
+ size: "sm",
387
+ footer: /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: 8, justifyContent: "flex-end" }, children: [
388
+ /* @__PURE__ */ jsxRuntime.jsx(
389
+ "button",
390
+ {
391
+ type: "button",
392
+ className: "rmod-btn rmod-btn--cancel",
393
+ onClick: () => finish(false),
394
+ children: opts.cancelLabel ?? "Cancel"
395
+ }
396
+ ),
397
+ /* @__PURE__ */ jsxRuntime.jsx(
398
+ "button",
399
+ {
400
+ type: "button",
401
+ className: `rmod-btn rmod-btn--confirm${opts.danger ? " rmod-btn--danger" : ""}`,
402
+ onClick: () => finish(true),
403
+ autoFocus: true,
404
+ children: opts.confirmLabel ?? "Confirm"
405
+ }
406
+ )
407
+ ] }),
408
+ children: opts.body ?? null
409
+ }
410
+ );
411
+ }
412
+ root.render(/* @__PURE__ */ jsxRuntime.jsx(ConfirmShell, {}));
413
+ });
414
+ }
415
+ var modal = { confirm };
271
416
 
272
417
  exports.ModalStyled = ModalStyled;
418
+ exports.confirm = confirm;
419
+ exports.modal = modal;
273
420
  //# sourceMappingURL=styled.cjs.map
274
421
  //# sourceMappingURL=styled.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/useFocusTrap.ts","../src/styled/ModalStyled.tsx"],"names":["useRef","useCallback","forwardRef","ModalStyled","useId","useState","useEffect","createPortal","jsx","jsxs"],"mappings":"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","file":"styled.cjs","sourcesContent":["import { useCallback, useRef } from \"react\";\n\nconst FOCUSABLE_SELECTORS = [\n \"a[href]\",\n \"button:not([disabled])\",\n \"input:not([disabled])\",\n \"select:not([disabled])\",\n \"textarea:not([disabled])\",\n \"[tabindex]:not([tabindex='-1'])\",\n \"details > summary\",\n].join(\", \");\n\nfunction getFocusableElements(container: HTMLElement): HTMLElement[] {\n return Array.from(container.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTORS)).filter(\n (el) => !el.closest(\"[inert]\") && getComputedStyle(el).display !== \"none\",\n );\n}\n\nexport interface UseFocusTrapResult {\n activate: (container: HTMLElement) => void;\n deactivate: () => void;\n handleKeyDown: (e: KeyboardEvent) => void;\n}\n\nexport function useFocusTrap(): UseFocusTrapResult {\n const containerRef = useRef<HTMLElement | null>(null);\n const previousFocusRef = useRef<HTMLElement | null>(null);\n\n const activate = useCallback((container: HTMLElement) => {\n containerRef.current = container;\n previousFocusRef.current = document.activeElement as HTMLElement | null;\n\n const focusable = getFocusableElements(container);\n const first = focusable[0];\n if (first) {\n first.focus();\n } else {\n container.focus();\n }\n }, []);\n\n const deactivate = useCallback(() => {\n containerRef.current = null;\n const prev = previousFocusRef.current;\n if (prev && typeof prev.focus === \"function\") {\n prev.focus();\n }\n previousFocusRef.current = null;\n }, []);\n\n const handleKeyDown = useCallback((e: KeyboardEvent) => {\n if (e.key !== \"Tab\" || !containerRef.current) return;\n\n const focusable = getFocusableElements(containerRef.current);\n if (focusable.length === 0) {\n e.preventDefault();\n return;\n }\n\n const first = focusable[0]!;\n const last = focusable[focusable.length - 1]!;\n\n if (e.shiftKey) {\n if (document.activeElement === first) {\n e.preventDefault();\n last.focus();\n }\n } else {\n if (document.activeElement === last) {\n e.preventDefault();\n first.focus();\n }\n }\n }, []);\n\n return { activate, deactivate, handleKeyDown };\n}\n","import {\n forwardRef,\n useCallback,\n useEffect,\n useId,\n useRef,\n useState,\n type ReactNode,\n type RefObject,\n} from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { useFocusTrap } from \"../useFocusTrap\";\n\nexport type ModalSize = \"sm\" | \"md\" | \"lg\" | \"full\";\nexport type ModalVariant = \"dialog\" | \"drawer-left\" | \"drawer-right\" | \"drawer-bottom\";\nexport type CloseReason = \"esc\" | \"overlay\" | \"close-button\" | \"programmatic\";\n\nexport interface ModalStyledProps {\n /** Open state. `open` is the canonical name; `isOpen` continues to work. */\n open?: boolean;\n isOpen?: boolean;\n onClose: () => void;\n /** Modal heading — omit to hide the header entirely */\n title?: ReactNode;\n /** Optional accessible description. Renders below the title and links via aria-describedby. */\n description?: ReactNode;\n children: ReactNode;\n /** Footer content — omit to hide footer */\n footer?: ReactNode;\n size?: ModalSize;\n variant?: ModalVariant;\n closeOnOverlayClick?: boolean;\n closeOnEsc?: boolean;\n showCloseButton?: boolean;\n /** Backdrop blur intensity. Default: \"md\" */\n blur?: \"none\" | \"sm\" | \"md\" | \"lg\";\n /** Custom overlay color e.g. \"rgba(0,0,0,0.6)\" */\n overlayColor?: string;\n /** Padding inside the body. Default: \"md\" */\n padding?: \"none\" | \"sm\" | \"md\" | \"lg\";\n /** Max height of scrollable body. Default: auto */\n scrollable?: boolean;\n className?: string;\n /** Element to focus when the modal opens. Defaults to the first focusable child inside the panel. */\n initialFocusRef?: RefObject<HTMLElement | null>;\n /** Element to focus when the modal closes. Defaults to whatever was focused before opening. */\n finalFocusRef?: RefObject<HTMLElement | null>;\n /** Fires after the open transition completes. */\n onAfterOpen?: () => void;\n /** Fires after the close transition completes (and unmount). */\n onAfterClose?: () => void;\n /** Return false to veto a close attempt. Receives the trigger reason. */\n preventClose?: (reason: CloseReason) => boolean;\n /** Disable body scroll lock while open. Default: true (locked). */\n lockBodyScroll?: boolean;\n /** Override the portal container. Default: document.body. */\n container?: HTMLElement | null;\n}\n\nexport const ModalStyled = forwardRef<HTMLDivElement, ModalStyledProps>(\n function ModalStyled(\n {\n open,\n isOpen,\n onClose,\n title,\n description,\n children,\n footer,\n size = \"md\",\n variant = \"dialog\",\n closeOnOverlayClick = true,\n closeOnEsc = true,\n showCloseButton = true,\n blur = \"md\",\n overlayColor,\n padding = \"md\",\n scrollable = true,\n className,\n initialFocusRef,\n finalFocusRef,\n onAfterOpen,\n onAfterClose,\n preventClose,\n lockBodyScroll = true,\n container,\n },\n ref,\n ) {\n const titleId = useId();\n const descId = useId();\n const isActuallyOpen = open ?? isOpen ?? false;\n const [mounted, setMounted] = useState(false);\n const [rendered, setRendered] = useState(false);\n const [visible, setVisible] = useState(false);\n const panelRef = useRef<HTMLDivElement | null>(null);\n const originalOverflowRef = useRef(\"\");\n const exitTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const enterTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const { activate, deactivate, handleKeyDown } = useFocusTrap();\n\n const onAfterOpenRef = useRef(onAfterOpen);\n onAfterOpenRef.current = onAfterOpen;\n const onAfterCloseRef = useRef(onAfterClose);\n onAfterCloseRef.current = onAfterClose;\n\n useEffect(() => { setMounted(true); }, []);\n\n const requestClose = useCallback(\n (reason: CloseReason) => {\n if (preventClose && !preventClose(reason)) return;\n onClose();\n },\n [preventClose, onClose],\n );\n\n useEffect(() => {\n if (exitTimerRef.current) {\n clearTimeout(exitTimerRef.current);\n exitTimerRef.current = null;\n }\n if (enterTimerRef.current) {\n clearTimeout(enterTimerRef.current);\n enterTimerRef.current = null;\n }\n\n if (isActuallyOpen) {\n setRendered(true);\n requestAnimationFrame(() => {\n requestAnimationFrame(() => {\n setVisible(true);\n });\n });\n if (lockBodyScroll) {\n originalOverflowRef.current = document.body.style.overflow;\n document.body.style.overflow = \"hidden\";\n }\n // Fire onAfterOpen once the enter transition has had time to settle.\n enterTimerRef.current = setTimeout(() => {\n onAfterOpenRef.current?.();\n }, 320);\n } else {\n setVisible(false);\n exitTimerRef.current = setTimeout(() => {\n setRendered(false);\n onAfterCloseRef.current?.();\n }, 300);\n if (lockBodyScroll) {\n document.body.style.overflow = originalOverflowRef.current;\n }\n }\n }, [isActuallyOpen, lockBodyScroll]);\n\n useEffect(() => {\n return () => {\n if (exitTimerRef.current) clearTimeout(exitTimerRef.current);\n if (enterTimerRef.current) clearTimeout(enterTimerRef.current);\n if (lockBodyScroll) {\n document.body.style.overflow = originalOverflowRef.current;\n }\n };\n }, [lockBodyScroll]);\n\n // Focus management\n const previouslyFocusedRef = useRef<HTMLElement | null>(null);\n useEffect(() => {\n if (isActuallyOpen) {\n previouslyFocusedRef.current = document.activeElement as HTMLElement | null;\n if (panelRef.current) {\n activate(panelRef.current);\n // Honor initialFocusRef after the panel is mounted+visible\n requestAnimationFrame(() => {\n if (initialFocusRef?.current) {\n initialFocusRef.current.focus();\n }\n });\n }\n } else {\n deactivate();\n // Honor finalFocusRef on close, otherwise restore previous focus\n const target = finalFocusRef?.current ?? previouslyFocusedRef.current;\n if (target && typeof target.focus === \"function\") {\n target.focus();\n }\n }\n }, [isActuallyOpen, activate, deactivate, initialFocusRef, finalFocusRef]);\n\n const onKeyDown = useCallback(\n (e: React.KeyboardEvent) => {\n if (closeOnEsc && e.key === \"Escape\") {\n requestClose(\"esc\");\n return;\n }\n handleKeyDown(e.nativeEvent);\n },\n [closeOnEsc, requestClose, handleKeyDown],\n );\n\n const handleOverlayClick = useCallback(\n (e: React.MouseEvent<HTMLDivElement>) => {\n if (closeOnOverlayClick && e.target === e.currentTarget) {\n requestClose(\"overlay\");\n }\n },\n [closeOnOverlayClick, requestClose],\n );\n\n const setPanelRef = useCallback(\n (el: HTMLDivElement | null) => {\n panelRef.current = el;\n if (typeof ref === \"function\") ref(el);\n else if (ref) (ref as React.MutableRefObject<HTMLDivElement | null>).current = el;\n if (el && isActuallyOpen) activate(el);\n },\n [ref, isActuallyOpen, activate],\n );\n\n if (!mounted || !rendered) return null;\n\n const hasHeader = title !== undefined || showCloseButton;\n const portalTarget = container ?? document.body;\n\n return createPortal(\n <div\n className={[\n \"rmod-overlay\",\n visible ? \"rmod-overlay--visible\" : \"\",\n ].filter(Boolean).join(\" \")}\n data-variant={variant}\n data-blur={blur}\n data-state={visible ? \"open\" : \"closed\"}\n style={overlayColor ? { \"--rmod-overlay-bg\": overlayColor } as React.CSSProperties : undefined}\n onClick={handleOverlayClick}\n onKeyDown={onKeyDown}\n >\n <div\n ref={setPanelRef}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={title ? titleId : undefined}\n aria-describedby={description ? descId : undefined}\n tabIndex={-1}\n className={[\n \"rmod-panel\",\n className,\n visible ? \"rmod-panel--visible\" : \"\",\n ].filter(Boolean).join(\" \")}\n data-size={size}\n data-variant={variant}\n data-padding={padding}\n data-scrollable={scrollable ? \"true\" : undefined}\n data-state={visible ? \"open\" : \"closed\"}\n >\n {hasHeader && (\n <div className=\"rmod-header\">\n {title ? (\n <h2 id={titleId} className=\"rmod-title\">{title}</h2>\n ) : (\n <span />\n )}\n {showCloseButton && (\n <button\n type=\"button\"\n className=\"rmod-close\"\n aria-label=\"Close\"\n onClick={() => requestClose(\"close-button\")}\n >\n <svg aria-hidden=\"true\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\">\n <path d=\"M12 4L4 12M4 4l8 8\" stroke=\"currentColor\" strokeWidth=\"1.75\" strokeLinecap=\"round\" />\n </svg>\n </button>\n )}\n </div>\n )}\n {description !== undefined && (\n <p id={descId} className=\"rmod-description\">{description}</p>\n )}\n <div className=\"rmod-body\">{children}</div>\n {footer && <div className=\"rmod-footer\">{footer}</div>}\n </div>\n </div>,\n portalTarget,\n );\n },\n);\n"]}
1
+ {"version":3,"sources":["../src/useFocusTrap.ts","../src/styled/ModalStyled.tsx","../src/styled/confirm.tsx"],"names":["useRef","useCallback","forwardRef","ModalStyled","useState","useId","useEffect","createPortal","jsx","jsxs","createRoot"],"mappings":"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","file":"styled.cjs","sourcesContent":["import { useCallback, useRef } from \"react\";\n\nconst FOCUSABLE_SELECTORS = [\n \"a[href]\",\n \"button:not([disabled])\",\n \"input:not([disabled])\",\n \"select:not([disabled])\",\n \"textarea:not([disabled])\",\n \"[tabindex]:not([tabindex='-1'])\",\n \"details > summary\",\n].join(\", \");\n\nfunction getFocusableElements(container: HTMLElement): HTMLElement[] {\n return Array.from(container.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTORS)).filter(\n (el) => !el.closest(\"[inert]\") && getComputedStyle(el).display !== \"none\",\n );\n}\n\nexport interface UseFocusTrapResult {\n activate: (container: HTMLElement) => void;\n deactivate: () => void;\n handleKeyDown: (e: KeyboardEvent) => void;\n}\n\nexport function useFocusTrap(): UseFocusTrapResult {\n const containerRef = useRef<HTMLElement | null>(null);\n const previousFocusRef = useRef<HTMLElement | null>(null);\n\n const activate = useCallback((container: HTMLElement) => {\n containerRef.current = container;\n previousFocusRef.current = document.activeElement as HTMLElement | null;\n\n const focusable = getFocusableElements(container);\n const first = focusable[0];\n if (first) {\n first.focus();\n } else {\n container.focus();\n }\n }, []);\n\n const deactivate = useCallback(() => {\n containerRef.current = null;\n const prev = previousFocusRef.current;\n if (prev && typeof prev.focus === \"function\") {\n prev.focus();\n }\n previousFocusRef.current = null;\n }, []);\n\n const handleKeyDown = useCallback((e: KeyboardEvent) => {\n if (e.key !== \"Tab\" || !containerRef.current) return;\n\n const focusable = getFocusableElements(containerRef.current);\n if (focusable.length === 0) {\n e.preventDefault();\n return;\n }\n\n const first = focusable[0]!;\n const last = focusable[focusable.length - 1]!;\n\n if (e.shiftKey) {\n if (document.activeElement === first) {\n e.preventDefault();\n last.focus();\n }\n } else {\n if (document.activeElement === last) {\n e.preventDefault();\n first.focus();\n }\n }\n }, []);\n\n return { activate, deactivate, handleKeyDown };\n}\n","import {\n forwardRef,\n useCallback,\n useEffect,\n useId,\n useRef,\n useState,\n type ReactNode,\n type RefObject,\n} from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { useFocusTrap } from \"../useFocusTrap\";\n\nexport type ModalSize = \"sm\" | \"md\" | \"lg\" | \"full\";\nexport type ModalVariant = \"dialog\" | \"drawer-left\" | \"drawer-right\" | \"drawer-bottom\";\nexport type CloseReason = \"esc\" | \"overlay\" | \"close-button\" | \"programmatic\" | \"swipe\" | \"submit\";\nexport type ModalTransition = \"fade\" | \"zoom\" | \"slide-up\" | \"slide-down\";\n\n// ---- Stacking registry (depth-aware z-index + scale-behind effect) -------\nconst _openModals: { id: number }[] = [];\nlet _modalIdCounter = 0;\nconst _stackListeners = new Set<() => void>();\nfunction _notifyStack() { _stackListeners.forEach((fn) => fn()); }\n\nexport interface ModalStyledProps {\n /** Open state. `open` is the canonical name; `isOpen` continues to work. */\n open?: boolean;\n isOpen?: boolean;\n onClose: () => void;\n /** Modal heading — omit to hide the header entirely */\n title?: ReactNode;\n /** Optional accessible description. Renders below the title and links via aria-describedby. */\n description?: ReactNode;\n children: ReactNode;\n /** Footer content — omit to hide footer */\n footer?: ReactNode;\n size?: ModalSize;\n variant?: ModalVariant;\n closeOnOverlayClick?: boolean;\n closeOnEsc?: boolean;\n showCloseButton?: boolean;\n /** Backdrop blur intensity. Default: \"md\" */\n blur?: \"none\" | \"sm\" | \"md\" | \"lg\";\n /** Custom overlay color e.g. \"rgba(0,0,0,0.6)\" */\n overlayColor?: string;\n /** Padding inside the body. Default: \"md\" */\n padding?: \"none\" | \"sm\" | \"md\" | \"lg\";\n /** Max height of scrollable body. Default: auto */\n scrollable?: boolean;\n className?: string;\n /** Element to focus when the modal opens. Defaults to the first focusable child inside the panel. */\n initialFocusRef?: RefObject<HTMLElement | null>;\n /** Element to focus when the modal closes. Defaults to whatever was focused before opening. */\n finalFocusRef?: RefObject<HTMLElement | null>;\n /** Fires after the open transition completes. */\n onAfterOpen?: () => void;\n /** Fires after the close transition completes (and unmount). */\n onAfterClose?: () => void;\n /** Return false to veto a close attempt. Receives the trigger reason. */\n preventClose?: (reason: CloseReason) => boolean;\n /** Disable body scroll lock while open. Default: true (locked). */\n lockBodyScroll?: boolean;\n /** Override the portal container. Default: document.body. */\n container?: HTMLElement | null;\n /** Transition variant for the panel (only `dialog`; drawers always slide). Default: \"fade\" */\n transition?: ModalTransition;\n /** Allow swipe-down-to-dismiss on touch devices. Default: false (true for `drawer-bottom`). */\n swipeToDismiss?: boolean;\n /** When the modal contains a single `<form>` and it submits successfully, auto-close. Default: false. */\n closeOnSubmit?: boolean;\n}\n\nexport const ModalStyled = forwardRef<HTMLDivElement, ModalStyledProps>(\n function ModalStyled(\n {\n open,\n isOpen,\n onClose,\n title,\n description,\n children,\n footer,\n size = \"md\",\n variant = \"dialog\",\n closeOnOverlayClick = true,\n closeOnEsc = true,\n showCloseButton = true,\n blur = \"md\",\n overlayColor,\n padding = \"md\",\n scrollable = true,\n className,\n initialFocusRef,\n finalFocusRef,\n onAfterOpen,\n onAfterClose,\n preventClose,\n lockBodyScroll = true,\n container,\n transition = \"fade\",\n swipeToDismiss,\n closeOnSubmit = false,\n },\n ref,\n ) {\n const swipeEnabled = swipeToDismiss ?? variant === \"drawer-bottom\";\n const myStackIdRef = useRef<number>(-1);\n const [stackDepth, setStackDepth] = useState(0);\n const [stackPosition, setStackPosition] = useState(0); // 0 = top\n const [swipeY, setSwipeY] = useState(0);\n const titleId = useId();\n const descId = useId();\n const isActuallyOpen = open ?? isOpen ?? false;\n const [mounted, setMounted] = useState(false);\n const [rendered, setRendered] = useState(false);\n const [visible, setVisible] = useState(false);\n const panelRef = useRef<HTMLDivElement | null>(null);\n const originalOverflowRef = useRef(\"\");\n const exitTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const enterTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const { activate, deactivate, handleKeyDown } = useFocusTrap();\n\n const onAfterOpenRef = useRef(onAfterOpen);\n onAfterOpenRef.current = onAfterOpen;\n const onAfterCloseRef = useRef(onAfterClose);\n onAfterCloseRef.current = onAfterClose;\n\n useEffect(() => { setMounted(true); }, []);\n\n const requestClose = useCallback(\n (reason: CloseReason) => {\n if (preventClose && !preventClose(reason)) return;\n onClose();\n },\n [preventClose, onClose],\n );\n\n useEffect(() => {\n if (exitTimerRef.current) {\n clearTimeout(exitTimerRef.current);\n exitTimerRef.current = null;\n }\n if (enterTimerRef.current) {\n clearTimeout(enterTimerRef.current);\n enterTimerRef.current = null;\n }\n\n if (isActuallyOpen) {\n setRendered(true);\n requestAnimationFrame(() => {\n requestAnimationFrame(() => {\n setVisible(true);\n });\n });\n if (lockBodyScroll) {\n originalOverflowRef.current = document.body.style.overflow;\n document.body.style.overflow = \"hidden\";\n }\n // Fire onAfterOpen once the enter transition has had time to settle.\n enterTimerRef.current = setTimeout(() => {\n onAfterOpenRef.current?.();\n }, 320);\n } else {\n setVisible(false);\n exitTimerRef.current = setTimeout(() => {\n setRendered(false);\n onAfterCloseRef.current?.();\n }, 300);\n if (lockBodyScroll) {\n document.body.style.overflow = originalOverflowRef.current;\n }\n }\n }, [isActuallyOpen, lockBodyScroll]);\n\n useEffect(() => {\n return () => {\n if (exitTimerRef.current) clearTimeout(exitTimerRef.current);\n if (enterTimerRef.current) clearTimeout(enterTimerRef.current);\n if (lockBodyScroll) {\n document.body.style.overflow = originalOverflowRef.current;\n }\n };\n }, [lockBodyScroll]);\n\n // ---- Stacking: register / deregister this modal ---------------------\n useEffect(() => {\n if (!isActuallyOpen) return;\n const id = ++_modalIdCounter;\n myStackIdRef.current = id;\n _openModals.push({ id });\n _notifyStack();\n const sync = () => {\n const idx = _openModals.findIndex((m) => m.id === id);\n const total = _openModals.length;\n setStackDepth(total);\n setStackPosition(idx === -1 ? 0 : total - 1 - idx);\n };\n sync();\n _stackListeners.add(sync);\n return () => {\n _stackListeners.delete(sync);\n const idx = _openModals.findIndex((m) => m.id === id);\n if (idx !== -1) _openModals.splice(idx, 1);\n _notifyStack();\n };\n }, [isActuallyOpen]);\n\n // ---- closeOnSubmit: auto-close on a contained <form> submit ---------\n useEffect(() => {\n if (!closeOnSubmit || !isActuallyOpen || !panelRef.current) return;\n const panel = panelRef.current;\n const onSubmit = (e: Event) => {\n // Defer the close so the consumer's onSubmit handler runs first\n // and can call e.preventDefault() if it wants to abort the close.\n if (e.defaultPrevented) return;\n setTimeout(() => requestClose(\"submit\"), 0);\n };\n panel.addEventListener(\"submit\", onSubmit);\n return () => panel.removeEventListener(\"submit\", onSubmit);\n }, [closeOnSubmit, isActuallyOpen, requestClose]);\n\n // ---- Swipe-to-dismiss (touch only) -----------------------------------\n const swipeStartYRef = useRef<number | null>(null);\n const handleSwipePointerDown = (e: React.PointerEvent<HTMLDivElement>) => {\n if (!swipeEnabled || e.pointerType !== \"touch\") return;\n // Only initiate from the panel header / the panel itself, not buttons.\n swipeStartYRef.current = e.clientY;\n };\n const handleSwipePointerMove = (e: React.PointerEvent<HTMLDivElement>) => {\n if (swipeStartYRef.current === null) return;\n const dy = Math.max(0, e.clientY - swipeStartYRef.current);\n setSwipeY(dy);\n };\n const handleSwipePointerUp = () => {\n if (swipeStartYRef.current === null) return;\n if (swipeY > 120) requestClose(\"swipe\");\n setSwipeY(0);\n swipeStartYRef.current = null;\n };\n\n // Focus management\n const previouslyFocusedRef = useRef<HTMLElement | null>(null);\n useEffect(() => {\n if (isActuallyOpen) {\n previouslyFocusedRef.current = document.activeElement as HTMLElement | null;\n if (panelRef.current) {\n activate(panelRef.current);\n // Honor initialFocusRef after the panel is mounted+visible\n requestAnimationFrame(() => {\n if (initialFocusRef?.current) {\n initialFocusRef.current.focus();\n }\n });\n }\n } else {\n deactivate();\n // Honor finalFocusRef on close, otherwise restore previous focus\n const target = finalFocusRef?.current ?? previouslyFocusedRef.current;\n if (target && typeof target.focus === \"function\") {\n target.focus();\n }\n }\n }, [isActuallyOpen, activate, deactivate, initialFocusRef, finalFocusRef]);\n\n const onKeyDown = useCallback(\n (e: React.KeyboardEvent) => {\n if (closeOnEsc && e.key === \"Escape\") {\n requestClose(\"esc\");\n return;\n }\n handleKeyDown(e.nativeEvent);\n },\n [closeOnEsc, requestClose, handleKeyDown],\n );\n\n const handleOverlayClick = useCallback(\n (e: React.MouseEvent<HTMLDivElement>) => {\n if (closeOnOverlayClick && e.target === e.currentTarget) {\n requestClose(\"overlay\");\n }\n },\n [closeOnOverlayClick, requestClose],\n );\n\n const setPanelRef = useCallback(\n (el: HTMLDivElement | null) => {\n panelRef.current = el;\n if (typeof ref === \"function\") ref(el);\n else if (ref) (ref as React.MutableRefObject<HTMLDivElement | null>).current = el;\n if (el && isActuallyOpen) activate(el);\n },\n [ref, isActuallyOpen, activate],\n );\n\n if (!mounted || !rendered) return null;\n\n const hasHeader = title !== undefined || showCloseButton;\n const portalTarget = container ?? document.body;\n\n const overlayStyle: React.CSSProperties = {\n ...(overlayColor ? ({ \"--rmod-overlay-bg\": overlayColor } as React.CSSProperties) : {}),\n // Higher stack depth → lower modals scale down + fade slightly behind the topmost.\n zIndex: 1000 + stackDepth * 10,\n };\n\n const panelStyle: React.CSSProperties = {\n // Stack-position effects: only applied when this isn't the topmost modal.\n ...(stackPosition > 0\n ? {\n transform: `translateY(${-stackPosition * 8}px) scale(${1 - stackPosition * 0.03})`,\n opacity: Math.max(0.6, 1 - stackPosition * 0.18),\n }\n : {}),\n ...(swipeY > 0 ? { transform: `translateY(${swipeY}px)`, transition: \"none\" } : {}),\n };\n\n return createPortal(\n <div\n className={[\n \"rmod-overlay\",\n visible ? \"rmod-overlay--visible\" : \"\",\n ].filter(Boolean).join(\" \")}\n data-variant={variant}\n data-blur={blur}\n data-transition={transition}\n data-stack-position={stackPosition || undefined}\n data-state={visible ? \"open\" : \"closed\"}\n style={overlayStyle}\n onClick={handleOverlayClick}\n onKeyDown={onKeyDown}\n >\n <div\n ref={setPanelRef}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={title ? titleId : undefined}\n aria-describedby={description ? descId : undefined}\n tabIndex={-1}\n className={[\n \"rmod-panel\",\n className,\n visible ? \"rmod-panel--visible\" : \"\",\n ].filter(Boolean).join(\" \")}\n data-size={size}\n data-variant={variant}\n data-padding={padding}\n data-scrollable={scrollable ? \"true\" : undefined}\n data-transition={transition}\n data-state={visible ? \"open\" : \"closed\"}\n data-swiping={swipeY > 0 ? \"true\" : undefined}\n style={panelStyle}\n onPointerDown={swipeEnabled ? handleSwipePointerDown : undefined}\n onPointerMove={swipeEnabled ? handleSwipePointerMove : undefined}\n onPointerUp={swipeEnabled ? handleSwipePointerUp : undefined}\n onPointerCancel={swipeEnabled ? handleSwipePointerUp : undefined}\n >\n {hasHeader && (\n <div className=\"rmod-header\">\n {title ? (\n <h2 id={titleId} className=\"rmod-title\">{title}</h2>\n ) : (\n <span />\n )}\n {showCloseButton && (\n <button\n type=\"button\"\n className=\"rmod-close\"\n aria-label=\"Close\"\n onClick={() => requestClose(\"close-button\")}\n >\n <svg aria-hidden=\"true\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\">\n <path d=\"M12 4L4 12M4 4l8 8\" stroke=\"currentColor\" strokeWidth=\"1.75\" strokeLinecap=\"round\" />\n </svg>\n </button>\n )}\n </div>\n )}\n {description !== undefined && (\n <p id={descId} className=\"rmod-description\">{description}</p>\n )}\n <div className=\"rmod-body\">{children}</div>\n {footer && <div className=\"rmod-footer\">{footer}</div>}\n </div>\n </div>,\n portalTarget,\n );\n },\n);\n","import { useState, type ReactNode } from \"react\";\nimport { createRoot } from \"react-dom/client\";\nimport { ModalStyled } from \"./ModalStyled\";\n\nexport interface ConfirmOptions {\n title?: ReactNode;\n body?: ReactNode;\n confirmLabel?: ReactNode;\n cancelLabel?: ReactNode;\n /** Style the confirm button as the danger CTA (red). */\n danger?: boolean;\n}\n\n/**\n * Programmatic confirm dialog. Resolves `true` if confirmed, `false` if cancelled\n * or dismissed via overlay/Esc/close button.\n *\n * Self-mounts a `<ModalStyled>` into a temporary div appended to `document.body`\n * and unmounts on close — no provider required.\n */\nexport function confirm(opts: ConfirmOptions = {}): Promise<boolean> {\n return new Promise((resolve) => {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") {\n resolve(false);\n return;\n }\n const host = document.createElement(\"div\");\n document.body.appendChild(host);\n const root = createRoot(host);\n\n const cleanup = () => {\n // Defer unmount so the close transition can run.\n setTimeout(() => {\n try { root.unmount(); } catch { /* ignore */ }\n if (host.parentNode) host.parentNode.removeChild(host);\n }, 320);\n };\n\n function ConfirmShell() {\n const [open, setOpen] = useState(true);\n const finish = (value: boolean) => {\n setOpen(false);\n cleanup();\n resolve(value);\n };\n return (\n <ModalStyled\n open={open}\n onClose={() => finish(false)}\n title={opts.title ?? \"Are you sure?\"}\n size=\"sm\"\n footer={\n <div style={{ display: \"flex\", gap: 8, justifyContent: \"flex-end\" }}>\n <button\n type=\"button\"\n className=\"rmod-btn rmod-btn--cancel\"\n onClick={() => finish(false)}\n >\n {opts.cancelLabel ?? \"Cancel\"}\n </button>\n <button\n type=\"button\"\n className={`rmod-btn rmod-btn--confirm${opts.danger ? \" rmod-btn--danger\" : \"\"}`}\n onClick={() => finish(true)}\n autoFocus\n >\n {opts.confirmLabel ?? \"Confirm\"}\n </button>\n </div>\n }\n >\n {opts.body ?? null}\n </ModalStyled>\n );\n }\n\n root.render(<ConfirmShell />);\n });\n}\n\n/** Module-style export so consumers can do `modal.confirm(...)`. */\nexport const modal = { confirm };\n"]}
package/dist/styled.d.cts CHANGED
@@ -3,7 +3,8 @@ import { ReactNode, RefObject } from 'react';
3
3
 
4
4
  type ModalSize = "sm" | "md" | "lg" | "full";
5
5
  type ModalVariant = "dialog" | "drawer-left" | "drawer-right" | "drawer-bottom";
6
- type CloseReason = "esc" | "overlay" | "close-button" | "programmatic";
6
+ type CloseReason = "esc" | "overlay" | "close-button" | "programmatic" | "swipe" | "submit";
7
+ type ModalTransition = "fade" | "zoom" | "slide-up" | "slide-down";
7
8
  interface ModalStyledProps {
8
9
  /** Open state. `open` is the canonical name; `isOpen` continues to work. */
9
10
  open?: boolean;
@@ -44,7 +45,34 @@ interface ModalStyledProps {
44
45
  lockBodyScroll?: boolean;
45
46
  /** Override the portal container. Default: document.body. */
46
47
  container?: HTMLElement | null;
48
+ /** Transition variant for the panel (only `dialog`; drawers always slide). Default: "fade" */
49
+ transition?: ModalTransition;
50
+ /** Allow swipe-down-to-dismiss on touch devices. Default: false (true for `drawer-bottom`). */
51
+ swipeToDismiss?: boolean;
52
+ /** When the modal contains a single `<form>` and it submits successfully, auto-close. Default: false. */
53
+ closeOnSubmit?: boolean;
47
54
  }
48
55
  declare const ModalStyled: react.ForwardRefExoticComponent<ModalStyledProps & react.RefAttributes<HTMLDivElement>>;
49
56
 
50
- export { type ModalSize, ModalStyled, type ModalStyledProps, type ModalVariant };
57
+ interface ConfirmOptions {
58
+ title?: ReactNode;
59
+ body?: ReactNode;
60
+ confirmLabel?: ReactNode;
61
+ cancelLabel?: ReactNode;
62
+ /** Style the confirm button as the danger CTA (red). */
63
+ danger?: boolean;
64
+ }
65
+ /**
66
+ * Programmatic confirm dialog. Resolves `true` if confirmed, `false` if cancelled
67
+ * or dismissed via overlay/Esc/close button.
68
+ *
69
+ * Self-mounts a `<ModalStyled>` into a temporary div appended to `document.body`
70
+ * and unmounts on close — no provider required.
71
+ */
72
+ declare function confirm(opts?: ConfirmOptions): Promise<boolean>;
73
+ /** Module-style export so consumers can do `modal.confirm(...)`. */
74
+ declare const modal: {
75
+ confirm: typeof confirm;
76
+ };
77
+
78
+ export { type ConfirmOptions, type ModalSize, ModalStyled, type ModalStyledProps, type ModalTransition, type ModalVariant, confirm, modal };
package/dist/styled.d.ts CHANGED
@@ -3,7 +3,8 @@ import { ReactNode, RefObject } from 'react';
3
3
 
4
4
  type ModalSize = "sm" | "md" | "lg" | "full";
5
5
  type ModalVariant = "dialog" | "drawer-left" | "drawer-right" | "drawer-bottom";
6
- type CloseReason = "esc" | "overlay" | "close-button" | "programmatic";
6
+ type CloseReason = "esc" | "overlay" | "close-button" | "programmatic" | "swipe" | "submit";
7
+ type ModalTransition = "fade" | "zoom" | "slide-up" | "slide-down";
7
8
  interface ModalStyledProps {
8
9
  /** Open state. `open` is the canonical name; `isOpen` continues to work. */
9
10
  open?: boolean;
@@ -44,7 +45,34 @@ interface ModalStyledProps {
44
45
  lockBodyScroll?: boolean;
45
46
  /** Override the portal container. Default: document.body. */
46
47
  container?: HTMLElement | null;
48
+ /** Transition variant for the panel (only `dialog`; drawers always slide). Default: "fade" */
49
+ transition?: ModalTransition;
50
+ /** Allow swipe-down-to-dismiss on touch devices. Default: false (true for `drawer-bottom`). */
51
+ swipeToDismiss?: boolean;
52
+ /** When the modal contains a single `<form>` and it submits successfully, auto-close. Default: false. */
53
+ closeOnSubmit?: boolean;
47
54
  }
48
55
  declare const ModalStyled: react.ForwardRefExoticComponent<ModalStyledProps & react.RefAttributes<HTMLDivElement>>;
49
56
 
50
- export { type ModalSize, ModalStyled, type ModalStyledProps, type ModalVariant };
57
+ interface ConfirmOptions {
58
+ title?: ReactNode;
59
+ body?: ReactNode;
60
+ confirmLabel?: ReactNode;
61
+ cancelLabel?: ReactNode;
62
+ /** Style the confirm button as the danger CTA (red). */
63
+ danger?: boolean;
64
+ }
65
+ /**
66
+ * Programmatic confirm dialog. Resolves `true` if confirmed, `false` if cancelled
67
+ * or dismissed via overlay/Esc/close button.
68
+ *
69
+ * Self-mounts a `<ModalStyled>` into a temporary div appended to `document.body`
70
+ * and unmounts on close — no provider required.
71
+ */
72
+ declare function confirm(opts?: ConfirmOptions): Promise<boolean>;
73
+ /** Module-style export so consumers can do `modal.confirm(...)`. */
74
+ declare const modal: {
75
+ confirm: typeof confirm;
76
+ };
77
+
78
+ export { type ConfirmOptions, type ModalSize, ModalStyled, type ModalStyledProps, type ModalTransition, type ModalVariant, confirm, modal };
package/dist/styled.js CHANGED
@@ -1,8 +1,15 @@
1
1
  import { useFocusTrap } from './chunk-BFH4N5GR.js';
2
- import { forwardRef, useId, useState, useRef, useEffect, useCallback } from 'react';
2
+ import { forwardRef, useRef, useState, useId, useEffect, useCallback } from 'react';
3
3
  import { createPortal } from 'react-dom';
4
4
  import { jsx, jsxs } from 'react/jsx-runtime';
5
+ import { createRoot } from 'react-dom/client';
5
6
 
7
+ var _openModals = [];
8
+ var _modalIdCounter = 0;
9
+ var _stackListeners = /* @__PURE__ */ new Set();
10
+ function _notifyStack() {
11
+ _stackListeners.forEach((fn) => fn());
12
+ }
6
13
  var ModalStyled = forwardRef(
7
14
  function ModalStyled2({
8
15
  open,
@@ -28,8 +35,16 @@ var ModalStyled = forwardRef(
28
35
  onAfterClose,
29
36
  preventClose,
30
37
  lockBodyScroll = true,
31
- container
38
+ container,
39
+ transition = "fade",
40
+ swipeToDismiss,
41
+ closeOnSubmit = false
32
42
  }, ref) {
43
+ const swipeEnabled = swipeToDismiss ?? variant === "drawer-bottom";
44
+ const myStackIdRef = useRef(-1);
45
+ const [stackDepth, setStackDepth] = useState(0);
46
+ const [stackPosition, setStackPosition] = useState(0);
47
+ const [swipeY, setSwipeY] = useState(0);
33
48
  const titleId = useId();
34
49
  const descId = useId();
35
50
  const isActuallyOpen = open ?? isOpen ?? false;
@@ -98,6 +113,53 @@ var ModalStyled = forwardRef(
98
113
  }
99
114
  };
100
115
  }, [lockBodyScroll]);
116
+ useEffect(() => {
117
+ if (!isActuallyOpen) return;
118
+ const id = ++_modalIdCounter;
119
+ myStackIdRef.current = id;
120
+ _openModals.push({ id });
121
+ _notifyStack();
122
+ const sync = () => {
123
+ const idx = _openModals.findIndex((m) => m.id === id);
124
+ const total = _openModals.length;
125
+ setStackDepth(total);
126
+ setStackPosition(idx === -1 ? 0 : total - 1 - idx);
127
+ };
128
+ sync();
129
+ _stackListeners.add(sync);
130
+ return () => {
131
+ _stackListeners.delete(sync);
132
+ const idx = _openModals.findIndex((m) => m.id === id);
133
+ if (idx !== -1) _openModals.splice(idx, 1);
134
+ _notifyStack();
135
+ };
136
+ }, [isActuallyOpen]);
137
+ useEffect(() => {
138
+ if (!closeOnSubmit || !isActuallyOpen || !panelRef.current) return;
139
+ const panel = panelRef.current;
140
+ const onSubmit = (e) => {
141
+ if (e.defaultPrevented) return;
142
+ setTimeout(() => requestClose("submit"), 0);
143
+ };
144
+ panel.addEventListener("submit", onSubmit);
145
+ return () => panel.removeEventListener("submit", onSubmit);
146
+ }, [closeOnSubmit, isActuallyOpen, requestClose]);
147
+ const swipeStartYRef = useRef(null);
148
+ const handleSwipePointerDown = (e) => {
149
+ if (!swipeEnabled || e.pointerType !== "touch") return;
150
+ swipeStartYRef.current = e.clientY;
151
+ };
152
+ const handleSwipePointerMove = (e) => {
153
+ if (swipeStartYRef.current === null) return;
154
+ const dy = Math.max(0, e.clientY - swipeStartYRef.current);
155
+ setSwipeY(dy);
156
+ };
157
+ const handleSwipePointerUp = () => {
158
+ if (swipeStartYRef.current === null) return;
159
+ if (swipeY > 120) requestClose("swipe");
160
+ setSwipeY(0);
161
+ swipeStartYRef.current = null;
162
+ };
101
163
  const previouslyFocusedRef = useRef(null);
102
164
  useEffect(() => {
103
165
  if (isActuallyOpen) {
@@ -148,6 +210,19 @@ var ModalStyled = forwardRef(
148
210
  if (!mounted || !rendered) return null;
149
211
  const hasHeader = title !== void 0 || showCloseButton;
150
212
  const portalTarget = container ?? document.body;
213
+ const overlayStyle = {
214
+ ...overlayColor ? { "--rmod-overlay-bg": overlayColor } : {},
215
+ // Higher stack depth → lower modals scale down + fade slightly behind the topmost.
216
+ zIndex: 1e3 + stackDepth * 10
217
+ };
218
+ const panelStyle = {
219
+ // Stack-position effects: only applied when this isn't the topmost modal.
220
+ ...stackPosition > 0 ? {
221
+ transform: `translateY(${-stackPosition * 8}px) scale(${1 - stackPosition * 0.03})`,
222
+ opacity: Math.max(0.6, 1 - stackPosition * 0.18)
223
+ } : {},
224
+ ...swipeY > 0 ? { transform: `translateY(${swipeY}px)`, transition: "none" } : {}
225
+ };
151
226
  return createPortal(
152
227
  /* @__PURE__ */ jsx(
153
228
  "div",
@@ -158,8 +233,10 @@ var ModalStyled = forwardRef(
158
233
  ].filter(Boolean).join(" "),
159
234
  "data-variant": variant,
160
235
  "data-blur": blur,
236
+ "data-transition": transition,
237
+ "data-stack-position": stackPosition || void 0,
161
238
  "data-state": visible ? "open" : "closed",
162
- style: overlayColor ? { "--rmod-overlay-bg": overlayColor } : void 0,
239
+ style: overlayStyle,
163
240
  onClick: handleOverlayClick,
164
241
  onKeyDown,
165
242
  children: /* @__PURE__ */ jsxs(
@@ -180,7 +257,14 @@ var ModalStyled = forwardRef(
180
257
  "data-variant": variant,
181
258
  "data-padding": padding,
182
259
  "data-scrollable": scrollable ? "true" : void 0,
260
+ "data-transition": transition,
183
261
  "data-state": visible ? "open" : "closed",
262
+ "data-swiping": swipeY > 0 ? "true" : void 0,
263
+ style: panelStyle,
264
+ onPointerDown: swipeEnabled ? handleSwipePointerDown : void 0,
265
+ onPointerMove: swipeEnabled ? handleSwipePointerMove : void 0,
266
+ onPointerUp: swipeEnabled ? handleSwipePointerUp : void 0,
267
+ onPointerCancel: swipeEnabled ? handleSwipePointerUp : void 0,
184
268
  children: [
185
269
  hasHeader && /* @__PURE__ */ jsxs("div", { className: "rmod-header", children: [
186
270
  title ? /* @__PURE__ */ jsx("h2", { id: titleId, className: "rmod-title", children: title }) : /* @__PURE__ */ jsx("span", {}),
@@ -207,7 +291,68 @@ var ModalStyled = forwardRef(
207
291
  );
208
292
  }
209
293
  );
294
+ function confirm(opts = {}) {
295
+ return new Promise((resolve) => {
296
+ if (typeof window === "undefined" || typeof document === "undefined") {
297
+ resolve(false);
298
+ return;
299
+ }
300
+ const host = document.createElement("div");
301
+ document.body.appendChild(host);
302
+ const root = createRoot(host);
303
+ const cleanup = () => {
304
+ setTimeout(() => {
305
+ try {
306
+ root.unmount();
307
+ } catch {
308
+ }
309
+ if (host.parentNode) host.parentNode.removeChild(host);
310
+ }, 320);
311
+ };
312
+ function ConfirmShell() {
313
+ const [open, setOpen] = useState(true);
314
+ const finish = (value) => {
315
+ setOpen(false);
316
+ cleanup();
317
+ resolve(value);
318
+ };
319
+ return /* @__PURE__ */ jsx(
320
+ ModalStyled,
321
+ {
322
+ open,
323
+ onClose: () => finish(false),
324
+ title: opts.title ?? "Are you sure?",
325
+ size: "sm",
326
+ footer: /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 8, justifyContent: "flex-end" }, children: [
327
+ /* @__PURE__ */ jsx(
328
+ "button",
329
+ {
330
+ type: "button",
331
+ className: "rmod-btn rmod-btn--cancel",
332
+ onClick: () => finish(false),
333
+ children: opts.cancelLabel ?? "Cancel"
334
+ }
335
+ ),
336
+ /* @__PURE__ */ jsx(
337
+ "button",
338
+ {
339
+ type: "button",
340
+ className: `rmod-btn rmod-btn--confirm${opts.danger ? " rmod-btn--danger" : ""}`,
341
+ onClick: () => finish(true),
342
+ autoFocus: true,
343
+ children: opts.confirmLabel ?? "Confirm"
344
+ }
345
+ )
346
+ ] }),
347
+ children: opts.body ?? null
348
+ }
349
+ );
350
+ }
351
+ root.render(/* @__PURE__ */ jsx(ConfirmShell, {}));
352
+ });
353
+ }
354
+ var modal = { confirm };
210
355
 
211
- export { ModalStyled };
356
+ export { ModalStyled, confirm, modal };
212
357
  //# sourceMappingURL=styled.js.map
213
358
  //# sourceMappingURL=styled.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/styled/ModalStyled.tsx"],"names":["ModalStyled"],"mappings":"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","file":"styled.js","sourcesContent":["import {\n forwardRef,\n useCallback,\n useEffect,\n useId,\n useRef,\n useState,\n type ReactNode,\n type RefObject,\n} from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { useFocusTrap } from \"../useFocusTrap\";\n\nexport type ModalSize = \"sm\" | \"md\" | \"lg\" | \"full\";\nexport type ModalVariant = \"dialog\" | \"drawer-left\" | \"drawer-right\" | \"drawer-bottom\";\nexport type CloseReason = \"esc\" | \"overlay\" | \"close-button\" | \"programmatic\";\n\nexport interface ModalStyledProps {\n /** Open state. `open` is the canonical name; `isOpen` continues to work. */\n open?: boolean;\n isOpen?: boolean;\n onClose: () => void;\n /** Modal heading — omit to hide the header entirely */\n title?: ReactNode;\n /** Optional accessible description. Renders below the title and links via aria-describedby. */\n description?: ReactNode;\n children: ReactNode;\n /** Footer content — omit to hide footer */\n footer?: ReactNode;\n size?: ModalSize;\n variant?: ModalVariant;\n closeOnOverlayClick?: boolean;\n closeOnEsc?: boolean;\n showCloseButton?: boolean;\n /** Backdrop blur intensity. Default: \"md\" */\n blur?: \"none\" | \"sm\" | \"md\" | \"lg\";\n /** Custom overlay color e.g. \"rgba(0,0,0,0.6)\" */\n overlayColor?: string;\n /** Padding inside the body. Default: \"md\" */\n padding?: \"none\" | \"sm\" | \"md\" | \"lg\";\n /** Max height of scrollable body. Default: auto */\n scrollable?: boolean;\n className?: string;\n /** Element to focus when the modal opens. Defaults to the first focusable child inside the panel. */\n initialFocusRef?: RefObject<HTMLElement | null>;\n /** Element to focus when the modal closes. Defaults to whatever was focused before opening. */\n finalFocusRef?: RefObject<HTMLElement | null>;\n /** Fires after the open transition completes. */\n onAfterOpen?: () => void;\n /** Fires after the close transition completes (and unmount). */\n onAfterClose?: () => void;\n /** Return false to veto a close attempt. Receives the trigger reason. */\n preventClose?: (reason: CloseReason) => boolean;\n /** Disable body scroll lock while open. Default: true (locked). */\n lockBodyScroll?: boolean;\n /** Override the portal container. Default: document.body. */\n container?: HTMLElement | null;\n}\n\nexport const ModalStyled = forwardRef<HTMLDivElement, ModalStyledProps>(\n function ModalStyled(\n {\n open,\n isOpen,\n onClose,\n title,\n description,\n children,\n footer,\n size = \"md\",\n variant = \"dialog\",\n closeOnOverlayClick = true,\n closeOnEsc = true,\n showCloseButton = true,\n blur = \"md\",\n overlayColor,\n padding = \"md\",\n scrollable = true,\n className,\n initialFocusRef,\n finalFocusRef,\n onAfterOpen,\n onAfterClose,\n preventClose,\n lockBodyScroll = true,\n container,\n },\n ref,\n ) {\n const titleId = useId();\n const descId = useId();\n const isActuallyOpen = open ?? isOpen ?? false;\n const [mounted, setMounted] = useState(false);\n const [rendered, setRendered] = useState(false);\n const [visible, setVisible] = useState(false);\n const panelRef = useRef<HTMLDivElement | null>(null);\n const originalOverflowRef = useRef(\"\");\n const exitTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const enterTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const { activate, deactivate, handleKeyDown } = useFocusTrap();\n\n const onAfterOpenRef = useRef(onAfterOpen);\n onAfterOpenRef.current = onAfterOpen;\n const onAfterCloseRef = useRef(onAfterClose);\n onAfterCloseRef.current = onAfterClose;\n\n useEffect(() => { setMounted(true); }, []);\n\n const requestClose = useCallback(\n (reason: CloseReason) => {\n if (preventClose && !preventClose(reason)) return;\n onClose();\n },\n [preventClose, onClose],\n );\n\n useEffect(() => {\n if (exitTimerRef.current) {\n clearTimeout(exitTimerRef.current);\n exitTimerRef.current = null;\n }\n if (enterTimerRef.current) {\n clearTimeout(enterTimerRef.current);\n enterTimerRef.current = null;\n }\n\n if (isActuallyOpen) {\n setRendered(true);\n requestAnimationFrame(() => {\n requestAnimationFrame(() => {\n setVisible(true);\n });\n });\n if (lockBodyScroll) {\n originalOverflowRef.current = document.body.style.overflow;\n document.body.style.overflow = \"hidden\";\n }\n // Fire onAfterOpen once the enter transition has had time to settle.\n enterTimerRef.current = setTimeout(() => {\n onAfterOpenRef.current?.();\n }, 320);\n } else {\n setVisible(false);\n exitTimerRef.current = setTimeout(() => {\n setRendered(false);\n onAfterCloseRef.current?.();\n }, 300);\n if (lockBodyScroll) {\n document.body.style.overflow = originalOverflowRef.current;\n }\n }\n }, [isActuallyOpen, lockBodyScroll]);\n\n useEffect(() => {\n return () => {\n if (exitTimerRef.current) clearTimeout(exitTimerRef.current);\n if (enterTimerRef.current) clearTimeout(enterTimerRef.current);\n if (lockBodyScroll) {\n document.body.style.overflow = originalOverflowRef.current;\n }\n };\n }, [lockBodyScroll]);\n\n // Focus management\n const previouslyFocusedRef = useRef<HTMLElement | null>(null);\n useEffect(() => {\n if (isActuallyOpen) {\n previouslyFocusedRef.current = document.activeElement as HTMLElement | null;\n if (panelRef.current) {\n activate(panelRef.current);\n // Honor initialFocusRef after the panel is mounted+visible\n requestAnimationFrame(() => {\n if (initialFocusRef?.current) {\n initialFocusRef.current.focus();\n }\n });\n }\n } else {\n deactivate();\n // Honor finalFocusRef on close, otherwise restore previous focus\n const target = finalFocusRef?.current ?? previouslyFocusedRef.current;\n if (target && typeof target.focus === \"function\") {\n target.focus();\n }\n }\n }, [isActuallyOpen, activate, deactivate, initialFocusRef, finalFocusRef]);\n\n const onKeyDown = useCallback(\n (e: React.KeyboardEvent) => {\n if (closeOnEsc && e.key === \"Escape\") {\n requestClose(\"esc\");\n return;\n }\n handleKeyDown(e.nativeEvent);\n },\n [closeOnEsc, requestClose, handleKeyDown],\n );\n\n const handleOverlayClick = useCallback(\n (e: React.MouseEvent<HTMLDivElement>) => {\n if (closeOnOverlayClick && e.target === e.currentTarget) {\n requestClose(\"overlay\");\n }\n },\n [closeOnOverlayClick, requestClose],\n );\n\n const setPanelRef = useCallback(\n (el: HTMLDivElement | null) => {\n panelRef.current = el;\n if (typeof ref === \"function\") ref(el);\n else if (ref) (ref as React.MutableRefObject<HTMLDivElement | null>).current = el;\n if (el && isActuallyOpen) activate(el);\n },\n [ref, isActuallyOpen, activate],\n );\n\n if (!mounted || !rendered) return null;\n\n const hasHeader = title !== undefined || showCloseButton;\n const portalTarget = container ?? document.body;\n\n return createPortal(\n <div\n className={[\n \"rmod-overlay\",\n visible ? \"rmod-overlay--visible\" : \"\",\n ].filter(Boolean).join(\" \")}\n data-variant={variant}\n data-blur={blur}\n data-state={visible ? \"open\" : \"closed\"}\n style={overlayColor ? { \"--rmod-overlay-bg\": overlayColor } as React.CSSProperties : undefined}\n onClick={handleOverlayClick}\n onKeyDown={onKeyDown}\n >\n <div\n ref={setPanelRef}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={title ? titleId : undefined}\n aria-describedby={description ? descId : undefined}\n tabIndex={-1}\n className={[\n \"rmod-panel\",\n className,\n visible ? \"rmod-panel--visible\" : \"\",\n ].filter(Boolean).join(\" \")}\n data-size={size}\n data-variant={variant}\n data-padding={padding}\n data-scrollable={scrollable ? \"true\" : undefined}\n data-state={visible ? \"open\" : \"closed\"}\n >\n {hasHeader && (\n <div className=\"rmod-header\">\n {title ? (\n <h2 id={titleId} className=\"rmod-title\">{title}</h2>\n ) : (\n <span />\n )}\n {showCloseButton && (\n <button\n type=\"button\"\n className=\"rmod-close\"\n aria-label=\"Close\"\n onClick={() => requestClose(\"close-button\")}\n >\n <svg aria-hidden=\"true\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\">\n <path d=\"M12 4L4 12M4 4l8 8\" stroke=\"currentColor\" strokeWidth=\"1.75\" strokeLinecap=\"round\" />\n </svg>\n </button>\n )}\n </div>\n )}\n {description !== undefined && (\n <p id={descId} className=\"rmod-description\">{description}</p>\n )}\n <div className=\"rmod-body\">{children}</div>\n {footer && <div className=\"rmod-footer\">{footer}</div>}\n </div>\n </div>,\n portalTarget,\n );\n },\n);\n"]}
1
+ {"version":3,"sources":["../src/styled/ModalStyled.tsx","../src/styled/confirm.tsx"],"names":["ModalStyled","useState","jsx","jsxs"],"mappings":"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","file":"styled.js","sourcesContent":["import {\n forwardRef,\n useCallback,\n useEffect,\n useId,\n useRef,\n useState,\n type ReactNode,\n type RefObject,\n} from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { useFocusTrap } from \"../useFocusTrap\";\n\nexport type ModalSize = \"sm\" | \"md\" | \"lg\" | \"full\";\nexport type ModalVariant = \"dialog\" | \"drawer-left\" | \"drawer-right\" | \"drawer-bottom\";\nexport type CloseReason = \"esc\" | \"overlay\" | \"close-button\" | \"programmatic\" | \"swipe\" | \"submit\";\nexport type ModalTransition = \"fade\" | \"zoom\" | \"slide-up\" | \"slide-down\";\n\n// ---- Stacking registry (depth-aware z-index + scale-behind effect) -------\nconst _openModals: { id: number }[] = [];\nlet _modalIdCounter = 0;\nconst _stackListeners = new Set<() => void>();\nfunction _notifyStack() { _stackListeners.forEach((fn) => fn()); }\n\nexport interface ModalStyledProps {\n /** Open state. `open` is the canonical name; `isOpen` continues to work. */\n open?: boolean;\n isOpen?: boolean;\n onClose: () => void;\n /** Modal heading — omit to hide the header entirely */\n title?: ReactNode;\n /** Optional accessible description. Renders below the title and links via aria-describedby. */\n description?: ReactNode;\n children: ReactNode;\n /** Footer content — omit to hide footer */\n footer?: ReactNode;\n size?: ModalSize;\n variant?: ModalVariant;\n closeOnOverlayClick?: boolean;\n closeOnEsc?: boolean;\n showCloseButton?: boolean;\n /** Backdrop blur intensity. Default: \"md\" */\n blur?: \"none\" | \"sm\" | \"md\" | \"lg\";\n /** Custom overlay color e.g. \"rgba(0,0,0,0.6)\" */\n overlayColor?: string;\n /** Padding inside the body. Default: \"md\" */\n padding?: \"none\" | \"sm\" | \"md\" | \"lg\";\n /** Max height of scrollable body. Default: auto */\n scrollable?: boolean;\n className?: string;\n /** Element to focus when the modal opens. Defaults to the first focusable child inside the panel. */\n initialFocusRef?: RefObject<HTMLElement | null>;\n /** Element to focus when the modal closes. Defaults to whatever was focused before opening. */\n finalFocusRef?: RefObject<HTMLElement | null>;\n /** Fires after the open transition completes. */\n onAfterOpen?: () => void;\n /** Fires after the close transition completes (and unmount). */\n onAfterClose?: () => void;\n /** Return false to veto a close attempt. Receives the trigger reason. */\n preventClose?: (reason: CloseReason) => boolean;\n /** Disable body scroll lock while open. Default: true (locked). */\n lockBodyScroll?: boolean;\n /** Override the portal container. Default: document.body. */\n container?: HTMLElement | null;\n /** Transition variant for the panel (only `dialog`; drawers always slide). Default: \"fade\" */\n transition?: ModalTransition;\n /** Allow swipe-down-to-dismiss on touch devices. Default: false (true for `drawer-bottom`). */\n swipeToDismiss?: boolean;\n /** When the modal contains a single `<form>` and it submits successfully, auto-close. Default: false. */\n closeOnSubmit?: boolean;\n}\n\nexport const ModalStyled = forwardRef<HTMLDivElement, ModalStyledProps>(\n function ModalStyled(\n {\n open,\n isOpen,\n onClose,\n title,\n description,\n children,\n footer,\n size = \"md\",\n variant = \"dialog\",\n closeOnOverlayClick = true,\n closeOnEsc = true,\n showCloseButton = true,\n blur = \"md\",\n overlayColor,\n padding = \"md\",\n scrollable = true,\n className,\n initialFocusRef,\n finalFocusRef,\n onAfterOpen,\n onAfterClose,\n preventClose,\n lockBodyScroll = true,\n container,\n transition = \"fade\",\n swipeToDismiss,\n closeOnSubmit = false,\n },\n ref,\n ) {\n const swipeEnabled = swipeToDismiss ?? variant === \"drawer-bottom\";\n const myStackIdRef = useRef<number>(-1);\n const [stackDepth, setStackDepth] = useState(0);\n const [stackPosition, setStackPosition] = useState(0); // 0 = top\n const [swipeY, setSwipeY] = useState(0);\n const titleId = useId();\n const descId = useId();\n const isActuallyOpen = open ?? isOpen ?? false;\n const [mounted, setMounted] = useState(false);\n const [rendered, setRendered] = useState(false);\n const [visible, setVisible] = useState(false);\n const panelRef = useRef<HTMLDivElement | null>(null);\n const originalOverflowRef = useRef(\"\");\n const exitTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const enterTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const { activate, deactivate, handleKeyDown } = useFocusTrap();\n\n const onAfterOpenRef = useRef(onAfterOpen);\n onAfterOpenRef.current = onAfterOpen;\n const onAfterCloseRef = useRef(onAfterClose);\n onAfterCloseRef.current = onAfterClose;\n\n useEffect(() => { setMounted(true); }, []);\n\n const requestClose = useCallback(\n (reason: CloseReason) => {\n if (preventClose && !preventClose(reason)) return;\n onClose();\n },\n [preventClose, onClose],\n );\n\n useEffect(() => {\n if (exitTimerRef.current) {\n clearTimeout(exitTimerRef.current);\n exitTimerRef.current = null;\n }\n if (enterTimerRef.current) {\n clearTimeout(enterTimerRef.current);\n enterTimerRef.current = null;\n }\n\n if (isActuallyOpen) {\n setRendered(true);\n requestAnimationFrame(() => {\n requestAnimationFrame(() => {\n setVisible(true);\n });\n });\n if (lockBodyScroll) {\n originalOverflowRef.current = document.body.style.overflow;\n document.body.style.overflow = \"hidden\";\n }\n // Fire onAfterOpen once the enter transition has had time to settle.\n enterTimerRef.current = setTimeout(() => {\n onAfterOpenRef.current?.();\n }, 320);\n } else {\n setVisible(false);\n exitTimerRef.current = setTimeout(() => {\n setRendered(false);\n onAfterCloseRef.current?.();\n }, 300);\n if (lockBodyScroll) {\n document.body.style.overflow = originalOverflowRef.current;\n }\n }\n }, [isActuallyOpen, lockBodyScroll]);\n\n useEffect(() => {\n return () => {\n if (exitTimerRef.current) clearTimeout(exitTimerRef.current);\n if (enterTimerRef.current) clearTimeout(enterTimerRef.current);\n if (lockBodyScroll) {\n document.body.style.overflow = originalOverflowRef.current;\n }\n };\n }, [lockBodyScroll]);\n\n // ---- Stacking: register / deregister this modal ---------------------\n useEffect(() => {\n if (!isActuallyOpen) return;\n const id = ++_modalIdCounter;\n myStackIdRef.current = id;\n _openModals.push({ id });\n _notifyStack();\n const sync = () => {\n const idx = _openModals.findIndex((m) => m.id === id);\n const total = _openModals.length;\n setStackDepth(total);\n setStackPosition(idx === -1 ? 0 : total - 1 - idx);\n };\n sync();\n _stackListeners.add(sync);\n return () => {\n _stackListeners.delete(sync);\n const idx = _openModals.findIndex((m) => m.id === id);\n if (idx !== -1) _openModals.splice(idx, 1);\n _notifyStack();\n };\n }, [isActuallyOpen]);\n\n // ---- closeOnSubmit: auto-close on a contained <form> submit ---------\n useEffect(() => {\n if (!closeOnSubmit || !isActuallyOpen || !panelRef.current) return;\n const panel = panelRef.current;\n const onSubmit = (e: Event) => {\n // Defer the close so the consumer's onSubmit handler runs first\n // and can call e.preventDefault() if it wants to abort the close.\n if (e.defaultPrevented) return;\n setTimeout(() => requestClose(\"submit\"), 0);\n };\n panel.addEventListener(\"submit\", onSubmit);\n return () => panel.removeEventListener(\"submit\", onSubmit);\n }, [closeOnSubmit, isActuallyOpen, requestClose]);\n\n // ---- Swipe-to-dismiss (touch only) -----------------------------------\n const swipeStartYRef = useRef<number | null>(null);\n const handleSwipePointerDown = (e: React.PointerEvent<HTMLDivElement>) => {\n if (!swipeEnabled || e.pointerType !== \"touch\") return;\n // Only initiate from the panel header / the panel itself, not buttons.\n swipeStartYRef.current = e.clientY;\n };\n const handleSwipePointerMove = (e: React.PointerEvent<HTMLDivElement>) => {\n if (swipeStartYRef.current === null) return;\n const dy = Math.max(0, e.clientY - swipeStartYRef.current);\n setSwipeY(dy);\n };\n const handleSwipePointerUp = () => {\n if (swipeStartYRef.current === null) return;\n if (swipeY > 120) requestClose(\"swipe\");\n setSwipeY(0);\n swipeStartYRef.current = null;\n };\n\n // Focus management\n const previouslyFocusedRef = useRef<HTMLElement | null>(null);\n useEffect(() => {\n if (isActuallyOpen) {\n previouslyFocusedRef.current = document.activeElement as HTMLElement | null;\n if (panelRef.current) {\n activate(panelRef.current);\n // Honor initialFocusRef after the panel is mounted+visible\n requestAnimationFrame(() => {\n if (initialFocusRef?.current) {\n initialFocusRef.current.focus();\n }\n });\n }\n } else {\n deactivate();\n // Honor finalFocusRef on close, otherwise restore previous focus\n const target = finalFocusRef?.current ?? previouslyFocusedRef.current;\n if (target && typeof target.focus === \"function\") {\n target.focus();\n }\n }\n }, [isActuallyOpen, activate, deactivate, initialFocusRef, finalFocusRef]);\n\n const onKeyDown = useCallback(\n (e: React.KeyboardEvent) => {\n if (closeOnEsc && e.key === \"Escape\") {\n requestClose(\"esc\");\n return;\n }\n handleKeyDown(e.nativeEvent);\n },\n [closeOnEsc, requestClose, handleKeyDown],\n );\n\n const handleOverlayClick = useCallback(\n (e: React.MouseEvent<HTMLDivElement>) => {\n if (closeOnOverlayClick && e.target === e.currentTarget) {\n requestClose(\"overlay\");\n }\n },\n [closeOnOverlayClick, requestClose],\n );\n\n const setPanelRef = useCallback(\n (el: HTMLDivElement | null) => {\n panelRef.current = el;\n if (typeof ref === \"function\") ref(el);\n else if (ref) (ref as React.MutableRefObject<HTMLDivElement | null>).current = el;\n if (el && isActuallyOpen) activate(el);\n },\n [ref, isActuallyOpen, activate],\n );\n\n if (!mounted || !rendered) return null;\n\n const hasHeader = title !== undefined || showCloseButton;\n const portalTarget = container ?? document.body;\n\n const overlayStyle: React.CSSProperties = {\n ...(overlayColor ? ({ \"--rmod-overlay-bg\": overlayColor } as React.CSSProperties) : {}),\n // Higher stack depth → lower modals scale down + fade slightly behind the topmost.\n zIndex: 1000 + stackDepth * 10,\n };\n\n const panelStyle: React.CSSProperties = {\n // Stack-position effects: only applied when this isn't the topmost modal.\n ...(stackPosition > 0\n ? {\n transform: `translateY(${-stackPosition * 8}px) scale(${1 - stackPosition * 0.03})`,\n opacity: Math.max(0.6, 1 - stackPosition * 0.18),\n }\n : {}),\n ...(swipeY > 0 ? { transform: `translateY(${swipeY}px)`, transition: \"none\" } : {}),\n };\n\n return createPortal(\n <div\n className={[\n \"rmod-overlay\",\n visible ? \"rmod-overlay--visible\" : \"\",\n ].filter(Boolean).join(\" \")}\n data-variant={variant}\n data-blur={blur}\n data-transition={transition}\n data-stack-position={stackPosition || undefined}\n data-state={visible ? \"open\" : \"closed\"}\n style={overlayStyle}\n onClick={handleOverlayClick}\n onKeyDown={onKeyDown}\n >\n <div\n ref={setPanelRef}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={title ? titleId : undefined}\n aria-describedby={description ? descId : undefined}\n tabIndex={-1}\n className={[\n \"rmod-panel\",\n className,\n visible ? \"rmod-panel--visible\" : \"\",\n ].filter(Boolean).join(\" \")}\n data-size={size}\n data-variant={variant}\n data-padding={padding}\n data-scrollable={scrollable ? \"true\" : undefined}\n data-transition={transition}\n data-state={visible ? \"open\" : \"closed\"}\n data-swiping={swipeY > 0 ? \"true\" : undefined}\n style={panelStyle}\n onPointerDown={swipeEnabled ? handleSwipePointerDown : undefined}\n onPointerMove={swipeEnabled ? handleSwipePointerMove : undefined}\n onPointerUp={swipeEnabled ? handleSwipePointerUp : undefined}\n onPointerCancel={swipeEnabled ? handleSwipePointerUp : undefined}\n >\n {hasHeader && (\n <div className=\"rmod-header\">\n {title ? (\n <h2 id={titleId} className=\"rmod-title\">{title}</h2>\n ) : (\n <span />\n )}\n {showCloseButton && (\n <button\n type=\"button\"\n className=\"rmod-close\"\n aria-label=\"Close\"\n onClick={() => requestClose(\"close-button\")}\n >\n <svg aria-hidden=\"true\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\">\n <path d=\"M12 4L4 12M4 4l8 8\" stroke=\"currentColor\" strokeWidth=\"1.75\" strokeLinecap=\"round\" />\n </svg>\n </button>\n )}\n </div>\n )}\n {description !== undefined && (\n <p id={descId} className=\"rmod-description\">{description}</p>\n )}\n <div className=\"rmod-body\">{children}</div>\n {footer && <div className=\"rmod-footer\">{footer}</div>}\n </div>\n </div>,\n portalTarget,\n );\n },\n);\n","import { useState, type ReactNode } from \"react\";\nimport { createRoot } from \"react-dom/client\";\nimport { ModalStyled } from \"./ModalStyled\";\n\nexport interface ConfirmOptions {\n title?: ReactNode;\n body?: ReactNode;\n confirmLabel?: ReactNode;\n cancelLabel?: ReactNode;\n /** Style the confirm button as the danger CTA (red). */\n danger?: boolean;\n}\n\n/**\n * Programmatic confirm dialog. Resolves `true` if confirmed, `false` if cancelled\n * or dismissed via overlay/Esc/close button.\n *\n * Self-mounts a `<ModalStyled>` into a temporary div appended to `document.body`\n * and unmounts on close — no provider required.\n */\nexport function confirm(opts: ConfirmOptions = {}): Promise<boolean> {\n return new Promise((resolve) => {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") {\n resolve(false);\n return;\n }\n const host = document.createElement(\"div\");\n document.body.appendChild(host);\n const root = createRoot(host);\n\n const cleanup = () => {\n // Defer unmount so the close transition can run.\n setTimeout(() => {\n try { root.unmount(); } catch { /* ignore */ }\n if (host.parentNode) host.parentNode.removeChild(host);\n }, 320);\n };\n\n function ConfirmShell() {\n const [open, setOpen] = useState(true);\n const finish = (value: boolean) => {\n setOpen(false);\n cleanup();\n resolve(value);\n };\n return (\n <ModalStyled\n open={open}\n onClose={() => finish(false)}\n title={opts.title ?? \"Are you sure?\"}\n size=\"sm\"\n footer={\n <div style={{ display: \"flex\", gap: 8, justifyContent: \"flex-end\" }}>\n <button\n type=\"button\"\n className=\"rmod-btn rmod-btn--cancel\"\n onClick={() => finish(false)}\n >\n {opts.cancelLabel ?? \"Cancel\"}\n </button>\n <button\n type=\"button\"\n className={`rmod-btn rmod-btn--confirm${opts.danger ? \" rmod-btn--danger\" : \"\"}`}\n onClick={() => finish(true)}\n autoFocus\n >\n {opts.confirmLabel ?? \"Confirm\"}\n </button>\n </div>\n }\n >\n {opts.body ?? null}\n </ModalStyled>\n );\n }\n\n root.render(<ConfirmShell />);\n });\n}\n\n/** Module-style export so consumers can do `modal.confirm(...)`. */\nexport const modal = { confirm };\n"]}
package/dist/styles.css CHANGED
@@ -259,3 +259,104 @@
259
259
  .rmod-panel[data-padding="none"] .rmod-description { padding: 0; }
260
260
  .rmod-panel[data-padding="sm"] .rmod-description { padding: 0 0.75rem; }
261
261
  .rmod-panel[data-padding="lg"] .rmod-description { padding: 0 1.5rem; }
262
+
263
+ /* ============================================================================
264
+ * 1.x additions — transition variants · stacked behind · swipe · confirm()
265
+ * ============================================================================ */
266
+
267
+ /* Transition variants on the dialog panel.
268
+ * Drawers (drawer-left/right/bottom) keep their existing slide-from-edge
269
+ * transition; these only apply to data-variant="dialog". */
270
+ .rmod-panel[data-variant="dialog"][data-transition="zoom"] {
271
+ transform: scale(0.94);
272
+ opacity: 0;
273
+ transition: transform 220ms cubic-bezier(0.22, 1, 0.36, 1), opacity 200ms ease;
274
+ }
275
+ .rmod-panel[data-variant="dialog"][data-transition="zoom"].rmod-panel--visible {
276
+ transform: scale(1);
277
+ opacity: 1;
278
+ }
279
+
280
+ .rmod-panel[data-variant="dialog"][data-transition="slide-up"] {
281
+ transform: translateY(20px);
282
+ opacity: 0;
283
+ transition: transform 220ms cubic-bezier(0.22, 1, 0.36, 1), opacity 200ms ease;
284
+ }
285
+ .rmod-panel[data-variant="dialog"][data-transition="slide-up"].rmod-panel--visible {
286
+ transform: translateY(0);
287
+ opacity: 1;
288
+ }
289
+
290
+ .rmod-panel[data-variant="dialog"][data-transition="slide-down"] {
291
+ transform: translateY(-20px);
292
+ opacity: 0;
293
+ transition: transform 220ms cubic-bezier(0.22, 1, 0.36, 1), opacity 200ms ease;
294
+ }
295
+ .rmod-panel[data-variant="dialog"][data-transition="slide-down"].rmod-panel--visible {
296
+ transform: translateY(0);
297
+ opacity: 1;
298
+ }
299
+
300
+ /* Stacked-behind effect — applied via inline style from JS but we add a
301
+ * smooth transition here so depth changes animate. */
302
+ .rmod-overlay[data-stack-position] .rmod-panel:not([data-swiping="true"]) {
303
+ transition:
304
+ transform 240ms cubic-bezier(0.22, 1, 0.36, 1),
305
+ opacity 220ms ease;
306
+ }
307
+
308
+ /* Swipe-to-dismiss visual */
309
+ .rmod-panel[data-swiping="true"] {
310
+ transition: none;
311
+ cursor: grabbing;
312
+ }
313
+
314
+ /* Confirm() utility's footer buttons — minimal, inherit consumer themes
315
+ * via CSS variables. */
316
+ .rmod-btn {
317
+ appearance: none;
318
+ font: inherit;
319
+ padding: 0.5rem 1rem;
320
+ border-radius: 6px;
321
+ cursor: pointer;
322
+ font-weight: 500;
323
+ font-size: 0.875rem;
324
+ transition: background 120ms ease, border-color 120ms ease;
325
+ }
326
+ .rmod-btn--cancel {
327
+ background: transparent;
328
+ border: 1px solid var(--rmod-border, rgba(0, 0, 0, 0.12));
329
+ color: var(--rmod-fg, #18181b);
330
+ }
331
+ .rmod-btn--cancel:hover {
332
+ background: var(--rmod-surface-hover, rgba(0, 0, 0, 0.04));
333
+ }
334
+ .rmod-btn--confirm {
335
+ background: var(--rmod-confirm-bg, #6366f1);
336
+ border: 1px solid var(--rmod-confirm-bg, #6366f1);
337
+ color: white;
338
+ }
339
+ .rmod-btn--confirm:hover {
340
+ background: var(--rmod-confirm-bg-hover, #5558e6);
341
+ }
342
+ .rmod-btn--danger {
343
+ background: var(--rmod-danger-bg, #dc2626);
344
+ border-color: var(--rmod-danger-bg, #dc2626);
345
+ }
346
+ .rmod-btn--danger:hover {
347
+ background: var(--rmod-danger-bg-hover, #c01f1f);
348
+ }
349
+ [data-theme="dark"] .rmod-btn--cancel {
350
+ border-color: var(--rmod-border, rgba(255, 255, 255, 0.16));
351
+ color: var(--rmod-fg, #f4f4f5);
352
+ }
353
+ [data-theme="dark"] .rmod-btn--cancel:hover {
354
+ background: var(--rmod-surface-hover, rgba(255, 255, 255, 0.06));
355
+ }
356
+
357
+ @media (prefers-reduced-motion: reduce) {
358
+ .rmod-panel[data-variant="dialog"][data-transition] {
359
+ transition: opacity 100ms linear;
360
+ transform: none !important;
361
+ }
362
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mshafiqyajid/react-modal",
3
- "version": "0.2.0",
3
+ "version": "0.3.1",
4
4
  "description": "Headless modal hook and styled component for React. Accessible, focus-trapped, scroll-locked, animated, SSR-safe, fully typed.",
5
5
  "keywords": [
6
6
  "react",