braid-design-system 34.9.0 → 34.10.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.
Files changed (76) hide show
  1. package/CHANGELOG.md +62 -0
  2. package/dist/color-mode/query-param.d.cts +1 -1
  3. package/dist/color-mode/query-param.d.mts +1 -1
  4. package/dist/lib/components/Accordion/Accordion.cjs +61 -13
  5. package/dist/lib/components/Accordion/Accordion.d.cts +56 -2
  6. package/dist/lib/components/Accordion/Accordion.d.mts +56 -2
  7. package/dist/lib/components/Accordion/Accordion.mjs +62 -14
  8. package/dist/lib/components/Accordion/AccordionContext.d.cts +7 -0
  9. package/dist/lib/components/Accordion/AccordionContext.d.mts +7 -0
  10. package/dist/lib/components/Accordion/AccordionItem.cjs +86 -12
  11. package/dist/lib/components/Accordion/AccordionItem.css.cjs +15 -0
  12. package/dist/lib/components/Accordion/AccordionItem.css.mjs +13 -1
  13. package/dist/lib/components/Accordion/AccordionItem.d.cts +5 -0
  14. package/dist/lib/components/Accordion/AccordionItem.d.mts +5 -0
  15. package/dist/lib/components/Accordion/AccordionItem.mjs +88 -14
  16. package/dist/lib/components/Accordion/AccordionItem.playroom.cjs +6 -2
  17. package/dist/lib/components/Accordion/AccordionItem.playroom.d.cts +1 -1
  18. package/dist/lib/components/Accordion/AccordionItem.playroom.d.mts +1 -1
  19. package/dist/lib/components/Accordion/AccordionItem.playroom.mjs +6 -2
  20. package/dist/lib/components/Avatar/Avatar.cjs +1 -1
  21. package/dist/lib/components/Avatar/Avatar.css.cjs +2 -1
  22. package/dist/lib/components/Avatar/Avatar.css.mjs +2 -1
  23. package/dist/lib/components/Avatar/Avatar.mjs +1 -1
  24. package/dist/lib/components/BraidPortal/BraidPortal.cjs +0 -1
  25. package/dist/lib/components/BraidPortal/BraidPortal.mjs +0 -1
  26. package/dist/lib/components/BraidProvider/BraidProvider.cjs +0 -1
  27. package/dist/lib/components/BraidProvider/BraidProvider.mjs +0 -1
  28. package/dist/lib/components/BraidProvider/VanillaThemeContainer.cjs +2 -2
  29. package/dist/lib/components/BraidProvider/VanillaThemeContainer.mjs +2 -2
  30. package/dist/lib/components/Drawer/Drawer.cjs +5 -0
  31. package/dist/lib/components/Drawer/Drawer.mjs +5 -0
  32. package/dist/lib/components/private/Modal/Modal.cjs +0 -10
  33. package/dist/lib/components/private/Modal/Modal.mjs +0 -10
  34. package/dist/lib/components/private/Modal/ModalContent.cjs +9 -4
  35. package/dist/lib/components/private/Modal/ModalContent.mjs +9 -4
  36. package/dist/lib/components/useToast/Toast.cjs +8 -3
  37. package/dist/lib/components/useToast/Toast.mjs +8 -3
  38. package/dist/lib/components/useToast/ToastTypes.d.cts +3 -1
  39. package/dist/lib/components/useToast/ToastTypes.d.mts +3 -1
  40. package/dist/lib/components/useToast/useFlipList.cjs +3 -3
  41. package/dist/lib/components/useToast/useFlipList.mjs +3 -3
  42. package/dist/lib/components/useToast/useTimeout.cjs +1 -1
  43. package/dist/lib/components/useToast/useTimeout.mjs +1 -1
  44. package/dist/lib/playroom/snippets/Accordion.cjs +8 -12
  45. package/dist/lib/playroom/snippets/Accordion.mjs +8 -12
  46. package/dist/lib/playroom/snippets/BulkActions.cjs +8 -0
  47. package/dist/lib/playroom/snippets/BulkActions.mjs +8 -0
  48. package/dist/lib/playroom/snippets/DividedLists.cjs +8 -0
  49. package/dist/lib/playroom/snippets/DividedLists.mjs +8 -0
  50. package/dist/lib/playroom/snippets/EmptyState.cjs +12 -0
  51. package/dist/lib/playroom/snippets/EmptyState.mjs +12 -0
  52. package/dist/lib/playroom/snippets/ErrorState.cjs +12 -0
  53. package/dist/lib/playroom/snippets/ErrorState.mjs +12 -0
  54. package/dist/lib/playroom/snippets/Forms.cjs +12 -0
  55. package/dist/lib/playroom/snippets/Forms.mjs +12 -0
  56. package/dist/lib/playroom/snippets/Nudge.cjs +20 -0
  57. package/dist/lib/playroom/snippets/Nudge.mjs +20 -0
  58. package/dist/lib/playroom/snippets/ServiceOutageBanner.cjs +12 -0
  59. package/dist/lib/playroom/snippets/ServiceOutageBanner.mjs +12 -0
  60. package/dist/lib/playroom/snippets/SkeletonLoader.cjs +8 -0
  61. package/dist/lib/playroom/snippets/SkeletonLoader.mjs +8 -0
  62. package/dist/lib/playroom/snippets/SocialShare.cjs +8 -0
  63. package/dist/lib/playroom/snippets/SocialShare.mjs +8 -0
  64. package/dist/lib/playroom/snippets.cjs +20 -1
  65. package/dist/lib/playroom/snippets.mjs +116 -97
  66. package/dist/playroom/FrameComponent.mjs +1 -2
  67. package/dist/playroom/components.d.cts +5 -0
  68. package/dist/playroom/components.d.mts +5 -0
  69. package/dist/playroom/scope.mjs +1 -2
  70. package/dist/playroom/snippets.mjs +1 -2
  71. package/dist/test.mjs +1 -2
  72. package/dist/themes/docs.mjs +1 -2
  73. package/dist/themes/seekBusiness.mjs +1 -2
  74. package/dist/themes/seekJobs.mjs +1 -2
  75. package/dist/themes/wireframe.mjs +1 -2
  76. package/package.json +4 -3
