braid-design-system 34.3.0 → 34.4.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,41 @@
1
1
  # braid-design-system
2
2
 
3
+ ## 34.4.0
4
+
5
+ ### Minor Changes
6
+
7
+ - **ButtonIcon:** Add `loading` support ([#2083](https://github.com/seek-oss/braid-design-system/pull/2083))
8
+
9
+ Provide the same `loading` behaviour as available on `Button`
10
+
11
+ **EXAMPLE USAGE:**
12
+
13
+ ```jsx
14
+ <ButtonIcon icon={<IconSend />} label="Send" loading />
15
+ ```
16
+
17
+ - **Button, ButtonIcon:** Add `aria-pressed` support ([#2084](https://github.com/seek-oss/braid-design-system/pull/2084))
18
+
19
+ - **ButtonIcon:** Add support for `solid` variant ([#2079](https://github.com/seek-oss/braid-design-system/pull/2079))
20
+
21
+ **EXAMPLE USAGE:**
22
+
23
+ ```jsx
24
+ <ButtonIcon variant="solid" label="Solid variant" icon={<IconSend />} />
25
+ ```
26
+
27
+ ### Patch Changes
28
+
29
+ - **Button:** Update `loading` indicator ([#2080](https://github.com/seek-oss/braid-design-system/pull/2080))
30
+
31
+ Update design asset for loading state, moving from progressive ellipsis animation, to a spinning indicator.
32
+
33
+ - **Button:** Ensure active state is not applied when disabled ([#2080](https://github.com/seek-oss/braid-design-system/pull/2080))
34
+
35
+ - Ensure fallback ids pass HTML validation ([#2083](https://github.com/seek-oss/braid-design-system/pull/2083))
36
+
37
+ Adopt recommended practice of ensuring all generated `id` attributes start with a letter as per HTML validation rules.
38
+
3
39
  ## 34.3.0
4
40
 
5
41
  ### Minor Changes
@@ -43,6 +43,6 @@ declare const colorModeQueryParamCheck: string;
43
43
  *
44
44
  * @returns Braid color mode preference as query string parameter
45
45
  */
46
- declare const getColorModeQueryParam: () => "_bdsdm=0" | "_bdsdm=1" | "_bdsdm=2" | "";
46
+ declare const getColorModeQueryParam: () => "" | "_bdsdm=1" | "_bdsdm=2" | "_bdsdm=0";
47
47
  //#endregion
48
48
  export { colorModeQueryParamCheck, getColorModeQueryParam };
@@ -43,6 +43,6 @@ declare const colorModeQueryParamCheck: string;
43
43
  *
44
44
  * @returns Braid color mode preference as query string parameter
45
45
  */
46
- declare const getColorModeQueryParam: () => "_bdsdm=0" | "_bdsdm=1" | "_bdsdm=2" | "";
46
+ declare const getColorModeQueryParam: () => "" | "_bdsdm=1" | "_bdsdm=2" | "_bdsdm=0";
47
47
  //#endregion
48
48
  export { colorModeQueryParamCheck, getColorModeQueryParam };
@@ -2,6 +2,7 @@ const require_runtime = require('../../../_virtual/_rolldown/runtime.cjs');
2
2
  const require_BackgroundContext = require('../Box/BackgroundContext.cjs');
3
3
  const require_buildDataAttributes = require('../private/buildDataAttributes.cjs');
4
4
  const require_Box = require('../Box/Box.cjs');
5
+ const require_iconSlotSpace = require('../private/iconSlotSpace.cjs');
5
6
  const require_Text = require('../Text/Text.cjs');
6
7
  const require_virtualTouchable_css = require('../private/touchable/virtualTouchable.css.cjs');
7
8
  const require_ActionsContext = require('../Actions/ActionsContext.cjs');
@@ -9,6 +10,7 @@ const require_negativeMargin = require('../../css/negativeMargin/negativeMargin.
9
10
  const require_Bleed = require('../Bleed/Bleed.cjs');
10
11
  const require_AvoidWidowIcon = require('../private/AvoidWidowIcon/AvoidWidowIcon.cjs');
11
12
  const require_FieldOverlay = require('../private/FieldOverlay/FieldOverlay.cjs');
13
+ const require_ButtonLoader = require('./ButtonLoader.cjs');
12
14
  const require_Button_css = require('./Button.css.cjs');
13
15
  let assert = require("assert");
14
16
  assert = require_runtime.__toESM(assert);
@@ -165,28 +167,6 @@ const variants = {
165
167
  }
166
168
  }
167
169
  };
168
- const ButtonLoader = () => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_Box.Box, {
169
- "aria-hidden": true,
170
- component: "span",
171
- display: "inlineBlock",
172
- children: [
173
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_Box.Box, {
174
- component: "span",
175
- className: require_Button_css.loadingDot,
176
- children: "."
177
- }),
178
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_Box.Box, {
179
- component: "span",
180
- className: require_Button_css.loadingDot,
181
- children: "."
182
- }),
183
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_Box.Box, {
184
- component: "span",
185
- className: require_Button_css.loadingDot,
186
- children: "."
187
- })
188
- ]
189
- });
190
170
  const transparentPaddingX = "small";
191
171
  const buttonRadius = "standard";
192
172
  const resolveToneAndVariant = ({ variant: variantProp, tone: toneProp }) => {
@@ -272,7 +252,11 @@ const ButtonText = ({ children, loading, size: sizeProp, icon, iconPosition = "l
272
252
  children: icon
273
253
  }) : null,
274
254
  children,
275
- loading ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ButtonLoader, {}) : null,
255
+ loading ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_Box.Box, {
256
+ component: "span",
257
+ paddingLeft: require_iconSlotSpace.iconSlotSpace,
258
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_ButtonLoader.ButtonLoader, {})
259
+ }) : null,
276
260
  !loading && icon && iconPosition === "trailing" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_AvoidWidowIcon.AvoidWidowIcon, {
277
261
  iconPosition,
278
262
  className: shouldReducePaddingX || bleed ? null : require_negativeMargin.negativeMargin("right", "xxsmall"),
@@ -326,7 +310,7 @@ const ButtonContainer = ({ children, bleed, variant = "solid" }) => bleed && var
326
310
  horizontal: transparentPaddingX,
327
311
  children
328
312
  }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children });
329
- const Button = (0, react.forwardRef)(({ onClick, children, size, tone, icon, iconPosition, bleed, variant, loading, type = "button", id, tabIndex, onKeyUp, onKeyDown, "aria-haspopup": ariaHasPopup, "aria-controls": ariaControls, "aria-expanded": ariaExpanded, "aria-describedby": ariaDescribedBy, "aria-label": ariaLabel, data, ...restProps }, ref) => {
313
+ const Button = (0, react.forwardRef)(({ onClick, children, size, tone, icon, iconPosition, bleed, variant, loading, type = "button", id, tabIndex, onKeyUp, onKeyDown, "aria-haspopup": ariaHasPopup, "aria-controls": ariaControls, "aria-expanded": ariaExpanded, "aria-describedby": ariaDescribedBy, "aria-label": ariaLabel, "aria-pressed": ariaPressed, data, ...restProps }, ref) => {
330
314
  const { root, content } = useButtonStyles({
331
315
  variant,
332
316
  tone,
@@ -350,6 +334,7 @@ const Button = (0, react.forwardRef)(({ onClick, children, size, tone, icon, ico
350
334
  "aria-expanded": ariaExpanded,
351
335
  "aria-describedby": ariaDescribedBy,
352
336
  "aria-label": ariaLabel,
337
+ "aria-pressed": ariaPressed,
353
338
  onClick,
354
339
  disabled: loading,
355
340
  ...root,
@@ -16,8 +16,8 @@ const root = (0, _vanilla_extract_css.style)({
16
16
  outline: "none"
17
17
  }, "root");
18
18
  const forceActive = (0, _vanilla_extract_css.style)({}, "forceActive");
19
- const activeAnimation = (0, _vanilla_extract_css.style)({ selectors: { [`${root}:active &, ${forceActive}&`]: { transform: require_vars_css.vars.transform.touchable } } }, "activeAnimation");
20
- const activeOverlay = (0, _vanilla_extract_css.style)({ selectors: { [`${root}:active &, ${forceActive}&`]: { opacity: 1 } } }, "activeOverlay");
19
+ const activeAnimation = (0, _vanilla_extract_css.style)({ selectors: { [`${root}:not(:disabled):active &, ${forceActive}&`]: { transform: require_vars_css.vars.transform.touchable } } }, "activeAnimation");
20
+ const activeOverlay = (0, _vanilla_extract_css.style)({ selectors: { [`${root}:not(:disabled):active &, ${forceActive}&`]: { opacity: 1 } } }, "activeOverlay");
21
21
  const hoverOverlay = (0, _vanilla_extract_css.style)({ selectors: { [`${root}:hover:not(:disabled) &`]: { opacity: 1 } } }, "hoverOverlay");
22
22
  const { transition: outlineTransition, ...restOutlineStyles } = require_outlineStyle.outlineStyle(`${root}:focus-visible > &`);
23
23
  const focusRing = (0, _vanilla_extract_css.style)([{ transition: [require_vars_css.vars.transition.touchable, outlineTransition].join(", ") }, restOutlineStyles], "focusRing");
@@ -43,28 +43,6 @@ const padToMinHeight = (0, _vanilla_extract_css.style)({
43
43
  paddingTop: capHeightToMinHeight,
44
44
  paddingBottom: capHeightToMinHeight
45
45
  }, "padToMinHeight");
46
- const dot1 = (0, _vanilla_extract_css.keyframes)({
47
- "14%": { opacity: 0 },
48
- "15%,100%": { opacity: 1 }
49
- }, "dot1");
50
- const dot2 = (0, _vanilla_extract_css.keyframes)({
51
- "29%": { opacity: 0 },
52
- "30%,100%": { opacity: 1 }
53
- }, "dot2");
54
- const dot3 = (0, _vanilla_extract_css.keyframes)({
55
- "44%": { opacity: 0 },
56
- "45%,100%": { opacity: 1 }
57
- }, "dot3");
58
- const loadingDot = (0, _vanilla_extract_css.style)({
59
- animationDuration: "1s",
60
- animationIterationCount: "infinite",
61
- opacity: 0,
62
- selectors: {
63
- [`&:nth-child(1)`]: { animationName: dot1 },
64
- [`&:nth-child(2)`]: { animationName: dot2 },
65
- [`&:nth-child(3)`]: { animationName: dot3 }
66
- }
67
- }, "loadingDot");
68
46
  const invertedBackgroundsLightMode = (0, _vanilla_extract_css.styleVariants)({
69
47
  soft: require_colorModeStyle.colorModeStyle({ lightMode: { background: (0, polished.rgba)("#fff", .1) } }),
70
48
  hover: require_colorModeStyle.colorModeStyle({ lightMode: { background: (0, polished.rgba)("#fff", .15) } }),
@@ -86,7 +64,6 @@ exports.forceActive = forceActive;
86
64
  exports.hoverOverlay = hoverOverlay;
87
65
  exports.invertedBackgroundsDarkMode = invertedBackgroundsDarkMode;
88
66
  exports.invertedBackgroundsLightMode = invertedBackgroundsLightMode;
89
- exports.loadingDot = loadingDot;
90
67
  exports.padToMinHeight = padToMinHeight;
91
68
  exports.root = root;
92
69
  exports.small = small;
@@ -3,7 +3,7 @@ import { outlineStyle } from "../../css/outlineStyle.mjs";
3
3
  import { colorModeStyle } from "../../css/colorModeStyle.mjs";
4
4
  import { responsiveStyle } from "../../css/responsiveStyle.mjs";
5
5
  import { endFileScope, setFileScope } from "@vanilla-extract/css/fileScope";
6
- import { createVar, keyframes, style, styleVariants } from "@vanilla-extract/css";
6
+ import { createVar, style, styleVariants } from "@vanilla-extract/css";
7
7
  import { calc } from "@vanilla-extract/css-utils";
8
8
  import { rgba } from "polished";
9
9
 
@@ -15,8 +15,8 @@ const root = style({
15
15
  outline: "none"
16
16
  }, "root");
17
17
  const forceActive = style({}, "forceActive");
18
- const activeAnimation = style({ selectors: { [`${root}:active &, ${forceActive}&`]: { transform: vars.transform.touchable } } }, "activeAnimation");
19
- const activeOverlay = style({ selectors: { [`${root}:active &, ${forceActive}&`]: { opacity: 1 } } }, "activeOverlay");
18
+ const activeAnimation = style({ selectors: { [`${root}:not(:disabled):active &, ${forceActive}&`]: { transform: vars.transform.touchable } } }, "activeAnimation");
19
+ const activeOverlay = style({ selectors: { [`${root}:not(:disabled):active &, ${forceActive}&`]: { opacity: 1 } } }, "activeOverlay");
20
20
  const hoverOverlay = style({ selectors: { [`${root}:hover:not(:disabled) &`]: { opacity: 1 } } }, "hoverOverlay");
21
21
  const { transition: outlineTransition, ...restOutlineStyles } = outlineStyle(`${root}:focus-visible > &`);
22
22
  const focusRing = style([{ transition: [vars.transition.touchable, outlineTransition].join(", ") }, restOutlineStyles], "focusRing");
@@ -42,28 +42,6 @@ const padToMinHeight = style({
42
42
  paddingTop: capHeightToMinHeight,
43
43
  paddingBottom: capHeightToMinHeight
44
44
  }, "padToMinHeight");
45
- const dot1 = keyframes({
46
- "14%": { opacity: 0 },
47
- "15%,100%": { opacity: 1 }
48
- }, "dot1");
49
- const dot2 = keyframes({
50
- "29%": { opacity: 0 },
51
- "30%,100%": { opacity: 1 }
52
- }, "dot2");
53
- const dot3 = keyframes({
54
- "44%": { opacity: 0 },
55
- "45%,100%": { opacity: 1 }
56
- }, "dot3");
57
- const loadingDot = style({
58
- animationDuration: "1s",
59
- animationIterationCount: "infinite",
60
- opacity: 0,
61
- selectors: {
62
- [`&:nth-child(1)`]: { animationName: dot1 },
63
- [`&:nth-child(2)`]: { animationName: dot2 },
64
- [`&:nth-child(3)`]: { animationName: dot3 }
65
- }
66
- }, "loadingDot");
67
45
  const invertedBackgroundsLightMode = styleVariants({
68
46
  soft: colorModeStyle({ lightMode: { background: rgba("#fff", .1) } }),
69
47
  hover: colorModeStyle({ lightMode: { background: rgba("#fff", .15) } }),
@@ -77,4 +55,4 @@ const invertedBackgroundsDarkMode = styleVariants({
77
55
  endFileScope();
78
56
 
79
57
  //#endregion
80
- export { activeAnimation, activeOverlay, bleedVerticallyToCapHeight, focusRing, forceActive, hoverOverlay, invertedBackgroundsDarkMode, invertedBackgroundsLightMode, loadingDot, padToMinHeight, root, small, standard };
58
+ export { activeAnimation, activeOverlay, bleedVerticallyToCapHeight, focusRing, forceActive, hoverOverlay, invertedBackgroundsDarkMode, invertedBackgroundsLightMode, padToMinHeight, root, small, standard };
@@ -32,6 +32,7 @@ interface ButtonProps extends ButtonStyleProps {
32
32
  'aria-expanded'?: NativeButtonProps['aria-expanded'];
33
33
  'aria-describedby'?: NativeButtonProps['aria-describedby'];
34
34
  'aria-label'?: NativeButtonProps['aria-label'];
35
+ 'aria-pressed'?: NativeButtonProps['aria-pressed'];
35
36
  tabIndex?: NativeButtonProps['tabIndex'];
36
37
  data?: DataAttributeMap;
37
38
  }
@@ -32,6 +32,7 @@ interface ButtonProps extends ButtonStyleProps {
32
32
  'aria-expanded'?: NativeButtonProps['aria-expanded'];
33
33
  'aria-describedby'?: NativeButtonProps['aria-describedby'];
34
34
  'aria-label'?: NativeButtonProps['aria-label'];
35
+ 'aria-pressed'?: NativeButtonProps['aria-pressed'];
35
36
  tabIndex?: NativeButtonProps['tabIndex'];
36
37
  data?: DataAttributeMap;
37
38
  }
@@ -1,6 +1,7 @@
1
1
  import { useBackgroundLightness, useColorContrast } from "../Box/BackgroundContext.mjs";
2
2
  import buildDataAttributes_default from "../private/buildDataAttributes.mjs";
3
3
  import { Box } from "../Box/Box.mjs";
4
+ import { iconSlotSpace } from "../private/iconSlotSpace.mjs";
4
5
  import { Text } from "../Text/Text.mjs";
5
6
  import { virtualTouchable } from "../private/touchable/virtualTouchable.css.mjs";
6
7
  import ActionsContext_default from "../Actions/ActionsContext.mjs";
@@ -8,7 +9,8 @@ import { negativeMargin } from "../../css/negativeMargin/negativeMargin.mjs";
8
9
  import { Bleed } from "../Bleed/Bleed.mjs";
9
10
  import { AvoidWidowIcon } from "../private/AvoidWidowIcon/AvoidWidowIcon.mjs";
10
11
  import { FieldOverlay } from "../private/FieldOverlay/FieldOverlay.mjs";
11
- import { activeAnimation, activeOverlay, bleedVerticallyToCapHeight, focusRing, forceActive, hoverOverlay, invertedBackgroundsDarkMode, invertedBackgroundsLightMode, loadingDot, padToMinHeight, root, small, standard } from "./Button.css.mjs";
12
+ import { ButtonLoader } from "./ButtonLoader.mjs";
13
+ import { activeAnimation, activeOverlay, bleedVerticallyToCapHeight, focusRing, forceActive, hoverOverlay, invertedBackgroundsDarkMode, invertedBackgroundsLightMode, padToMinHeight, root, small, standard } from "./Button.css.mjs";
12
14
  import assert from "assert";
13
15
  import { forwardRef, useContext } from "react";
14
16
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
@@ -163,28 +165,6 @@ const variants = {
163
165
  }
164
166
  }
165
167
  };
166
- const ButtonLoader = () => /* @__PURE__ */ jsxs(Box, {
167
- "aria-hidden": true,
168
- component: "span",
169
- display: "inlineBlock",
170
- children: [
171
- /* @__PURE__ */ jsx(Box, {
172
- component: "span",
173
- className: loadingDot,
174
- children: "."
175
- }),
176
- /* @__PURE__ */ jsx(Box, {
177
- component: "span",
178
- className: loadingDot,
179
- children: "."
180
- }),
181
- /* @__PURE__ */ jsx(Box, {
182
- component: "span",
183
- className: loadingDot,
184
- children: "."
185
- })
186
- ]
187
- });
188
168
  const transparentPaddingX = "small";
189
169
  const buttonRadius = "standard";
190
170
  const resolveToneAndVariant = ({ variant: variantProp, tone: toneProp }) => {
@@ -270,7 +250,11 @@ const ButtonText = ({ children, loading, size: sizeProp, icon, iconPosition = "l
270
250
  children: icon
271
251
  }) : null,
272
252
  children,
273
- loading ? /* @__PURE__ */ jsx(ButtonLoader, {}) : null,
253
+ loading ? /* @__PURE__ */ jsx(Box, {
254
+ component: "span",
255
+ paddingLeft: iconSlotSpace,
256
+ children: /* @__PURE__ */ jsx(ButtonLoader, {})
257
+ }) : null,
274
258
  !loading && icon && iconPosition === "trailing" ? /* @__PURE__ */ jsx(AvoidWidowIcon, {
275
259
  iconPosition,
276
260
  className: shouldReducePaddingX || bleed ? null : negativeMargin("right", "xxsmall"),
@@ -324,7 +308,7 @@ const ButtonContainer = ({ children, bleed, variant = "solid" }) => bleed && var
324
308
  horizontal: transparentPaddingX,
325
309
  children
326
310
  }) : /* @__PURE__ */ jsx(Fragment$1, { children });
327
- const Button = forwardRef(({ onClick, children, size, tone, icon, iconPosition, bleed, variant, loading, type = "button", id, tabIndex, onKeyUp, onKeyDown, "aria-haspopup": ariaHasPopup, "aria-controls": ariaControls, "aria-expanded": ariaExpanded, "aria-describedby": ariaDescribedBy, "aria-label": ariaLabel, data, ...restProps }, ref) => {
311
+ const Button = forwardRef(({ onClick, children, size, tone, icon, iconPosition, bleed, variant, loading, type = "button", id, tabIndex, onKeyUp, onKeyDown, "aria-haspopup": ariaHasPopup, "aria-controls": ariaControls, "aria-expanded": ariaExpanded, "aria-describedby": ariaDescribedBy, "aria-label": ariaLabel, "aria-pressed": ariaPressed, data, ...restProps }, ref) => {
328
312
  const { root, content } = useButtonStyles({
329
313
  variant,
330
314
  tone,
@@ -348,6 +332,7 @@ const Button = forwardRef(({ onClick, children, size, tone, icon, iconPosition,
348
332
  "aria-expanded": ariaExpanded,
349
333
  "aria-describedby": ariaDescribedBy,
350
334
  "aria-label": ariaLabel,
335
+ "aria-pressed": ariaPressed,
351
336
  onClick,
352
337
  disabled: loading,
353
338
  ...root,
@@ -0,0 +1,33 @@
1
+ const require_runtime = require('../../../_virtual/_rolldown/runtime.cjs');
2
+ const require_Box = require('../Box/Box.cjs');
3
+ const require_IconContainer = require('../icons/IconContainer.cjs');
4
+ const require_ButtonLoader_css = require('./ButtonLoader.css.cjs');
5
+ let react_jsx_runtime = require("react/jsx-runtime");
6
+
7
+ //#region src/lib/components/Button/ButtonLoader.tsx
8
+ const LoaderSvg = ({ title, titleId, ...props }) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
9
+ xmlns: "http://www.w3.org/2000/svg",
10
+ viewBox: "0 0 24 24",
11
+ xmlSpace: "preserve",
12
+ focusable: "false",
13
+ fill: "currentColor",
14
+ width: 16,
15
+ height: 16,
16
+ "aria-labelledby": titleId,
17
+ ...props,
18
+ children: [title ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("title", {
19
+ id: titleId,
20
+ children: title
21
+ }) : null, /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M12 22C6.486 22 2 17.514 2 12S6.486 2 12 2s10 4.486 10 10a1 1 0 1 1-2 0c0-4.411-3.589-8-8-8s-8 3.589-8 8 3.589 8 8 8a1 1 0 1 1 0 2" })]
22
+ });
23
+ const ButtonLoader = (props) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_IconContainer.IconContainer, {
24
+ ...props,
25
+ children: ({ className, ...svgProps }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_Box.Box, {
26
+ component: LoaderSvg,
27
+ className: [className, require_ButtonLoader_css.loading],
28
+ ...svgProps
29
+ })
30
+ });
31
+
32
+ //#endregion
33
+ exports.ButtonLoader = ButtonLoader;
@@ -0,0 +1,12 @@
1
+ const require_runtime = require('../../../_virtual/_rolldown/runtime.cjs');
2
+ let _vanilla_extract_css_fileScope = require("@vanilla-extract/css/fileScope");
3
+ let _vanilla_extract_css = require("@vanilla-extract/css");
4
+
5
+ //#region src/lib/components/Button/ButtonLoader.css.ts
6
+ (0, _vanilla_extract_css_fileScope.setFileScope)("src/lib/components/Button/ButtonLoader.css.ts", "braid-design-system");
7
+ const spin = (0, _vanilla_extract_css.keyframes)({ to: { transform: "rotate(360deg)" } }, "spin");
8
+ const loading = (0, _vanilla_extract_css.style)({ animation: `${spin} 0.7s linear infinite` }, "loading");
9
+ (0, _vanilla_extract_css_fileScope.endFileScope)();
10
+
11
+ //#endregion
12
+ exports.loading = loading;
@@ -0,0 +1,11 @@
1
+ import { endFileScope, setFileScope } from "@vanilla-extract/css/fileScope";
2
+ import { keyframes, style } from "@vanilla-extract/css";
3
+
4
+ //#region src/lib/components/Button/ButtonLoader.css.ts
5
+ setFileScope("src/lib/components/Button/ButtonLoader.css.ts", "braid-design-system");
6
+ const spin = keyframes({ to: { transform: "rotate(360deg)" } }, "spin");
7
+ const loading = style({ animation: `${spin} 0.7s linear infinite` }, "loading");
8
+ endFileScope();
9
+
10
+ //#endregion
11
+ export { loading };
@@ -0,0 +1,32 @@
1
+ import { Box } from "../Box/Box.mjs";
2
+ import { IconContainer } from "../icons/IconContainer.mjs";
3
+ import { loading } from "./ButtonLoader.css.mjs";
4
+ import { jsx, jsxs } from "react/jsx-runtime";
5
+
6
+ //#region src/lib/components/Button/ButtonLoader.tsx
7
+ const LoaderSvg = ({ title, titleId, ...props }) => /* @__PURE__ */ jsxs("svg", {
8
+ xmlns: "http://www.w3.org/2000/svg",
9
+ viewBox: "0 0 24 24",
10
+ xmlSpace: "preserve",
11
+ focusable: "false",
12
+ fill: "currentColor",
13
+ width: 16,
14
+ height: 16,
15
+ "aria-labelledby": titleId,
16
+ ...props,
17
+ children: [title ? /* @__PURE__ */ jsx("title", {
18
+ id: titleId,
19
+ children: title
20
+ }) : null, /* @__PURE__ */ jsx("path", { d: "M12 22C6.486 22 2 17.514 2 12S6.486 2 12 2s10 4.486 10 10a1 1 0 1 1-2 0c0-4.411-3.589-8-8-8s-8 3.589-8 8 3.589 8 8 8a1 1 0 1 1 0 2" })]
21
+ });
22
+ const ButtonLoader = (props) => /* @__PURE__ */ jsx(IconContainer, {
23
+ ...props,
24
+ children: ({ className, ...svgProps }) => /* @__PURE__ */ jsx(Box, {
25
+ component: LoaderSvg,
26
+ className: [className, loading],
27
+ ...svgProps
28
+ })
29
+ });
30
+
31
+ //#endregion
32
+ export { ButtonLoader };
@@ -5,6 +5,7 @@ const require_useFallbackId = require('../../hooks/useFallbackId.cjs');
5
5
  const require_Text = require('../Text/Text.cjs');
6
6
  const require_index = require('../../hooks/useIcon/index.cjs');
7
7
  const require_Bleed = require('../Bleed/Bleed.cjs');
8
+ const require_ButtonLoader = require('../Button/ButtonLoader.cjs');
8
9
  const require_Button = require('../Button/Button.cjs');
9
10
  const require_TooltipRenderer = require('../TooltipRenderer/TooltipRenderer.cjs');
10
11
  const require_ButtonIcon_css = require('./ButtonIcon.css.cjs');
@@ -14,7 +15,11 @@ let react = require("react");
14
15
  let react_jsx_runtime = require("react/jsx-runtime");
15
16
 
16
17
  //#region src/lib/components/ButtonIcon/ButtonIcon.tsx
17
- const buttonIconVariants = ["soft", "transparent"];
18
+ const buttonIconVariants = [
19
+ "soft",
20
+ "transparent",
21
+ "solid"
22
+ ];
18
23
  const buttonIconTones = ["neutral", "formAccent"];
19
24
  const buttonIconSizes = [
20
25
  "small",
@@ -26,13 +31,16 @@ const padding = {
26
31
  standard: "xsmall",
27
32
  large: "xsmall"
28
33
  };
29
- const ButtonIconContent = (0, react.forwardRef)(({ icon, label, id, size = "standard", tone = "neutral", variant = "soft", type = "button", bleed, tooltipPlacement, onClick, onMouseDown, onKeyUp, onKeyDown, "aria-haspopup": ariaHasPopUp, "aria-expanded": ariaExpanded, "aria-describedby": ariaDescribedBy, tabIndex, data, ...restProps }, forwardedRef) => {
34
+ const ButtonIconContent = (0, react.forwardRef)(({ icon, label, id, size = "standard", tone = "neutral", variant = "soft", type = "button", bleed, tooltipPlacement, loading, onClick, onMouseDown, onKeyUp, onKeyDown, "aria-haspopup": ariaHasPopUp, "aria-expanded": ariaExpanded, "aria-describedby": ariaDescribedBy, "aria-pressed": ariaPressed, tabIndex, data, ...restProps }, forwardedRef) => {
30
35
  const { root, content } = require_Button.useButtonStyles({
31
36
  variant,
32
37
  tone,
38
+ loading,
33
39
  size: size === "small" ? "small" : "standard",
34
40
  radius: "full"
35
41
  });
42
+ const fallbackIconTone = variant === "solid" ? "neutral" : tone;
43
+ const resolvedIconTone = icon.props.tone || fallbackIconTone;
36
44
  (0, assert.default)(icon && icon.props.size === void 0, "Icons cannot set the 'size' prop when passed to a ButtonIcon component");
37
45
  const button$1 = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_Box.Box, {
38
46
  component: "button",
@@ -44,6 +52,7 @@ const ButtonIconContent = (0, react.forwardRef)(({ icon, label, id, size = "stan
44
52
  "aria-haspopup": ariaHasPopUp,
45
53
  "aria-expanded": ariaExpanded,
46
54
  "aria-describedby": ariaDescribedBy,
55
+ "aria-pressed": ariaPressed,
47
56
  onClick,
48
57
  onKeyUp,
49
58
  onKeyDown,
@@ -51,6 +60,7 @@ const ButtonIconContent = (0, react.forwardRef)(({ icon, label, id, size = "stan
51
60
  maxWidth: "content",
52
61
  tabIndex,
53
62
  ...root,
63
+ disabled: loading,
54
64
  className: [root.className, require_ButtonIcon_css.button],
55
65
  ...require_buildDataAttributes.default({
56
66
  data,
@@ -72,8 +82,11 @@ const ButtonIconContent = (0, react.forwardRef)(({ icon, label, id, size = "stan
72
82
  size,
73
83
  crop: true
74
84
  }),
75
- children: (0, react.cloneElement)(icon, {
76
- tone: icon.props.tone || tone,
85
+ children: loading ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_ButtonLoader.ButtonLoader, {
86
+ tone: resolvedIconTone,
87
+ size: "fill"
88
+ }) : (0, react.cloneElement)(icon, {
89
+ tone: resolvedIconTone,
77
90
  size: "fill"
78
91
  })
79
92
  })]
@@ -5,7 +5,7 @@ import * as react from "react";
5
5
  import { AllHTMLAttributes, ReactElement } from "react";
6
6
 
7
7
  //#region src/lib/components/ButtonIcon/ButtonIcon.d.ts
8
- declare const buttonIconVariants: Array<Extract<ButtonStyleProps['variant'], 'soft' | 'transparent'>>;
8
+ declare const buttonIconVariants: Array<Extract<ButtonStyleProps['variant'], 'soft' | 'transparent' | 'solid'>>;
9
9
  declare const buttonIconTones: Array<Extract<ButtonStyleProps['tone'], 'neutral' | 'formAccent'>>;
10
10
  declare const buttonIconSizes: readonly ["small", "standard", "large"];
11
11
  type ButtonIconSize = (typeof buttonIconSizes)[number];
@@ -25,7 +25,9 @@ interface ButtonIconProps {
25
25
  'aria-haspopup'?: NativeButtonProps['aria-haspopup'];
26
26
  'aria-expanded'?: NativeButtonProps['aria-expanded'];
27
27
  'aria-describedby'?: NativeButtonProps['aria-describedby'];
28
+ 'aria-pressed'?: NativeButtonProps['aria-pressed'];
28
29
  tabIndex?: number;
30
+ loading?: boolean;
29
31
  data?: DataAttributeMap;
30
32
  bleed?: boolean;
31
33
  tooltipPlacement?: 'bottom' | 'top';
@@ -5,7 +5,7 @@ import * as react from "react";
5
5
  import { AllHTMLAttributes, ReactElement } from "react";
6
6
 
7
7
  //#region src/lib/components/ButtonIcon/ButtonIcon.d.ts
8
- declare const buttonIconVariants: Array<Extract<ButtonStyleProps['variant'], 'soft' | 'transparent'>>;
8
+ declare const buttonIconVariants: Array<Extract<ButtonStyleProps['variant'], 'soft' | 'transparent' | 'solid'>>;
9
9
  declare const buttonIconTones: Array<Extract<ButtonStyleProps['tone'], 'neutral' | 'formAccent'>>;
10
10
  declare const buttonIconSizes: readonly ["small", "standard", "large"];
11
11
  type ButtonIconSize = (typeof buttonIconSizes)[number];
@@ -25,7 +25,9 @@ interface ButtonIconProps {
25
25
  'aria-haspopup'?: NativeButtonProps['aria-haspopup'];
26
26
  'aria-expanded'?: NativeButtonProps['aria-expanded'];
27
27
  'aria-describedby'?: NativeButtonProps['aria-describedby'];
28
+ 'aria-pressed'?: NativeButtonProps['aria-pressed'];
28
29
  tabIndex?: number;
30
+ loading?: boolean;
29
31
  data?: DataAttributeMap;
30
32
  bleed?: boolean;
31
33
  tooltipPlacement?: 'bottom' | 'top';
@@ -4,6 +4,7 @@ import { useFallbackId } from "../../hooks/useFallbackId.mjs";
4
4
  import { Text } from "../Text/Text.mjs";
5
5
  import { iconContainerSize, iconSize } from "../../hooks/useIcon/index.mjs";
6
6
  import { Bleed } from "../Bleed/Bleed.mjs";
7
+ import { ButtonLoader } from "../Button/ButtonLoader.mjs";
7
8
  import { ButtonOverlays, useButtonStyles } from "../Button/Button.mjs";
8
9
  import { TooltipRenderer } from "../TooltipRenderer/TooltipRenderer.mjs";
9
10
  import { button } from "./ButtonIcon.css.mjs";
@@ -12,7 +13,11 @@ import { cloneElement, forwardRef } from "react";
12
13
  import { jsx, jsxs } from "react/jsx-runtime";
13
14
 
14
15
  //#region src/lib/components/ButtonIcon/ButtonIcon.tsx
15
- const buttonIconVariants = ["soft", "transparent"];
16
+ const buttonIconVariants = [
17
+ "soft",
18
+ "transparent",
19
+ "solid"
20
+ ];
16
21
  const buttonIconTones = ["neutral", "formAccent"];
17
22
  const buttonIconSizes = [
18
23
  "small",
@@ -24,13 +29,16 @@ const padding = {
24
29
  standard: "xsmall",
25
30
  large: "xsmall"
26
31
  };
27
- const ButtonIconContent = forwardRef(({ icon, label, id, size = "standard", tone = "neutral", variant = "soft", type = "button", bleed, tooltipPlacement, onClick, onMouseDown, onKeyUp, onKeyDown, "aria-haspopup": ariaHasPopUp, "aria-expanded": ariaExpanded, "aria-describedby": ariaDescribedBy, tabIndex, data, ...restProps }, forwardedRef) => {
32
+ const ButtonIconContent = forwardRef(({ icon, label, id, size = "standard", tone = "neutral", variant = "soft", type = "button", bleed, tooltipPlacement, loading, onClick, onMouseDown, onKeyUp, onKeyDown, "aria-haspopup": ariaHasPopUp, "aria-expanded": ariaExpanded, "aria-describedby": ariaDescribedBy, "aria-pressed": ariaPressed, tabIndex, data, ...restProps }, forwardedRef) => {
28
33
  const { root, content } = useButtonStyles({
29
34
  variant,
30
35
  tone,
36
+ loading,
31
37
  size: size === "small" ? "small" : "standard",
32
38
  radius: "full"
33
39
  });
40
+ const fallbackIconTone = variant === "solid" ? "neutral" : tone;
41
+ const resolvedIconTone = icon.props.tone || fallbackIconTone;
34
42
  assert(icon && icon.props.size === void 0, "Icons cannot set the 'size' prop when passed to a ButtonIcon component");
35
43
  const button$1 = /* @__PURE__ */ jsx(Box, {
36
44
  component: "button",
@@ -42,6 +50,7 @@ const ButtonIconContent = forwardRef(({ icon, label, id, size = "standard", tone
42
50
  "aria-haspopup": ariaHasPopUp,
43
51
  "aria-expanded": ariaExpanded,
44
52
  "aria-describedby": ariaDescribedBy,
53
+ "aria-pressed": ariaPressed,
45
54
  onClick,
46
55
  onKeyUp,
47
56
  onKeyDown,
@@ -49,6 +58,7 @@ const ButtonIconContent = forwardRef(({ icon, label, id, size = "standard", tone
49
58
  maxWidth: "content",
50
59
  tabIndex,
51
60
  ...root,
61
+ disabled: loading,
52
62
  className: [root.className, button],
53
63
  ...buildDataAttributes_default({
54
64
  data,
@@ -70,8 +80,11 @@ const ButtonIconContent = forwardRef(({ icon, label, id, size = "standard", tone
70
80
  size,
71
81
  crop: true
72
82
  }),
73
- children: cloneElement(icon, {
74
- tone: icon.props.tone || tone,
83
+ children: loading ? /* @__PURE__ */ jsx(ButtonLoader, {
84
+ tone: resolvedIconTone,
85
+ size: "fill"
86
+ }) : cloneElement(icon, {
87
+ tone: resolvedIconTone,
75
88
  size: "fill"
76
89
  })
77
90
  })]
@@ -4,7 +4,7 @@ let react = require("react");
4
4
  //#region src/lib/hooks/useFallbackId.ts
5
5
  const useFallbackId = (id) => {
6
6
  const fallbackId = (0, react.useId)();
7
- return id || fallbackId;
7
+ return id || `id-${fallbackId}`;
8
8
  };
9
9
 
10
10
  //#endregion
@@ -3,7 +3,7 @@ import { useId } from "react";
3
3
  //#region src/lib/hooks/useFallbackId.ts
4
4
  const useFallbackId = (id) => {
5
5
  const fallbackId = useId();
6
- return id || fallbackId;
6
+ return id || `id-${fallbackId}`;
7
7
  };
8
8
 
9
9
  //#endregion
@@ -5,6 +5,10 @@ const snippets = [
5
5
  description: "Soft",
6
6
  code: "<ButtonIcon variant=\"soft\" icon={<IconBookmark />} label=\"Bookmark\" />"
7
7
  },
8
+ {
9
+ description: "Solid",
10
+ code: "<ButtonIcon variant=\"solid\" icon={<IconBookmark />} label=\"Bookmark\" />"
11
+ },
8
12
  {
9
13
  description: "Transparent",
10
14
  code: "<ButtonIcon variant=\"transparent\" icon={<IconHelp />} label=\"Help\" />"
@@ -4,6 +4,10 @@ const snippets = [
4
4
  description: "Soft",
5
5
  code: "<ButtonIcon variant=\"soft\" icon={<IconBookmark />} label=\"Bookmark\" />"
6
6
  },
7
+ {
8
+ description: "Solid",
9
+ code: "<ButtonIcon variant=\"solid\" icon={<IconBookmark />} label=\"Bookmark\" />"
10
+ },
7
11
  {
8
12
  description: "Transparent",
9
13
  code: "<ButtonIcon variant=\"transparent\" icon={<IconHelp />} label=\"Help\" />"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "braid-design-system",
3
- "version": "34.3.0",
3
+ "version": "34.4.0",
4
4
  "description": "Themeable design system for the SEEK Group",
5
5
  "homepage": "https://seek-oss.github.io/braid-design-system/",
6
6
  "bugs": {