@heliosgraphics/ui 2.0.0-alpha.93 → 2.0.0-alpha.94

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 (56) hide show
  1. package/components/Breadcrumb/Breadcrumb.tsx +25 -23
  2. package/components/Button/Button.tsx +172 -174
  3. package/components/Checkbox/Checkbox.tsx +54 -60
  4. package/components/Confirm/Confirm.tsx +1 -1
  5. package/components/Dialog/Dialog.meta.ts +4 -0
  6. package/components/Dialog/Dialog.tsx +11 -2
  7. package/components/Dialog/Dialog.types.ts +1 -0
  8. package/components/Donut/Donut.tsx +1 -0
  9. package/components/Dropdown/Dropdown.tsx +6 -1
  10. package/components/Flex/Flex.meta.ts +2 -0
  11. package/components/Flex/Flex.tsx +2 -3
  12. package/components/Flex/Flex.types.ts +3 -1
  13. package/components/Flex/Flex.utils.ts +36 -1
  14. package/components/Flex/index.ts +0 -2
  15. package/components/Heading/Heading.tsx +3 -3
  16. package/components/Icon/Icon.tsx +3 -3
  17. package/components/Input/Input.tsx +5 -2
  18. package/components/Layout/components/LayoutAside/components/LayoutAsideContent/LayoutAsideContent.tsx +0 -2
  19. package/components/Layout/components/LayoutAside/components/LayoutAsideFooter/LayoutAsideFooter.tsx +0 -2
  20. package/components/Loading/Loading.tsx +8 -4
  21. package/components/Masonry/Masonry.meta.ts +1 -5
  22. package/components/Masonry/Masonry.tsx +27 -14
  23. package/components/Masonry/Masonry.types.ts +4 -4
  24. package/components/Overlay/Overlay.tsx +1 -1
  25. package/components/Pie/Pie.tsx +1 -0
  26. package/components/Progress/Progress.meta.ts +4 -0
  27. package/components/Progress/Progress.tsx +8 -2
  28. package/components/Progress/Progress.types.ts +1 -0
  29. package/components/Radio/Radio.tsx +57 -63
  30. package/components/Range/Range.meta.ts +26 -0
  31. package/components/Range/Range.module.css +68 -0
  32. package/components/Range/Range.tsx +47 -0
  33. package/components/Range/Range.types.ts +13 -0
  34. package/components/Range/index.ts +1 -0
  35. package/components/Select/Select.meta.ts +4 -0
  36. package/components/Select/Select.tsx +2 -0
  37. package/components/Select/Select.types.ts +1 -0
  38. package/components/Separator/Separator.tsx +25 -20
  39. package/components/Separator/components/VerticalSeparator/VerticalSeparator.tsx +1 -1
  40. package/components/Setup/Setup.tsx +0 -13
  41. package/components/Setup/css/feat.responsive.css +402 -0
  42. package/components/Slider/Slider.meta.ts +4 -0
  43. package/components/Slider/Slider.tsx +2 -2
  44. package/components/Slider/Slider.types.ts +1 -0
  45. package/components/Spacer/Spacer.tsx +3 -3
  46. package/components/Tabs/Tabs.tsx +14 -3
  47. package/components/Text/Text.tsx +3 -3
  48. package/components/Tile/Tile.tsx +10 -1
  49. package/components/Tooltip/Tooltip.tsx +3 -1
  50. package/components/Tooltip/Tooltip.types.ts +1 -0
  51. package/components/Tooltip/components/TooltipContent/TooltipContent.tsx +9 -2
  52. package/components/Tooltip/components/TooltipTrigger/TooltipTrigger.tsx +2 -2
  53. package/constants/components.ts +2 -0
  54. package/globals.d.ts +12 -0
  55. package/index.ts +1 -0
  56. package/package.json +6 -5
