@cdx-ui/components 0.0.1-beta.142 → 0.0.1-beta.144

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 (48) hide show
  1. package/lib/commonjs/components/Checkbox/index.js +10 -4
  2. package/lib/commonjs/components/Checkbox/index.js.map +1 -1
  3. package/lib/commonjs/components/Checkbox/styles.js +8 -4
  4. package/lib/commonjs/components/Checkbox/styles.js.map +1 -1
  5. package/lib/commonjs/components/ProgressCircle/ProgressCircleRing.js +91 -0
  6. package/lib/commonjs/components/ProgressCircle/ProgressCircleRing.js.map +1 -0
  7. package/lib/commonjs/components/ProgressCircle/index.js +84 -0
  8. package/lib/commonjs/components/ProgressCircle/index.js.map +1 -0
  9. package/lib/commonjs/components/ProgressCircle/styles.js +50 -0
  10. package/lib/commonjs/components/ProgressCircle/styles.js.map +1 -0
  11. package/lib/commonjs/components/ProgressCircle/useProgressCircleColors.js +35 -0
  12. package/lib/commonjs/components/ProgressCircle/useProgressCircleColors.js.map +1 -0
  13. package/lib/commonjs/components/index.js +12 -0
  14. package/lib/commonjs/components/index.js.map +1 -1
  15. package/lib/module/components/Checkbox/index.js +10 -4
  16. package/lib/module/components/Checkbox/index.js.map +1 -1
  17. package/lib/module/components/Checkbox/styles.js +8 -4
  18. package/lib/module/components/Checkbox/styles.js.map +1 -1
  19. package/lib/module/components/ProgressCircle/ProgressCircleRing.js +86 -0
  20. package/lib/module/components/ProgressCircle/ProgressCircleRing.js.map +1 -0
  21. package/lib/module/components/ProgressCircle/index.js +80 -0
  22. package/lib/module/components/ProgressCircle/index.js.map +1 -0
  23. package/lib/module/components/ProgressCircle/styles.js +46 -0
  24. package/lib/module/components/ProgressCircle/styles.js.map +1 -0
  25. package/lib/module/components/ProgressCircle/useProgressCircleColors.js +32 -0
  26. package/lib/module/components/ProgressCircle/useProgressCircleColors.js.map +1 -0
  27. package/lib/module/components/index.js +1 -0
  28. package/lib/module/components/index.js.map +1 -1
  29. package/lib/typescript/components/Checkbox/index.d.ts.map +1 -1
  30. package/lib/typescript/components/Checkbox/styles.d.ts.map +1 -1
  31. package/lib/typescript/components/ProgressCircle/ProgressCircleRing.d.ts +7 -0
  32. package/lib/typescript/components/ProgressCircle/ProgressCircleRing.d.ts.map +1 -0
  33. package/lib/typescript/components/ProgressCircle/index.d.ts +15 -0
  34. package/lib/typescript/components/ProgressCircle/index.d.ts.map +1 -0
  35. package/lib/typescript/components/ProgressCircle/styles.d.ts +23 -0
  36. package/lib/typescript/components/ProgressCircle/styles.d.ts.map +1 -0
  37. package/lib/typescript/components/ProgressCircle/useProgressCircleColors.d.ts +6 -0
  38. package/lib/typescript/components/ProgressCircle/useProgressCircleColors.d.ts.map +1 -0
  39. package/lib/typescript/components/index.d.ts +1 -0
  40. package/lib/typescript/components/index.d.ts.map +1 -1
  41. package/package.json +10 -5
  42. package/src/components/Checkbox/index.tsx +15 -6
  43. package/src/components/Checkbox/styles.ts +25 -2
  44. package/src/components/ProgressCircle/ProgressCircleRing.tsx +105 -0
  45. package/src/components/ProgressCircle/index.tsx +94 -0
  46. package/src/components/ProgressCircle/styles.ts +47 -0
  47. package/src/components/ProgressCircle/useProgressCircleColors.ts +33 -0
  48. package/src/components/index.ts +1 -0
