@planningcenter/tapestry 3.8.1-rc.2 → 3.8.1-rc.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  import "./index.css";
2
- import React, { type DialogHTMLAttributes } from "react";
2
+ import React, { type HTMLAttributes } from "react";
3
3
  export type ModalSize = "sm" | "md" | "lg" | "xl";
4
4
  export interface ModalProps {
5
5
  /**
@@ -10,11 +10,11 @@ export interface ModalProps {
10
10
  dismissible?: boolean;
11
11
  /** Fires on every dismissal — Esc, backdrop, close button, or programmatic. */
12
12
  onClose?: () => void;
13
- /** Controls the dialog. Synced to `showModal()`/`close()`. */
13
+ /** Controls the modal. */
14
14
  open: boolean;
15
15
  /** Default `'md'`. */
16
16
  size?: ModalSize;
17
17
  }
18
- export type ModalElementProps = Omit<DialogHTMLAttributes<HTMLDialogElement>, keyof ModalProps | "onCancel"> & ModalProps;
19
- export declare const Modal: React.ForwardRefExoticComponent<Omit<React.DialogHTMLAttributes<HTMLDialogElement>, keyof ModalProps | "onCancel"> & ModalProps & React.RefAttributes<HTMLDialogElement>>;
18
+ export type ModalElementProps = Omit<HTMLAttributes<HTMLElement>, keyof ModalProps | "role"> & ModalProps;
19
+ export declare const Modal: React.ForwardRefExoticComponent<Omit<React.HTMLAttributes<HTMLElement>, "role" | keyof ModalProps> & ModalProps & React.RefAttributes<HTMLElement>>;
20
20
  //# sourceMappingURL=Modal.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Modal.d.ts","sourceRoot":"","sources":["../../../src/components/modal/Modal.tsx"],"names":[],"mappings":"AAAA,OAAO,aAAa,CAAA;AAIpB,OAAO,KAAK,EAAE,EACZ,KAAK,oBAAoB,EAK1B,MAAM,OAAO,CAAA;AAId,MAAM,MAAM,SAAS,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;AAEjD,MAAM,WAAW,UAAU;IACzB;;OAEG;IACH,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB,0FAA0F;IAC1F,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,+EAA+E;IAC/E,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;IACpB,8DAA8D;IAC9D,IAAI,EAAE,OAAO,CAAA;IACb,sBAAsB;IACtB,IAAI,CAAC,EAAE,SAAS,CAAA;CACjB;AAED,MAAM,MAAM,iBAAiB,GAAG,IAAI,CAClC,oBAAoB,CAAC,iBAAiB,CAAC,EACvC,MAAM,UAAU,GAAG,UAAU,CAC9B,GACC,UAAU,CAAA;AAuCZ,eAAO,MAAM,KAAK,2KAyHjB,CAAA"}
1
+ {"version":3,"file":"Modal.d.ts","sourceRoot":"","sources":["../../../src/components/modal/Modal.tsx"],"names":[],"mappings":"AAAA,OAAO,aAAa,CAAA;AAIpB,OAAO,KAAK,EAAE,EAEZ,KAAK,cAAc,EAIpB,MAAM,OAAO,CAAA;AAMd,MAAM,MAAM,SAAS,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;AAEjD,MAAM,WAAW,UAAU;IACzB;;OAEG;IACH,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB,0FAA0F;IAC1F,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,+EAA+E;IAC/E,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;IACpB,0BAA0B;IAC1B,IAAI,EAAE,OAAO,CAAA;IACb,sBAAsB;IACtB,IAAI,CAAC,EAAE,SAAS,CAAA;CACjB;AAED,MAAM,MAAM,iBAAiB,GAAG,IAAI,CAClC,cAAc,CAAC,WAAW,CAAC,EAC3B,MAAM,UAAU,GAAG,MAAM,CAC1B,GACC,UAAU,CAAA;AAEZ,eAAO,MAAM,KAAK,qJAiFhB,CAAA"}
@@ -1,111 +1,55 @@
1
1
  import { useId } from '../../utilities/useId.js';
2
2
  import classNames from 'classnames';
3
3
  import React__default, { forwardRef, useRef, useEffect, useCallback } from 'react';
4
+ import { Dialog } from 'react-aria-components/Dialog';
5
+ import { ModalOverlay, Modal as Modal$1 } from 'react-aria-components/Modal';
4
6
  import { ModalContext } from './ModalContext.js';
5
7
 