@@ -14,6 +14,11 @@ interface AccordionItemBaseProps {
14
14
  icon?: TextProps['icon'];
15
15
  data?: DataAttributeMap;
16
16
  badge?: ReactElement<BadgeProps> | null;
17
+ /**
18
+ * Key used when Accordion owns the open items.
19
+ * Required when `multiple` is `false`, or when `value`, `defaultValue`, or `onChange` is set on Accordion.
20
+ */
21
+ value?: string;
17
22
  }
18
23
  type AccordionItemProps = AccordionItemBaseProps & UseDisclosureProps;
19
24
  type AccordionItemStateProps = DisclosureStateProps;
@@ -1,16 +1,16 @@
1
1
  import buildDataAttributes_default from "../private/buildDataAttributes.mjs";
2
2
  import { Box } from "../Box/Box.mjs";
3
- import { Stack } from "../Stack/Stack.mjs";
4
3
  import { AccordionContext, validTones } from "./AccordionContext.mjs";
5
4
  import "./Accordion.mjs";
5
+ import { useFallbackId } from "../../hooks/useFallbackId.mjs";
6
6
  import { useDisclosure } from "../Disclosure/useDisclosure.mjs";
7
7
  import { Spread } from "../Spread/Spread.mjs";
8
8
  import { Text } from "../Text/Text.mjs";
9
9
  import { IconChevron } from "../icons/IconChevron/IconChevron.mjs";
10
10
  import { badgeSlotSpace } from "../private/badgeSlotSpace.mjs";
11
- import { button, focusRing } from "./AccordionItem.css.mjs";
11
+ import { button, content, contentClosed, contentOpen, focusRing } from "./AccordionItem.css.mjs";
12
12
  import assert from "assert";
13
- import { cloneElement, useContext } from "react";
13
+ import { cloneElement, useCallback, useContext, useLayoutEffect, useRef, useState } from "react";
14
14
  import { jsx, jsxs } from "react/jsx-runtime";
15
15
  //#region src/lib/components/Accordion/AccordionItem.tsx