@@ -8,6 +8,7 @@ export const checkboxRootVariants = cva([
8
8
  'flex-row items-center gap-2.5',
9
9
  DISABLED_OPACITY,
10
10
  DISABLED_CURSOR,
11
+ 'data-[readonly=true]:cursor-default',
11
12
  // Suppress stray outline on the Pressable/label wrapper; focus ring is drawn on Checkbox.Indicator (Radio pattern).
12
13
  'web:outline-none web:focus:outline-none web:focus-visible:outline-none',
13
14
  ]);
@@ -33,6 +34,12 @@ export const checkboxIndicatorVariants = cva([
33
34
  'data-[invalid=true]:border-stroke-danger',
34
35
  Platform.select({
35
36
  web: [
37
+ // Read-only — compound selectors evaluate correctly on web.
38
+ 'data-[readonly=true]:bg-surface-secondary',
39
+ 'data-[readonly=true]:data-[checked=true]:border-stroke-action',
40
+ 'data-[readonly=true]:data-[indeterminate=true]:border-stroke-action',
41
+ 'data-[readonly=true]:data-[checked=true]:bg-surface-secondary',
42
+ 'data-[readonly=true]:data-[indeterminate=true]:bg-surface-secondary',
36
43
  // Invalid bg — compound selectors evaluate correctly on web.
37
44
  'data-[invalid=true]:data-[checked=true]:bg-surface-danger-strong',
38
45
  'data-[invalid=true]:data-[indeterminate=true]:bg-surface-danger-strong',
@@ -63,13 +70,24 @@ export const checkboxIndicatorVariants = cva([
63
70
  'web:active:data-[hover=true]:data-[checked=true]:data-[invalid=true]:border-surface-danger-strong-active',
64
71
  'web:active:data-[hover=true]:data-[indeterminate=true]:data-[invalid=true]:bg-surface-danger-strong-active',
65
72
  'web:active:data-[hover=true]:data-[indeterminate=true]:data-[invalid=true]:border-surface-danger-strong-active',
66
- // Focus ring
73
+ // Focus ring — applies to read-only as well (focusable + aria-readonly).
67
74
  'web:data-[focus-visible=true]:outline-none',
68
75
  'web:data-[focus-visible=true]:ring-2 web:data-[focus-visible=true]:ring-stroke-focus web:data-[focus-visible=true]:ring-offset-2',
69
76
  ].join(' '),
70
77
  default: [
71
78
  // Native: Uniwind only evaluates the last data-[] condition in a compound chain.
72
79
  // Use single-condition data-[field-state=...] selectors (emitted by CheckboxIndicator).
80
+ // Read-only — derived field-state encodes check × readonly (no active/hover on readonly).
81
+ 'data-[field-state=readonly-unchecked]:bg-surface-secondary',
82
+ 'data-[field-state=readonly-checked]:bg-surface-secondary',
83
+ 'data-[field-state=readonly-checked]:border-stroke-action',
84
+ 'data-[field-state=readonly-indeterminate]:bg-surface-secondary',
85
+ 'data-[field-state=readonly-indeterminate]:border-stroke-action',
86
+ 'data-[field-state=readonly-unchecked-invalid]:bg-surface-secondary',
87
+ 'data-[field-state=readonly-checked-invalid]:bg-surface-secondary',
88
+ 'data-[field-state=readonly-checked-invalid]:border-stroke-action',
89
+ 'data-[field-state=readonly-indeterminate-invalid]:bg-surface-secondary',
90
+ 'data-[field-state=readonly-indeterminate-invalid]:border-stroke-action',
73
91
  // checked/indeterminate + invalid → danger bg
74
92
  'data-[field-state=checked-invalid]:bg-surface-danger-strong',
75
93
  'data-[field-state=indeterminate-invalid]:bg-surface-danger-strong',
@@ -90,11 +108,16 @@ export const checkboxIndicatorVariants = cva([
90
108
  }),
91
109
  ]);
92
110
 
93
- export const checkboxIconVariants = cva(['text-white', 'h-5 w-5']);
111
+ export const checkboxIconVariants = cva([
112
+ 'text-white',
113
+ 'data-[readonly=true]:text-content-action',
114
+ 'h-5 w-5',
115
+ ]);
94
116
 
95
117
  export const checkboxLabelVariants = cva([
96
118
  'text-content-primary text-base',
97
119
  'data-[disabled=true]:text-content-tertiary',
120
+ 'data-[readonly=true]:cursor-default',
98
121
  ]);
99
122
 
100
123
  export const checkboxGroupVariants = cva([], {
@@ -0,0 +1,105 @@
1
+ import { useEffect } from 'react';
2
+ import { View } from 'react-native';
3
+ import Animated, {
4
+ Easing,
5
+ useAnimatedStyle,
6
+ useSharedValue,
7
+ withRepeat,
8
+ withTiming,
9
+ } from 'react-native-reanimated';
10
+ import { Circle, Svg } from 'react-native-svg';
11
+ import { useProgressContext } from '@cdx-ui/primitives';
12
+ import { PROGRESS_CIRCLE_DIMENSIONS, type ProgressCircleSize } from './styles';
13
+ import { useProgressCircleColors } from './useProgressCircleColors';
14
+
15
+ const INDETERMINATE_ARC_RATIO = 0.25;
16
+ const ROTATION_DURATION_MS = 1000;
17
+
18
+ export interface ProgressCircleRingProps {
19
+ readonly size?: ProgressCircleSize;
20
+ readonly color?: 'default' | 'inherit';
21
+ }
22
+
23
+ export function ProgressCircleRing({ size = 'large', color = 'default' }: ProgressCircleRingProps) {
24
+ const { percent, state } = useProgressContext();
25
+ const ringColors = useProgressCircleColors(color);
26
+ const rotation = useSharedValue(0);
27
+ const { size: dimension, strokeWidth } = PROGRESS_CIRCLE_DIMENSIONS[size];
28
+ const radius = (dimension - strokeWidth) / 2;
29
+ const center = dimension / 2;
30
+ const circumference = 2 * Math.PI * radius;
31
+ const isIndeterminate = state === 'indeterminate';
32
+
33
+ useEffect(() => {
34
+ if (!isIndeterminate) {
35
+ rotation.set(0);
36
+ return;
37
+ }
38
+
39
+ rotation.set(
40
+ withRepeat(
41
+ withTiming(360, {
42
+ duration: ROTATION_DURATION_MS,
43
+ easing: Easing.linear,
44
+ }),
45
+ -1,
46
+ false,
47
+ ),
48
+ );
49
+ }, [isIndeterminate, rotation]);
50
+
51
+ const animatedStyle = useAnimatedStyle(
52
+ () => ({
53
+ transform: [{ rotate: `${String(rotation.get())}deg` }],
54
+ }),
55
+ [rotation],
56
+ );
57
+
58
+ const progressLength = isIndeterminate
59
+ ? circumference * INDETERMINATE_ARC_RATIO
60
+ : (circumference * percent) / 100;
61
+ const gapLength = circumference - progressLength;
62
+
63
+ const ring = (
64
+ <Svg
65
+ width={dimension}
66
+ height={dimension}
67
+ viewBox={`0 0 ${String(dimension)} ${String(dimension)}`}
68
+ >
69
+ <Circle
70
+ cx={center}
71
+ cy={center}
72
+ r={radius}
73
+ stroke={ringColors.track}
74
+ strokeWidth={strokeWidth}
75
+ fill="none"
76
+ opacity={ringColors.trackOpacity}
77
+ />
78
+ {progressLength > 0 ? (
79
+ <Circle
80
+ cx={center}
81
+ cy={center}
82
+ r={radius}
83
+ stroke={ringColors.indicator}
84
+ strokeWidth={strokeWidth}
85
+ fill="none"
86
+ strokeDasharray={`${String(progressLength)} ${String(gapLength)}`}
87
+ strokeLinecap="round"
88
+ rotation={-90}
89
+ origin={`${String(center)}, ${String(center)}`}
90
+ />
91
+ ) : null}
92
+ </Svg>
93
+ );
94
+
95
+ if (isIndeterminate) {
96
+ const inheritClass = color === 'inherit' ? 'text-inherit' : undefined;
97
+ return (
98
+ <Animated.View style={animatedStyle} className={inheritClass}>
99
+ <View className={inheritClass}>{ring}</View>
100
+ </Animated.View>
101
+ );
102
+ }
103
+
104
+ return ring;
105
+ }
@@ -0,0 +1,94 @@
1
+ import { createContext, forwardRef, useContext, useMemo } from 'react';
2
+ import { View, type ViewProps } from 'react-native';
3
+ import {
4
+ ProgressBarRoot,
5
+ dataAttributes,
6
+ mergeDataAttributes,
7
+ type IProgressProps,
8
+ } from '@cdx-ui/primitives';
9
+ import { cn } from '@cdx-ui/utils';
10
+ import { ProgressCircleRing } from './ProgressCircleRing';
11
+ import {
12
+ progressCircleIndicatorVariants,
13
+ progressCircleRootVariants,
14
+ type ProgressCircleIndicatorVariantProps,
15
+ type ProgressCircleRootVariantProps,
16
+ type ProgressCircleSize,
17
+ } from './styles';
18
+
19
+ type ProgressCircleColor = NonNullable<ProgressCircleRootVariantProps['color']>;
20
+
21
+ interface ProgressCircleStyleContextValue {
22
+ readonly size: ProgressCircleSize;
23
+ readonly color: ProgressCircleColor;
24
+ }
25
+
26
+ const ProgressCircleStyleContext = createContext<ProgressCircleStyleContextValue>({
27
+ size: 'large',
28
+ color: 'default',
29
+ });
30
+
31
+ export interface ProgressCircleProps extends IProgressProps, ProgressCircleRootVariantProps {}
32
+
33
+ export interface ProgressCircleIndicatorProps
34
+ extends ViewProps, ProgressCircleIndicatorVariantProps {}
35
+
36
+ const ProgressCircleRootComponent = forwardRef<View, ProgressCircleProps>(
37
+ ({ className, style, size = 'large', color = 'default', children, ...props }, ref) => {
38
+ const rootClass = cn(progressCircleRootVariants({ size, color }), className);
39
+ const styleContext = useMemo(
40
+ () => ({
41
+ size: size ?? 'large',
42
+ color: color ?? 'default',
43
+ }),
44
+ [size, color],
45
+ );
46
+
47
+ return (
48
+ <ProgressCircleStyleContext.Provider value={styleContext}>
49
+ <ProgressBarRoot
50
+ ref={ref}
51
+ className={rootClass}
52
+ style={style}
53
+ {...props}
54
+ {...mergeDataAttributes(props, dataAttributes({ slot: 'progress-circle' }))}
55
+ >
56
+ {children}
57
+ </ProgressBarRoot>
58
+ </ProgressCircleStyleContext.Provider>
59
+ );
60
+ },
61
+ );
62
+
63
+ ProgressCircleRootComponent.displayName = 'ProgressCircle';
64
+
65
+ const ProgressCircleIndicatorComponent = forwardRef<View, ProgressCircleIndicatorProps>(
66
+ ({ className, style, size: sizeProp, color: colorProp, ...props }, ref) => {
67
+ const { size: contextSize, color: contextColor } = useContext(ProgressCircleStyleContext);
68
+ const size = sizeProp ?? contextSize;
69
+ const color = colorProp ?? contextColor;
70
+ const indicatorClass = cn(progressCircleIndicatorVariants({ size, color }), className);
71
+
72
+ return (
73
+ <View
74
+ ref={ref}
75
+ className={indicatorClass}
76
+ style={style}
77
+ {...props}
78
+ {...mergeDataAttributes(props, dataAttributes({ slot: 'progress-circle-indicator' }))}
79
+ >
80
+ <ProgressCircleRing size={size} color={color} />
81
+ </View>
82
+ );
83
+ },
84
+ );
85
+
86
+ ProgressCircleIndicatorComponent.displayName = 'ProgressCircle.Indicator';
87
+
88
+ type ProgressCircleCompoundComponent = typeof ProgressCircleRootComponent & {
89
+ Indicator: typeof ProgressCircleIndicatorComponent;
90
+ };
91
+
92
+ export const ProgressCircle = Object.assign(ProgressCircleRootComponent, {
93
+ Indicator: ProgressCircleIndicatorComponent,
94
+ }) as ProgressCircleCompoundComponent;
@@ -0,0 +1,47 @@
1
+ import { cva, type VariantProps } from '@cdx-ui/utils';
2
+
3
+ export const progressCircleRootVariants = cva(['items-center justify-center'], {
4
+ variants: {
5
+ size: {
6
+ large: 'size-[32px]',
7
+ small: 'size-[24px]',
8
+ },
9
+ color: {
10
+ default: '',
11
+ inherit: 'text-inherit',
12
+ },
13
+ },
14
+ defaultVariants: {
15
+ size: 'large',
16
+ color: 'default',
17
+ },
18
+ });
19
+
20
+ export const progressCircleIndicatorVariants = cva(['size-full items-center justify-center'], {
21
+ variants: {
22
+ size: {
23
+ large: '',
24
+ small: '',
25
+ },
26
+ color: {
27
+ default: '',
28
+ inherit: 'text-inherit',
29
+ },
30
+ },
31
+ defaultVariants: {
32
+ size: 'large',
33
+ color: 'default',
34
+ },
35
+ });
36
+
37
+ export type ProgressCircleRootVariantProps = VariantProps<typeof progressCircleRootVariants>;
38
+ export type ProgressCircleIndicatorVariantProps = VariantProps<
39
+ typeof progressCircleIndicatorVariants
40
+ >;
41
+
42
+ export const PROGRESS_CIRCLE_DIMENSIONS = {
43
+ large: { size: 32, strokeWidth: 3 },
44
+ small: { size: 24, strokeWidth: 2 },
45
+ } as const;
46
+
47
+ export type ProgressCircleSize = keyof typeof PROGRESS_CIRCLE_DIMENSIONS;
@@ -0,0 +1,33 @@
1
+ import { useCSSVariable } from 'uniwind';
2
+
3
+ /** Light-theme fallbacks when Uniwind CSS context is unavailable (e.g. Jest). */
4
+ const FALLBACKS = {
5
+ track: '#e8eefc',
6
+ indicator: '#346099',
7
+ } as const;
8
+
9
+ function toColor(value: string | number | undefined, fallback: string): string {
10
+ if (typeof value === 'string' && value.length > 0) {
11
+ return value;
12
+ }
13
+ return fallback;
14
+ }
15
+
16
+ export function useProgressCircleColors(color: 'default' | 'inherit') {
17
+ const trackToken = useCSSVariable('--color-surface-action-subtle');
18
+ const indicatorToken = useCSSVariable('--color-surface-action-strong');
19
+
20
+ if (color === 'inherit') {
21
+ return {
22
+ track: 'currentColor',
23
+ indicator: 'currentColor',
24
+ trackOpacity: 0.24,
25
+ };
26
+ }
27
+
28
+ return {
29
+ track: toColor(trackToken, FALLBACKS.track),
30
+ indicator: toColor(indicatorToken, FALLBACKS.indicator),
31
+ trackOpacity: 1,
32
+ };
33
+ }
@@ -32,6 +32,7 @@ export * from './OtpInput';
32
32
  export * from './Link';
33
33
  export * from './ListItem';
34
34
  export * from './ProgressBar';
35
+ export * from './ProgressCircle';
35
36
  export * from './Skeleton';
36
37
  export * from './ProgressSegmented';
37
38
  export * from './Radio';