6
- function supportsRequestClose(dialogEl) {
7
- return "requestClose" in dialogEl;
8
- }
9
- function supportsClosedBy(dialogEl) {
10
- return "closedBy" in dialogEl;
11
- }
12
- function getActiveFocusableElement() {
13
- return document.activeElement instanceof HTMLElement
14
- ? document.activeElement
15
- : null;
16
- }
17
- function isBackdropClick(event, dialogEl) {
18
- return event.target === dialogEl;
19
- }
20
- function closeViaRequestOrDirect(dialogEl, dismissible) {
21
- const dialogWithRequestClose = dialogEl;
22
- if (supportsRequestClose(dialogEl)) {
23
- dialogWithRequestClose.requestClose?.();
24
- }
25
- else if (dismissible) {
26
- dialogEl.close();
27
- }
28
- }
29
8
  const Modal = forwardRef(function Modal({ children, className, dismissible = false, id, onClose, open, size = "md", ...restProps }, ref) {
30
9
  const stableId = useId();
31
10
  const modalId = id || `tds-modal-${stableId}`;
32
11
  const titleId = `${modalId}-title`;
33
12
  const subtitleId = `${modalId}-subtitle`;
34
- const dialogRef = useRef(null);
35
- const previouslyFocusedElementRef = useRef(null);
36
13
  const onCloseRef = useRef(onClose);
37
- const requestClose = () => {
38
- const dialogEl = dialogRef.current;
39
- if (dialogEl)
40
- closeViaRequestOrDirect(dialogEl, dismissible);
41
- };
42
- const contextValue = { dismissible, requestClose, subtitleId, titleId };
43
14
  useEffect(() => {
44
15
  onCloseRef.current = onClose;
45
16
  }, [onClose]);
46
- const setDialogRef = useCallback((element) => {
47
- dialogRef.current = element;
48
- if (typeof ref === "function") {
49
- ref(element);
50
- }
51
- else if (ref) {
52
- ref.current = element;
53
- }
54
- }, [ref]);
55
- useEffect(() => {
56
- const dialogEl = dialogRef.current;
57
- if (!dialogEl)
17
+ // Tracks whether the current "open" has already been reported to
18
+ // `onClose`, so a dismiss gesture (Esc/backdrop/close button) and the
19
+ // `open=false` re-render it causes don't both fire the callback.
20
+ const reportedOpenRef = useRef(open);
21
+ const notifyClose = useCallback(() => {
22
+ if (!reportedOpenRef.current)
58
23
  return;
59
- if (open && !dialogEl.open) {
60
- previouslyFocusedElementRef.current = getActiveFocusableElement();
61
- dialogEl.showModal();
62
- }
63
- else if (!open && dialogEl.open) {
64
- dialogEl.close();
65
- }
66
- }, [open]);
24
+ reportedOpenRef.current = false;
25
+ onCloseRef.current?.();
26
+ }, []);
67
27
  useEffect(() => {
68
- const dialogEl = dialogRef.current;
69
- if (!dialogEl)
70
- return;
71
- const handleClose = () => {
72
- onCloseRef.current?.();
73
- previouslyFocusedElementRef.current?.focus();
74
- previouslyFocusedElementRef.current = null;
75
- };
76
- const handleCancel = (event) => {
77
- if (!dismissible)
78
- event.preventDefault();
79
- };
80
- dialogEl.addEventListener("close", handleClose);
81
- dialogEl.addEventListener("cancel", handleCancel);
82
- let handleBackdropClick;
83
- if (supportsClosedBy(dialogEl)) {
84
- dialogEl.setAttribute("closedby", dismissible ? "any" : "none");
28
+ if (open) {
29
+ reportedOpenRef.current = true;
85
30
  }
86
31
  else {
87
- handleBackdropClick = (event) => {
88
- if (isBackdropClick(event, dialogEl)) {
89
- closeViaRequestOrDirect(dialogEl, dismissible);
90
- }
91
- };
92
- dialogEl.addEventListener("click", handleBackdropClick);
32
+ notifyClose();
93
33
  }
94
- return () => {
95
- dialogEl.removeEventListener("close", handleClose);
96
- dialogEl.removeEventListener("cancel", handleCancel);
97
- if (handleBackdropClick) {
98
- dialogEl.removeEventListener("click", handleBackdropClick);
99
- }
100
- };
101
- }, [dismissible]);
102
- const combinedClassName = classNames("tds-modal", {
103
- "tds-modal--lg": size === "lg",
104
- "tds-modal--sm": size === "sm",
105
- "tds-modal--xl": size === "xl",
106
- }, className);
107
- return (React__default.createElement("dialog", { ...restProps, ref: setDialogRef, "aria-describedby": subtitleId, "aria-labelledby": titleId, className: combinedClassName, id: modalId },
108
- React__default.createElement(ModalContext.Provider, { value: contextValue }, children)));
34
+ }, [open, notifyClose]);
35
+ const contextValue = {
36
+ dismissible,
37
+ requestClose: notifyClose,
38
+ subtitleId,
39
+ titleId,
40
+ };
41
+ const frameClassName = classNames("tds-modal-frame", {
42
+ "tds-modal-frame--lg": size === "lg",
43
+ "tds-modal-frame--sm": size === "sm",
44
+ "tds-modal-frame--xl": size === "xl",
45
+ });
46
+ return (React__default.createElement(ModalOverlay, { className: "tds-modal-overlay", isDismissable: dismissible, isKeyboardDismissDisabled: !dismissible, isOpen: open, onOpenChange: (isOpen) => {
47
+ if (!isOpen)
48
+ notifyClose();
49
+ } },
50
+ React__default.createElement(Modal$1, { className: frameClassName },
51
+ React__default.createElement(Dialog, { ...restProps, ref: ref, "aria-describedby": subtitleId, "aria-labelledby": titleId, className: classNames("tds-modal", className), id: modalId },
52
+ React__default.createElement(ModalContext.Provider, { value: contextValue }, children)))));
109
53
  });
