@heliosgraphics/ui 2.0.0-alpha.92 → 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.
- package/components/Breadcrumb/Breadcrumb.tsx +25 -23
- package/components/Button/Button.tsx +172 -174
- package/components/Checkbox/Checkbox.tsx +54 -60
- package/components/Confirm/Confirm.tsx +1 -1
- package/components/Dialog/Dialog.meta.ts +4 -0
- package/components/Dialog/Dialog.tsx +11 -2
- package/components/Dialog/Dialog.types.ts +1 -0
- package/components/Donut/Donut.tsx +1 -0
- package/components/Dropdown/Dropdown.tsx +6 -1
- package/components/Flex/Flex.meta.ts +2 -0
- package/components/Flex/Flex.tsx +2 -3
- package/components/Flex/Flex.types.ts +3 -1
- package/components/Flex/Flex.utils.ts +36 -1
- package/components/Flex/index.ts +0 -2
- package/components/Heading/Heading.tsx +3 -3
- package/components/Icon/Icon.tsx +3 -3
- package/components/Input/Input.tsx +5 -2
- package/components/Layout/components/LayoutAside/components/LayoutAsideContent/LayoutAsideContent.tsx +0 -2
- package/components/Layout/components/LayoutAside/components/LayoutAsideFooter/LayoutAsideFooter.tsx +0 -2
- package/components/Loading/Loading.tsx +8 -4
- package/components/Masonry/Masonry.meta.ts +1 -5
- package/components/Masonry/Masonry.tsx +27 -14
- package/components/Masonry/Masonry.types.ts +4 -4
- package/components/Overlay/Overlay.tsx +1 -1
- package/components/Pie/Pie.tsx +1 -0
- package/components/Progress/Progress.meta.ts +4 -0
- package/components/Progress/Progress.tsx +8 -2
- package/components/Progress/Progress.types.ts +1 -0
- package/components/Radio/Radio.tsx +57 -63
- package/components/Range/Range.meta.ts +26 -0
- package/components/Range/Range.module.css +68 -0
- package/components/Range/Range.tsx +47 -0
- package/components/Range/Range.types.ts +13 -0
- package/components/Range/index.ts +1 -0
- package/components/Select/Select.meta.ts +4 -0
- package/components/Select/Select.tsx +2 -0
- package/components/Select/Select.types.ts +1 -0
- package/components/Separator/Separator.tsx +25 -20
- package/components/Separator/components/VerticalSeparator/VerticalSeparator.tsx +1 -1
- package/components/Setup/Setup.tsx +0 -13
- package/components/Setup/css/feat.responsive.css +402 -0
- package/components/Slider/Slider.meta.ts +4 -0
- package/components/Slider/Slider.tsx +2 -2
- package/components/Slider/Slider.types.ts +1 -0
- package/components/Spacer/Spacer.tsx +3 -3
- package/components/Tabs/Tabs.tsx +14 -3
- package/components/Text/Text.tsx +3 -3
- package/components/Tile/Tile.tsx +10 -1
- package/components/Tooltip/Tooltip.tsx +3 -1
- package/components/Tooltip/Tooltip.types.ts +1 -0
- package/components/Tooltip/components/TooltipContent/TooltipContent.tsx +9 -2
- package/components/Tooltip/components/TooltipTrigger/TooltipTrigger.tsx +2 -2
- package/constants/components.ts +2 -0
- package/globals.d.ts +12 -0
- package/index.ts +1 -0
- package/package.json +9 -8
package/components/Flex/Flex.tsx
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import { memo } from "react"
|
|
2
1
|
import { getFlexUtility, getSafeFlexProps } from "../Flex/Flex.utils"
|
|
3
2
|
import type { FlexProps } from "./Flex.types"
|
|
4
3
|
|
|
5
|
-
export const Flex =
|
|
4
|
+
export const Flex = (props: FlexProps) => {
|
|
6
5
|
const { ref, ...restProps } = props
|
|
7
6
|
const flexClasses: string = getFlexUtility(restProps)
|
|
8
7
|
const safeProps = getSafeFlexProps(restProps)
|
|
@@ -12,4 +11,4 @@ export const Flex = memo((props: FlexProps) => {
|
|
|
12
11
|
{restProps.children}
|
|
13
12
|
</div>
|
|
14
13
|
)
|
|
15
|
-
}
|
|
14
|
+
}
|
|
@@ -8,7 +8,7 @@ export type ResponsiveRadiusType = HeliosRadiusType | [HeliosRadiusType, HeliosR
|
|
|
8
8
|
export interface FlexBaseProps {
|
|
9
9
|
className?: string
|
|
10
10
|
elevation?: "small" | "medium" | "large"
|
|
11
|
-
gap?:
|
|
11
|
+
gap?: ResponsiveScaleType
|
|
12
12
|
grow?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8
|
|
13
13
|
isAround?: boolean
|
|
14
14
|
isBetween?: boolean
|
|
@@ -23,6 +23,8 @@ export interface FlexBaseProps {
|
|
|
23
23
|
isYCentered?: boolean
|
|
24
24
|
onClick?: MouseEventHandler<HTMLDivElement>
|
|
25
25
|
padding?: ResponsiveScaleType
|
|
26
|
+
paddingBlock?: ResponsiveScaleType
|
|
27
|
+
paddingInline?: ResponsiveScaleType
|
|
26
28
|
paddingX?: HeliosScaleType
|
|
27
29
|
paddingY?: HeliosScaleType
|
|
28
30
|
ref?: Ref<HTMLDivElement>
|
|
@@ -42,7 +42,21 @@ export const getFlexUtility = (props?: FlexProps): string => {
|
|
|
42
42
|
flexClasses.add(responsivePadding)
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
if (props.gap)
|
|
45
|
+
if (props.gap) {
|
|
46
|
+
const responsiveGap: string = getResponsiveScale(props.gap, "gap")
|
|
47
|
+
flexClasses.add(responsiveGap)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (props.paddingBlock) {
|
|
51
|
+
const responsivePaddingBlock: string = getResponsiveScale(props.paddingBlock, "py")
|
|
52
|
+
flexClasses.add(responsivePaddingBlock)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (props.paddingInline) {
|
|
56
|
+
const responsivePaddingInline: string = getResponsiveScale(props.paddingInline, "px")
|
|
57
|
+
flexClasses.add(responsivePaddingInline)
|
|
58
|
+
}
|
|
59
|
+
|
|
46
60
|
if (props.paddingY) flexClasses.add(`py-${props.paddingY}`)
|
|
47
61
|
if (props.paddingX) flexClasses.add(`px-${props.paddingX}`)
|
|
48
62
|
|
|
@@ -54,6 +68,25 @@ export const getFlexUtility = (props?: FlexProps): string => {
|
|
|
54
68
|
return Array.from(flexClasses).join(" ")
|
|
55
69
|
}
|
|
56
70
|
|
|
71
|
+
export const getResponsiveScale = (value?: ResponsiveScaleType, prefix: string = "p"): string => {
|
|
72
|
+
if (!value) return ""
|
|
73
|
+
|
|
74
|
+
const isArray: boolean = Boolean(value && value instanceof Array)
|
|
75
|
+
const classes = new Set<string>()
|
|
76
|
+
|
|
77
|
+
if (!isArray) return `${prefix}-${value}`
|
|
78
|
+
|
|
79
|
+
for (let index: number = 0; index < 3; index++) {
|
|
80
|
+
const element = (value as [HeliosScaleType, HeliosScaleType, HeliosScaleType])[index]
|
|
81
|
+
|
|
82
|
+
if (index === 0) classes.add(`mobile:${prefix}-${element ?? 0}`)
|
|
83
|
+
if (index === 1) classes.add(`tablet:${prefix}-${element ?? 0}`)
|
|
84
|
+
if (index === 2) classes.add(`${prefix}-${element ?? 0}`)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return Array.from(classes).join(" ")
|
|
88
|
+
}
|
|
89
|
+
|
|
57
90
|
export const getPadding = (paddingValue?: ResponsiveScaleType): string => {
|
|
58
91
|
if (!paddingValue) return ""
|
|
59
92
|
|
|
@@ -111,6 +144,8 @@ export const getSafeFlexProps = (props: any): Partial<FlexProps> => {
|
|
|
111
144
|
isXCentered: _isXCentered,
|
|
112
145
|
isYCentered: _isYCentered,
|
|
113
146
|
padding: _padding,
|
|
147
|
+
paddingBlock: _paddingBlock,
|
|
148
|
+
paddingInline: _paddingInline,
|
|
114
149
|
paddingX: _paddingX,
|
|
115
150
|
paddingY: _paddingY,
|
|
116
151
|
withBackground: _withBackground,
|
package/components/Flex/index.ts
CHANGED
|
@@ -9,9 +9,9 @@ import { H5 } from "./components/H5"
|
|
|
9
9
|
import { H6 } from "./components/H6"
|
|
10
10
|
import styles from "./Heading.module.css"
|
|
11
11
|
import type { HeadingProps } from "./Heading.types"
|
|
12
|
-
import {
|
|
12
|
+
import type { FC, CSSProperties } from "react"
|
|
13
13
|
|
|
14
|
-
export const Heading: FC<HeadingProps> =
|
|
14
|
+
export const Heading: FC<HeadingProps> = (props) => {
|
|
15
15
|
const { level, lineClamp, style, className, ...rest } = props
|
|
16
16
|
|
|
17
17
|
const headingClasses: string = getClasses(className, styles.heading, {
|
|
@@ -60,4 +60,4 @@ export const Heading: FC<HeadingProps> = memo((props) => {
|
|
|
60
60
|
default:
|
|
61
61
|
return null
|
|
62
62
|
}
|
|
63
|
-
}
|
|
63
|
+
}
|
package/components/Icon/Icon.tsx
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { getClasses } from "@heliosgraphics/utils"
|
|
2
2
|
import { icons } from "@heliosgraphics/icons"
|
|
3
3
|
import styles from "./Icon.module.css"
|
|
4
|
-
import {
|
|
4
|
+
import type { FC } from "react"
|
|
5
5
|
import type { IconProps } from "./Icon.types"
|
|
6
6
|
|
|
7
|
-
export const Icon: FC<IconProps> =
|
|
7
|
+
export const Icon: FC<IconProps> = ({ icon, className, emphasis, size }) => {
|
|
8
8
|
const iconSizeStyle = {
|
|
9
9
|
height: size + "px",
|
|
10
10
|
minHeight: size + "px",
|
|
@@ -27,4 +27,4 @@ export const Icon: FC<IconProps> = memo(({ icon, className, emphasis, size }) =>
|
|
|
27
27
|
dangerouslySetInnerHTML={{ __html: icons[icon] || "" }}
|
|
28
28
|
/>
|
|
29
29
|
)
|
|
30
|
-
}
|
|
30
|
+
}
|
|
@@ -14,7 +14,9 @@ import type { InputProps } from "./Input.types"
|
|
|
14
14
|
import type { ResultItem } from "../shared/ResultList"
|
|
15
15
|
|
|
16
16
|
export const Input: FC<InputProps> = ({
|
|
17
|
+
className,
|
|
17
18
|
helperText,
|
|
19
|
+
id,
|
|
18
20
|
isDisabled,
|
|
19
21
|
autoComplete,
|
|
20
22
|
autoFocus,
|
|
@@ -36,7 +38,8 @@ export const Input: FC<InputProps> = ({
|
|
|
36
38
|
type = "text",
|
|
37
39
|
value,
|
|
38
40
|
}) => {
|
|
39
|
-
const
|
|
41
|
+
const generatedId: string = useId()
|
|
42
|
+
const htmlFor: string = id || generatedId
|
|
40
43
|
const helperId: string = `${htmlFor}-helper`
|
|
41
44
|
|
|
42
45
|
const filteredItems: Array<ResultItem> =
|
|
@@ -51,7 +54,7 @@ export const Input: FC<InputProps> = ({
|
|
|
51
54
|
|
|
52
55
|
const showingResults: boolean = Boolean(!!filteredItems?.length && showResults)
|
|
53
56
|
|
|
54
|
-
const inputClasses: string = getClasses(styles.input, "relative flex flex-column", {
|
|
57
|
+
const inputClasses: string = getClasses(styles.input, "relative flex flex-column", className, {
|
|
55
58
|
[styles.inputDisabled]: isDisabled,
|
|
56
59
|
[styles.inputShowingResults]: showingResults,
|
|
57
60
|
})
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
1
3
|
import { getClasses } from "@heliosgraphics/utils"
|
|
4
|
+
import { useId, type FC } from "react"
|
|
2
5
|
import styles from "./Loading.module.css"
|
|
3
|
-
import { memo, type FC } from "react"
|
|
4
6
|
import type { LoadingProps } from "./Loading.types"
|
|
5
7
|
|
|
6
|
-
export const Loading: FC<LoadingProps> =
|
|
8
|
+
export const Loading: FC<LoadingProps> = ({ className, size, emphasis }) => {
|
|
9
|
+
const titleId: string = useId()
|
|
7
10
|
const rSize = size / 2
|
|
8
11
|
const cSize = rSize + 2
|
|
9
12
|
const dashSize = size + cSize
|
|
@@ -24,9 +27,10 @@ export const Loading: FC<LoadingProps> = memo(({ className, size, emphasis }) =>
|
|
|
24
27
|
height={size + 4}
|
|
25
28
|
width={size + 4}
|
|
26
29
|
role="status"
|
|
27
|
-
aria-
|
|
30
|
+
aria-labelledby={titleId}
|
|
28
31
|
data-ui-component="Loading"
|
|
29
32
|
>
|
|
33
|
+
<title id={titleId}>Loading</title>
|
|
30
34
|
<circle
|
|
31
35
|
fill="none"
|
|
32
36
|
strokeWidth={4}
|
|
@@ -48,4 +52,4 @@ export const Loading: FC<LoadingProps> = memo(({ className, size, emphasis }) =>
|
|
|
48
52
|
/>
|
|
49
53
|
</svg>
|
|
50
54
|
)
|
|
51
|
-
}
|
|
55
|
+
}
|
|
@@ -19,10 +19,6 @@ export const meta: HeliosAttributeMeta<MasonryBaseProps> = {
|
|
|
19
19
|
type: "[number, number, number]",
|
|
20
20
|
},
|
|
21
21
|
gap: {
|
|
22
|
-
type: "[
|
|
23
|
-
},
|
|
24
|
-
useBalancedLayout: {
|
|
25
|
-
type: "boolean",
|
|
26
|
-
isOptional: true,
|
|
22
|
+
type: "[HeliosScaleType, HeliosScaleType, HeliosScaleType]",
|
|
27
23
|
},
|
|
28
24
|
}
|
|
@@ -1,24 +1,37 @@
|
|
|
1
1
|
"use client"
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import { Children, type ReactElement, type FC } from "react"
|
|
3
|
+
import { type FC, useMemo } from "react"
|
|
5
4
|
import type { MasonryProps } from "./Masonry.types"
|
|
6
5
|
|
|
7
|
-
export const Masonry: FC<MasonryProps> = ({
|
|
6
|
+
export const Masonry: FC<MasonryProps> = ({
|
|
7
|
+
children,
|
|
8
|
+
columns = [1, 2, 3],
|
|
9
|
+
gap = [2, 4, 6],
|
|
10
|
+
breakpoints = [0, 640, 960],
|
|
11
|
+
}) => {
|
|
12
|
+
const id: string = useMemo(() => `masonry-${Math.random().toString(36).slice(2, 8)}`, [])
|
|
13
|
+
|
|
8
14
|
if (!children) return null
|
|
9
15
|
|
|
10
|
-
const
|
|
16
|
+
const mediaStyles = `
|
|
17
|
+
.${id} { column-count: ${columns[0]}; column-gap: ${gap[0]}px; }
|
|
18
|
+
.${id} > * { break-inside: avoid; margin-bottom: ${gap[0]}px; }
|
|
19
|
+
|
|
20
|
+
@media (min-width: ${breakpoints[1]}px) {
|
|
21
|
+
.${id} { column-count: ${columns[1]}; column-gap: ${gap[1]}px; }
|
|
22
|
+
.${id} > * { margin-bottom: ${gap[1]}px; }
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
@media (min-width: ${breakpoints[2]}px) {
|
|
26
|
+
.${id} { column-count: ${columns[2]}; column-gap: ${gap[2]}px; }
|
|
27
|
+
.${id} > * { margin-bottom: ${gap[2]}px; }
|
|
28
|
+
}
|
|
29
|
+
`
|
|
11
30
|
|
|
12
31
|
return (
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
columns: columns,
|
|
18
|
-
gap: gap,
|
|
19
|
-
media: breakpoints,
|
|
20
|
-
useBalancedLayout: Boolean(useBalancedLayout),
|
|
21
|
-
}}
|
|
22
|
-
/>
|
|
32
|
+
<>
|
|
33
|
+
<style dangerouslySetInnerHTML={{ __html: mediaStyles }} />
|
|
34
|
+
<div className={id}>{children}</div>
|
|
35
|
+
</>
|
|
23
36
|
)
|
|
24
37
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { PropsWithChildren } from "react"
|
|
2
|
+
import type { HeliosScaleType } from "../../types/scale"
|
|
2
3
|
|
|
3
4
|
export interface MasonryBaseProps {
|
|
4
|
-
columns
|
|
5
|
-
gap
|
|
6
|
-
breakpoints
|
|
7
|
-
useBalancedLayout?: boolean
|
|
5
|
+
columns?: [number, number, number]
|
|
6
|
+
gap?: [HeliosScaleType, HeliosScaleType, HeliosScaleType]
|
|
7
|
+
breakpoints?: [number, number, number]
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
export type MasonryProps = PropsWithChildren<MasonryBaseProps>
|
|
@@ -18,7 +18,7 @@ export const Overlay: FC<OverlayProps> = ({ onClose, isCentered, children, isOpe
|
|
|
18
18
|
})
|
|
19
19
|
|
|
20
20
|
return (
|
|
21
|
-
<section className={styles.overlay} data-ui-component="Overlay">
|
|
21
|
+
<section className={styles.overlay} role="dialog" aria-modal="true" data-ui-component="Overlay">
|
|
22
22
|
<div className={contentClasses}>{children}</div>
|
|
23
23
|
<div className={styles.overlay__layer} onClick={hideFunction} />
|
|
24
24
|
</section>
|
package/components/Pie/Pie.tsx
CHANGED
|
@@ -19,6 +19,7 @@ export const Pie: FC<PieProps> = ({ color, size, data }) => {
|
|
|
19
19
|
aria-label={`Pie chart with ${data.length} segments`}
|
|
20
20
|
data-ui-component="Pie"
|
|
21
21
|
>
|
|
22
|
+
<title>{data.map((item) => `${item.name}: ${item.value}`).join(", ")}</title>
|
|
22
23
|
{data.map((item, index) => {
|
|
23
24
|
const thisData: Array<PieItem> = data.slice(0, index)
|
|
24
25
|
const thisSize = thisData?.reduce((a, b) => a + b.value, 0)
|
|
@@ -4,7 +4,13 @@ import { getColorClasses } from "../../utils/colors"
|
|
|
4
4
|
import type { FC } from "react"
|
|
5
5
|
import type { ProgressProps } from "./Progress.types"
|
|
6
6
|
|
|
7
|
-
export const Progress: FC<ProgressProps> = ({
|
|
7
|
+
export const Progress: FC<ProgressProps> = ({
|
|
8
|
+
"aria-label": ariaLabel,
|
|
9
|
+
color = "blue",
|
|
10
|
+
isSmall,
|
|
11
|
+
max = 100,
|
|
12
|
+
value = 0,
|
|
13
|
+
}) => {
|
|
8
14
|
const progressColorClasses: Array<string> = getColorClasses(color, "dark")
|
|
9
15
|
const progressClasses: string = getClasses(styles.progress, ...progressColorClasses, {
|
|
10
16
|
[styles.progressSmall]: isSmall,
|
|
@@ -15,7 +21,7 @@ export const Progress: FC<ProgressProps> = ({ color = "blue", isSmall, max = 100
|
|
|
15
21
|
className={progressClasses}
|
|
16
22
|
max={max}
|
|
17
23
|
value={value}
|
|
18
|
-
aria-label={`Progress: ${value} of ${max}`}
|
|
24
|
+
aria-label={ariaLabel ?? `Progress: ${value} of ${max}`}
|
|
19
25
|
data-ui-component="Progress"
|
|
20
26
|
/>
|
|
21
27
|
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client"
|
|
2
2
|
|
|
3
|
-
import { useId
|
|
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"
|
|
@@ -9,69 +9,63 @@ import styles from "./Radio.module.css"
|
|
|
9
9
|
import type { FC } from "react"
|
|
10
10
|
import type { RadioProps } from "./Radio.types"
|
|
11
11
|
|
|
12
|
-
export const Radio: FC<RadioProps> =
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
const radioId: string = useId()
|
|
12
|
+
export const Radio: FC<RadioProps> = ({
|
|
13
|
+
color = "gray",
|
|
14
|
+
isChecked,
|
|
15
|
+
isVertical,
|
|
16
|
+
isLabelHidden = false,
|
|
17
|
+
isSmall,
|
|
18
|
+
description,
|
|
19
|
+
isDisabled,
|
|
20
|
+
isRequired,
|
|
21
|
+
onChange,
|
|
22
|
+
label,
|
|
23
|
+
name,
|
|
24
|
+
value,
|
|
25
|
+
}) => {
|
|
26
|
+
const radioId: string = useId()
|
|
28
27
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
28
|
+
const colorClasses = getColorClasses(color, "dark")
|
|
29
|
+
const radioClasses = getClasses(styles.radio, ...colorClasses, {
|
|
30
|
+
[styles.radioDisabled]: isDisabled,
|
|
31
|
+
[styles.radioSmall]: isSmall,
|
|
32
|
+
})
|
|
34
33
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
34
|
+
const radioLabelClasses = getClasses(styles.radio__radioLabel, "flex gap-4", {
|
|
35
|
+
"flex-x-center flex-column": isVertical,
|
|
36
|
+
"flex-y-center": !isVertical,
|
|
37
|
+
})
|
|
39
38
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
>
|
|
64
|
-
{
|
|
39
|
+
return (
|
|
40
|
+
<div className={radioClasses} data-ui-component="Radio">
|
|
41
|
+
<label className={radioLabelClasses} htmlFor={radioId}>
|
|
42
|
+
<span className={styles.radio__mark}>
|
|
43
|
+
<input
|
|
44
|
+
type="radio"
|
|
45
|
+
checked={isChecked}
|
|
46
|
+
onChange={onChange}
|
|
47
|
+
disabled={isDisabled}
|
|
48
|
+
required={isRequired}
|
|
49
|
+
aria-label={isLabelHidden ? label : undefined}
|
|
50
|
+
id={radioId}
|
|
51
|
+
name={name}
|
|
52
|
+
value={value}
|
|
53
|
+
/>
|
|
54
|
+
<div className={styles.radio__radioMark} />
|
|
55
|
+
</span>
|
|
56
|
+
{!isLabelHidden && (
|
|
57
|
+
<Flex isColumn={true}>
|
|
58
|
+
<Text type={isSmall ? "tiny" : "small"} fontWeight="medium" emphasis={isDisabled ? "tertiary" : "inherit"}>
|
|
59
|
+
{label}
|
|
60
|
+
</Text>
|
|
61
|
+
{description && (
|
|
62
|
+
<Text type="tiny" emphasis="secondary">
|
|
63
|
+
{description}
|
|
65
64
|
</Text>
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
</label>
|
|
74
|
-
</div>
|
|
75
|
-
)
|
|
76
|
-
},
|
|
77
|
-
)
|
|
65
|
+
)}
|
|
66
|
+
</Flex>
|
|
67
|
+
)}
|
|
68
|
+
</label>
|
|
69
|
+
</div>
|
|
70
|
+
)
|
|
71
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { HeliosAttributeMeta } from "../../types/meta"
|
|
2
|
+
import type { RangeProps } from "./Range.types"
|
|
3
|
+
|
|
4
|
+
export const meta: HeliosAttributeMeta<RangeProps> = {
|
|
5
|
+
_patterns: [
|
|
6
|
+
{
|
|
7
|
+
id: "ui-range-default",
|
|
8
|
+
description: "default",
|
|
9
|
+
content: `<Range value={3} min={0} max={100} step={1} label={LABEL} onChange={FUNCTION} isDisabled={IS_DISABLED}/>`,
|
|
10
|
+
},
|
|
11
|
+
],
|
|
12
|
+
_status: "experimental",
|
|
13
|
+
_category: "core",
|
|
14
|
+
isDisabled: { type: "boolean", isOptional: true },
|
|
15
|
+
isLabelHidden: { type: "boolean", isOptional: true },
|
|
16
|
+
label: { type: "string" },
|
|
17
|
+
max: { type: "number", isOptional: true, default: 100 },
|
|
18
|
+
min: { type: "number", isOptional: true, default: 0 },
|
|
19
|
+
name: { type: "string", isOptional: true },
|
|
20
|
+
onChange: {
|
|
21
|
+
type: "(event?: ChangeEvent<HTMLInputElement>) => unknown",
|
|
22
|
+
isOptional: true,
|
|
23
|
+
},
|
|
24
|
+
step: { type: "number", isOptional: true, default: 1 },
|
|
25
|
+
value: { type: "number", isOptional: true },
|
|
26
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
.range {
|
|
2
|
+
width: 100%;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
.range__input {
|
|
6
|
+
width: 100%;
|
|
7
|
+
height: 4px;
|
|
8
|
+
margin: var(--space-3) 0;
|
|
9
|
+
|
|
10
|
+
appearance: none;
|
|
11
|
+
background: var(--ui-bg-soft-gray);
|
|
12
|
+
border-radius: var(--radius-sm);
|
|
13
|
+
outline: none;
|
|
14
|
+
transition: all var(--speed-sm) var(--ease-in-out-sine);
|
|
15
|
+
cursor: pointer;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.range__input::-webkit-slider-thumb {
|
|
19
|
+
appearance: none;
|
|
20
|
+
width: 18px;
|
|
21
|
+
height: 18px;
|
|
22
|
+
|
|
23
|
+
background: var(--ui-text-primary);
|
|
24
|
+
border: 2px solid var(--ui-bg-primary);
|
|
25
|
+
border-radius: var(--radius-round);
|
|
26
|
+
box-shadow: 0 0 0 1px var(--ui-border-primary);
|
|
27
|
+
cursor: pointer;
|
|
28
|
+
transition: all var(--speed-sm) var(--ease-in-out-sine);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.range__input::-moz-range-thumb {
|
|
32
|
+
width: 18px;
|
|
33
|
+
height: 18px;
|
|
34
|
+
|
|
35
|
+
background: var(--ui-text-primary);
|
|
36
|
+
border: 2px solid var(--ui-bg-primary);
|
|
37
|
+
border-radius: var(--radius-round);
|
|
38
|
+
box-shadow: 0 0 0 1px var(--ui-border-primary);
|
|
39
|
+
cursor: pointer;
|
|
40
|
+
transition: all var(--speed-sm) var(--ease-in-out-sine);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.range__input:focus::-webkit-slider-thumb {
|
|
44
|
+
box-shadow: 0 0 0 4px var(--ui-border-secondary);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.range__input:focus::-moz-range-thumb {
|
|
48
|
+
box-shadow: 0 0 0 4px var(--ui-border-secondary);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.range__input::-webkit-slider-runnable-track {
|
|
52
|
+
height: 4px;
|
|
53
|
+
|
|
54
|
+
background: var(--ui-bg-soft-gray);
|
|
55
|
+
border-radius: var(--radius-sm);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.range__input::-moz-range-track {
|
|
59
|
+
height: 4px;
|
|
60
|
+
|
|
61
|
+
background: var(--ui-bg-soft-gray);
|
|
62
|
+
border-radius: var(--radius-sm);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.rangeDisabled {
|
|
66
|
+
pointer-events: none;
|
|
67
|
+
opacity: 0.5;
|
|
68
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { useId } from "react"
|
|
4
|
+
import { getClasses } from "@heliosgraphics/utils"
|
|
5
|
+
import { InputLabel } from "../shared/InputLabel"
|
|
6
|
+
import styles from "./Range.module.css"
|
|
7
|
+
import type { FC } from "react"
|
|
8
|
+
import type { RangeProps } from "./Range.types"
|
|
9
|
+
|
|
10
|
+
export const Range: FC<RangeProps> = ({
|
|
11
|
+
isDisabled,
|
|
12
|
+
isLabelHidden = false,
|
|
13
|
+
label,
|
|
14
|
+
max = 100,
|
|
15
|
+
min = 0,
|
|
16
|
+
name,
|
|
17
|
+
onChange,
|
|
18
|
+
step = 1,
|
|
19
|
+
value,
|
|
20
|
+
}) => {
|
|
21
|
+
const htmlFor: string = useId()
|
|
22
|
+
|
|
23
|
+
const rangeClasses: string = getClasses(styles.range, {
|
|
24
|
+
[styles.rangeDisabled]: isDisabled,
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<div className={rangeClasses} data-ui-component="Range">
|
|
29
|
+
<InputLabel id={htmlFor} label={label} isDisabled={!!isDisabled} isHidden={!!isLabelHidden} />
|
|
30
|
+
<input
|
|
31
|
+
className={styles.range__input}
|
|
32
|
+
disabled={isDisabled}
|
|
33
|
+
id={htmlFor}
|
|
34
|
+
max={max}
|
|
35
|
+
min={min}
|
|
36
|
+
name={name}
|
|
37
|
+
onChange={onChange}
|
|
38
|
+
step={step}
|
|
39
|
+
type="range"
|
|
40
|
+
value={value}
|
|
41
|
+
aria-valuemin={min}
|
|
42
|
+
aria-valuemax={max}
|
|
43
|
+
aria-valuenow={value}
|
|
44
|
+
/>
|
|
45
|
+
</div>
|
|
46
|
+
)
|
|
47
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ChangeEventHandler } from "react"
|
|
2
|
+
|
|
3
|
+
export interface RangeProps {
|
|
4
|
+
isDisabled?: boolean
|
|
5
|
+
isLabelHidden?: boolean
|
|
6
|
+
label: string
|
|
7
|
+
max?: number
|
|
8
|
+
min?: number
|
|
9
|
+
name?: string
|
|
10
|
+
onChange?: ChangeEventHandler<HTMLInputElement>
|
|
11
|
+
step?: number
|
|
12
|
+
value?: number
|
|
13
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Range } from "./Range"
|