@homebound/beam 2.369.6 → 2.370.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -22,6 +22,14 @@ export interface ModalProps {
|
|
|
22
22
|
api?: MutableRefObject<ModalApi | undefined>;
|
|
23
23
|
/** Adds a border for the header. */
|
|
24
24
|
drawHeaderBorder?: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Defaults to `true`
|
|
27
|
+
* Renders `x` icon and closes modal when users click outside of it.
|
|
28
|
+
*
|
|
29
|
+
* When false, relies on you to provide a way to close the modal, i.e. a cancel or confirm button.
|
|
30
|
+
* Useful if you definitely need to force the user to make a choice.
|
|
31
|
+
* */
|
|
32
|
+
allowClosing?: boolean;
|
|
25
33
|
}
|
|
26
34
|
export type ModalApi = {
|
|
27
35
|
setSize: (size: ModalProps["size"]) => void;
|
|
@@ -23,7 +23,7 @@ const ModalContext_1 = require("./ModalContext");
|
|
|
23
23
|
* Provides underlay, modal container, and header. Will disable scrolling of page under the modal.
|
|
24
24
|
*/
|
|
25
25
|
function Modal(props) {
|
|
26
|
-
const { size = "md", content, forceScrolling, api, drawHeaderBorder = false } = props;
|
|
26
|
+
const { size = "md", content, forceScrolling, api, drawHeaderBorder = false, allowClosing = true } = props;
|
|
27
27
|
const isFixedHeight = typeof size !== "string";
|
|
28
28
|
const ref = (0, react_1.useRef)(null);
|
|
29
29
|
const { modalBodyDiv, modalFooterDiv, modalHeaderDiv } = (0, BeamContext_1.useBeamContext)();
|
|
@@ -35,7 +35,8 @@ function Modal(props) {
|
|
|
35
35
|
isDismissable: true,
|
|
36
36
|
shouldCloseOnInteractOutside: (el) => {
|
|
37
37
|
// Do not close the Modal if the user is interacting with the Tribute mentions dropdown (via RichTextField) or with another 3rd party dialog (such as a lightbox) on top of it.
|
|
38
|
-
return
|
|
38
|
+
return (allowClosing &&
|
|
39
|
+
!(el.closest(".tribute-container") || el.closest("[role='dialog']") || el.closest("[role='alert']")));
|
|
39
40
|
},
|
|
40
41
|
}, ref);
|
|
41
42
|
const { modalProps } = (0, react_aria_1.useModal)();
|
|
@@ -82,7 +83,7 @@ function Modal(props) {
|
|
|
82
83
|
.if(sm)
|
|
83
84
|
.add("height", "100dvh")
|
|
84
85
|
.add("width", "100dvw")
|
|
85
|
-
.maxh("none").br0.$, ref: ref, ...overlayProps, ...dialogProps, ...modalProps, ...testId, children: [(0, jsx_runtime_1.jsxs)("header", { css: Css_1.Css.df.fdrr.p3.fs0.if(drawHeaderBorder).bb.bcGray200.$, children: [(0, jsx_runtime_1.jsx)("span", { css: Css_1.Css.fs0.pl1.$, children: (0, jsx_runtime_1.jsx)(IconButton_1.IconButton, { icon: "x", onClick: closeModal, ...testId.titleClose }) }), (0, jsx_runtime_1.jsx)("h1", { css: Css_1.Css.fg1.xl2Sb.gray900.$, ref: modalHeaderRef, ...titleProps, ...testId.title })] }), (0, jsx_runtime_1.jsx)("main", { ref: modalBodyRef, css: Css_1.Css.fg1.oya.if(hasScroll).bb.bcGray200.if(!!forceScrolling).oys.$, children: content }), (0, jsx_runtime_1.jsx)("footer", { css: Css_1.Css.fs0.$, children: (0, jsx_runtime_1.jsx)("div", { ref: modalFooterRef }) })] }) }) }) }) }) }));
|
|
86
|
+
.maxh("none").br0.$, ref: ref, ...overlayProps, ...dialogProps, ...modalProps, ...testId, children: [(0, jsx_runtime_1.jsxs)("header", { css: Css_1.Css.df.fdrr.p3.fs0.if(drawHeaderBorder).bb.bcGray200.$, children: [(0, jsx_runtime_1.jsx)("span", { css: Css_1.Css.fs0.pl1.$, children: allowClosing && (0, jsx_runtime_1.jsx)(IconButton_1.IconButton, { icon: "x", onClick: closeModal, ...testId.titleClose }) }), (0, jsx_runtime_1.jsx)("h1", { css: Css_1.Css.fg1.xl2Sb.gray900.$, ref: modalHeaderRef, ...titleProps, ...testId.title })] }), (0, jsx_runtime_1.jsx)("main", { ref: modalBodyRef, css: Css_1.Css.fg1.oya.if(hasScroll).bb.bcGray200.if(!!forceScrolling).oys.$, children: content }), (0, jsx_runtime_1.jsx)("footer", { css: Css_1.Css.fs0.$, children: (0, jsx_runtime_1.jsx)("div", { ref: modalFooterRef }) })] }) }) }) }) }) }));
|
|
86
87
|
}
|
|
87
88
|
function ModalHeader({ children }) {
|
|
88
89
|
const { modalHeaderDiv } = (0, BeamContext_1.useBeamContext)();
|
|
@@ -5,6 +5,7 @@ export interface TestModalContentProps {
|
|
|
5
5
|
withDateField?: boolean;
|
|
6
6
|
withTextArea?: boolean;
|
|
7
7
|
withTextField?: boolean;
|
|
8
|
+
allowClosing?: boolean;
|
|
8
9
|
}
|
|
9
10
|
/** A fake modal content component that we share across the modal and superdrawer stories. */
|
|
10
11
|
export declare function TestModalContent(props: TestModalContentProps): import("@emotion/react/jsx-runtime").JSX.Element;
|
|
@@ -26,7 +26,10 @@ function TestModalContent(props) {
|
|
|
26
26
|
const [date, setDate] = (0, react_1.useState)(formStateDomain_1.jan1);
|
|
27
27
|
const [internalValue, setValue] = (0, react_1.useState)("");
|
|
28
28
|
const { triggerNotice } = (0, Snackbar_1.useSnackbar)();
|
|
29
|
-
return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(Modal_1.ModalHeader, { children: props.withTag ? ((0, jsx_runtime_1.jsxs)("div", { css: Css_1.Css.df.aic.$, children: [(0, jsx_runtime_1.jsx)("span", { children: "Modal Title with Tag" }), (0, jsx_runtime_1.jsx)(Tag_1.Tag, { text: "In progress", type: "info", xss: Css_1.Css.ml1.$ })] })) : props.withTextField ? ((0, jsx_runtime_1.jsx)(inputs_1.TextField, { label: "Title", placeholder: "Test title", value: internalValue, onChange: (v) => setValue(v), labelStyle: "hidden", onEscapeBubble: true, borderless: true, xss: Css_1.Css.xl.$ })) : props.withTextArea ? ((0, jsx_runtime_1.jsx)(inputs_1.TextAreaField, { label: "Title", placeholder: "Test title", value: internalValue, onChange: (v) => setValue(v), preventNewLines: true, labelStyle: "hidden", borderless: true, xss: Css_1.Css.xl.$ })) : ("The title of the modal that might wrap") }), (0, jsx_runtime_1.jsxs)(Modal_1.ModalBody, { children: [(0, jsx_runtime_1.jsxs)("div", { css: Css_1.Css.df.gap1.fdc.aifs.$, children: [(0, jsx_runtime_1.jsxs)("div", { css: Css_1.Css.df.gap1.$, children: [(0, jsx_runtime_1.jsx)(Button_1.Button, { label: "More", onClick: () => setNumSentences(numSentences + 2) }), (0, jsx_runtime_1.jsx)(Button_1.Button, { label: "Clear", onClick: () => setNumSentences(0) }), (0, jsx_runtime_1.jsx)(Button_1.Button, { label: "Primary", onClick: () => setPrimaryDisabled(!primaryDisabled) }), (0, jsx_runtime_1.jsx)(Button_1.Button, { label: "Trigger Snackbar", onClick: () => triggerNotice({ message: "Snackbar message" }) }), showLeftAction && ((0, jsx_runtime_1.jsx)(Button_1.Button, { label: "Left Action", onClick: () => setLeftActionDisabled(!leftActionDisabled) }))] }), (0, jsx_runtime_1.jsx)("p", { children: "The body content of the modal. This content can be anything!".repeat(numSentences) })] }), withDateField && (0, jsx_runtime_1.jsx)(inputs_1.DateField, { value: date, label: "Date", onChange: setDate })] }), (0, jsx_runtime_1.jsxs)(Modal_1.ModalFooter, { xss: showLeftAction ? Css_1.Css.jcsb.$ : undefined, children: [showLeftAction && ((0, jsx_runtime_1.jsx)("div", { children: (0, jsx_runtime_1.jsx)(Button_1.Button, { label: "Clear", onClick: (0, addon_actions_1.action)("Clear Action"), variant: "tertiary", disabled: leftActionDisabled }) })), (0, jsx_runtime_1.jsxs)("div", { css: Css_1.Css.df.gap1.$, children: [(0, jsx_runtime_1.jsx)(Button_1.Button, { label: "Cancel", onClick: closeModal, variant: "tertiary" }), (0, jsx_runtime_1.jsx)(Button_1.Button, { label: "Apply", onClick: (
|
|
29
|
+
return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(Modal_1.ModalHeader, { children: props.withTag ? ((0, jsx_runtime_1.jsxs)("div", { css: Css_1.Css.df.aic.$, children: [(0, jsx_runtime_1.jsx)("span", { children: "Modal Title with Tag" }), (0, jsx_runtime_1.jsx)(Tag_1.Tag, { text: "In progress", type: "info", xss: Css_1.Css.ml1.$ })] })) : props.withTextField ? ((0, jsx_runtime_1.jsx)(inputs_1.TextField, { label: "Title", placeholder: "Test title", value: internalValue, onChange: (v) => setValue(v), labelStyle: "hidden", onEscapeBubble: true, borderless: true, xss: Css_1.Css.xl.$ })) : props.withTextArea ? ((0, jsx_runtime_1.jsx)(inputs_1.TextAreaField, { label: "Title", placeholder: "Test title", value: internalValue, onChange: (v) => setValue(v), preventNewLines: true, labelStyle: "hidden", borderless: true, xss: Css_1.Css.xl.$ })) : ("The title of the modal that might wrap") }), (0, jsx_runtime_1.jsxs)(Modal_1.ModalBody, { children: [(0, jsx_runtime_1.jsxs)("div", { css: Css_1.Css.df.gap1.fdc.aifs.$, children: [(0, jsx_runtime_1.jsxs)("div", { css: Css_1.Css.df.gap1.$, children: [(0, jsx_runtime_1.jsx)(Button_1.Button, { label: "More", onClick: () => setNumSentences(numSentences + 2) }), (0, jsx_runtime_1.jsx)(Button_1.Button, { label: "Clear", onClick: () => setNumSentences(0) }), (0, jsx_runtime_1.jsx)(Button_1.Button, { label: "Primary", onClick: () => setPrimaryDisabled(!primaryDisabled) }), (0, jsx_runtime_1.jsx)(Button_1.Button, { label: "Trigger Snackbar", onClick: () => triggerNotice({ message: "Snackbar message" }) }), showLeftAction && ((0, jsx_runtime_1.jsx)(Button_1.Button, { label: "Left Action", onClick: () => setLeftActionDisabled(!leftActionDisabled) }))] }), (0, jsx_runtime_1.jsx)("p", { children: "The body content of the modal. This content can be anything!".repeat(numSentences) })] }), withDateField && (0, jsx_runtime_1.jsx)(inputs_1.DateField, { value: date, label: "Date", onChange: setDate })] }), (0, jsx_runtime_1.jsxs)(Modal_1.ModalFooter, { xss: showLeftAction ? Css_1.Css.jcsb.$ : undefined, children: [showLeftAction && ((0, jsx_runtime_1.jsx)("div", { children: (0, jsx_runtime_1.jsx)(Button_1.Button, { label: "Clear", onClick: (0, addon_actions_1.action)("Clear Action"), variant: "tertiary", disabled: leftActionDisabled }) })), (0, jsx_runtime_1.jsxs)("div", { css: Css_1.Css.df.gap1.$, children: [(0, jsx_runtime_1.jsx)(Button_1.Button, { label: "Cancel", onClick: closeModal, variant: "tertiary" }), (0, jsx_runtime_1.jsx)(Button_1.Button, { label: "Apply", onClick: () => {
|
|
30
|
+
(0, addon_actions_1.action)("Primary action");
|
|
31
|
+
!(props === null || props === void 0 ? void 0 : props.allowClosing) && closeModal();
|
|
32
|
+
}, disabled: primaryDisabled })] })] })] }));
|
|
30
33
|
}
|
|
31
34
|
function TestModalFilterTable() {
|
|
32
35
|
const [filter, setFilter] = (0, react_1.useState)();
|
|
@@ -36,6 +36,9 @@ function useGrowingTextField({ inputRef, inputWrapRef, value, disabled }) {
|
|
|
36
36
|
return;
|
|
37
37
|
}
|
|
38
38
|
onHeightChange();
|
|
39
|
+
// This is a hack to re-measure the height of the textarea after all the content has been rendered (i.e. after multiple GridTable children have been rendered)
|
|
40
|
+
// See the InVirtualizedTable storybook for reproducing this behavior
|
|
41
|
+
setTimeout(() => onHeightChange(), 200);
|
|
39
42
|
}
|
|
40
43
|
}, [onHeightChange, value, inputRef, disabled, inputWrapRef]);
|
|
41
44
|
}
|