110
54
  Modal.displayName = "Modal";
111
55
 
@@ -1 +1 @@
1
- {"version":3,"file":"Modal.js","sources":["../../../src/components/modal/Modal.tsx"],"sourcesContent":["import \"./index.css\"\n\nimport { useId } from \"@utilities/useId\"\nimport classNames from \"classnames\"\nimport React, {\n type DialogHTMLAttributes,\n forwardRef,\n useCallback,\n useEffect,\n useRef,\n} from \"react\"\n\nimport { ModalContext } from \"./ModalContext\"\n\nexport type ModalSize = \"sm\" | \"md\" | \"lg\" | \"xl\"\n\nexport interface ModalProps {\n /**\n * A `<ModalHeader>` followed by a `<ModalBody>` and a `<ModalFooter>`.\n */\n children: React.ReactNode\n /** Default `false`. `true` enables Esc, backdrop-click, and the close button together. */\n dismissible?: boolean\n /** Fires on every dismissal — Esc, backdrop, close button, or programmatic. */\n onClose?: () => void\n /** Controls the dialog. Synced to `showModal()`/`close()`. */\n open: boolean\n /** Default `'md'`. */\n size?: ModalSize\n}\n\nexport type ModalElementProps = Omit<\n DialogHTMLAttributes<HTMLDialogElement>,\n keyof ModalProps | \"onCancel\"\n> &\n ModalProps\n\ntype DialogWithRequestClose = HTMLDialogElement & {\n requestClose?: (returnValue?: string) => void\n}\n\nfunction supportsRequestClose(dialogEl: HTMLDialogElement): boolean {\n return \"requestClose\" in dialogEl\n}\n\nfunction supportsClosedBy(dialogEl: HTMLDialogElement): boolean {\n return \"closedBy\" in dialogEl\n}\n\nfunction getActiveFocusableElement(): HTMLElement | null {\n return document.activeElement instanceof HTMLElement\n ? document.activeElement\n : null\n}\n\nfunction isBackdropClick(\n event: MouseEvent,\n dialogEl: HTMLDialogElement\n): boolean {\n return event.target === dialogEl\n}\n\nfunction closeViaRequestOrDirect(\n dialogEl: HTMLDialogElement,\n dismissible: boolean\n) {\n const dialogWithRequestClose = dialogEl as DialogWithRequestClose\n if (supportsRequestClose(dialogEl)) {\n dialogWithRequestClose.requestClose?.()\n } else if (dismissible) {\n dialogEl.close()\n }\n}\n\nexport const Modal = forwardRef<HTMLDialogElement, ModalElementProps>(\n function Modal(\n {\n children,\n className,\n dismissible = false,\n id,\n onClose,\n open,\n size = \"md\",\n ...restProps\n }: ModalElementProps,\n ref\n ) {\n const stableId = useId()\n const modalId = id || `tds-modal-${stableId}`\n const titleId = `${modalId}-title`\n const subtitleId = `${modalId}-subtitle`\n\n const dialogRef = useRef<HTMLDialogElement | null>(null)\n const previouslyFocusedElementRef = useRef<HTMLElement | null>(null)\n const onCloseRef = useRef(onClose)\n\n const requestClose = () => {\n const dialogEl = dialogRef.current\n if (dialogEl) closeViaRequestOrDirect(dialogEl, dismissible)\n }\n\n const contextValue = { dismissible, requestClose, subtitleId, titleId }\n\n useEffect(() => {\n onCloseRef.current = onClose\n }, [onClose])\n\n const setDialogRef = useCallback(\n (element: HTMLDialogElement | null) => {\n dialogRef.current = element\n if (typeof ref === \"function\") {\n ref(element)\n } else if (ref) {\n ref.current = element\n }\n },\n [ref]\n )\n\n useEffect(() => {\n const dialogEl = dialogRef.current\n if (!dialogEl) return\n\n if (open && !dialogEl.open) {\n previouslyFocusedElementRef.current = getActiveFocusableElement()\n dialogEl.showModal()\n } else if (!open && dialogEl.open) {\n dialogEl.close()\n }\n }, [open])\n\n useEffect(() => {\n const dialogEl = dialogRef.current\n if (!dialogEl) return\n\n const handleClose = () => {\n onCloseRef.current?.()\n previouslyFocusedElementRef.current?.focus()\n previouslyFocusedElementRef.current = null\n }\n\n const handleCancel = (event: Event) => {\n if (!dismissible) event.preventDefault()\n }\n\n dialogEl.addEventListener(\"close\", handleClose)\n dialogEl.addEventListener(\"cancel\", handleCancel)\n\n let handleBackdropClick: ((event: MouseEvent) => void) | undefined\n if (supportsClosedBy(dialogEl)) {\n dialogEl.setAttribute(\"closedby\", dismissible ? \"any\" : \"none\")\n } else {\n handleBackdropClick = (event) => {\n if (isBackdropClick(event, dialogEl)) {\n closeViaRequestOrDirect(dialogEl, dismissible)\n }\n }\n dialogEl.addEventListener(\"click\", handleBackdropClick)\n }\n\n return () => {\n dialogEl.removeEventListener(\"close\", handleClose)\n dialogEl.removeEventListener(\"cancel\", handleCancel)\n if (handleBackdropClick) {\n dialogEl.removeEventListener(\"click\", handleBackdropClick)\n }\n }\n }, [dismissible])\n\n const combinedClassName = classNames(\n \"tds-modal\",\n {\n \"tds-modal--lg\": size === \"lg\",\n \"tds-modal--sm\": size === \"sm\",\n \"tds-modal--xl\": size === \"xl\",\n },\n className\n )\n\n return (\n <dialog\n {...restProps}\n ref={setDialogRef}\n aria-describedby={subtitleId}\n aria-labelledby={titleId}\n className={combinedClassName}\n id={modalId}\n >\n <ModalContext.Provider value={contextValue}>\n {children}\n </ModalContext.Provider>\n </dialog>\n )\n }\n)\n\nModal.displayName = \"Modal\"\n"],"names":["React"],"mappings":";;;;;AAyCA,SAAS,oBAAoB,CAAC,QAA2B,EAAA;IACvD,OAAO,cAAc,IAAI,QAAQ;AACnC;AAEA,SAAS,gBAAgB,CAAC,QAA2B,EAAA;IACnD,OAAO,UAAU,IAAI,QAAQ;AAC/B;AAEA,SAAS,yBAAyB,GAAA;AAChC,IAAA,OAAO,QAAQ,CAAC,aAAa,YAAY;UACrC,QAAQ,CAAC;UACT,IAAI;AACV;AAEA,SAAS,eAAe,CACtB,KAAiB,EACjB,QAA2B,EAAA;AAE3B,IAAA,OAAO,KAAK,CAAC,MAAM,KAAK,QAAQ;AAClC;AAEA,SAAS,uBAAuB,CAC9B,QAA2B,EAC3B,WAAoB,EAAA;IAEpB,MAAM,sBAAsB,GAAG,QAAkC;AACjE,IAAA,IAAI,oBAAoB,CAAC,QAAQ,CAAC,EAAE;AAClC,QAAA,sBAAsB,CAAC,YAAY,IAAI;IACzC;SAAO,IAAI,WAAW,EAAE;QACtB,QAAQ,CAAC,KAAK,EAAE;IAClB;AACF;AAEO,MAAM,KAAK,GAAG,UAAU,CAC7B,SAAS,KAAK,CACZ,EACE,QAAQ,EACR,SAAS,EACT,WAAW,GAAG,KAAK,EACnB,EAAE,EACF,OAAO,EACP,IAAI,EACJ,IAAI,GAAG,IAAI,EACX,GAAG,SAAS,EACM,EACpB,GAAG,EAAA;AAEH,IAAA,MAAM,QAAQ,GAAG,KAAK,EAAE;AACxB,IAAA,MAAM,OAAO,GAAG,EAAE,IAAI,CAAA,UAAA,EAAa,QAAQ,EAAE;AAC7C,IAAA,MAAM,OAAO,GAAG,CAAA,EAAG,OAAO,QAAQ;AAClC,IAAA,MAAM,UAAU,GAAG,CAAA,EAAG,OAAO,WAAW;AAExC,IAAA,MAAM,SAAS,GAAG,MAAM,CAA2B,IAAI,CAAC;AACxD,IAAA,MAAM,2BAA2B,GAAG,MAAM,CAAqB,IAAI,CAAC;AACpE,IAAA,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC;IAElC,MAAM,YAAY,GAAG,MAAK;AACxB,QAAA,MAAM,QAAQ,GAAG,SAAS,CAAC,OAAO;AAClC,QAAA,IAAI,QAAQ;AAAE,YAAA,uBAAuB,CAAC,QAAQ,EAAE,WAAW,CAAC;AAC9D,IAAA,CAAC;IAED,MAAM,YAAY,GAAG,EAAE,WAAW,EAAE,YAAY,EAAE,UAAU,EAAE,OAAO,EAAE;IAEvE,SAAS,CAAC,MAAK;AACb,QAAA,UAAU,CAAC,OAAO,GAAG,OAAO;AAC9B,IAAA,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC;AAEb,IAAA,MAAM,YAAY,GAAG,WAAW,CAC9B,CAAC,OAAiC,KAAI;AACpC,QAAA,SAAS,CAAC,OAAO,GAAG,OAAO;AAC3B,QAAA,IAAI,OAAO,GAAG,KAAK,UAAU,EAAE;YAC7B,GAAG,CAAC,OAAO,CAAC;QACd;aAAO,IAAI,GAAG,EAAE;AACd,YAAA,GAAG,CAAC,OAAO,GAAG,OAAO;QACvB;AACF,IAAA,CAAC,EACD,CAAC,GAAG,CAAC,CACN;IAED,SAAS,CAAC,MAAK;AACb,QAAA,MAAM,QAAQ,GAAG,SAAS,CAAC,OAAO;AAClC,QAAA,IAAI,CAAC,QAAQ;YAAE;AAEf,QAAA,IAAI,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE;AAC1B,YAAA,2BAA2B,CAAC,OAAO,GAAG,yBAAyB,EAAE;YACjE,QAAQ,CAAC,SAAS,EAAE;QACtB;AAAO,aAAA,IAAI,CAAC,IAAI,IAAI,QAAQ,CAAC,IAAI,EAAE;YACjC,QAAQ,CAAC,KAAK,EAAE;QAClB;AACF,IAAA,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC;IAEV,SAAS,CAAC,MAAK;AACb,QAAA,MAAM,QAAQ,GAAG,SAAS,CAAC,OAAO;AAClC,QAAA,IAAI,CAAC,QAAQ;YAAE;QAEf,MAAM,WAAW,GAAG,MAAK;AACvB,YAAA,UAAU,CAAC,OAAO,IAAI;AACtB,YAAA,2BAA2B,CAAC,OAAO,EAAE,KAAK,EAAE;AAC5C,YAAA,2BAA2B,CAAC,OAAO,GAAG,IAAI;AAC5C,QAAA,CAAC;AAED,QAAA,MAAM,YAAY,GAAG,CAAC,KAAY,KAAI;AACpC,YAAA,IAAI,CAAC,WAAW;gBAAE,KAAK,CAAC,cAAc,EAAE;AAC1C,QAAA,CAAC;AAED,QAAA,QAAQ,CAAC,gBAAgB,CAAC,OAAO,EAAE,WAAW,CAAC;AAC/C,QAAA,QAAQ,CAAC,gBAAgB,CAAC,QAAQ,EAAE,YAAY,CAAC;AAEjD,QAAA,IAAI,mBAA8D;AAClE,QAAA,IAAI,gBAAgB,CAAC,QAAQ,CAAC,EAAE;AAC9B,YAAA,QAAQ,CAAC,YAAY,CAAC,UAAU,EAAE,WAAW,GAAG,KAAK,GAAG,MAAM,CAAC;QACjE;aAAO;AACL,YAAA,mBAAmB,GAAG,CAAC,KAAK,KAAI;AAC9B,gBAAA,IAAI,eAAe,CAAC,KAAK,EAAE,QAAQ,CAAC,EAAE;AACpC,oBAAA,uBAAuB,CAAC,QAAQ,EAAE,WAAW,CAAC;gBAChD;AACF,YAAA,CAAC;AACD,YAAA,QAAQ,CAAC,gBAAgB,CAAC,OAAO,EAAE,mBAAmB,CAAC;QACzD;AAEA,QAAA,OAAO,MAAK;AACV,YAAA,QAAQ,CAAC,mBAAmB,CAAC,OAAO,EAAE,WAAW,CAAC;AAClD,YAAA,QAAQ,CAAC,mBAAmB,CAAC,QAAQ,EAAE,YAAY,CAAC;YACpD,IAAI,mBAAmB,EAAE;AACvB,gBAAA,QAAQ,CAAC,mBAAmB,CAAC,OAAO,EAAE,mBAAmB,CAAC;YAC5D;AACF,QAAA,CAAC;AACH,IAAA,CAAC,EAAE,CAAC,WAAW,CAAC,CAAC;AAEjB,IAAA,MAAM,iBAAiB,GAAG,UAAU,CAClC,WAAW,EACX;QACE,eAAe,EAAE,IAAI,KAAK,IAAI;QAC9B,eAAe,EAAE,IAAI,KAAK,IAAI;QAC9B,eAAe,EAAE,IAAI,KAAK,IAAI;KAC/B,EACD,SAAS,CACV;AAED,IAAA,QACEA,cAAA,CAAA,aAAA,CAAA,QAAA,EAAA,EAAA,GACM,SAAS,EACb,GAAG,EAAE,YAAY,EAAA,kBAAA,EACC,UAAU,EAAA,iBAAA,EACX,OAAO,EACxB,SAAS,EAAE,iBAAiB,EAC5B,EAAE,EAAE,OAAO,EAAA;AAEX,QAAAA,cAAA,CAAA,aAAA,CAAC,YAAY,CAAC,QAAQ,EAAA,EAAC,KAAK,EAAE,YAAY,EAAA,EACvC,QAAQ,CACa,CACjB;AAEb,CAAC;AAGH,KAAK,CAAC,WAAW,GAAG,OAAO;;;;"}
1
+ {"version":3,"file":"Modal.js","sources":["../../../src/components/modal/Modal.tsx"],"sourcesContent":["import \"./index.css\"\n\nimport { useId } from \"@utilities/useId\"\nimport classNames from \"classnames\"\nimport React, {\n forwardRef,\n type HTMLAttributes,\n useCallback,\n useEffect,\n useRef,\n} from \"react\"\nimport { Dialog } from \"react-aria-components/Dialog\"\nimport { Modal as AriaModal, ModalOverlay } from \"react-aria-components/Modal\"\n\nimport { ModalContext } from \"./ModalContext\"\n\nexport type ModalSize = \"sm\" | \"md\" | \"lg\" | \"xl\"\n\nexport interface ModalProps {\n /**\n * A `<ModalHeader>` followed by a `<ModalBody>` and a `<ModalFooter>`.\n */\n children: React.ReactNode\n /** Default `false`. `true` enables Esc, backdrop-click, and the close button together. */\n dismissible?: boolean\n /** Fires on every dismissal — Esc, backdrop, close button, or programmatic. */\n onClose?: () => void\n /** Controls the modal. */\n open: boolean\n /** Default `'md'`. */\n size?: ModalSize\n}\n\nexport type ModalElementProps = Omit<\n HTMLAttributes<HTMLElement>,\n keyof ModalProps | \"role\"\n> &\n ModalProps\n\nexport const Modal = forwardRef<HTMLElement, ModalElementProps>(function Modal(\n {\n children,\n className,\n dismissible = false,\n id,\n onClose,\n open,\n size = \"md\",\n ...restProps\n }: ModalElementProps,\n ref\n) {\n const stableId = useId()\n const modalId = id || `tds-modal-${stableId}`\n const titleId = `${modalId}-title`\n const subtitleId = `${modalId}-subtitle`\n\n const onCloseRef = useRef(onClose)\n useEffect(() => {\n onCloseRef.current = onClose\n }, [onClose])\n\n // Tracks whether the current \"open\" has already been reported to\n // `onClose`, so a dismiss gesture (Esc/backdrop/close button) and the\n // `open=false` re-render it causes don't both fire the callback.\n const reportedOpenRef = useRef(open)\n\n const notifyClose = useCallback(() => {\n if (!reportedOpenRef.current) return\n reportedOpenRef.current = false\n onCloseRef.current?.()\n }, [])\n\n useEffect(() => {\n if (open) {\n reportedOpenRef.current = true\n } else {\n notifyClose()\n }\n }, [open, notifyClose])\n\n const contextValue = {\n dismissible,\n requestClose: notifyClose,\n subtitleId,\n titleId,\n }\n\n const frameClassName = classNames(\"tds-modal-frame\", {\n \"tds-modal-frame--lg\": size === \"lg\",\n \"tds-modal-frame--sm\": size === \"sm\",\n \"tds-modal-frame--xl\": size === \"xl\",\n })\n\n return (\n <ModalOverlay\n className=\"tds-modal-overlay\"\n isDismissable={dismissible}\n isKeyboardDismissDisabled={!dismissible}\n isOpen={open}\n onOpenChange={(isOpen) => {\n if (!isOpen) notifyClose()\n }}\n >\n <AriaModal className={frameClassName}>\n <Dialog\n {...restProps}\n ref={ref}\n aria-describedby={subtitleId}\n aria-labelledby={titleId}\n className={classNames(\"tds-modal\", className)}\n id={modalId}\n >\n <ModalContext.Provider value={contextValue}>\n {children}\n </ModalContext.Provider>\n </Dialog>\n </AriaModal>\n </ModalOverlay>\n )\n})\n\nModal.displayName = \"Modal\"\n"],"names":["React","AriaModal"],"mappings":";;;;;;;AAuCO,MAAM,KAAK,GAAG,UAAU,CAAiC,SAAS,KAAK,CAC5E,EACE,QAAQ,EACR,SAAS,EACT,WAAW,GAAG,KAAK,EACnB,EAAE,EACF,OAAO,EACP,IAAI,EACJ,IAAI,GAAG,IAAI,EACX,GAAG,SAAS,EACM,EACpB,GAAG,EAAA;AAEH,IAAA,MAAM,QAAQ,GAAG,KAAK,EAAE;AACxB,IAAA,MAAM,OAAO,GAAG,EAAE,IAAI,CAAA,UAAA,EAAa,QAAQ,EAAE;AAC7C,IAAA,MAAM,OAAO,GAAG,CAAA,EAAG,OAAO,QAAQ;AAClC,IAAA,MAAM,UAAU,GAAG,CAAA,EAAG,OAAO,WAAW;AAExC,IAAA,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC;IAClC,SAAS,CAAC,MAAK;AACb,QAAA,UAAU,CAAC,OAAO,GAAG,OAAO;AAC9B,IAAA,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC;;;;AAKb,IAAA,MAAM,eAAe,GAAG,MAAM,CAAC,IAAI,CAAC;AAEpC,IAAA,MAAM,WAAW,GAAG,WAAW,CAAC,MAAK;QACnC,IAAI,CAAC,eAAe,CAAC,OAAO;YAAE;AAC9B,QAAA,eAAe,CAAC,OAAO,GAAG,KAAK;AAC/B,QAAA,UAAU,CAAC,OAAO,IAAI;IACxB,CAAC,EAAE,EAAE,CAAC;IAEN,SAAS,CAAC,MAAK;QACb,IAAI,IAAI,EAAE;AACR,YAAA,eAAe,CAAC,OAAO,GAAG,IAAI;QAChC;aAAO;AACL,YAAA,WAAW,EAAE;QACf;AACF,IAAA,CAAC,EAAE,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;AAEvB,IAAA,MAAM,YAAY,GAAG;QACnB,WAAW;AACX,QAAA,YAAY,EAAE,WAAW;QACzB,UAAU;QACV,OAAO;KACR;AAED,IAAA,MAAM,cAAc,GAAG,UAAU,CAAC,iBAAiB,EAAE;QACnD,qBAAqB,EAAE,IAAI,KAAK,IAAI;QACpC,qBAAqB,EAAE,IAAI,KAAK,IAAI;QACpC,qBAAqB,EAAE,IAAI,KAAK,IAAI;AACrC,KAAA,CAAC;IAEF,QACEA,cAAA,CAAA,aAAA,CAAC,YAAY,EAAA,EACX,SAAS,EAAC,mBAAmB,EAC7B,aAAa,EAAE,WAAW,EAC1B,yBAAyB,EAAE,CAAC,WAAW,EACvC,MAAM,EAAE,IAAI,EACZ,YAAY,EAAE,CAAC,MAAM,KAAI;AACvB,YAAA,IAAI,CAAC,MAAM;AAAE,gBAAA,WAAW,EAAE;QAC5B,CAAC,EAAA;AAED,QAAAA,cAAA,CAAA,aAAA,CAACC,OAAS,EAAA,EAAC,SAAS,EAAE,cAAc,EAAA;YAClCD,cAAA,CAAA,aAAA,CAAC,MAAM,OACD,SAAS,EACb,GAAG,EAAE,GAAG,EAAA,kBAAA,EACU,UAAU,EAAA,iBAAA,EACX,OAAO,EACxB,SAAS,EAAE,UAAU,CAAC,WAAW,EAAE,SAAS,CAAC,EAC7C,EAAE,EAAE,OAAO,EAAA;AAEX,gBAAAA,cAAA,CAAA,aAAA,CAAC,YAAY,CAAC,QAAQ,EAAA,EAAC,KAAK,EAAE,YAAY,EAAA,EACvC,QAAQ,CACa,CACjB,CACC,CACC;AAEnB,CAAC;AAED,KAAK,CAAC,WAAW,GAAG,OAAO;;;;"}
package/dist/index.css CHANGED
@@ -4985,76 +4985,90 @@ a.tds-dropdown-item:is(:hover, :active){
4985
4985
  line-height:1.35;
4986
4986
  }