@@ -8,30 +8,32 @@ export const Breadcrumb: FC<BreadcrumbProps> = ({ items }) => {
8
8
  if (!items?.length) return null
9
9
 
10
10
  return (
11
- <Flex isYCentered={true} gap={2} isWrapped={true} data-ui-component="Breadcrumb">
12
- {items?.map((item, index) => {
13
- const isLast: boolean = Boolean(index + 1 === items?.length)
14
- const AComponent: ElementType = item.as || "a"
11
+ <nav aria-label="Breadcrumb" data-ui-component="Breadcrumb">
12
+ <Flex isYCentered={true} gap={2} isWrapped={true}>
13
+ {items?.map((item, index) => {
14
+ const isLast: boolean = Boolean(index + 1 === items?.length)
15
+ const AComponent: ElementType = item.as || "a"
15
16
 
16
- return (
17
- <Fragment key={item.href ?? item.name}>
18
- <AComponent
19
- href={item.href}
20
- onClick={item.onClick}
21
- {...(item.isActive && { "aria-current": "page" as const })}
22
- >
23
- <Text
24
- type="div"
25
- fontStyle={item?.isActive ? "italic" : "normal"}
26
- emphasis={item.isActive ? "secondary" : "primary"}
17
+ return (
18
+ <Fragment key={item.href ?? item.name}>
19
+ <AComponent
20
+ href={item.href}
21
+ onClick={item.onClick}
22
+ {...(item.isActive && { "aria-current": "page" as const })}
27
23
  >
28
- {item.name}
29
- </Text>
30
- </AComponent>
31
- {!isLast && <Icon icon="arrow-right" size={12} emphasis="tertiary" aria-hidden={true} />}
32
- </Fragment>
33
- )
34
- })}
35
- </Flex>
24
+ <Text
25
+ type="div"
26
+ fontStyle={item?.isActive ? "italic" : "normal"}
27
+ emphasis={item.isActive ? "secondary" : "primary"}
28
+ >
29
+ {item.name}
30
+ </Text>
31
+ </AComponent>
32
+ {!isLast && <Icon icon="arrow-right" size={12} emphasis="tertiary" aria-hidden={true} />}
33
+ </Fragment>
34
+ )
35
+ })}
36
+ </Flex>
37
+ </nav>
36
38
  )
37
39
  }
@@ -6,7 +6,7 @@ import { Flex } from "../Flex"
6
6
  import { Icon } from "../Icon"
7
7
  import { Loading } from "../Loading"
8
8
  import { Text } from "../Text"
9
- import { useState, useId, memo, type KeyboardEvent, type MouseEvent, type FC, useMemo } from "react"
9
+ import { useState, useId, type KeyboardEvent, type MouseEvent, type FC, useMemo } from "react"
10
10
  import { getColorClasses } from "../../utils/colors"
11
11
  import { INTENTION_COLOR_MAP } from "../../constants/intentions"
12
12
  import type { HeliosIconType } from "../../types/icons"
@@ -25,181 +25,179 @@ const BUTTON_ICON_LABEL_SIZE: Record<string, number> = {
25
25
  normal: 16,
26
26
  }
27
27
 
