@trackunit/react-modal 1.10.1 → 1.10.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.
package/index.cjs.js CHANGED
@@ -56,26 +56,73 @@ const cvaModalContainer = cssClassVarianceUtilities.cvaMerge([
56
56
  "w-full",
57
57
  "flex",
58
58
  "fixed",
59
- "top-0",
60
- "left-0",
59
+ "inset-0",
61
60
  "min-h-[100dvh]",
62
61
  "min-w-[100dvw]",
63
62
  "overflow-auto",
64
63
  "items-center",
65
64
  ]);
65
+ const modalSizeConfigs = {
66
+ small: {
67
+ // Small modal — confirmations / short messages
68
+ minWidth: "320px",
69
+ viewportWidthPercent: "40dvw",
70
+ paddingOffset: "1rem",
71
+ breakpoint: "480px",
72
+ transitionRate: 0.15,
73
+ maxWidth: "520px",
74
+ maxHeight: "48dvh",
75
+ },
76
+ medium: {
77
+ // Medium modal — simple forms and single-step flows
78
+ minWidth: "320px",
79
+ viewportWidthPercent: "64dvw",
80
+ paddingOffset: "1rem",
81
+ breakpoint: "480px",
82
+ transitionRate: 0.15,
83
+ maxWidth: "700px",
84
+ maxHeight: "60dvh",
85
+ },
86
+ large: {
87
+ // Large modal — tables, complex layouts, multi-column flows
88
+ minWidth: "320px",
89
+ viewportWidthPercent: "90dvw",
90
+ paddingOffset: "1rem",
91
+ breakpoint: "480px",
92
+ transitionRate: 0.15,
93
+ maxWidth: "1400px",
94
+ maxHeight: "min(80dvh, 1000px)",
95
+ },
96
+ };
97
+ /**
98
+ * Returns the CSS properties for the modal card based on the size.
99
+ */
100
+ const getModalCardCSSVariables = (size) => {
101
+ const config = modalSizeConfigs[size];
102
+ // eslint-disable-next-line local-rules/no-typescript-assertion
103
+ return {
104
+ "--modal-min-width": config.minWidth,
105
+ "--modal-viewport-width-percent": config.viewportWidthPercent,
106
+ "--modal-padding-offset": config.paddingOffset,
107
+ "--modal-breakpoint": config.breakpoint,
108
+ "--modal-transition-rate": config.transitionRate,
109
+ "--modal-max-width": config.maxWidth,
110
+ "--modal-max-height": config.maxHeight,
111
+ };
112
+ };
66
113
  const cvaModalCard = cssClassVarianceUtilities.cvaMerge([
67
114
  "m-auto",
68
115
  "shadow-2xl",
69
- "w-[calc(100%-1rem)]",
70
- "max-h-[min(100%-1rem,30%+600px)]",
71
- "max-w-[min(100%-1rem,30%+600px)]",
116
+ "overflow-y-auto",
117
+ "w-[clamp(var(--modal-min-width),calc(var(--modal-viewport-width-percent)-var(--modal-padding-offset)-((100dvw-var(--modal-breakpoint))*var(--modal-transition-rate))),var(--modal-max-width))]",
118
+ "max-h-[var(--modal-max-height)]",
119
+ "@container", // This is used to target the container size for all children
72
120
  ]);