4987
4987
 
4988
- .tds-modal{
4989
- --tds-modal-width:600px;
4990
- --tds-modal-opacity:0;
4991
- --tds-modal-backdrop-opacity:0;
4992
- --tds-modal-scale:.96;
4993
- --tds-modal-transition-duration:var(--t-duration-200);
4994
- --tds-modal-transition-timing-function:var(--t-ease-in);
4995
- --tds-modal-transition:opacity var(--tds-modal-transition-duration) var(--tds-modal-transition-timing-function), scale var(--tds-modal-transition-duration) var(--tds-modal-transition-timing-function);
4996
- --tds-modal-backdrop-transition:opacity var(--tds-modal-transition-duration) var(--tds-modal-transition-timing-function);
4988
+ .tds-modal-overlay{
4989
+ position:fixed;
4990
+ inset:0;
4991
+ z-index:1000;
4992
+ display:flex;
4993
+ align-items:center;
4994
+ justify-content:center;
4995
+ background:hsl(0 0% 0% / 40%);
4996
+ }
4997
4997
 
4998
- flex-direction:column;
4999
- width:min(var(--tds-modal-width), calc(100% - 48px));
5000
- padding-block:var(--t-spacing-3);
5001
- padding-inline:0;
5002
- background:var(--t-surface-color-card);
4998
+ .tds-modal-overlay[data-entering]{
4999
+ animation:tds-modal-overlay-fade var(--t-duration-300) var(--t-ease-out);
5000
+ }
5003
5001
 
5004
- border:0;
5005
- border-radius:var(--t-border-radius-lg);
5006
- box-shadow:0 4px 18px 0 #0003;
5002
+ .tds-modal-overlay[data-exiting]{
5003
+ animation:tds-modal-overlay-fade var(--t-duration-200) var(--t-ease-in) reverse;
5004
+ }
5007
5005
 
5008
- opacity:var(--tds-modal-opacity);
5009
- scale:var(--tds-modal-scale);
5010
- transition:var(--tds-modal-transition);
5006
+ @keyframes tds-modal-overlay-fade{
5007
+ from{
5008
+ opacity:0;
5009
+ }
5011
5010
  }
5012
5011
 
5013
- .tds-modal[open]{
5014
- --tds-modal-opacity:1;
5015
- --tds-modal-backdrop-opacity:1;
5016
- --tds-modal-scale:1;
5017
- --tds-modal-transition-duration:var(--t-duration-300);
5018
- --tds-modal-transition-timing-function:var(--t-ease-out);
5012
+ .tds-modal-frame{
5013
+ --tds-modal-width:600px;
5019
5014
 
5020
- display:flex;
5021
- }
5015
+ display:flex;
5016
+ justify-content:center;
5017
+ width:min(var(--tds-modal-width), calc(100% - 48px));
5018
+ }
5022
5019
 
5023
- .tds-modal::backdrop{
5024
- background:hsl(0 0% 0% / 40%);
5025
- opacity:var(--tds-modal-backdrop-opacity);
5026
- transition:var(--tds-modal-backdrop-transition);
5020
+ .tds-modal-frame[data-entering]{
5021
+ animation:tds-modal-zoom var(--t-duration-300) var(--t-ease-out);
5027
5022
  }
5028
5023
 
5029
- @starting-style{
5030
- .tds-modal[open]{
5031
- --tds-modal-opacity:0;
5032
- --tds-modal-scale:.96;
5024
+ .tds-modal-frame[data-exiting]{
5025
+ animation:tds-modal-zoom var(--t-duration-200) var(--t-ease-in) reverse;
5033
5026
  }
5034
5027
 
5035
- .tds-modal[open]::backdrop{
5036
- --tds-modal-backdrop-opacity:0;
5028
+ .tds-modal-frame--sm{
5029
+ --tds-modal-width:400px;
5030
+ }
5031
+
5032
+ .tds-modal-frame--lg{
5033
+ --tds-modal-width:800px;
5034
+ }
5035
+
5036
+ .tds-modal-frame--xl{
5037
+ --tds-modal-width:1000px;
5038
+ }
5039
+
5040
+ @keyframes tds-modal-zoom{
5041
+ from{
5042
+ opacity:0;
5043
+ scale:.96;
5037
5044
  }
5038
5045
  }
5039
5046
 
5040
5047
  @media (prefers-reduced-motion: reduce){
5041
- .tds-modal{
5042
- --tds-modal-transition:none;
5043
- --tds-modal-backdrop-transition:none;
5048
+ .tds-modal-overlay[data-entering],
5049
+ .tds-modal-overlay[data-exiting],
5050
+ .tds-modal-frame[data-entering],
5051
+ .tds-modal-frame[data-exiting]{
5052
+ animation:none;
5044
5053
  }
5045
5054
  }
5046
5055
 
5047
- .tds-modal--sm{
5048
- --tds-modal-width:400px;
5049
- }
5056
+ .tds-modal{
5057
+ display:flex;
5058
+ flex-direction:column;
5059
+ width:100%;
5060
+ padding-block:var(--t-spacing-3);
5061
+ padding-inline:0;
5062
+ background:var(--t-surface-color-card);
5050
5063
 
5051
- .tds-modal--lg{
5052
- --tds-modal-width:800px;
5064
+ border:0;
5065
+ border-radius:var(--t-border-radius-lg);
5066
+ box-shadow:0 4px 18px 0 #0003;
5053
5067
  }
5054
5068
 
5055
- .tds-modal--xl{
5056
- --tds-modal-width:1000px;
5057
- }
5069
+ .tds-modal:focus{
5070
+ outline:none;
5071
+ }
5058
5072
 
5059
5073
  .tds-modal-header{
5060
5074
  display:grid;