28
- export const Button: FC<ButtonProps> = memo(
29
- ({
30
- accept,
31
- appearance,
32
- badge,
33
- badgeIcon,
34
- color,
35
- flair,
36
- icon,
37
- iconLeft,
38
- iconRight,
39
- intent = "neutral",
40
- isDisabled,
41
- isIconOnly,
42
- isLoading,
43
- isRounded,
44
- multiple,
45
- onChange,
46
- onClick,
47
- role = "button",
48
- size = "normal",
49
- tabIndex,
50
- type = "button",
51
- value,
52
- ...restProps
53
- }) => {
54
- const [isActive, setIsActive] = useState<boolean>(false)
55
-
56
- const buttonId = useId()
57
- const isIconOnlyLoading: boolean = !!isIconOnly && !!isLoading
58
-
59
- const localIconLeft: HeliosIconType | undefined = icon || iconLeft
60
- const localIconRight: HeliosIconType | undefined = iconRight
61
-
62
- const consideredColor: HeliosColorType = color ? color : INTENTION_COLOR_MAP[intent]
63
- const consideredAppearance: HeliosAppearanceType = useMemo(
64
- () => (appearance ? appearance : consideredColor === "gray" ? "light" : "dark"),
65
- [appearance, consideredColor],
66
- )
67
-
68
- const colorClasses = getColorClasses(consideredColor, consideredAppearance)
69
- const buttonClasses = getClasses(styles.button, "relative cursor-pointer", ...colorClasses, {
70
- [styles.buttonLight]: consideredAppearance === "light",
71
-
72
- [styles.buttonActive]: isActive,
73
- [styles.buttonDisabled]: isDisabled,
74
- [styles.buttonLoading]: isLoading,
75
-
76
- [styles.buttonRounded]: isRounded,
77
- [styles.buttonRound]: !isRounded && size !== "tiny",
78
- [styles.buttonRoundTiny]: !isRounded && size === "tiny",
79
-
80
- [styles.buttonSizeNormal]: !size || size === "normal",
81
- [styles.buttonSizeSmall]: size === "small",
82
- [styles.buttonSizeTiny]: size === "tiny",
83
-
84
- [styles.buttonWithIconLeft]: localIconLeft,
85
- [styles.buttonWithIconRight]: localIconRight,
86
- [styles.buttonIconOnly]: isIconOnly,
87
- [styles.buttonIconOnlyLoading]: isIconOnlyLoading,
88
- })
89
-
90
- const buttonIconLeftClasses = getClasses("relative", styles.button__icon, {
91
- [styles.button__iconLeft]: localIconLeft,
92
- })
93
-
94
- const buttonIconRightClasses = getClasses("relative", styles.button__icon, {
95
- [styles.button__iconRight]: localIconRight,
96
- [styles.button__iconRightLoading]: isLoading,
97
- })
98
-
99
- const buttonLoadingSize: 10 | 20 = size && size !== "normal" ? 10 : 20
100
- const isFileType: boolean = type === "file"
101
- const showFileLabel: boolean = isFileType && !isIconOnly
102
-
103
- const baseInputClasses = getClasses(styles.button__baseElement, "sans fw-medium", {
104
- tiny: size === "tiny" || size === "small",
105
- "small ": size === "normal",
106
- "radius-max": isRounded,
107
- [styles.button__baseElementNormal]: size === "normal" && !isIconOnly,
108
- [styles.button__baseElementSmall]: size === "small" && !isIconOnly,
109
- [styles.button__baseElementTiny]: size === "tiny" && !isIconOnly,
110
- })
111
-
112
- const buttonLabelClasses: string = getClasses(baseInputClasses, styles.button__label)
113
- const buttonInputClasses: string = getClasses(baseInputClasses, styles.button__input, {
114
- [styles.button__inputFile]: isFileType,
115
- })
116
-
117
- const handleClick = (event: MouseEvent<HTMLDivElement>): void => {
118
- if (isDisabled || isLoading) {
119
- event.preventDefault()
120
- return
121
- }
122
-
123
- onClick?.(event)
124
- }
125
-
126
- const handleKeyUp = (): void => {
127
- setIsActive(false)
128
- }
129
-
130
- const handleKeyDown = (event: KeyboardEvent<HTMLDivElement>): void => {
131
- if (event.key !== "Enter" && event.key !== " ") return
28
+ export const Button: FC<ButtonProps> = ({
29
+ accept,
30
+ appearance,
31
+ badge,
32
+ badgeIcon,
33
+ color,
34
+ flair,
35
+ icon,
36
+ iconLeft,
37
+ iconRight,
38
+ intent = "neutral",
39
+ isDisabled,
40
+ isIconOnly,
41
+ isLoading,
42
+ isRounded,
43
+ multiple,
44
+ onChange,
45
+ onClick,
46
+ role = "button",
47
+ size = "normal",
48
+ tabIndex,
49
+ type = "button",
50
+ value,
51
+ ...restProps
52
+ }) => {
53
+ const [isActive, setIsActive] = useState<boolean>(false)
54
+
55
+ const buttonId = useId()
56
+ const isIconOnlyLoading: boolean = !!isIconOnly && !!isLoading
57
+
58
+ const localIconLeft: HeliosIconType | undefined = icon || iconLeft
59
+ const localIconRight: HeliosIconType | undefined = iconRight
60
+
61
+ const consideredColor: HeliosColorType = color ? color : INTENTION_COLOR_MAP[intent]
62
+ const consideredAppearance: HeliosAppearanceType = useMemo(
63
+ () => (appearance ? appearance : consideredColor === "gray" ? "light" : "dark"),
64
+ [appearance, consideredColor],
65
+ )
66
+
67
+ const colorClasses = getColorClasses(consideredColor, consideredAppearance)
68
+ const buttonClasses = getClasses(styles.button, "relative cursor-pointer", ...colorClasses, {
69
+ [styles.buttonLight]: consideredAppearance === "light",
70
+
71
+ [styles.buttonActive]: isActive,
72
+ [styles.buttonDisabled]: isDisabled,
73
+ [styles.buttonLoading]: isLoading,
74
+
75
+ [styles.buttonRounded]: isRounded,
76
+ [styles.buttonRound]: !isRounded && size !== "tiny",
77
+ [styles.buttonRoundTiny]: !isRounded && size === "tiny",
78
+
79
+ [styles.buttonSizeNormal]: !size || size === "normal",
80
+ [styles.buttonSizeSmall]: size === "small",
81
+ [styles.buttonSizeTiny]: size === "tiny",
82
+
83
+ [styles.buttonWithIconLeft]: localIconLeft,
84
+ [styles.buttonWithIconRight]: localIconRight,
85
+ [styles.buttonIconOnly]: isIconOnly,
86
+ [styles.buttonIconOnlyLoading]: isIconOnlyLoading,
87
+ })
88
+
89
+ const buttonIconLeftClasses = getClasses("relative", styles.button__icon, {
90
+ [styles.button__iconLeft]: localIconLeft,
91
+ })
92
+
93
+ const buttonIconRightClasses = getClasses("relative", styles.button__icon, {
94
+ [styles.button__iconRight]: localIconRight,
95
+ [styles.button__iconRightLoading]: isLoading,
96
+ })
97
+
98
+ const buttonLoadingSize: 10 | 20 = size && size !== "normal" ? 10 : 20
99
+ const isFileType: boolean = type === "file"
100
+ const showFileLabel: boolean = isFileType && !isIconOnly
101
+
102
+ const baseInputClasses = getClasses(styles.button__baseElement, "sans fw-medium", {
103
+ tiny: size === "tiny" || size === "small",
104
+ "small ": size === "normal",
105
+ "radius-max": isRounded,
106
+ [styles.button__baseElementNormal]: size === "normal" && !isIconOnly,
107
+ [styles.button__baseElementSmall]: size === "small" && !isIconOnly,
108
+ [styles.button__baseElementTiny]: size === "tiny" && !isIconOnly,
109
+ })
110
+
111
+ const buttonLabelClasses: string = getClasses(baseInputClasses, styles.button__label)
112
+ const buttonInputClasses: string = getClasses(baseInputClasses, styles.button__input, {
113
+ [styles.button__inputFile]: isFileType,
114
+ })
115
+
116
+ const handleClick = (event: MouseEvent<HTMLDivElement>): void => {
117
+ if (isDisabled || isLoading) {
132
118
  event.preventDefault()
133
-
134
- if (isDisabled || isLoading || !buttonId) return
135
-
136
- setIsActive(true)
137
- document.getElementById(buttonId)?.click()
119
+ return
138
120
  }
139
121
 
140
- return (
141
- <Flex
142
- className={buttonClasses}
143
- isInline={true}
144
- isCentered={true}
145
- onKeyDown={handleKeyDown}
146
- onKeyUp={handleKeyUp}
147
- data-ui-component="Button"
148
- data-ui-size={size}
122
+ onClick?.(event)
123
+ }
124
+
125
+ const handleKeyUp = (): void => {
126
+ setIsActive(false)
127
+ }
128
+
129
+ const handleKeyDown = (event: KeyboardEvent<HTMLDivElement>): void => {
130
+ if (event.key !== "Enter" && event.key !== " ") return
131
+ event.preventDefault()
132
+
133
+ if (isDisabled || isLoading || !buttonId) return
134
+
135
+ setIsActive(true)
136
+ document.getElementById(buttonId)?.click()
137
+ }
138
+
139
+ return (
140
+ <Flex
141
+ className={buttonClasses}
142
+ isInline={true}
143
+ isCentered={true}
144
+ onKeyDown={handleKeyDown}
145
+ onKeyUp={handleKeyUp}
146
+ data-ui-component="Button"
147
+ data-ui-size={size}
148
+ aria-disabled={isDisabled || isLoading}
149
+ aria-busy={isLoading}
150
+ aria-label={isIconOnly ? value : undefined}
151
+ tabIndex={isDisabled ? -1 : (tabIndex ?? 0)}
152
+ role={role}
153
+ {...(onClick && { onClick: handleClick })}
154
+ {...restProps}
155
+ >
156
+ {localIconLeft && !isIconOnlyLoading && (
157
+ <Flex className={buttonIconLeftClasses}>
158
+ <Icon icon={localIconLeft} size={BUTTON_ICON_SIZE[size] || 24} />
159
+ </Flex>
160
+ )}
161
+ <input
162
+ id={buttonId}
149
163
  aria-disabled={isDisabled || isLoading}
150
- aria-busy={isLoading}
151
- aria-label={isIconOnly ? value : undefined}
152
- tabIndex={isDisabled ? -1 : (tabIndex ?? 0)}
153
- role={role}
154
- {...(onClick && { onClick: handleClick })}
155
- {...restProps}
156
- >
157
- {localIconLeft && !isIconOnlyLoading && (
158
- <Flex className={buttonIconLeftClasses}>
159
- <Icon icon={localIconLeft} size={BUTTON_ICON_SIZE[size] || 24} />
160
- </Flex>
161
- )}
162
- <input
163
- id={buttonId}
164
- aria-disabled={isDisabled || isLoading}
165
- disabled={isDisabled || isLoading}
166
- type={type}
167
- className={buttonInputClasses}
168
- accept={accept}
169
- multiple={multiple}
170
- tabIndex={-1}
171
- onChange={onChange}
172
- defaultValue={isIconOnly || isFileType ? "" : value}
173
- />
174
- {showFileLabel && (
175
- <label htmlFor={buttonId} className={buttonLabelClasses}>
176
- {value}
177
- </label>
178
- )}
179
- {isLoading && <Loading size={buttonLoadingSize} className={styles.button__loading} />}
180
- {localIconRight && !isIconOnlyLoading && (
181
- <Flex className={buttonIconRightClasses}>
182
- <Icon icon={localIconRight} size={BUTTON_ICON_SIZE[size] || 24} />
183
- </Flex>
184
- )}
185
- {!!flair && (
186
- <Flex className={styles.button__flair} isCentered={true}>
187
- <Text type={size !== "normal" ? "micro" : "tiny"} fontFamily="mono">
188
- {flair}
164
+ disabled={isDisabled || isLoading}
165
+ type={type}
166
+ className={buttonInputClasses}
167
+ accept={accept}
168
+ multiple={multiple}
169
+ tabIndex={-1}
170
+ onChange={onChange}
171
+ defaultValue={isIconOnly || isFileType ? "" : value}
172
+ />
173
+ {showFileLabel && (
174
+ <label htmlFor={buttonId} className={buttonLabelClasses}>
175
+ {value}
176
+ </label>
177
+ )}
178
+ {isLoading && <Loading size={buttonLoadingSize} className={styles.button__loading} />}
179
+ {localIconRight && !isIconOnlyLoading && (
180
+ <Flex className={buttonIconRightClasses}>
181
+ <Icon icon={localIconRight} size={BUTTON_ICON_SIZE[size] || 24} />
182
+ </Flex>
183
+ )}
184
+ {!!flair && (
185
+ <Flex className={styles.button__flair} isCentered={true}>
186
+ <Text type={size !== "normal" ? "micro" : "tiny"} fontFamily="mono">
187
+ {flair}
188
+ </Text>
189
+ </Flex>
190
+ )}
191
+ {(!!badge || !!badgeIcon) && (
192
+ <Flex className={styles.button__badge} isCentered={true} gap={2}>
193
+ {badgeIcon && <Icon icon={badgeIcon} size={BUTTON_ICON_LABEL_SIZE[size] || 16} />}
194
+ {badge && (
195
+ <Text type="tiny" fontFamily="mono" emphasis="inherit">
196
+ {badge}
189
197
  </Text>
190
- </Flex>
191
- )}
192
- {(!!badge || !!badgeIcon) && (
193
- <Flex className={styles.button__badge} isCentered={true} gap={2}>
194
- {badgeIcon && <Icon icon={badgeIcon} size={BUTTON_ICON_LABEL_SIZE[size] || 16} />}
195
- {badge && (
196
- <Text type="tiny" fontFamily="mono" emphasis="inherit">
197
- {badge}
198
- </Text>
199
- )}
200
- </Flex>
201
- )}
202
- </Flex>
203
- )
204
- },
205
- )
198
+ )}
199
+ </Flex>
200
+ )}
201
+ </Flex>
202
+ )
203
+ }
@@ -1,6 +1,6 @@
1
1
  "use client"
2
2
 
3
- import { useId, memo } from "react"
3
+ import { useId } from "react"
4
4
  import { getClasses } from "@heliosgraphics/utils"
5
5
  import { getColorClasses } from "../../utils/colors"
6
6
  import { Flex } from "../Flex"
@@ -10,66 +10,60 @@ import styles from "./Checkbox.module.css"
10
10
  import type { FC } from "react"
11
11
  import type { CheckboxProps } from "./Checkbox.types"
12
12
 
13
- export const Checkbox: FC<CheckboxProps> = memo(
14
- ({
15
- color = "gray",
16
- isChecked,
17
- isVertical,
18
- isLabelHidden = false,
19
- isSmall,
20
- description,
21
- isDisabled,
22
- isRequired,
23
- onChange,
24
- label,
25
- }) => {
26
- const checkboxId: string = useId()
13
+ export const Checkbox: FC<CheckboxProps> = ({
14
+ color = "gray",
15
+ isChecked,
16
+ isVertical,
17
+ isLabelHidden = false,
18
+ isSmall,
19
+ description,
20
+ isDisabled,
21
+ isRequired,
22
+ onChange,
23
+ label,
24
+ }) => {
25
+ const checkboxId: string = useId()
27
26
 
28
- const colorClasses = getColorClasses(color, "dark")
29
- const checkboxClasses = getClasses(styles.checkbox, ...colorClasses, {
30
- [styles.checkboxDisabled]: isDisabled,
31
- [styles.checkboxSmall]: isSmall,
32
- })
27
+ const colorClasses = getColorClasses(color, "dark")
28
+ const checkboxClasses = getClasses(styles.checkbox, ...colorClasses, {
29
+ [styles.checkboxDisabled]: isDisabled,
30
+ [styles.checkboxSmall]: isSmall,
31
+ })
33
32
 
34
- const checkboxLabelClasses = getClasses(styles.checkbox__checkboxLabel, "flex gap-4", {
35
- "flex-x-center flex-column": isVertical,
36
- "flex-y-center": !isVertical,
37
- })
33
+ const checkboxLabelClasses = getClasses(styles.checkbox__checkboxLabel, "flex gap-4", {
34
+ "flex-x-center flex-column": isVertical,
35
+ "flex-y-center": !isVertical,
36
+ })
38
37
 
39
- return (
40
- <div className={checkboxClasses} data-ui-component="Checkbox">
41
- <label className={checkboxLabelClasses} htmlFor={checkboxId}>
42
- <span className={styles.checkbox__mark}>
43
- <input
44
- type="checkbox"
45
- checked={isChecked}
46
- onChange={onChange}
47
- disabled={isDisabled}
48
- required={isRequired}
49
- aria-label={isLabelHidden ? label : undefined}
50
- id={checkboxId}
51
- />
52
- <Icon icon="check" size={isSmall ? 14 : 18} className={styles.checkbox__checkboxIcon} />
53
- <div className={styles.checkbox__checkboxMark} />
54
- </span>
55
- {!isLabelHidden && (
56
- <Flex isColumn={true}>
57
- <Text
58
- type={isSmall ? "tiny" : "small"}
59
- fontWeight="medium"
60
- emphasis={isDisabled ? "tertiary" : "inherit"}
61
- >
62
- {label}
38
+ return (
39
+ <div className={checkboxClasses} data-ui-component="Checkbox">
40
+ <label className={checkboxLabelClasses} htmlFor={checkboxId}>
41
+ <span className={styles.checkbox__mark}>
42
+ <input
43
+ type="checkbox"
44
+ checked={isChecked}
45
+ onChange={onChange}
46
+ disabled={isDisabled}
47
+ required={isRequired}
48
+ aria-label={isLabelHidden ? label : undefined}
49
+ id={checkboxId}
50
+ />
51
+ <Icon icon="check" size={isSmall ? 14 : 18} className={styles.checkbox__checkboxIcon} />
52
+ <div className={styles.checkbox__checkboxMark} />
53
+ </span>
54
+ {!isLabelHidden && (
55
+ <Flex isColumn={true}>
56
+ <Text type={isSmall ? "tiny" : "small"} fontWeight="medium" emphasis={isDisabled ? "tertiary" : "inherit"}>
57
+ {label}
58
+ </Text>
59
+ {description && (
60
+ <Text type="tiny" emphasis="secondary">
61
+ {description}
63
62
  </Text>
64
- {description && (
65
- <Text type="tiny" emphasis="secondary">
66
- {description}
67
- </Text>
68
- )}
69
- </Flex>
70
- )}
71
- </label>
72
- </div>
73
- )
74
- },
75
- )
63
+ )}
64
+ </Flex>
65
+ )}
66
+ </label>
67
+ </div>
68
+ )
69
+ }
@@ -24,7 +24,7 @@ export const Confirm: FC<ConfirmProps> = ({
24
24
  )
25
25
 
26
26
  return (
27
- <Dialog title={title} onClose={onCancel} isOpen={isOpen} isCentered={true}>
27
+ <Dialog title={title} onClose={onCancel} isOpen={isOpen} isCentered={true} role="alertdialog">
28
28
  <Flex gap={12} isColumn={true} data-ui-component="Confirm">
29
29
  {!!description && <Text type="paragraph">{description}</Text>}
30
30
  <ButtonGroup align={buttonGroupAlign}>
@@ -37,6 +37,10 @@ export const meta: HeliosAttributeMeta<DialogBaseProps> = {
37
37
  onClose: {
38
38
  type: "(_?: unknown) => Promise<void> | void",
39
39
  },
40
+ role: {
41
+ type: '"dialog" | "alertdialog"',
42
+ isOptional: true,
43
+ },
40
44
  title: {
41
45
  type: "string",
42
46
  isOptional: true,
@@ -12,7 +12,16 @@ import type { DialogProps } from "./Dialog.types"
12
12
  const ATTRIBUTE_SCROLL = "data-scroll" as const
13
13
  const POSITION_FIXED_CLASS = "fixed" as const
14
14
 
15
- export const Dialog: FC<DialogProps> = ({ title, children, isNarrow, isOpen, isCentered, onClose, noPadding }) => {
15
+ export const Dialog: FC<DialogProps> = ({
16
+ title,
17
+ children,
18
+ isNarrow,
19
+ isOpen,
20
+ isCentered,
21
+ onClose,
22
+ noPadding,
23
+ role,
24
+ }) => {
16
25
  const dialogRef = useRef<HTMLDialogElement | null>(null)
17
26
  const triggerRef = useRef<Element | null>(null)
18
27
 
@@ -108,7 +117,7 @@ export const Dialog: FC<DialogProps> = ({ title, children, isNarrow, isOpen, isC
108
117
  })
109
118
 
110
119
  return (
111
- <dialog ref={dialogRef} className={dialogClasses} onClick={onDialogClose} data-ui-component="Dialog">
120
+ <dialog ref={dialogRef} className={dialogClasses} onClick={onDialogClose} role={role} data-ui-component="Dialog">
112
121
  {!!title && (
113
122
  <Flex isBetween={true} isYCentered={true} padding={8} className={dialogFlexClasses}>
114
123
  <Heading level={5} fontWeight="medium">
@@ -6,6 +6,7 @@ export interface DialogBaseProps {
6
6
  isOpen?: boolean
7
7
  noPadding?: boolean
8
8
  onClose: (_?: unknown) => Promise<void> | void
9
+ role?: "dialog" | "alertdialog"
9
10
  title?: string
10
11
  }
11
12
 
@@ -36,6 +36,7 @@ export const Donut: FC<DonutProps> = ({ children, size, percentage = 0, color })
36
36
  role="img"
37
37
  aria-label={`Donut chart: ${percentage}%`}
38
38
  >
39
+ <title>{`${percentage}%`}</title>
39
40
  <circle
40
41
  fill="none"
41
42
  strokeWidth={borderSize}
@@ -22,6 +22,7 @@ export const Dropdown: FC<DropdownProps> = ({ children, items, isDisabled, posit
22
22
  const hoverStateRef = useRef<boolean>(false)
23
23
  const resultListRef = useRef<HTMLOListElement | null>(null)
24
24
  const hideTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null)
25
+ const isMountedRef = useRef<boolean>(true)
25
26
 
26
27
  const [isVisible, setVisible] = useState<boolean>(false)
27
28
  const [isHovering, setHovering] = useState<boolean>(false)
@@ -34,8 +35,12 @@ export const Dropdown: FC<DropdownProps> = ({ children, items, isDisabled, posit
34
35
  }, [isVisible, items?.length])
35
36
 
36
37
  useEffect(() => {
38
+ isMountedRef.current = true
39
+
37
40
  return (): void => {
41
+ isMountedRef.current = false
38
42
  if (hideTimeoutRef.current) globalThis.clearTimeout(hideTimeoutRef.current)
43
+ hideTimeoutRef.current = null
39
44
  }
40
45
  }, [])
41
46
 
@@ -62,7 +67,7 @@ export const Dropdown: FC<DropdownProps> = ({ children, items, isDisabled, posit
62
67
  const mouseLeave = (_event: MouseEvent<HTMLDivElement>): void => {
63
68
  setHovering(false)
64
69
  hideTimeoutRef.current = setTimeout(() => {
65
- if (!hoverStateRef.current) setVisible(false)
70
+ if (isMountedRef.current && !hoverStateRef.current) setVisible(false)
66
71
  hideTimeoutRef.current = null
67
72
  }, ANIMATION_FAST)
68
73
  }
@@ -34,6 +34,8 @@ export const meta: HeliosAttributeMeta<FlexBaseProps> = {
34
34
  isYCentered: { type: "boolean", isOptional: true },
35
35
  onClick: { type: "HeliosOnClickType", isOptional: true },
36
36
  padding: { type: "ResponsiveScaleType", isOptional: true },
37
+ paddingBlock: { type: "ResponsiveScaleType", isOptional: true },
38
+ paddingInline: { type: "ResponsiveScaleType", isOptional: true },
37
39
  paddingX: { type: "HeliosScale", isOptional: true },
38
40
  paddingY: { type: "HeliosScale", isOptional: true },
39
41
  ref: { type: "RefObject<HTMLDivElement>", isOptional: true },