@utilitywarehouse/hearth-react-native 0.23.0-test-list → 0.24.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/.turbo/turbo-build.log +4 -5
- package/.turbo/turbo-lint.log +72 -0
- package/CHANGELOG.md +36 -0
- package/build/components/List/List.js +2 -2
- package/build/components/Modal/Modal.js +5 -4
- package/build/components/Modal/Modal.props.d.ts +10 -4
- package/build/components/index.d.ts +1 -0
- package/build/components/index.js +1 -0
- package/docs/components/AllComponents.web.tsx +6 -0
- package/package.json +2 -2
- package/src/components/List/List.tsx +4 -5
- package/src/components/Modal/Modal.props.ts +13 -4
- package/src/components/Modal/Modal.stories.tsx +1 -1
- package/src/components/Modal/Modal.tsx +28 -11
- package/src/components/ProgressBar/ProgressBar.docs.mdx +90 -0
- package/src/components/ProgressBar/ProgressBar.figma.tsx +79 -0
- package/src/components/ProgressBar/ProgressBar.props.ts +60 -0
- package/src/components/ProgressBar/ProgressBar.stories.tsx +117 -0
- package/src/components/ProgressBar/ProgressBar.tsx +74 -0
- package/src/components/ProgressBar/ProgressBarCircular.tsx +181 -0
- package/src/components/ProgressBar/ProgressBarLinear.tsx +127 -0
- package/src/components/ProgressBar/index.ts +7 -0
- package/src/components/index.ts +1 -0
- package/build/components/SegmentedControl/SegmentedControl.context.d.ts +0 -14
- package/build/components/SegmentedControl/SegmentedControl.context.js +0 -9
- package/build/components/SegmentedControl/SegmentedControl.d.ts +0 -6
- package/build/components/SegmentedControl/SegmentedControl.js +0 -199
- package/build/components/SegmentedControl/SegmentedControl.props.d.ts +0 -18
- package/build/components/SegmentedControl/SegmentedControl.props.js +0 -1
- package/build/components/SegmentedControl/SegmentedControlOption.d.ts +0 -18
- package/build/components/SegmentedControl/SegmentedControlOption.js +0 -144
- package/build/components/SegmentedControl/SegmentedControlOption.props.d.ts +0 -14
- package/build/components/SegmentedControl/SegmentedControlOption.props.js +0 -1
- package/build/components/SegmentedControl/index.d.ts +0 -4
- package/build/components/SegmentedControl/index.js +0 -2
- package/build/components/TimePicker/TimePicker.d.ts +0 -6
- package/build/components/TimePicker/TimePicker.js +0 -78
- package/build/components/TimePicker/TimePicker.props.d.ts +0 -45
- package/build/components/TimePicker/TimePicker.props.js +0 -1
- package/build/components/TimePicker/TimePickerView.d.ts +0 -12
- package/build/components/TimePicker/TimePickerView.js +0 -130
- package/build/components/TimePicker/TimePickerWheel.d.ts +0 -8
- package/build/components/TimePicker/TimePickerWheel.js +0 -86
- package/build/components/TimePicker/TimePickerWheel.web.d.ts +0 -8
- package/build/components/TimePicker/TimePickerWheel.web.js +0 -122
- package/build/components/TimePicker/index.d.ts +0 -6
- package/build/components/TimePicker/index.js +0 -3
- package/build/components/TimePickerInput/TimePickerInput.d.ts +0 -6
- package/build/components/TimePickerInput/TimePickerInput.js +0 -127
- package/build/components/TimePickerInput/TimePickerInput.props.d.ts +0 -52
- package/build/components/TimePickerInput/TimePickerInput.props.js +0 -1
- package/build/components/TimePickerInput/TimePickerInputDoneButton.d.ts +0 -8
- package/build/components/TimePickerInput/TimePickerInputDoneButton.js +0 -19
- package/build/components/TimePickerInput/TimePickerInputDoneButton.web.d.ts +0 -5
- package/build/components/TimePickerInput/TimePickerInputDoneButton.web.js +0 -5
- package/build/components/TimePickerInput/index.d.ts +0 -2
- package/build/components/TimePickerInput/index.js +0 -1
- package/build/components/VerificationInput/VerificationInput.utils.d.ts +0 -8
- package/build/components/VerificationInput/VerificationInput.utils.js +0 -17
- package/build/components/VerificationInput/VerificationInput.utils.test.d.ts +0 -1
- package/build/components/VerificationInput/VerificationInput.utils.test.js +0 -36
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { ViewProps } from 'react-native';
|
|
2
|
+
|
|
3
|
+
export type ProgressBarVariant = 'linear' | 'circular';
|
|
4
|
+
export type ProgressBarColorScheme = 'default' | 'success' | 'danger';
|
|
5
|
+
export type ProgressBarSize = 'sm' | 'md';
|
|
6
|
+
|
|
7
|
+
export interface ProgressBarProps extends ViewProps {
|
|
8
|
+
variant?: ProgressBarVariant;
|
|
9
|
+
/**
|
|
10
|
+
* Set the visual appearance.
|
|
11
|
+
* @default 'default'
|
|
12
|
+
*/
|
|
13
|
+
colorScheme?: ProgressBarColorScheme;
|
|
14
|
+
/**
|
|
15
|
+
* Sets the circular variant size. Does not affect the appearance of the linear variant.
|
|
16
|
+
* @default 'md'
|
|
17
|
+
*/
|
|
18
|
+
size?: ProgressBarSize;
|
|
19
|
+
/**
|
|
20
|
+
* The current progress value.
|
|
21
|
+
*/
|
|
22
|
+
value: number;
|
|
23
|
+
/**
|
|
24
|
+
* The minimum value.
|
|
25
|
+
* @default 0
|
|
26
|
+
*/
|
|
27
|
+
min?: number;
|
|
28
|
+
/**
|
|
29
|
+
* The maximum value.
|
|
30
|
+
* @default 100
|
|
31
|
+
*/
|
|
32
|
+
max?: number;
|
|
33
|
+
/**
|
|
34
|
+
* Required text label for the progress bar.
|
|
35
|
+
*/
|
|
36
|
+
label: string;
|
|
37
|
+
/**
|
|
38
|
+
* Visually hide the label and value text.
|
|
39
|
+
*/
|
|
40
|
+
hideLabel?: boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Override the default percentage value label formatting.
|
|
43
|
+
*/
|
|
44
|
+
formatValueText?: (value: number, meta: { min: number; max: number; percent: number }) => string;
|
|
45
|
+
/**
|
|
46
|
+
* A human-readable text alternative for the current value.
|
|
47
|
+
*/
|
|
48
|
+
'aria-valuetext'?: string;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface ProgressBarInternalProps {
|
|
52
|
+
percent: number;
|
|
53
|
+
label: string;
|
|
54
|
+
valueText: string;
|
|
55
|
+
hideLabel?: boolean;
|
|
56
|
+
colorScheme: ProgressBarColorScheme;
|
|
57
|
+
size: ProgressBarSize;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export default ProgressBarProps;
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { Meta, StoryObj } from '@storybook/react-native';
|
|
2
|
+
import type { ComponentProps } from 'react';
|
|
3
|
+
import { ProgressBar } from '.';
|
|
4
|
+
import { VariantTitle } from '../../../docs/components';
|
|
5
|
+
import { Box } from '../Box';
|
|
6
|
+
|
|
7
|
+
const meta = {
|
|
8
|
+
title: 'Stories / ProgressBar',
|
|
9
|
+
component: ProgressBar,
|
|
10
|
+
parameters: {
|
|
11
|
+
layout: 'centered',
|
|
12
|
+
},
|
|
13
|
+
argTypes: {
|
|
14
|
+
variant: {
|
|
15
|
+
options: ['linear', 'circular'],
|
|
16
|
+
control: 'select',
|
|
17
|
+
description: 'The progress bar variant.',
|
|
18
|
+
},
|
|
19
|
+
colorScheme: {
|
|
20
|
+
options: ['default', 'success', 'danger'],
|
|
21
|
+
control: 'select',
|
|
22
|
+
description: 'The color scheme for the progress indicator.',
|
|
23
|
+
},
|
|
24
|
+
size: {
|
|
25
|
+
options: ['sm', 'md'],
|
|
26
|
+
control: 'select',
|
|
27
|
+
description: 'The circular size. Only applies to the circular variant.',
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
args: {
|
|
31
|
+
variant: 'linear',
|
|
32
|
+
colorScheme: 'default',
|
|
33
|
+
size: 'md',
|
|
34
|
+
value: 35,
|
|
35
|
+
min: 0,
|
|
36
|
+
max: 100,
|
|
37
|
+
label: 'Progress',
|
|
38
|
+
},
|
|
39
|
+
} satisfies Meta<typeof ProgressBar>;
|
|
40
|
+
|
|
41
|
+
export default meta;
|
|
42
|
+
|
|
43
|
+
type Story = StoryObj<typeof meta>;
|
|
44
|
+
type ProgressBarStoryArgs = ComponentProps<typeof ProgressBar>;
|
|
45
|
+
|
|
46
|
+
export const Playground: Story = {};
|
|
47
|
+
|
|
48
|
+
export const Variants: Story = {
|
|
49
|
+
args: {
|
|
50
|
+
value: 55,
|
|
51
|
+
label: 'Downloads',
|
|
52
|
+
},
|
|
53
|
+
render: (args: ProgressBarStoryArgs) => (
|
|
54
|
+
<Box gap="300" style={{ width: 260 }}>
|
|
55
|
+
<VariantTitle title="Linear">
|
|
56
|
+
<ProgressBar {...args} variant="linear" />
|
|
57
|
+
</VariantTitle>
|
|
58
|
+
<VariantTitle title="Circular">
|
|
59
|
+
<ProgressBar {...args} variant="circular" />
|
|
60
|
+
</VariantTitle>
|
|
61
|
+
</Box>
|
|
62
|
+
),
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export const ColorSchemes: Story = {
|
|
66
|
+
args: {
|
|
67
|
+
value: 72,
|
|
68
|
+
label: 'Storage',
|
|
69
|
+
variant: 'linear',
|
|
70
|
+
},
|
|
71
|
+
render: (args: ProgressBarStoryArgs) => (
|
|
72
|
+
<Box gap="300" style={{ width: 260 }}>
|
|
73
|
+
<VariantTitle title="Default">
|
|
74
|
+
<ProgressBar {...args} colorScheme="default" />
|
|
75
|
+
</VariantTitle>
|
|
76
|
+
<VariantTitle title="Success">
|
|
77
|
+
<ProgressBar {...args} colorScheme="success" />
|
|
78
|
+
</VariantTitle>
|
|
79
|
+
<VariantTitle title="Danger">
|
|
80
|
+
<ProgressBar {...args} colorScheme="danger" />
|
|
81
|
+
</VariantTitle>
|
|
82
|
+
</Box>
|
|
83
|
+
),
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
export const CircularSizes: Story = {
|
|
87
|
+
args: {
|
|
88
|
+
value: 65,
|
|
89
|
+
label: 'Usage',
|
|
90
|
+
variant: 'circular',
|
|
91
|
+
},
|
|
92
|
+
render: (args: ProgressBarStoryArgs) => (
|
|
93
|
+
<Box gap="300">
|
|
94
|
+
<VariantTitle title="Small">
|
|
95
|
+
<ProgressBar {...args} size="sm" />
|
|
96
|
+
</VariantTitle>
|
|
97
|
+
<VariantTitle title="Medium">
|
|
98
|
+
<ProgressBar {...args} size="md" />
|
|
99
|
+
</VariantTitle>
|
|
100
|
+
</Box>
|
|
101
|
+
),
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
export const CustomValueLabels: Story = {
|
|
105
|
+
args: {
|
|
106
|
+
value: 68,
|
|
107
|
+
max: 100,
|
|
108
|
+
label: 'Data allowance',
|
|
109
|
+
variant: 'linear',
|
|
110
|
+
formatValueText: (value: number, { max }: { max: number }) => `${max - value}GB remaining`,
|
|
111
|
+
},
|
|
112
|
+
render: (args: ProgressBarStoryArgs) => (
|
|
113
|
+
<Box style={{ width: 260 }}>
|
|
114
|
+
<ProgressBar {...args} />
|
|
115
|
+
</Box>
|
|
116
|
+
),
|
|
117
|
+
};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { View } from 'react-native';
|
|
2
|
+
import type ProgressBarProps from './ProgressBar.props';
|
|
3
|
+
import ProgressBarCircular from './ProgressBarCircular';
|
|
4
|
+
import ProgressBarLinear from './ProgressBarLinear';
|
|
5
|
+
|
|
6
|
+
const clampValue = (value: number, min: number, max: number) => {
|
|
7
|
+
if (max <= min) return min;
|
|
8
|
+
return Math.min(Math.max(value, min), max);
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const valueToPercent = (value: number, min: number, max: number) => {
|
|
12
|
+
const range = max - min;
|
|
13
|
+
if (range <= 0) return 0;
|
|
14
|
+
return ((value - min) / range) * 100;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const ProgressBar = ({
|
|
18
|
+
variant = 'linear',
|
|
19
|
+
colorScheme = 'default',
|
|
20
|
+
size = 'md',
|
|
21
|
+
value,
|
|
22
|
+
min = 0,
|
|
23
|
+
max = 100,
|
|
24
|
+
label,
|
|
25
|
+
hideLabel,
|
|
26
|
+
formatValueText,
|
|
27
|
+
'aria-valuetext': ariaValueText,
|
|
28
|
+
accessibilityLabel,
|
|
29
|
+
...rest
|
|
30
|
+
}: ProgressBarProps) => {
|
|
31
|
+
const effectiveValue =
|
|
32
|
+
colorScheme === 'success' && !formatValueText ? max : clampValue(value, min, max);
|
|
33
|
+
const percentValue = valueToPercent(effectiveValue, min, max);
|
|
34
|
+
const clampedPercent = Math.max(0, Math.min(100, percentValue));
|
|
35
|
+
const valueText = formatValueText
|
|
36
|
+
? formatValueText(effectiveValue, { min, max, percent: clampedPercent })
|
|
37
|
+
: `${Math.round(clampedPercent)}%`;
|
|
38
|
+
const valueTextForAria = ariaValueText ?? valueText;
|
|
39
|
+
|
|
40
|
+
const internalProps = {
|
|
41
|
+
percent: clampedPercent,
|
|
42
|
+
label,
|
|
43
|
+
valueText,
|
|
44
|
+
hideLabel,
|
|
45
|
+
colorScheme,
|
|
46
|
+
size,
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<View
|
|
51
|
+
{...rest}
|
|
52
|
+
accessible
|
|
53
|
+
role="progressbar"
|
|
54
|
+
accessibilityRole="progressbar"
|
|
55
|
+
accessibilityLabel={accessibilityLabel ?? label}
|
|
56
|
+
accessibilityValue={{ min, max, now: effectiveValue, text: valueTextForAria }}
|
|
57
|
+
aria-valuenow={effectiveValue}
|
|
58
|
+
aria-valuemin={min}
|
|
59
|
+
aria-valuemax={max}
|
|
60
|
+
aria-valuetext={valueTextForAria}
|
|
61
|
+
data-colorscheme={colorScheme}
|
|
62
|
+
>
|
|
63
|
+
{variant === 'circular' ? (
|
|
64
|
+
<ProgressBarCircular {...internalProps} />
|
|
65
|
+
) : (
|
|
66
|
+
<ProgressBarLinear {...internalProps} />
|
|
67
|
+
)}
|
|
68
|
+
</View>
|
|
69
|
+
);
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
ProgressBar.displayName = 'ProgressBar';
|
|
73
|
+
|
|
74
|
+
export default ProgressBar;
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import React, { useEffect, useRef } from 'react';
|
|
2
|
+
import { Text, View } from 'react-native';
|
|
3
|
+
import Animated, {
|
|
4
|
+
Easing,
|
|
5
|
+
useAnimatedProps,
|
|
6
|
+
useReducedMotion,
|
|
7
|
+
useSharedValue,
|
|
8
|
+
withTiming,
|
|
9
|
+
} from 'react-native-reanimated';
|
|
10
|
+
import { Circle, G, Svg } from 'react-native-svg';
|
|
11
|
+
import { StyleSheet } from 'react-native-unistyles';
|
|
12
|
+
import useTheme from '../../hooks/useTheme';
|
|
13
|
+
import { BodyText } from '../BodyText';
|
|
14
|
+
import type { ProgressBarInternalProps } from './ProgressBar.props';
|
|
15
|
+
|
|
16
|
+
const AnimatedCircle = Animated.createAnimatedComponent(Circle as React.ComponentType<any>);
|
|
17
|
+
|
|
18
|
+
const ProgressBarCircular = ({
|
|
19
|
+
percent,
|
|
20
|
+
label,
|
|
21
|
+
valueText,
|
|
22
|
+
hideLabel,
|
|
23
|
+
colorScheme,
|
|
24
|
+
size,
|
|
25
|
+
}: ProgressBarInternalProps) => {
|
|
26
|
+
const { components } = useTheme();
|
|
27
|
+
const isReducedMotion = useReducedMotion();
|
|
28
|
+
const progress = useSharedValue(0);
|
|
29
|
+
const hasMountedRef = useRef(false);
|
|
30
|
+
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
const target = Math.max(0, Math.min(100, percent)) / 100;
|
|
33
|
+
if (isReducedMotion) {
|
|
34
|
+
progress.value = target;
|
|
35
|
+
hasMountedRef.current = true;
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (!hasMountedRef.current) {
|
|
40
|
+
progress.value = target;
|
|
41
|
+
hasMountedRef.current = true;
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
progress.value = withTiming(target, { duration: 300, easing: Easing.out(Easing.ease) });
|
|
46
|
+
}, [percent, isReducedMotion, progress]);
|
|
47
|
+
|
|
48
|
+
const circularTokens = components.progressBar.circular[size];
|
|
49
|
+
const barWidth = 'bar' in circularTokens ? circularTokens.bar.width : circularTokens.barWidth;
|
|
50
|
+
const diameter = circularTokens.height;
|
|
51
|
+
const radius = (diameter - barWidth) / 2;
|
|
52
|
+
const circumference = 2 * Math.PI * radius;
|
|
53
|
+
|
|
54
|
+
const animatedCircleProps = useAnimatedProps(() => ({
|
|
55
|
+
strokeDashoffset: circumference * (1 - progress.value),
|
|
56
|
+
}));
|
|
57
|
+
|
|
58
|
+
const indicatorColor =
|
|
59
|
+
colorScheme === 'success'
|
|
60
|
+
? components.progressBar.progress.successColor
|
|
61
|
+
: colorScheme === 'danger'
|
|
62
|
+
? components.progressBar.progress.dangerColor
|
|
63
|
+
: components.progressBar.progress.defaultColor;
|
|
64
|
+
|
|
65
|
+
styles.useVariants({ size });
|
|
66
|
+
|
|
67
|
+
return (
|
|
68
|
+
<View style={styles.container}>
|
|
69
|
+
<Svg
|
|
70
|
+
width={diameter}
|
|
71
|
+
height={diameter}
|
|
72
|
+
viewBox={`0 0 ${diameter} ${diameter}`}
|
|
73
|
+
style={styles.svg}
|
|
74
|
+
>
|
|
75
|
+
<G origin={`${diameter / 2}, ${diameter / 2}`} rotation={-90}>
|
|
76
|
+
<Circle
|
|
77
|
+
cx="50%"
|
|
78
|
+
cy="50%"
|
|
79
|
+
r={radius}
|
|
80
|
+
stroke={components.progressBar.barColor}
|
|
81
|
+
strokeWidth={barWidth}
|
|
82
|
+
fill="transparent"
|
|
83
|
+
/>
|
|
84
|
+
<AnimatedCircle
|
|
85
|
+
cx="50%"
|
|
86
|
+
cy="50%"
|
|
87
|
+
r={radius}
|
|
88
|
+
stroke={indicatorColor}
|
|
89
|
+
strokeWidth={barWidth}
|
|
90
|
+
fill="transparent"
|
|
91
|
+
strokeLinecap="round"
|
|
92
|
+
strokeDasharray={circumference}
|
|
93
|
+
animatedProps={animatedCircleProps}
|
|
94
|
+
/>
|
|
95
|
+
</G>
|
|
96
|
+
</Svg>
|
|
97
|
+
<View style={styles.content}>
|
|
98
|
+
<Text style={styles.valueText}>{valueText}</Text>
|
|
99
|
+
{!hideLabel && size === 'md' ? (
|
|
100
|
+
<BodyText style={styles.label} size="md" weight="semibold">
|
|
101
|
+
{label}
|
|
102
|
+
</BodyText>
|
|
103
|
+
) : null}
|
|
104
|
+
</View>
|
|
105
|
+
</View>
|
|
106
|
+
);
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
ProgressBarCircular.displayName = 'ProgressBarCircular';
|
|
110
|
+
|
|
111
|
+
const styles = StyleSheet.create(theme => ({
|
|
112
|
+
container: {
|
|
113
|
+
alignItems: 'center',
|
|
114
|
+
justifyContent: 'center',
|
|
115
|
+
position: 'relative',
|
|
116
|
+
variants: {
|
|
117
|
+
size: {
|
|
118
|
+
md: {
|
|
119
|
+
width: theme.components.progressBar.circular.md.height,
|
|
120
|
+
height: theme.components.progressBar.circular.md.height,
|
|
121
|
+
},
|
|
122
|
+
sm: {
|
|
123
|
+
width: theme.components.progressBar.circular.sm.height,
|
|
124
|
+
height: theme.components.progressBar.circular.sm.height,
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
svg: {
|
|
130
|
+
position: 'absolute',
|
|
131
|
+
top: 0,
|
|
132
|
+
left: 0,
|
|
133
|
+
},
|
|
134
|
+
content: {
|
|
135
|
+
alignItems: 'center',
|
|
136
|
+
justifyContent: 'center',
|
|
137
|
+
_web: {
|
|
138
|
+
position: 'absolute',
|
|
139
|
+
top: 0,
|
|
140
|
+
left: 0,
|
|
141
|
+
width: '100%',
|
|
142
|
+
height: '100%',
|
|
143
|
+
},
|
|
144
|
+
variants: {
|
|
145
|
+
size: {
|
|
146
|
+
md: {
|
|
147
|
+
gap: theme.components.progressBar.circular.md.gap,
|
|
148
|
+
},
|
|
149
|
+
sm: {
|
|
150
|
+
gap: 0,
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
valueText: {
|
|
156
|
+
color: theme.color.text.primary,
|
|
157
|
+
textAlign: 'center',
|
|
158
|
+
variants: {
|
|
159
|
+
size: {
|
|
160
|
+
md: {
|
|
161
|
+
fontFamily: theme.components.progressBar.circular.md.label.fontFamily,
|
|
162
|
+
fontSize: theme.components.progressBar.circular.md.label.fontSize,
|
|
163
|
+
lineHeight: theme.components.progressBar.circular.md.label.lineHeight,
|
|
164
|
+
fontWeight: theme.components.progressBar.circular.md.label.fontWeight,
|
|
165
|
+
},
|
|
166
|
+
sm: {
|
|
167
|
+
fontFamily: theme.typography.mobile.bodyText.fontFamily,
|
|
168
|
+
fontSize: theme.typography.mobile.bodyText.md.fontSize,
|
|
169
|
+
lineHeight: theme.typography.mobile.bodyText.md.lineHeight,
|
|
170
|
+
fontWeight: theme.fontWeight.semibold,
|
|
171
|
+
},
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
label: {
|
|
176
|
+
textAlign: 'center',
|
|
177
|
+
maxWidth: 90,
|
|
178
|
+
},
|
|
179
|
+
}));
|
|
180
|
+
|
|
181
|
+
export default ProgressBarCircular;
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { useEffect, useRef, useState } from 'react';
|
|
2
|
+
import { LayoutChangeEvent, Platform, View } from 'react-native';
|
|
3
|
+
import Animated, {
|
|
4
|
+
Easing,
|
|
5
|
+
useAnimatedStyle,
|
|
6
|
+
useReducedMotion,
|
|
7
|
+
useSharedValue,
|
|
8
|
+
withTiming,
|
|
9
|
+
} from 'react-native-reanimated';
|
|
10
|
+
import { StyleSheet } from 'react-native-unistyles';
|
|
11
|
+
import useTheme from '../../hooks/useTheme';
|
|
12
|
+
import { BodyText } from '../BodyText';
|
|
13
|
+
import type { ProgressBarInternalProps } from './ProgressBar.props';
|
|
14
|
+
|
|
15
|
+
const ProgressBarLinear = ({
|
|
16
|
+
percent,
|
|
17
|
+
label,
|
|
18
|
+
valueText,
|
|
19
|
+
hideLabel,
|
|
20
|
+
colorScheme,
|
|
21
|
+
}: ProgressBarInternalProps) => {
|
|
22
|
+
const { components } = useTheme();
|
|
23
|
+
const isReducedMotion = useReducedMotion();
|
|
24
|
+
const progress = useSharedValue(0);
|
|
25
|
+
const hasMountedRef = useRef(false);
|
|
26
|
+
const [trackWidth, setTrackWidth] = useState(0);
|
|
27
|
+
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
const target = Math.max(0, Math.min(100, percent)) / 100;
|
|
30
|
+
if (isReducedMotion) {
|
|
31
|
+
progress.value = target;
|
|
32
|
+
hasMountedRef.current = true;
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (!hasMountedRef.current) {
|
|
37
|
+
progress.value = target;
|
|
38
|
+
hasMountedRef.current = true;
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
progress.value = withTiming(target, { duration: 300, easing: Easing.out(Easing.ease) });
|
|
43
|
+
}, [percent, isReducedMotion, progress]);
|
|
44
|
+
|
|
45
|
+
const animatedStyle = useAnimatedStyle(() => ({
|
|
46
|
+
width: trackWidth * progress.value,
|
|
47
|
+
}));
|
|
48
|
+
|
|
49
|
+
const indicatorColor =
|
|
50
|
+
colorScheme === 'success'
|
|
51
|
+
? components.progressBar.progress.successColor
|
|
52
|
+
: colorScheme === 'danger'
|
|
53
|
+
? components.progressBar.progress.dangerColor
|
|
54
|
+
: components.progressBar.progress.defaultColor;
|
|
55
|
+
|
|
56
|
+
const handleTrackLayout = (event: LayoutChangeEvent) => {
|
|
57
|
+
setTrackWidth(event.nativeEvent.layout.width);
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<View style={styles.container}>
|
|
62
|
+
{!hideLabel && (
|
|
63
|
+
<View style={styles.header}>
|
|
64
|
+
<BodyText size="md" weight="semibold" style={styles.label}>
|
|
65
|
+
{label}
|
|
66
|
+
</BodyText>
|
|
67
|
+
<BodyText size="md" style={styles.value}>
|
|
68
|
+
{valueText}
|
|
69
|
+
</BodyText>
|
|
70
|
+
</View>
|
|
71
|
+
)}
|
|
72
|
+
<View style={styles.track} onLayout={handleTrackLayout}>
|
|
73
|
+
{Platform.OS === 'web' ? (
|
|
74
|
+
<View
|
|
75
|
+
style={[
|
|
76
|
+
styles.indicator,
|
|
77
|
+
{ width: `${Math.max(0, Math.min(100, percent))}%` },
|
|
78
|
+
{ backgroundColor: indicatorColor },
|
|
79
|
+
]}
|
|
80
|
+
/>
|
|
81
|
+
) : (
|
|
82
|
+
<Animated.View
|
|
83
|
+
style={[styles.indicator, animatedStyle, { backgroundColor: indicatorColor }]}
|
|
84
|
+
/>
|
|
85
|
+
)}
|
|
86
|
+
</View>
|
|
87
|
+
</View>
|
|
88
|
+
);
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
ProgressBarLinear.displayName = 'ProgressBarLinear';
|
|
92
|
+
|
|
93
|
+
const styles = StyleSheet.create(theme => ({
|
|
94
|
+
container: {
|
|
95
|
+
width: '100%',
|
|
96
|
+
gap: theme.components.progressBar.linear.gap,
|
|
97
|
+
},
|
|
98
|
+
header: {
|
|
99
|
+
flexDirection: 'row',
|
|
100
|
+
alignItems: 'center',
|
|
101
|
+
justifyContent: 'space-between',
|
|
102
|
+
gap: theme.components.progressBar.linear.label.gap,
|
|
103
|
+
},
|
|
104
|
+
label: {
|
|
105
|
+
flex: 1,
|
|
106
|
+
},
|
|
107
|
+
value: {
|
|
108
|
+
flexShrink: 0,
|
|
109
|
+
textAlign: 'right',
|
|
110
|
+
},
|
|
111
|
+
track: {
|
|
112
|
+
width: '100%',
|
|
113
|
+
height: theme.components.progressBar.linear.bar.height,
|
|
114
|
+
backgroundColor: theme.components.progressBar.barColor,
|
|
115
|
+
borderRadius: theme.components.progressBar.linear.bar.borderRadius,
|
|
116
|
+
overflow: 'hidden',
|
|
117
|
+
},
|
|
118
|
+
indicator: {
|
|
119
|
+
height: '100%',
|
|
120
|
+
borderRadius: theme.components.progressBar.linear.bar.borderRadius,
|
|
121
|
+
_web: {
|
|
122
|
+
height: '100%',
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
}));
|
|
126
|
+
|
|
127
|
+
export default ProgressBarLinear;
|
package/src/components/index.ts
CHANGED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
export type SegmentedControlContextValue = {
|
|
2
|
-
value?: string;
|
|
3
|
-
select: (value: string) => void;
|
|
4
|
-
disabled?: boolean;
|
|
5
|
-
size: 'sm' | 'md';
|
|
6
|
-
registerOptionLayout: (value: string, layout: {
|
|
7
|
-
x: number;
|
|
8
|
-
y: number;
|
|
9
|
-
width: number;
|
|
10
|
-
height: number;
|
|
11
|
-
}) => void;
|
|
12
|
-
};
|
|
13
|
-
export declare const SegmentedControlContext: import("react").Context<SegmentedControlContextValue | null>;
|
|
14
|
-
export declare const useSegmentedControlContext: () => SegmentedControlContextValue;
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import { createContext, useContext } from 'react';
|
|
2
|
-
export const SegmentedControlContext = createContext(null);
|
|
3
|
-
export const useSegmentedControlContext = () => {
|
|
4
|
-
const context = useContext(SegmentedControlContext);
|
|
5
|
-
if (!context) {
|
|
6
|
-
throw new Error('SegmentedControlOption must be used within SegmentedControl');
|
|
7
|
-
}
|
|
8
|
-
return context;
|
|
9
|
-
};
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import type SegmentedControlProps from './SegmentedControl.props';
|
|
2
|
-
declare const SegmentedControl: {
|
|
3
|
-
({ value: controlledValue, defaultValue, onValueChange, size, disabled, children, style, ...props }: SegmentedControlProps): import("react/jsx-runtime").JSX.Element;
|
|
4
|
-
displayName: string;
|
|
5
|
-
};
|
|
6
|
-
export default SegmentedControl;
|