73
121
  const cvaModalBackdrop = cssClassVarianceUtilities.cvaMerge([
74
122
  "justify-center",
75
123
  "items-center",
76
124
  "fixed",
77
- "top-0",
78
- "left-0",
125
+ "inset-0",
79
126
  "min-h-[100dvh]",
80
127
  "min-w-[100dvw]",
81
128
  "w-full",
@@ -236,11 +283,14 @@ const useModalFooterBorder = (rootRef, { footerClass = "border-t", enabled = tru
236
283
  * };
237
284
  * ```
238
285
  */
239
- const Modal = ({ children, isOpen, role = "dialog", "data-testid": dataTestId, className, containerClassName, onBackdropClick, floatingUi, ref, }) => {
240
- const { rootElement, refs, floatingStyles, context, getFloatingProps } = floatingUi;
286
+ const Modal = ({ children, isOpen, role = "dialog", "data-testid": dataTestId, className, size, onBackdropClick, floatingUi, ref, }) => {
287
+ // For dialogs/modals, Floating UI recommends not using floatingStyles since the modal
288
+ // is viewport-centered via CSS, not positioned relative to a reference element.
289
+ // See: https://floating-ui.com/docs/dialog
290
+ const { rootElement, refs, context, getFloatingProps } = floatingUi;
241
291
  const cardRef = react.useRef(null);
242
292
  useModalFooterBorder(cardRef, { enabled: isOpen, footerClass: "border-t pt-4" });
243
- return (jsxRuntime.jsx(reactComponents.Portal, { root: rootElement, children: isOpen ? (jsxRuntime.jsx(react$1.FloatingFocusManager, { context: context, children: jsxRuntime.jsx("div", { ref: refs.setFloating, style: { ...floatingStyles, zIndex: uiDesignTokens.zIndex.overlay }, ...getFloatingProps(), children: jsxRuntime.jsx(ModalBackdrop, { onClick: onBackdropClick, children: jsxRuntime.jsx("div", { "aria-modal": true, className: cvaModalContainer({ className: containerClassName }), ref: ref, role: role, children: jsxRuntime.jsx(reactComponents.Card, { className: cvaModalCard({ className }), "data-testid": dataTestId, ref: cardRef, children: children }) }) }) }) })) : null }));
293
+ return (jsxRuntime.jsx(reactComponents.Portal, { root: rootElement, children: isOpen ? (jsxRuntime.jsx(react$1.FloatingFocusManager, { context: context, children: jsxRuntime.jsx("div", { ref: refs.setFloating, style: { zIndex: uiDesignTokens.zIndex.overlay }, ...getFloatingProps(), children: jsxRuntime.jsx(ModalBackdrop, { onClick: onBackdropClick, children: jsxRuntime.jsx("div", { "aria-modal": true, className: cvaModalContainer(), ref: ref, role: role, children: jsxRuntime.jsx(reactComponents.Card, { className: cvaModalCard({ className }), "data-testid": dataTestId, ref: cardRef, style: getModalCardCSSVariables(size), children: children }) }) }) }) })) : null }));
244
294
  };
245
295
  Modal.displayName = "Modal";
246
296
 
@@ -338,30 +388,32 @@ const ModalHeader = react.forwardRef(({ heading, subHeading, onClose, "data-test
338
388
  *
339
389
  * - `useModal` should be used with the `Modal` component.
340
390
  * - const modal = useModal();
341
- * - <Modal {...modal} />
391
+ * - <Modal {...modal} size="medium" />
342
392
  */
343
- const useModal = (props = {}) => {
344
- const [isOpen, setIsOpen] = react.useState(Boolean(props.isOpen));
345
- return useModalInner({
346
- ...props,
347
- setIsOpen,
348
- isOpen: typeof props.isOpen === "boolean" ? props.isOpen : isOpen,
349
- });
350
- };
351
- const useModalInner = ({ isOpen, setIsOpen, onOpenChange, rootElement, closeOnOutsideClick = true, closeOnEsc = true, onClose, onOpen, ref: customRef, }) => {
393
+ const useModal = (props) => {
394
+ const { isOpen: controlledIsOpen, onOpenChange, rootElement, closeOnOutsideClick = true, closeOnEsc = true, onClose, onOpen, ref: customRef, role, } = props ?? {};
395
+ const [internalIsOpen, setIsOpen] = react.useState(Boolean(controlledIsOpen));
396
+ const isOpen = typeof controlledIsOpen === "boolean" ? controlledIsOpen : internalIsOpen;
397
+ const { blockScroll, restoreScroll } = reactComponents.useScrollBlock();
352
398
  const ref = react.useRef(null);
353
399
  const modalDialogContext = react.useContext(reactCoreContextsApi.ModalDialogContext);
354
400
  if (!modalDialogContext) {
355
401
  throw new Error("useModal must be used within the ModalDialogContextProvider");
356
402
  }
357
403
  const { openModal, closeModal } = modalDialogContext;
358
- const { refs, floatingStyles, context } = react$1.useFloating({
404
+ const { refs, context } = react$1.useFloating({
359
405
  open: isOpen,
360
- onOpenChange: (bool, event, openChangeReason) => {
406
+ onOpenChange: (newIsOpen, event, openChangeReason) => {
407
+ if (newIsOpen) {
408
+ blockScroll();
409
+ }
410
+ else {
411
+ restoreScroll();
412
+ }
361
413
  if (onOpenChange) {
362
- return onOpenChange(bool, event, openChangeReason);
414
+ return onOpenChange(newIsOpen, event, openChangeReason);
363
415
  }
364
- setIsOpen(bool);
416
+ setIsOpen(newIsOpen);
365
417
  },
366
418
  whileElementsMounted: react$1.autoUpdate,
367
419
  middleware: [react$1.shift()],
@@ -386,8 +438,9 @@ const useModalInner = ({ isOpen, setIsOpen, onOpenChange, rootElement, closeOnOu
386
438
  }, [isOpen, openModal, closeModal]);
387
439
  const open = react.useCallback((...args) => {
388
440
  onOpen?.(...args);
441
+ blockScroll();
389
442
  setIsOpen(true);
390
- }, [onOpen, setIsOpen]);
443
+ }, [onOpen, blockScroll]);
391
444
  const close = react.useCallback((...args) => {
392
445
  for (const arg of args) {
393
446
  if (!arg || typeof arg !== "object") {
@@ -403,8 +456,9 @@ const useModalInner = ({ isOpen, setIsOpen, onOpenChange, rootElement, closeOnOu
403
456
  }
404
457
  }
405
458
  onClose?.(...args);
459
+ restoreScroll();
406
460
  setIsOpen(false);
407
- }, [onClose, setIsOpen]);
461
+ }, [onClose, restoreScroll]);
408
462
  const toggle = react.useCallback((...args) => {
409
463
  if (isOpen) {
410
464
  return close(...args);
@@ -423,23 +477,11 @@ const useModalInner = ({ isOpen, setIsOpen, onOpenChange, rootElement, closeOnOu
423
477
  floatingUi: {
424
478
  refs,
425
479
  rootElement,
426
- floatingStyles,
427
480
  context,
428
481
  getFloatingProps,
429
482
  },
430
- }), [
431
- close,
432
- closeOnOutsideClick,
433
- context,
434
- customRef,
435
- floatingStyles,
436
- getFloatingProps,
437
- isOpen,
438
- open,
439
- refs,
440
- rootElement,
441
- toggle,
442
- ]);
483
+ role,
484
+ }), [isOpen, closeOnOutsideClick, toggle, open, close, customRef, refs, rootElement, context, getFloatingProps, role]);
443
485
  };
444
486
 
445
487
  /*
package/index.esm.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { jsx, jsxs } from 'react/jsx-runtime';
2
2
  import { registerTranslations } from '@trackunit/i18n-library-translation';
3
3
  import { FloatingFocusManager, useFloating, autoUpdate, shift, useDismiss, useInteractions } from '@floating-ui/react';
4
- import { Portal, Card, Button, Heading, Text, IconButton, Icon } from '@trackunit/react-components';
4
+ import { Portal, Card, Button, Heading, Text, IconButton, Icon, useScrollBlock } from '@trackunit/react-components';
5
5
  import { zIndex } from '@trackunit/ui-design-tokens';
6
6
  import { useRef, useLayoutEffect, forwardRef, useState, useContext, useEffect, useCallback, useMemo } from 'react';
7
7
  import { cvaMerge } from '@trackunit/css-class-variance-utilities';
@@ -54,26 +54,73 @@ const cvaModalContainer = cvaMerge([
54
54
  "w-full",
55
55
  "flex",
56
56
  "fixed",
57
- "top-0",
58
- "left-0",
57
+ "inset-0",
59
58
  "min-h-[100dvh]",
60
59
  "min-w-[100dvw]",
61
60
  "overflow-auto",
62
61
  "items-center",
63
62
  ]);
63
+ const modalSizeConfigs = {
64
+ small: {
65
+ // Small modal — confirmations / short messages
66
+ minWidth: "320px",
67
+ viewportWidthPercent: "40dvw",
68
+ paddingOffset: "1rem",
69
+ breakpoint: "480px",
70
+ transitionRate: 0.15,
71
+ maxWidth: "520px",
72
+ maxHeight: "48dvh",
73
+ },
74
+ medium: {
75
+ // Medium modal — simple forms and single-step flows
76
+ minWidth: "320px",
77
+ viewportWidthPercent: "64dvw",
78
+ paddingOffset: "1rem",
79
+ breakpoint: "480px",
80
+ transitionRate: 0.15,
81
+ maxWidth: "700px",
82
+ maxHeight: "60dvh",
83
+ },
84
+ large: {
85
+ // Large modal — tables, complex layouts, multi-column flows
86
+ minWidth: "320px",
87
+ viewportWidthPercent: "90dvw",
88
+ paddingOffset: "1rem",
89
+ breakpoint: "480px",
90
+ transitionRate: 0.15,
91
+ maxWidth: "1400px",
92
+ maxHeight: "min(80dvh, 1000px)",
93
+ },
94
+ };
95
+ /**
96
+ * Returns the CSS properties for the modal card based on the size.
97
+ */
98
+ const getModalCardCSSVariables = (size) => {
99
+ const config = modalSizeConfigs[size];
100
+ // eslint-disable-next-line local-rules/no-typescript-assertion
101
+ return {
102
+ "--modal-min-width": config.minWidth,
103
+ "--modal-viewport-width-percent": config.viewportWidthPercent,
104
+ "--modal-padding-offset": config.paddingOffset,
105
+ "--modal-breakpoint": config.breakpoint,
106
+ "--modal-transition-rate": config.transitionRate,
107
+ "--modal-max-width": config.maxWidth,
108
+ "--modal-max-height": config.maxHeight,
109
+ };
110
+ };
64
111
  const cvaModalCard = cvaMerge([
65
112
  "m-auto",
66
113
  "shadow-2xl",
67
- "w-[calc(100%-1rem)]",
68
- "max-h-[min(100%-1rem,30%+600px)]",
69
- "max-w-[min(100%-1rem,30%+600px)]",
114
+ "overflow-y-auto",
115
+ "w-[clamp(var(--modal-min-width),calc(var(--modal-viewport-width-percent)-var(--modal-padding-offset)-((100dvw-var(--modal-breakpoint))*var(--modal-transition-rate))),var(--modal-max-width))]",
116
+ "max-h-[var(--modal-max-height)]",
117
+ "@container", // This is used to target the container size for all children
70
118
  ]);
71
119
  const cvaModalBackdrop = cvaMerge([
72
120
  "justify-center",
73
121
  "items-center",
74
122
  "fixed",
75
- "top-0",
76
- "left-0",
123
+ "inset-0",
77
124
  "min-h-[100dvh]",
78
125
  "min-w-[100dvw]",
79
126
  "w-full",
@@ -234,11 +281,14 @@ const useModalFooterBorder = (rootRef, { footerClass = "border-t", enabled = tru
234
281
  * };
235
282
  * ```
236
283
  */
237
- const Modal = ({ children, isOpen, role = "dialog", "data-testid": dataTestId, className, containerClassName, onBackdropClick, floatingUi, ref, }) => {
238
- const { rootElement, refs, floatingStyles, context, getFloatingProps } = floatingUi;
284
+ const Modal = ({ children, isOpen, role = "dialog", "data-testid": dataTestId, className, size, onBackdropClick, floatingUi, ref, }) => {
285
+ // For dialogs/modals, Floating UI recommends not using floatingStyles since the modal
286
+ // is viewport-centered via CSS, not positioned relative to a reference element.
287
+ // See: https://floating-ui.com/docs/dialog
288
+ const { rootElement, refs, context, getFloatingProps } = floatingUi;
239
289
  const cardRef = useRef(null);
240
290
  useModalFooterBorder(cardRef, { enabled: isOpen, footerClass: "border-t pt-4" });
241
- return (jsx(Portal, { root: rootElement, children: isOpen ? (jsx(FloatingFocusManager, { context: context, children: jsx("div", { ref: refs.setFloating, style: { ...floatingStyles, zIndex: zIndex.overlay }, ...getFloatingProps(), children: jsx(ModalBackdrop, { onClick: onBackdropClick, children: jsx("div", { "aria-modal": true, className: cvaModalContainer({ className: containerClassName }), ref: ref, role: role, children: jsx(Card, { className: cvaModalCard({ className }), "data-testid": dataTestId, ref: cardRef, children: children }) }) }) }) })) : null }));
291
+ return (jsx(Portal, { root: rootElement, children: isOpen ? (jsx(FloatingFocusManager, { context: context, children: jsx("div", { ref: refs.setFloating, style: { zIndex: zIndex.overlay }, ...getFloatingProps(), children: jsx(ModalBackdrop, { onClick: onBackdropClick, children: jsx("div", { "aria-modal": true, className: cvaModalContainer(), ref: ref, role: role, children: jsx(Card, { className: cvaModalCard({ className }), "data-testid": dataTestId, ref: cardRef, style: getModalCardCSSVariables(size), children: children }) }) }) }) })) : null }));
242
292
  };
243
293
  Modal.displayName = "Modal";
244
294
 
@@ -336,30 +386,32 @@ const ModalHeader = forwardRef(({ heading, subHeading, onClose, "data-testid": d
336
386
  *
337
387
  * - `useModal` should be used with the `Modal` component.
338
388
  * - const modal = useModal();
339
- * - <Modal {...modal} />
389
+ * - <Modal {...modal} size="medium" />
340
390
  */
341
- const useModal = (props = {}) => {
342
- const [isOpen, setIsOpen] = useState(Boolean(props.isOpen));
343
- return useModalInner({
344
- ...props,
345
- setIsOpen,
346
- isOpen: typeof props.isOpen === "boolean" ? props.isOpen : isOpen,
347
- });
348
- };
349
- const useModalInner = ({ isOpen, setIsOpen, onOpenChange, rootElement, closeOnOutsideClick = true, closeOnEsc = true, onClose, onOpen, ref: customRef, }) => {
391
+ const useModal = (props) => {
392
+ const { isOpen: controlledIsOpen, onOpenChange, rootElement, closeOnOutsideClick = true, closeOnEsc = true, onClose, onOpen, ref: customRef, role, } = props ?? {};
393
+ const [internalIsOpen, setIsOpen] = useState(Boolean(controlledIsOpen));
394
+ const isOpen = typeof controlledIsOpen === "boolean" ? controlledIsOpen : internalIsOpen;
395
+ const { blockScroll, restoreScroll } = useScrollBlock();
350
396
  const ref = useRef(null);
351
397
  const modalDialogContext = useContext(ModalDialogContext);
352
398
  if (!modalDialogContext) {
353
399
  throw new Error("useModal must be used within the ModalDialogContextProvider");
354
400
  }
355
401
  const { openModal, closeModal } = modalDialogContext;
356
- const { refs, floatingStyles, context } = useFloating({
402
+ const { refs, context } = useFloating({
357
403
  open: isOpen,
358
- onOpenChange: (bool, event, openChangeReason) => {
404
+ onOpenChange: (newIsOpen, event, openChangeReason) => {
405
+ if (newIsOpen) {
406
+ blockScroll();
407
+ }
408
+ else {
409
+ restoreScroll();
410
+ }
359
411
  if (onOpenChange) {
360
- return onOpenChange(bool, event, openChangeReason);
412
+ return onOpenChange(newIsOpen, event, openChangeReason);
361
413
  }
362
- setIsOpen(bool);
414
+ setIsOpen(newIsOpen);
363
415
  },
364
416
  whileElementsMounted: autoUpdate,
365
417
  middleware: [shift()],
@@ -384,8 +436,9 @@ const useModalInner = ({ isOpen, setIsOpen, onOpenChange, rootElement, closeOnOu
384
436
  }, [isOpen, openModal, closeModal]);
385
437
  const open = useCallback((...args) => {
386
438
  onOpen?.(...args);
439
+ blockScroll();
387
440
  setIsOpen(true);
388
- }, [onOpen, setIsOpen]);
441
+ }, [onOpen, blockScroll]);
389
442
  const close = useCallback((...args) => {
390
443
  for (const arg of args) {
391
444
  if (!arg || typeof arg !== "object") {
@@ -401,8 +454,9 @@ const useModalInner = ({ isOpen, setIsOpen, onOpenChange, rootElement, closeOnOu
401
454
  }
402
455
  }
403
456
  onClose?.(...args);
457
+ restoreScroll();
404
458
  setIsOpen(false);
405
- }, [onClose, setIsOpen]);
459
+ }, [onClose, restoreScroll]);
406
460
  const toggle = useCallback((...args) => {
407
461
  if (isOpen) {
408
462
  return close(...args);
@@ -421,23 +475,11 @@ const useModalInner = ({ isOpen, setIsOpen, onOpenChange, rootElement, closeOnOu
421
475
  floatingUi: {
422
476
  refs,
423
477
  rootElement,
424
- floatingStyles,
425
478
  context,
426
479
  getFloatingProps,
427
480
  },
428
- }), [
429
- close,
430
- closeOnOutsideClick,
431
- context,
432
- customRef,
433
- floatingStyles,
434
- getFloatingProps,
435
- isOpen,
436
- open,
437
- refs,
438
- rootElement,
439
- toggle,
440
- ]);
481
+ role,
482
+ }), [isOpen, closeOnOutsideClick, toggle, open, close, customRef, refs, rootElement, context, getFloatingProps, role]);
441
483
  };
442
484
 
443
485
  /*
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trackunit/react-modal",
3
- "version": "1.10.1",
3
+ "version": "1.10.4",
4
4
  "repository": "https://github.com/Trackunit/manager",
5
5
  "license": "SEE LICENSE IN LICENSE.txt",
6
6
  "engines": {
@@ -8,13 +8,14 @@
8
8
  },
9
9
  "dependencies": {
10
10
  "react": "19.0.0",
11
- "@trackunit/react-components": "1.12.1",
12
- "@trackunit/css-class-variance-utilities": "1.9.1",
13
- "@trackunit/i18n-library-translation": "1.9.1",
11
+ "@trackunit/react-components": "1.12.4",
12
+ "@trackunit/css-class-variance-utilities": "1.9.3",
13
+ "@trackunit/i18n-library-translation": "1.9.3",
14
14
  "@floating-ui/react": "^0.26.25",
15
15
  "@floating-ui/react-dom": "2.1.2",
16
- "@trackunit/ui-design-tokens": "1.9.1",
17
- "@trackunit/react-core-contexts-api": "1.10.1"
16
+ "@trackunit/ui-design-tokens": "1.9.3",
17
+ "@trackunit/react-core-contexts-api": "1.10.3",
18
+ "@trackunit/shared-utils": "1.11.3"
18
19
  },
19
20
  "module": "./index.esm.js",
20
21
  "main": "./index.cjs.js",
@@ -1,23 +1,23 @@
1
- import { AriaRole, PropsWithChildren } from "react";
1
+ import { Size } from "@trackunit/react-components";
2
+ import { PropsWithChildren, ReactElement } from "react";
2
3
  import { BaseUseModalActionArgs, UseModalReturnValue } from "./useModal";
3
- export type ModalProps<TOnCloseFnArgs extends BaseUseModalActionArgs = [], TOnOpenFnArgs extends BaseUseModalActionArgs = []> = {
4
+ /**
5
+ * Modal props extend the return type of useModal and add presentational-only props.
6
+ */
7
+ export type ModalProps<TOnCloseFnArgs extends BaseUseModalActionArgs = [], TOnOpenFnArgs extends BaseUseModalActionArgs = []> = PropsWithChildren<UseModalReturnValue<TOnCloseFnArgs, TOnOpenFnArgs> & {
4
8
  /**
5
- * The aria role for the modal.
9
+ * The size of the modal card.
6
10
  */
7
- role?: AriaRole;
11
+ size: Size;
8
12
  /**
9
13
  * The classes for the modal.
10
14
  */
11
15
  className?: string;
12
- /**
13
- * The classes for the container of the modal.
14
- */
15
- containerClassName?: string;
16
16
  /**
17
17
  * The test ID applied to the modal.
18
18
  */
19
19
  "data-testid"?: string;
20
- } & PropsWithChildren<UseModalReturnValue<TOnCloseFnArgs, TOnOpenFnArgs>>;
20
+ }>;
21
21
  /**
22
22
  * - Modals are used to present critical information or request user input needed to complete a user's workflow.
23
23
  * - Modals interrupt a user's workflow by design.
@@ -46,6 +46,6 @@ export type ModalProps<TOnCloseFnArgs extends BaseUseModalActionArgs = [], TOnOp
46
46
  * ```
47
47
  */
48
48
  export declare const Modal: {
49
- <TOnCloseFnArgs extends BaseUseModalActionArgs = [], TOnOpenFnArgs extends BaseUseModalActionArgs = []>({ children, isOpen, role, "data-testid": dataTestId, className, containerClassName, onBackdropClick, floatingUi, ref, }: ModalProps<TOnCloseFnArgs, TOnOpenFnArgs>): import("react/jsx-runtime").JSX.Element;
49
+ <TOnCloseFnArgs extends BaseUseModalActionArgs = [], TOnOpenFnArgs extends BaseUseModalActionArgs = []>({ children, isOpen, role, "data-testid": dataTestId, className, size, onBackdropClick, floatingUi, ref, }: ModalProps<TOnCloseFnArgs, TOnOpenFnArgs>): ReactElement;
50
50
  displayName: string;
51
51
  };
@@ -1,13 +1,18 @@
1
+ import { StoryObjWithOptionalArgs } from "@trackunit/shared-utils";
1
2
  import { ReactElement } from "react";
3
+ type Story = StoryObjWithOptionalArgs<typeof meta, "ref" | "isOpen" | "close" | "open" | "toggle" | "onBackdropClick" | "floatingUi" | "children" | "closeOnOutsideClick">;
2
4
  declare const meta: {
3
5
  title: string;
4
6
  component: {
5
- <TOnCloseFnArgs extends import("./useModal").BaseUseModalActionArgs = [], TOnOpenFnArgs extends import("./useModal").BaseUseModalActionArgs = []>({ children, isOpen, role, "data-testid": dataTestId, className, containerClassName, onBackdropClick, floatingUi, ref, }: import("./Modal").ModalProps<TOnCloseFnArgs, TOnOpenFnArgs>): import("react/jsx-runtime").JSX.Element;
7
+ <TOnCloseFnArgs extends import("./useModal").BaseUseModalActionArgs = [], TOnOpenFnArgs extends import("./useModal").BaseUseModalActionArgs = []>({ children, isOpen, role, "data-testid": dataTestId, className, size, onBackdropClick, floatingUi, ref, }: import("./Modal").ModalProps<TOnCloseFnArgs, TOnOpenFnArgs>): ReactElement;
6
8
  displayName: string;
7
9
  };
8
10
  tags: string[];
9
11
  parameters: {
10
12
  docs: {
13
+ description: {
14
+ component: string;
15
+ };
11
16
  source: {
12
17
  type: string;
13
18
  excludeDecorators: boolean;
@@ -16,44 +21,56 @@ declare const meta: {
16
21
  };
17
22
  argTypes: {
18
23
  floatingUi: {
19
- table: {
20
- disable: true;
21
- };
22
- };
23
- closeOnOutsideClick: {
24
+ control: false;
25
+ description: string;
24
26
  table: {
25
27
  category: string;
26
- defaultValue: {
27
- summary: string;
28
- };
29
28
  };
30
29
  };
31
30
  ref: {
31
+ control: false;
32
+ description: string;
32
33
  table: {
33
34
  category: string;
34
35
  };
35
36
  };
36
37
  onBackdropClick: {
38
+ control: false;
39
+ description: string;
37
40
  table: {
38
41
  category: string;
39
42
  };
40
43
  };
41
44
  open: {
45
+ control: false;
46
+ description: string;
42
47
  table: {
43
48
  category: string;
44
49
  };
45
50
  };
46
51
  close: {
52
+ control: false;
53
+ description: string;
47
54
  table: {
48
55
  category: string;
49
56
  };
50
57
  };
51
58
  toggle: {
59
+ control: false;
60
+ description: string;
52
61
  table: {
53
62
  category: string;
54
63
  };
55
64
  };
56
65
  isOpen: {
66
+ control: false;
67
+ description: string;
68
+ table: {
69
+ category: string;
70
+ };
71
+ };
72
+ closeOnOutsideClick: {
73
+ control: false;
57
74
  description: string;
58
75
  table: {
59
76
  category: string;
@@ -62,35 +79,22 @@ declare const meta: {
62
79
  };
63
80
  };
64
81
  };
82
+ size: {
83
+ control: {
84
+ type: "select";
85
+ };
86
+ options: string[];
87
+ description: string;
88
+ table: {
89
+ defaultValue: {
90
+ summary: string;
91
+ };
92
+ };
93
+ };
65
94
  };
66
95
  };
67
96
  export default meta;
68
97
  export declare const PackageName: () => ReactElement;
69
- export declare const Default: {
70
- render: () => import("react/jsx-runtime").JSX.Element;
71
- args: {};
72
- };
73
- export declare const IncorrectUsageErrorModal: {
74
- render: () => import("react/jsx-runtime").JSX.Element;
75
- args: {};
76
- };
77
- export declare const CorrectUsagePrimaryAction: {
78
- render: () => import("react/jsx-runtime").JSX.Element;
79
- args: {};
80
- };
81
- export declare const IncorrectUsageLongText: {
82
- render: () => import("react/jsx-runtime").JSX.Element;
83
- args: {};
84
- };
85
- export declare const CorrectUsageImportantAction: {
86
- render: () => import("react/jsx-runtime").JSX.Element;
87
- args: {};
88
- };
89
- export declare const CorrectAccessibility: {
90
- render: () => import("react/jsx-runtime").JSX.Element;
91
- args: {};
92
- };
93
- export declare const CorrectStateManagement: {
94
- render: () => import("react/jsx-runtime").JSX.Element;
95
- args: {};
96
- };
98
+ export declare const Default: Story;
99
+ export declare const WithScrollableContent: Story;
100
+ export declare const MultiColumnLayout: Story;
@@ -1,3 +1,9 @@
1
+ import { Size } from "@trackunit/react-components";
2
+ import { CSSProperties } from "react";
1
3
  export declare const cvaModalContainer: (props?: import("class-variance-authority/dist/types").ClassProp | undefined) => string;
4
+ /**
5
+ * Returns the CSS properties for the modal card based on the size.
6
+ */
7
+ export declare const getModalCardCSSVariables: (size: Size) => CSSProperties;
2
8
  export declare const cvaModalCard: (props?: import("class-variance-authority/dist/types").ClassProp | undefined) => string;
3
9
  export declare const cvaModalBackdrop: (props?: import("class-variance-authority/dist/types").ClassProp | undefined) => string;
@@ -0,0 +1,11 @@
1
+ interface SizeSection {
2
+ label: string;
3
+ items: Array<string>;
4
+ }
5
+ interface SizeInfo {
6
+ description: string;
7
+ specs: Array<string>;
8
+ sections: Array<SizeSection>;
9
+ }
10
+ export declare const MODAL_SIZE_INFO: Record<"small" | "medium" | "large", SizeInfo>;
11
+ export {};
@@ -1,7 +1,7 @@
1
1
  import { UseFloatingOptions, UseFloatingReturn } from "@floating-ui/react";
2
2
  import { UseFloatingReturn as UseFloatingReturn_Dom } from "@floating-ui/react-dom";
3
3
  import { UseInteractionsReturn } from "@floating-ui/react/dist/floating-ui.react";
4
- import { MouseEvent, Ref } from "react";
4
+ import { AriaRole, MouseEvent, Ref } from "react";
5
5
  type BaseUseModalActionArg = string | number | boolean | null | undefined | [] | object | MouseEvent;
6
6
  export type BaseUseModalActionArgs = {} & Array<BaseUseModalActionArg>;
7
7
  type OnCloseFn<TOnCloseFnArgs extends BaseUseModalActionArgs> = (...args: TOnCloseFnArgs) => void;
@@ -15,10 +15,13 @@ export type UseModalProps<TOnCloseFnArgs extends BaseUseModalActionArgs = [], TO
15
15
  closeOnEsc?: boolean;
16
16
  closeOnOutsideClick?: boolean;
17
17
  ref?: Ref<HTMLDivElement>;
18
+ /**
19
+ * The aria role for the modal.
20
+ */
21
+ role?: AriaRole;
18
22
  };
19
23
  type FloatingUiProps = {
20
24
  refs: UseFloatingReturn_Dom["refs"];
21
- floatingStyles: UseFloatingReturn_Dom["floatingStyles"];
22
25
  rootElement?: HTMLElement;
23
26
  context: UseFloatingReturn["context"];
24
27
  getFloatingProps: UseInteractionsReturn["getFloatingProps"];
@@ -56,13 +59,17 @@ export type UseModalReturnValue<TOnCloseFnArgs extends BaseUseModalActionArgs =
56
59
  * The floating UI properties for the modal.
57
60
  */
58
61
  floatingUi: FloatingUiProps;
62
+ /**
63
+ * The aria role for the modal.
64
+ */
65
+ role?: AriaRole;
59
66
  };
60
67
  /**
61
68
  * A hook to handle the state and configuration of Modal components.
62
69
  *
63
70
  * - `useModal` should be used with the `Modal` component.
64
71
  * - const modal = useModal();
65
- * - <Modal {...modal} />
72
+ * - <Modal {...modal} size="medium" />
66
73
  */
67
74
  export declare const useModal: <TOnCloseFnArgs extends BaseUseModalActionArgs = [], TOnOpenFnArgs extends BaseUseModalActionArgs = []>(props?: UseModalProps<TOnCloseFnArgs, TOnOpenFnArgs>) => UseModalReturnValue<TOnCloseFnArgs, TOnOpenFnArgs>;
68
75
  export {};