16
16
  const itemSpaceForSize = {
@@ -19,12 +19,21 @@ const itemSpaceForSize = {
19
19
  standard: "medium",
20
20
  large: "medium"
21
21
  };
22
- const AccordionItem = ({ id, label, children, badge, size: sizeProp, tone: toneProp, weight: weightProp, icon, data, ...restProps }) => {
22
+ const minDurationMs = 200;
23
+ const maxDurationMs = 500;
24
+ const pixelsPerSecond = 320;
25
+ const durationMsForHeight = (height) => Math.round(Math.min(maxDurationMs, Math.max(minDurationMs, height / pixelsPerSecond * 1e3)));
26
+ const prefersReducedMotion = () => typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
27
+ const isHeightTransition = (event) => event.propertyName === "height" && event.target === event.currentTarget;
28
+ const AccordionItem = ({ id, value, label, children, badge, size: sizeProp, tone: toneProp, weight: weightProp, icon, data, expanded: expandedProp, onToggle, ...restProps }) => {
23
29
  const accordionContext = useContext(AccordionContext);
30
+ const managed = Boolean(accordionContext?.managed);
24
31
  assert(!(accordionContext && sizeProp), "Size cannot be set on AccordionItem when inside Accordion. Size should be set on Accordion instead.");
25
32
  assert(!(accordionContext && toneProp), "Tone cannot be set on AccordionItem when inside Accordion. Tone should be set on Accordion instead.");
26
33
  assert(!(accordionContext && weightProp), "Weight cannot be set on AccordionItem when inside Accordion. Weight should be set on Accordion instead.");
27
- assert(toneProp === void 0 || validTones.includes(toneProp), `The 'tone' prop should be one of the following: ${validTones.map((x) => `"${x}"`).join(", ")}`);
34
+ assert(toneProp === void 0 || validTones.includes(toneProp), `The 'tone' prop should be one of the following: ${validTones.map((tone) => `"${tone}"`).join(", ")}`);
35
+ assert(!managed || typeof value === "string" && value.length > 0, "AccordionItem 'value' must be a non-empty string when Accordion controls which items are open.");
36
+ assert(!(managed && expandedProp !== void 0), "expanded cannot be set on AccordionItem when Accordion controls which items are open. Use 'value' and 'onChange' on Accordion.");
28
37
  assert(!badge || badge.type.__isBadge__, `AccordionItem badge prop can only be an instance of Badge. e.g. <AccordionItem badge={<Badge>New</Badge>}>`);
29
38
  assert(!badge || badge.props.bleedY === void 0, "Badge elements cannot set the 'bleedY' prop when passed to an AccordionItem component");
30
39
  assert(!icon || icon.props.size === void 0 && icon.props.tone === void 0, "Icons cannot set the 'size' or 'tone' prop when passed to an AccordionItem component");
@@ -32,14 +41,64 @@ const AccordionItem = ({ id, label, children, badge, size: sizeProp, tone: toneP
32
41
  const tone = accordionContext?.tone ?? toneProp ?? "neutral";
33
42
  const weight = accordionContext?.weight ?? weightProp ?? "medium";
34
43
  const itemSpace = itemSpaceForSize[size] ?? "none";
44
+ const contentRef = useRef(null);
45
+ const contentSizeRef = useRef(null);
35
46
  assert(typeof label === "undefined" || typeof label === "string", "Label must be a string");
47
+ const resolvedId = useFallbackId(id);
48
+ let disclosureState = { onToggle };
49
+ if (managed && accordionContext && value) disclosureState = {
50
+ expanded: accordionContext.openValues.includes(value),
51
+ onToggle: (nextExpanded) => {
52
+ accordionContext.toggleValue(value);
53
+ onToggle?.(nextExpanded);
54
+ }
55
+ };
56
+ else if (expandedProp !== void 0) disclosureState = {
57
+ expanded: expandedProp,
58
+ onToggle
59
+ };
36
60
  const { expanded, buttonProps, contentProps } = useDisclosure({
37
- id,
38
- ...restProps.expanded !== void 0 ? {
39
- onToggle: restProps.onToggle,
40
- expanded: restProps.expanded
41
- } : { onToggle: restProps.onToggle }
61
+ id: resolvedId,
62
+ ...disclosureState
42
63
  });
64
+ const [trackedExpanded, setTrackedExpanded] = useState(expanded);
65
+ const [animatedHeight, setAnimatedHeight] = useState(null);
66
+ const [animationDurationMs, setAnimationDurationMs] = useState(minDurationMs);
67
+ const isAnimating = animatedHeight !== null;
68
+ if (expanded !== trackedExpanded) {
69
+ setTrackedExpanded(expanded);
70
+ if (prefersReducedMotion()) setAnimatedHeight(null);
71
+ else {
72
+ const height = contentSizeRef.current?.scrollHeight ?? 0;
73
+ setAnimatedHeight(height);
74
+ setAnimationDurationMs(durationMsForHeight(height));
75
+ }
76
+ }
77
+ const finishAnimation = useCallback(() => {
78
+ setAnimatedHeight(null);
79
+ }, []);
80
+ const finishHeightTransition = useCallback((event) => {
81
+ if (isHeightTransition(event)) finishAnimation();
82
+ }, [finishAnimation]);
83
+ useLayoutEffect(() => {
84
+ contentRef.current?.toggleAttribute("inert", !expanded);
85
+ }, [expanded]);
86
+ useLayoutEffect(() => {
87
+ if (!isAnimating) return;
88
+ if (!expanded) {
89
+ contentRef.current?.getBoundingClientRect();
90
+ setAnimatedHeight(0);
91
+ }
92
+ const timeoutId = window.setTimeout(finishAnimation, animationDurationMs + 50);
93
+ return () => {
94
+ window.clearTimeout(timeoutId);
95
+ };
96
+ }, [
97
+ animationDurationMs,
98
+ expanded,
99
+ finishAnimation,
100
+ isAnimating
101
+ ]);
43
102
  if (process.env.NODE_ENV !== "production")
44
103
  /**
45
104
  * Validate that consumers are not passing `data-*`props,
@@ -49,8 +108,7 @@ const AccordionItem = ({ id, label, children, badge, size: sizeProp, tone: toneP
49
108
  data,
50
109
  validateRestProps: restProps
51
110
  });
52
- return /* @__PURE__ */ jsxs(Stack, {
53
- space: itemSpace,
111
+ return /* @__PURE__ */ jsxs(Box, {
54
112
  data,
55
113
  children: [/* @__PURE__ */ jsx(Box, {
56
114
  position: "relative",
@@ -89,9 +147,25 @@ const AccordionItem = ({ id, label, children, badge, size: sizeProp, tone: toneP
89
147
  })
90
148
  })
91
149
  }), /* @__PURE__ */ jsx(Box, {
92
- display: expanded ? "block" : "none",
150
+ className: {
151
+ [content]: true,
152
+ [contentOpen]: expanded && !isAnimating,
153
+ [contentClosed]: !expanded && !isAnimating
154
+ },
155
+ style: isAnimating ? {
156
+ height: animatedHeight,
157
+ transitionDuration: `${animationDurationMs}ms`
158
+ } : void 0,
159
+ onTransitionEnd: finishHeightTransition,
160
+ onTransitionCancel: finishHeightTransition,
161
+ "aria-hidden": expanded ? void 0 : true,
162
+ ref: contentRef,
93
163
  ...contentProps,
94
- children
164
+ children: /* @__PURE__ */ jsx(Box, {
165
+ ref: contentSizeRef,
166
+ paddingTop: itemSpace,
167
+ children
168
+ })
95
169
  })]
96
170
  });
97
171
  };
@@ -1,12 +1,16 @@
1
+ const require_AccordionContext = require("./AccordionContext.cjs");
1
2
  const require_AccordionItem = require("./AccordionItem.cjs");
2
3
  const require_playroomState = require("../../playroom/playroomState.cjs");
4
+ let react = require("react");
3
5
  let react_jsx_runtime = require("react/jsx-runtime");
4
6
  //#region src/lib/components/Accordion/AccordionItem.playroom.tsx
5
- const AccordionItem = ({ label, stateName, expanded, onToggle, size, tone, weight, badge, icon, ...restProps }) => {
7
+ const AccordionItem = ({ label, stateName, expanded, onToggle, size, tone, weight, badge, icon, value, ...restProps }) => {
8
+ const managed = Boolean((0, react.useContext)(require_AccordionContext.AccordionContext)?.managed);
6
9
  const [state, handleChange] = require_playroomState.useFallbackState(stateName, expanded, onToggle, false);
7
10
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_AccordionItem.AccordionItem, {
8
- expanded: state,
11
+ value: typeof value === "string" ? value : void 0,
9
12
  onToggle: handleChange,
13
+ expanded: managed ? void 0 : state,
10
14
  label: typeof label !== "boolean" ? label : "",
11
15
  size: typeof size === "boolean" ? void 0 : size,
12
16
  tone: typeof tone === "boolean" ? void 0 : tone,
@@ -3,7 +3,7 @@ import { StateProp } from "../../playroom/playroomState.cjs";
3
3
  import { FC } from "react";
4
4
  //#region src/lib/components/Accordion/AccordionItem.playroom.d.ts
5
5
  type OptionalProps = 'id';
6
- type PlayroomAccordionItemProps = StateProp & AccordionItemBaseProps & AccordionItemStateProps & Partial<Pick<AccordionItemProps, OptionalProps>>;
6
+ type PlayroomAccordionItemProps = StateProp & AccordionItemBaseProps & AccordionItemStateProps & Partial<Pick<AccordionItemProps, OptionalProps | 'value'>>;
7
7
  declare const AccordionItem: FC<PlayroomAccordionItemProps>;
8
8
  //#endregion
9
9
  export { AccordionItem };
@@ -3,7 +3,7 @@ import { StateProp } from "../../playroom/playroomState.mjs";
3
3
  import { FC } from "react";
4
4
  //#region src/lib/components/Accordion/AccordionItem.playroom.d.ts
5
5
  type OptionalProps = 'id';
6
- type PlayroomAccordionItemProps = StateProp & AccordionItemBaseProps & AccordionItemStateProps & Partial<Pick<AccordionItemProps, OptionalProps>>;
6
+ type PlayroomAccordionItemProps = StateProp & AccordionItemBaseProps & AccordionItemStateProps & Partial<Pick<AccordionItemProps, OptionalProps | 'value'>>;
7
7
  declare const AccordionItem: FC<PlayroomAccordionItemProps>;
8
8
  //#endregion
9
9
  export { AccordionItem };
@@ -1,12 +1,16 @@
1
+ import { AccordionContext } from "./AccordionContext.mjs";
1
2
  import { AccordionItem as AccordionItem$1 } from "./AccordionItem.mjs";
2
3
  import { useFallbackState } from "../../playroom/playroomState.mjs";
4
+ import { useContext } from "react";
3
5
  import { jsx } from "react/jsx-runtime";
4
6
  //#region src/lib/components/Accordion/AccordionItem.playroom.tsx
5
- const AccordionItem = ({ label, stateName, expanded, onToggle, size, tone, weight, badge, icon, ...restProps }) => {
7
+ const AccordionItem = ({ label, stateName, expanded, onToggle, size, tone, weight, badge, icon, value, ...restProps }) => {
8
+ const managed = Boolean(useContext(AccordionContext)?.managed);
6
9
  const [state, handleChange] = useFallbackState(stateName, expanded, onToggle, false);
7
10
  return /* @__PURE__ */ jsx(AccordionItem$1, {
8
- expanded: state,
11
+ value: typeof value === "string" ? value : void 0,
9
12
  onToggle: handleChange,
13
+ expanded: managed ? void 0 : state,
10
14
  label: typeof label !== "boolean" ? label : "",
11
15
  size: typeof size === "boolean" ? void 0 : size,
12
16
  tone: typeof tone === "boolean" ? void 0 : tone,
@@ -187,7 +187,6 @@ const Avatar = (0, react.forwardRef)(({ name = "", "aria-label": ariaLabel, "ari
187
187
  outline: !clickable && focusable ? "focus" : void 0,
188
188
  className: {
189
189
  [require_Avatar_css.size[size$1]]: true,
190
- [require_Avatar_css.keyline]: true,
191
190
  [require_virtualTouchable_css.virtualTouchable]: clickable && (size$1 === "xsmall" || size$1 === "small")
192
191
  },
193
192
  style: colour ? { background: colour.value } : void 0,
@@ -204,6 +203,7 @@ const Avatar = (0, react.forwardRef)(({ name = "", "aria-label": ariaLabel, "ari
204
203
  height: "full",
205
204
  overflow: "hidden",
206
205
  borderRadius,
206
+ className: require_Avatar_css.keyline,
207
207
  children: content
208
208
  })
209
209
  });
@@ -1,3 +1,4 @@
1
+ const require_px = require("../../utils/px.cjs");
1
2
  const require_vars_css = require("../../themes/vars.css.cjs");
2
3
  let _vanilla_extract_css_fileScope = require("@vanilla-extract/css/fileScope");
3
4
  let _vanilla_extract_css = require("@vanilla-extract/css");
@@ -12,7 +13,7 @@ const size = (0, _vanilla_extract_css.styleVariants)({
12
13
  xxlarge: 96
13
14
  }, (pixels) => ({
14
15
  height: pixels,
15
- width: pixels
16
+ width: `${require_px.px(pixels)} !important`
16
17
  }), "size");
17
18
  const keyline = (0, _vanilla_extract_css.style)({
18
19
  borderWidth: require_vars_css.vars.borderWidth.standard,
@@ -1,3 +1,4 @@
1
+ import { px } from "../../utils/px.mjs";
1
2
  import { vars } from "../../themes/vars.css.mjs";
2
3
  import { endFileScope, setFileScope } from "@vanilla-extract/css/fileScope";
3
4
  import { style, styleVariants } from "@vanilla-extract/css";
@@ -12,7 +13,7 @@ const size = styleVariants({
12
13
  xxlarge: 96
13
14
  }, (pixels) => ({
14
15
  height: pixels,
15
- width: pixels
16
+ width: `${px(pixels)} !important`
16
17
  }), "size");
17
18
  const keyline = style({
18
19
  borderWidth: vars.borderWidth.standard,
@@ -185,7 +185,6 @@ const Avatar = forwardRef(({ name = "", "aria-label": ariaLabel, "aria-described
185
185
  outline: !clickable && focusable ? "focus" : void 0,
186
186
  className: {
187
187
  [size[size$1]]: true,
188
- [keyline]: true,
189
188
  [virtualTouchable]: clickable && (size$1 === "xsmall" || size$1 === "small")
190
189
  },
191
190
  style: colour ? { background: colour.value } : void 0,
@@ -202,6 +201,7 @@ const Avatar = forwardRef(({ name = "", "aria-label": ariaLabel, "aria-described
202
201
  height: "full",
203
202
  overflow: "hidden",
204
203
  borderRadius,
204
+ className: keyline,
205
205
  children: content
206
206
  })
207
207
  });
@@ -10,7 +10,6 @@ const BraidPortal = ({ children, container }) => {
10
10
  value: null,
11
11
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_VanillaThemeContainer.VanillaThemeContainer, {
12
12
  theme: vanillaTheme,
13
- setDefaultTextTones: true,
14
13
  children
15
14
  })
16
15
  }), container ?? document.body);
@@ -10,7 +10,6 @@ const BraidPortal = ({ children, container }) => {
10
10
  value: null,
11
11
  children: /* @__PURE__ */ jsx(VanillaThemeContainer, {
12
12
  theme: vanillaTheme,
13
- setDefaultTextTones: true,
14
13
  children
15
14
  })
16
15
  }), container ?? document.body);
@@ -46,7 +46,6 @@ const BraidProvider = ({ theme, styleBody = true, linkComponent, children }) =>
46
46
  `
47
47
  }) : null, /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_VanillaThemeContainer.VanillaThemeContainer, {
48
48
  theme: theme.vanillaTheme,
49
- setDefaultTextTones: !alreadyInBraidProvider,
50
49
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LinkComponentContext.Provider, {
51
50
  value: linkComponent || linkComponentFromContext,
52
51
  children: alreadyInBraidProvider || inTestProvider ? children : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_BreakpointContext.BreakpointProvider, { children })
@@ -43,7 +43,6 @@ const BraidProvider = ({ theme, styleBody = true, linkComponent, children }) =>
43
43
  `
44
44
  }) : null, /* @__PURE__ */ jsx(VanillaThemeContainer, {
45
45
  theme: theme.vanillaTheme,
46
- setDefaultTextTones: !alreadyInBraidProvider,
47
46
  children: /* @__PURE__ */ jsx(LinkComponentContext.Provider, {
48
47
  value: linkComponent || linkComponentFromContext,
49
48
  children: alreadyInBraidProvider || inTestProvider ? children : /* @__PURE__ */ jsx(BreakpointProvider, { children })
@@ -2,8 +2,8 @@ const require_typography_css = require("../../css/typography.css.cjs");
2
2
  let react_jsx_runtime = require("react/jsx-runtime");
3
3
  //#region src/lib/components/BraidProvider/VanillaThemeContainer.tsx
4
4
  const textTones = [require_typography_css.lightModeTone.light, require_typography_css.darkModeTone.dark].join(" ");
5
- const VanillaThemeContainer = ({ children, theme, setDefaultTextTones }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6
- className: `${theme}${setDefaultTextTones ? ` ${textTones}` : ""}`,
5
+ const VanillaThemeContainer = ({ children, theme }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6
+ className: `${theme} ${textTones}`,
7
7
  children
8
8
  });
9
9
  //#endregion
@@ -2,8 +2,8 @@ import { darkModeTone, lightModeTone } from "../../css/typography.css.mjs";
2
2
  import { jsx } from "react/jsx-runtime";
3
3
  //#region src/lib/components/BraidProvider/VanillaThemeContainer.tsx
4
4
  const textTones = [lightModeTone.light, darkModeTone.dark].join(" ");
5
- const VanillaThemeContainer = ({ children, theme, setDefaultTextTones }) => /* @__PURE__ */ jsx("div", {
6
- className: `${theme}${setDefaultTextTones ? ` ${textTones}` : ""}`,
5
+ const VanillaThemeContainer = ({ children, theme }) => /* @__PURE__ */ jsx("div", {
6
+ className: `${theme} ${textTones}`,
7
7
  children
8
8
  });
9
9
  //#endregion
@@ -23,6 +23,11 @@ const assertAccessibleName = ({ title, description, "aria-label": ariaLabel, "ar
23
23
  (0, assert.default)(typeof title === "string" !== (typeof ariaLabel === "string"), dedent.default`
24
24
  Drawer requires either a title or an aria-label.
25
25
 
26
+ ${docsMessage}
27
+ `);
28
+ (0, assert.default)(typeof title !== "string" || title.trim() !== "", dedent.default`
29
+ Drawer title must not be empty.
30
+
26
31
  ${docsMessage}
27
32
  `);
28
33
  (0, assert.default)(typeof ariaLabel !== "string" || ariaLabel.trim() !== "", dedent.default`
@@ -20,6 +20,11 @@ const assertAccessibleName = ({ title, description, "aria-label": ariaLabel, "ar
20
20
  assert(typeof title === "string" !== (typeof ariaLabel === "string"), dedent`
21
21
  Drawer requires either a title or an aria-label.
22
22
 
23
+ ${docsMessage}
24
+ `);
25
+ assert(typeof title !== "string" || title.trim() !== "", dedent`
26
+ Drawer title must not be empty.
27
+
23
28
  ${docsMessage}
24
29
  `);
25
30
  assert(typeof ariaLabel !== "string" || ariaLabel.trim() !== "", dedent`
@@ -64,7 +64,6 @@ const reducer = (prevState, action) => {
64
64
  };
65
65
  const ANIMATION_DURATION = 300;
66
66
  const Modal = ({ id, open, children, description, onClose, width, closeLabel, illustration, title, "aria-label": ariaLabel, "aria-description": ariaDescription, headingLevel, position, ...restProps }) => {
67
- const [trapActive, setTrapActive] = (0, react.useState)(true);
68
67
  const [state, dispatch] = (0, react.useReducer)(reducer, INITIAL);
69
68
  const allowClose = (0, react.useContext)(AllowCloseContext);
70
69
  const shouldFocus = typeof document !== "undefined" && typeof document.hasFocus === "function" && document.hasFocus();
@@ -100,17 +99,8 @@ const Modal = ({ id, open, children, description, onClose, width, closeLabel, il
100
99
  (0, react.useEffect)(() => {
101
100
  if (typeof onClose === "function") closeHandlerRef.current = onClose;
102
101
  }, [onClose]);
103
- (0, react.useEffect)(() => {
104
- const event = trapActive ? "blur" : "focus";
105
- const handleEvent = () => setTrapActive(!trapActive);
106
- window.addEventListener(event, handleEvent);
107
- return () => {
108
- window.removeEventListener(event, handleEvent);
109
- };
110
- }, [trapActive]);
111
102
  return state === OPENING || state === OPEN || state === CLOSING ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModalPortal, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(FocusLock, {
112
103
  className: require_Modal_css.resetStackingContext,
113
- disabled: !trapActive,
114
104
  autoFocus: false,
115
105
  onActivation: () => {
116
106
  if (state === OPEN) return;
@@ -62,7 +62,6 @@ const reducer = (prevState, action) => {
62
62
  };
63
63
  const ANIMATION_DURATION = 300;
64
64
  const Modal = ({ id, open, children, description, onClose, width, closeLabel, illustration, title, "aria-label": ariaLabel, "aria-description": ariaDescription, headingLevel, position, ...restProps }) => {
65
- const [trapActive, setTrapActive] = useState(true);
66
65
  const [state, dispatch] = useReducer(reducer, INITIAL);
67
66
  const allowClose = useContext(AllowCloseContext);
68
67
  const shouldFocus = typeof document !== "undefined" && typeof document.hasFocus === "function" && document.hasFocus();
@@ -98,17 +97,8 @@ const Modal = ({ id, open, children, description, onClose, width, closeLabel, il
98
97
  useEffect(() => {
99
98
  if (typeof onClose === "function") closeHandlerRef.current = onClose;
100
99
  }, [onClose]);
101
- useEffect(() => {
102
- const event = trapActive ? "blur" : "focus";
103
- const handleEvent = () => setTrapActive(!trapActive);
104
- window.addEventListener(event, handleEvent);
105
- return () => {
106
- window.removeEventListener(event, handleEvent);
107
- };
108
- }, [trapActive]);
109
100
  return state === OPENING || state === OPEN || state === CLOSING ? /* @__PURE__ */ jsx(ModalPortal, { children: /* @__PURE__ */ jsxs(FocusLock, {
110
101
  className: resetStackingContext,
111
- disabled: !trapActive,
112
102
  autoFocus: false,
113
103
  onActivation: () => {
114
104
  if (state === OPEN) return;
@@ -158,6 +158,11 @@ const ModalContent = ({ id, children, description, onClose, width, closeLabel =
158
158
  right: "flexEnd"
159
159
  }[position];
160
160
  const modalRadius = !isDrawer ? "xlarge" : void 0;
161
+ /**
162
+ * Only Drawer supports omitting the title, in which case there is no header
163
+ * to hold the content clear of the close button.
164
+ */
165
+ const untitledDrawer = isDrawer && !title;
161
166
  const modalLayout = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(ModalContentScrollLayout, {
162
167
  applyPageBlockGutters: isDrawer,
163
168
  applyFullHeight: isDrawer || allowColumnLayout && Boolean(footer) && !isMobile,
@@ -165,7 +170,7 @@ const ModalContent = ({ id, children, description, onClose, width, closeLabel =
165
170
  coverImageEnabled,
166
171
  hasFooter: Boolean(footer),
167
172
  children: [
168
- title ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModalContentHeader, {
173
+ !untitledDrawer ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModalContentHeader, {
169
174
  title,
170
175
  headingLevel,
171
176
  description,
@@ -174,7 +179,7 @@ const ModalContent = ({ id, children, description, onClose, width, closeLabel =
174
179
  ref: headingRef,
175
180
  reserveCloseArea: true
176
181
  }) : null,
177
- isDrawer && !title ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(
182
+ untitledDrawer ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(
178
183
  require_Bleed.Bleed,
179
184
  /**
180
185
  * Matches close button size so content starts below it. `bottom`
@@ -203,9 +208,9 @@ const ModalContent = ({ id, children, description, onClose, width, closeLabel =
203
208
  ref: dialogRef,
204
209
  role: "dialog",
205
210
  "aria-label": ariaLabel || title,
206
- "aria-describedby": title && description || ariaDescription ? descriptionId : void 0,
211
+ "aria-describedby": !untitledDrawer && description || ariaDescription ? descriptionId : void 0,
207
212
  "aria-modal": "true",
208
- tabIndex: title ? void 0 : -1,
213
+ tabIndex: untitledDrawer ? -1 : void 0,
209
214
  id: resolvedId,
210
215
  onKeyDown: handleEscape,
211
216
  position: "relative",
@@ -158,6 +158,11 @@ const ModalContent = ({ id, children, description, onClose, width, closeLabel =
158
158
  right: "flexEnd"
159
159
  }[position];
160
160
  const modalRadius = !isDrawer ? "xlarge" : void 0;
161
+ /**
162
+ * Only Drawer supports omitting the title, in which case there is no header
163
+ * to hold the content clear of the close button.
164
+ */
165
+ const untitledDrawer = isDrawer && !title;
161
166
  const modalLayout = /* @__PURE__ */ jsxs(ModalContentScrollLayout, {
162
167
  applyPageBlockGutters: isDrawer,
163
168
  applyFullHeight: isDrawer || allowColumnLayout && Boolean(footer) && !isMobile,
@@ -165,7 +170,7 @@ const ModalContent = ({ id, children, description, onClose, width, closeLabel =
165
170
  coverImageEnabled,
166
171
  hasFooter: Boolean(footer),
167
172
  children: [
168
- title ? /* @__PURE__ */ jsx(ModalContentHeader, {
173
+ !untitledDrawer ? /* @__PURE__ */ jsx(ModalContentHeader, {
169
174
  title,
170
175
  headingLevel,
171
176
  description,
@@ -174,7 +179,7 @@ const ModalContent = ({ id, children, description, onClose, width, closeLabel =
174
179
  ref: headingRef,
175
180
  reserveCloseArea: true
176
181
  }) : null,
177
- isDrawer && !title ? /* @__PURE__ */ jsx(
182
+ untitledDrawer ? /* @__PURE__ */ jsx(
178
183
  Bleed,
179
184
  /**
180
185
  * Matches close button size so content starts below it. `bottom`
@@ -203,9 +208,9 @@ const ModalContent = ({ id, children, description, onClose, width, closeLabel =
203
208
  ref: dialogRef,
204
209
  role: "dialog",
205
210
  "aria-label": ariaLabel || title,
206
- "aria-describedby": title && description || ariaDescription ? descriptionId : void 0,
211
+ "aria-describedby": !untitledDrawer && description || ariaDescription ? descriptionId : void 0,
207
212
  "aria-modal": "true",
208
- tabIndex: title ? void 0 : -1,
213
+ tabIndex: untitledDrawer ? -1 : void 0,
209
214
  id: resolvedId,
210
215
  onKeyDown: handleEscape,
211
216
  position: "relative",
@@ -25,7 +25,11 @@ const toneToIcon = {
25
25
  critical: require_IconCritical.IconCritical,
26
26
  positive: require_IconPositive.IconPositive
27
27
  };
28
- const toastDuration = 1e4;
28
+ const toastDuration = {
29
+ short: 6e3,
30
+ standard: 1e4,
31
+ indefinite: null
32
+ };
29
33
  const borderRadius = "large";
30
34
  const Action = ({ label, onClick, removeToast }) => {
31
35
  const handleClick = (0, react.useCallback)((event) => {
@@ -54,14 +58,15 @@ const ToastIcon = ({ tone, icon }) => {
54
58
  if (icon) return (0, react.cloneElement)(icon, { tone });
55
59
  return null;
56
60
  };
57
- const Toast = (0, react.forwardRef)(({ toastKey, dedupeKey, message, description, tone, icon, onClose, closeLabel = "Close", action, shouldRemove, data, expanded = true, ...restProps }, ref) => {
61
+ const Toast = (0, react.forwardRef)(({ toastKey, dedupeKey, message, duration = "standard", description, tone, icon, onClose, closeLabel = "Close", action, shouldRemove, data, expanded = true, ...restProps }, ref) => {
58
62
  const remove = (0, react.useCallback)(() => onClose(dedupeKey, toastKey), [
59
63
  onClose,
60
64
  dedupeKey,
61
65
  toastKey
62
66
  ]);
67
+ const resolvedDuration = toastDuration[duration];
63
68
  const { stopTimeout, startTimeout } = require_useTimeout.useTimeout({
64
- duration: toastDuration,
69
+ duration: resolvedDuration,
65
70
  onTimeout: remove
66
71
  });
67
72
  (0, react.useEffect)(() => {
@@ -23,7 +23,11 @@ const toneToIcon = {
23
23
  critical: IconCritical,
24
24
  positive: IconPositive
25
25
  };
26
- const toastDuration = 1e4;
26
+ const toastDuration = {
27
+ short: 6e3,
28
+ standard: 1e4,
29
+ indefinite: null
30
+ };
27
31
  const borderRadius = "large";
28
32
  const Action = ({ label, onClick, removeToast }) => {
29
33
  const handleClick = useCallback((event) => {
@@ -52,14 +56,15 @@ const ToastIcon = ({ tone, icon }) => {
52
56
  if (icon) return cloneElement(icon, { tone });
53
57
  return null;
54
58
  };
55
- const Toast = forwardRef(({ toastKey, dedupeKey, message, description, tone, icon, onClose, closeLabel = "Close", action, shouldRemove, data, expanded = true, ...restProps }, ref) => {
59
+ const Toast = forwardRef(({ toastKey, dedupeKey, message, duration = "standard", description, tone, icon, onClose, closeLabel = "Close", action, shouldRemove, data, expanded = true, ...restProps }, ref) => {
56
60
  const remove = useCallback(() => onClose(dedupeKey, toastKey), [
57
61
  onClose,
58
62
  dedupeKey,
59
63
  toastKey
60
64
  ]);
65
+ const resolvedDuration = toastDuration[duration];
61
66
  const { stopTimeout, startTimeout } = useTimeout({
62
- duration: toastDuration,
67
+ duration: resolvedDuration,
63
68
  onTimeout: remove
64
69
  });
65
70
  useEffect(() => {
@@ -6,6 +6,7 @@ interface ToastAction {
6
6
  label: string;
7
7
  onClick: () => void;
8
8
  }
9
+ type ToastDuration = 'short' | 'standard' | 'indefinite';
9
10
  type Toast = {
10
11
  key?: string;
11
12
  message: string;
@@ -13,6 +14,7 @@ type Toast = {
13
14
  action?: ToastAction;
14
15
  closeLabel?: string;
15
16
  data?: DataAttributeMap;
17
+ duration?: ToastDuration;
16
18
  } & ({
17
19
  tone: 'positive' | 'critical';
18
20
  } | {
@@ -20,4 +22,4 @@ type Toast = {
20
22
  icon?: ReactElement<UseIconProps>;
21
23
  });
22
24
  //#endregion
23
- export { Toast, ToastAction };
25
+ export { Toast, ToastAction, ToastDuration };
@@ -6,6 +6,7 @@ interface ToastAction {
6
6
  label: string;
7
7
  onClick: () => void;
8
8
  }
9
+ type ToastDuration = 'short' | 'standard' | 'indefinite';
9
10
  type Toast = {
10
11
  key?: string;
11
12
  message: string;
@@ -13,6 +14,7 @@ type Toast = {
13
14
  action?: ToastAction;
14
15
  closeLabel?: string;
15
16
  data?: DataAttributeMap;
17
+ duration?: ToastDuration;
16
18
  } & ({
17
19
  tone: 'positive' | 'critical';
18
20
  } | {
@@ -20,4 +22,4 @@ type Toast = {
20
22
  icon?: ReactElement<UseIconProps>;
21
23
  });
22
24
  //#endregion
23
- export { Toast, ToastAction };
25
+ export { Toast, ToastAction, ToastDuration };
@@ -154,11 +154,11 @@ const useFlipList = (expanded) => {
154
154
  property: "transform",
155
155
  from: transform
156
156
  },
157
- {
157
+ ...element.classList.contains(require_Toast_css.collapsed) ? [{
158
158
  property: "className",
159
- from: expanded ? require_Toast_css.collapsed : void 0,
159
+ from: require_Toast_css.collapsed,
160
160
  to: void 0
161
- }
161
+ }] : []
162
162
  ]
163
163
  });
164
164
  }