@idealyst/components 1.2.14 → 1.2.15
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/package.json +3 -3
- package/src/Accordion/Accordion.web.tsx +1 -1
- package/src/Alert/Alert.native.tsx +1 -1
- package/src/Alert/Alert.web.tsx +1 -1
- package/src/Badge/Badge.web.tsx +6 -2
- package/src/Badge/types.ts +5 -0
- package/src/Button/Button.native.tsx +3 -3
- package/src/Button/Button.web.tsx +5 -1
- package/src/Button/types.ts +5 -0
- package/src/Card/Card.web.tsx +4 -1
- package/src/Card/types.ts +5 -0
- package/src/Dialog/Dialog.native.tsx +1 -1
- package/src/Divider/Divider.web.tsx +2 -2
- package/src/Icon/Icon.web.tsx +2 -2
- package/src/Image/Image.styles.tsx +5 -5
- package/src/Image/Image.web.tsx +3 -3
- package/src/List/List.native.tsx +1 -2
- package/src/List/List.web.tsx +1 -2
- package/src/List/ListSection.web.tsx +3 -3
- package/src/Menu/Menu.web.tsx +8 -10
- package/src/Menu/MenuItem.web.tsx +1 -1
- package/src/Popover/Popover.web.tsx +1 -1
- package/src/Pressable/Pressable.web.tsx +1 -1
- package/src/Progress/Progress.styles.tsx +76 -30
- package/src/Progress/Progress.web.tsx +13 -15
- package/src/SVGImage/SVGImage.web.tsx +1 -1
- package/src/Select/Select.web.tsx +2 -2
- package/src/Slider/Slider.styles.tsx +131 -44
- package/src/Slider/Slider.web.tsx +22 -22
- package/src/Text/Text.web.tsx +29 -3
- package/src/Text/types.ts +14 -1
- package/src/TextArea/TextArea.styles.tsx +96 -57
- package/src/TextArea/TextArea.web.tsx +19 -28
- package/src/Tooltip/Tooltip.web.tsx +3 -3
- package/src/Video/Video.styles.tsx +3 -3
- package/src/Video/Video.web.tsx +1 -1
- package/src/View/View.styles.tsx +2 -2
- package/src/View/View.web.tsx +93 -9
- package/src/View/types.ts +5 -1
- package/src/examples/ViewExamples.tsx +34 -0
- package/src/extensions/index.ts +0 -7
- package/src/hooks/useMergeRefs.ts +12 -6
- package/src/utils/accessibility/keyboardPatterns.ts +4 -0
- package/src/utils/accessibility/types.ts +5 -1
- package/src/utils/accessibility/useAnnounce.ts +1 -1
- package/src/utils/accessibility/useKeyboardNavigation.ts +1 -1
- package/src/utils/index.ts +0 -3
- package/src/utils/viewStyleProps.ts +2 -0
- package/src/extensions/applyExtension.ts +0 -210
- package/src/utils/buildSizeVariants.ts +0 -16
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@idealyst/components",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.15",
|
|
4
4
|
"description": "Shared component library for React and React Native",
|
|
5
5
|
"documentation": "https://github.com/IdealystIO/idealyst-framework/tree/main/packages/components#readme",
|
|
6
6
|
"readme": "README.md",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"publish:npm": "npm publish"
|
|
57
57
|
},
|
|
58
58
|
"peerDependencies": {
|
|
59
|
-
"@idealyst/theme": "^1.2.
|
|
59
|
+
"@idealyst/theme": "^1.2.15",
|
|
60
60
|
"@mdi/js": ">=7.0.0",
|
|
61
61
|
"@mdi/react": ">=1.0.0",
|
|
62
62
|
"@react-native-vector-icons/common": ">=12.0.0",
|
|
@@ -106,7 +106,7 @@
|
|
|
106
106
|
}
|
|
107
107
|
},
|
|
108
108
|
"devDependencies": {
|
|
109
|
-
"@idealyst/theme": "^1.2.
|
|
109
|
+
"@idealyst/theme": "^1.2.15",
|
|
110
110
|
"@idealyst/tooling": "^1.2.4",
|
|
111
111
|
"@mdi/react": "^1.6.1",
|
|
112
112
|
"@types/react": "^19.1.0",
|
|
@@ -47,7 +47,7 @@ const AccordionItem: React.FC<AccordionItemProps> = ({
|
|
|
47
47
|
const itemStyle = (accordionStyles.item as any)({ type, isLast });
|
|
48
48
|
const itemProps = getWebProps([itemStyle]);
|
|
49
49
|
const headerProps = getWebProps([(accordionStyles.header as any)({})]);
|
|
50
|
-
const titleProps = getWebProps([accordionStyles.title]);
|
|
50
|
+
const titleProps = getWebProps([accordionStyles.title as any]);
|
|
51
51
|
const iconProps = getWebProps([(accordionStyles.icon as any)({})]);
|
|
52
52
|
// Pass expanded state to get correct maxHeight from styles
|
|
53
53
|
const contentProps = getWebProps([(accordionStyles.content as any)({ expanded: isExpanded })]);
|
|
@@ -6,7 +6,7 @@ import { isIconName } from '../Icon/icon-resolver';
|
|
|
6
6
|
import type { AlertProps } from './types';
|
|
7
7
|
|
|
8
8
|
// Default icon names for each intent
|
|
9
|
-
const defaultIcons = {
|
|
9
|
+
const defaultIcons: Record<string, string> = {
|
|
10
10
|
primary: 'information',
|
|
11
11
|
success: 'check-circle',
|
|
12
12
|
error: 'alert-circle',
|
package/src/Alert/Alert.web.tsx
CHANGED
|
@@ -7,7 +7,7 @@ import { isIconName } from '../Icon/icon-resolver';
|
|
|
7
7
|
import useMergeRefs from '../hooks/useMergeRefs';
|
|
8
8
|
|
|
9
9
|
// Default icons for each intent
|
|
10
|
-
const defaultIcons = {
|
|
10
|
+
const defaultIcons: Record<string, string> = {
|
|
11
11
|
primary: 'information',
|
|
12
12
|
success: 'check-circle',
|
|
13
13
|
error: 'alert-circle',
|
package/src/Badge/Badge.web.tsx
CHANGED
|
@@ -13,7 +13,8 @@ const Badge = forwardRef<HTMLSpanElement, BadgeProps>((props, ref) => {
|
|
|
13
13
|
const {
|
|
14
14
|
children,
|
|
15
15
|
size = 'md',
|
|
16
|
-
type
|
|
16
|
+
type: typeProp,
|
|
17
|
+
variant,
|
|
17
18
|
color = 'blue',
|
|
18
19
|
icon,
|
|
19
20
|
style,
|
|
@@ -21,13 +22,16 @@ const Badge = forwardRef<HTMLSpanElement, BadgeProps>((props, ref) => {
|
|
|
21
22
|
id,
|
|
22
23
|
} = props;
|
|
23
24
|
|
|
25
|
+
// variant is an alias for type - variant takes precedence if both are set
|
|
26
|
+
const type = variant ?? typeProp ?? 'filled';
|
|
27
|
+
|
|
24
28
|
badgeStyles.useVariants({
|
|
25
29
|
size,
|
|
26
30
|
type,
|
|
27
31
|
});
|
|
28
32
|
|
|
29
33
|
const badgeStyle = (badgeStyles.badge as any)({ color });
|
|
30
|
-
const contentStyle = badgeStyles.content;
|
|
34
|
+
const contentStyle = badgeStyles.content as any;
|
|
31
35
|
const textStyle = (badgeStyles.text as any)({ color });
|
|
32
36
|
|
|
33
37
|
const badgeProps = getWebProps([badgeStyle]);
|
package/src/Badge/types.ts
CHANGED
|
@@ -68,14 +68,14 @@ const Button = forwardRef<ComponentRef<typeof TouchableOpacity>, ButtonProps>((p
|
|
|
68
68
|
};
|
|
69
69
|
|
|
70
70
|
// Map button size to icon size
|
|
71
|
-
const iconSizeMap = {
|
|
71
|
+
const iconSizeMap: Record<string, number> = {
|
|
72
72
|
xs: 12,
|
|
73
73
|
sm: 14,
|
|
74
74
|
md: 16,
|
|
75
75
|
lg: 18,
|
|
76
76
|
xl: 20,
|
|
77
|
-
}
|
|
78
|
-
const iconSize = iconSizeMap[size];
|
|
77
|
+
};
|
|
78
|
+
const iconSize = iconSizeMap[size] ?? 16;
|
|
79
79
|
|
|
80
80
|
|
|
81
81
|
// Use children if available, otherwise use title
|
|
@@ -17,7 +17,8 @@ const Button = forwardRef<HTMLButtonElement, ButtonProps>((props, ref) => {
|
|
|
17
17
|
onPress,
|
|
18
18
|
disabled = false,
|
|
19
19
|
loading = false,
|
|
20
|
-
type
|
|
20
|
+
type: typeProp,
|
|
21
|
+
variant,
|
|
21
22
|
intent = 'primary',
|
|
22
23
|
size = 'md',
|
|
23
24
|
gradient,
|
|
@@ -41,6 +42,9 @@ const Button = forwardRef<HTMLButtonElement, ButtonProps>((props, ref) => {
|
|
|
41
42
|
accessibilityHasPopup,
|
|
42
43
|
} = props;
|
|
43
44
|
|
|
45
|
+
// variant is an alias for type - variant takes precedence if both are set
|
|
46
|
+
const type = variant ?? typeProp ?? 'contained';
|
|
47
|
+
|
|
44
48
|
// Button is effectively disabled when loading
|
|
45
49
|
const isDisabled = disabled || loading;
|
|
46
50
|
|
package/src/Button/types.ts
CHANGED
|
@@ -48,6 +48,11 @@ export interface ButtonProps extends BaseProps, InteractiveAccessibilityProps {
|
|
|
48
48
|
*/
|
|
49
49
|
type?: ButtonType;
|
|
50
50
|
|
|
51
|
+
/**
|
|
52
|
+
* Alias for type - the visual style variant of the button
|
|
53
|
+
*/
|
|
54
|
+
variant?: ButtonType;
|
|
55
|
+
|
|
51
56
|
/**
|
|
52
57
|
* The intent/color scheme of the button
|
|
53
58
|
*/
|
package/src/Card/Card.web.tsx
CHANGED
|
@@ -11,7 +11,8 @@ import { getWebInteractiveAriaProps } from '../utils/accessibility';
|
|
|
11
11
|
*/
|
|
12
12
|
const Card = forwardRef<HTMLDivElement | HTMLButtonElement, CardProps>(({
|
|
13
13
|
children,
|
|
14
|
-
type
|
|
14
|
+
type: typeProp,
|
|
15
|
+
variant,
|
|
15
16
|
radius = 'md',
|
|
16
17
|
intent,
|
|
17
18
|
clickable = false,
|
|
@@ -36,6 +37,8 @@ const Card = forwardRef<HTMLDivElement | HTMLButtonElement, CardProps>(({
|
|
|
36
37
|
accessibilityRole,
|
|
37
38
|
accessibilityPressed,
|
|
38
39
|
}, ref) => {
|
|
40
|
+
// variant is an alias for type - variant takes precedence if both are set
|
|
41
|
+
const type = variant ?? typeProp ?? 'elevated';
|
|
39
42
|
// Generate ARIA props
|
|
40
43
|
const ariaProps = useMemo(() => {
|
|
41
44
|
return getWebInteractiveAriaProps({
|
package/src/Card/types.ts
CHANGED
|
@@ -24,6 +24,11 @@ export interface CardProps extends ContainerStyleProps, InteractiveAccessibility
|
|
|
24
24
|
*/
|
|
25
25
|
type?: CardType;
|
|
26
26
|
|
|
27
|
+
/**
|
|
28
|
+
* Alias for type - the visual style variant of the card
|
|
29
|
+
*/
|
|
30
|
+
variant?: CardType;
|
|
31
|
+
|
|
27
32
|
/**
|
|
28
33
|
* The border radius of the card
|
|
29
34
|
*/
|
|
@@ -45,8 +45,8 @@ const Divider = forwardRef<HTMLDivElement, DividerProps>(({
|
|
|
45
45
|
|
|
46
46
|
// Generate web props
|
|
47
47
|
const dividerProps = getWebProps([dividerStyle, style as any]);
|
|
48
|
-
const containerProps = getWebProps([dividerStyles.container]);
|
|
49
|
-
const contentProps = getWebProps([dividerStyles.content]);
|
|
48
|
+
const containerProps = getWebProps([dividerStyles.container as any]);
|
|
49
|
+
const contentProps = getWebProps([dividerStyles.content as any]);
|
|
50
50
|
const lineProps = getWebProps([lineStyle]);
|
|
51
51
|
|
|
52
52
|
const mergedDividerRef = useMergeRefs(ref, dividerProps.ref);
|
package/src/Icon/Icon.web.tsx
CHANGED
|
@@ -45,7 +45,8 @@ const Icon = forwardRef<HTMLSpanElement, IconProps>((props, ref) => {
|
|
|
45
45
|
iconSize = size;
|
|
46
46
|
} else {
|
|
47
47
|
const themeSize = theme.sizes.icon[size as keyof typeof theme.sizes.icon];
|
|
48
|
-
|
|
48
|
+
const rawSize = typeof themeSize === 'number' ? themeSize : themeSize?.width;
|
|
49
|
+
iconSize = typeof rawSize === 'number' ? rawSize : 24;
|
|
49
50
|
}
|
|
50
51
|
|
|
51
52
|
// Compute color - priority: intent > color > textColor > default
|
|
@@ -70,7 +71,6 @@ const Icon = forwardRef<HTMLSpanElement, IconProps>((props, ref) => {
|
|
|
70
71
|
ref={mergedRef}
|
|
71
72
|
id={id}
|
|
72
73
|
style={{
|
|
73
|
-
...iconProps.style,
|
|
74
74
|
fontSize: iconSize,
|
|
75
75
|
width: '1em',
|
|
76
76
|
height: '1em',
|
|
@@ -20,7 +20,7 @@ export const imageStyles = defineStyle('Image', (theme: Theme) => ({
|
|
|
20
20
|
container: (_props: ImageDynamicProps) => ({
|
|
21
21
|
position: 'relative' as const,
|
|
22
22
|
overflow: 'hidden' as const,
|
|
23
|
-
backgroundColor: theme.colors['
|
|
23
|
+
backgroundColor: theme.colors.pallet.gray?.['200'] ?? theme.colors.surface.secondary,
|
|
24
24
|
}),
|
|
25
25
|
|
|
26
26
|
image: (_props: ImageDynamicProps) => ({
|
|
@@ -37,7 +37,7 @@ export const imageStyles = defineStyle('Image', (theme: Theme) => ({
|
|
|
37
37
|
display: 'flex' as const,
|
|
38
38
|
alignItems: 'center' as const,
|
|
39
39
|
justifyContent: 'center' as const,
|
|
40
|
-
backgroundColor: theme.colors['
|
|
40
|
+
backgroundColor: theme.colors.pallet.gray?.['200'] ?? theme.colors.surface.secondary,
|
|
41
41
|
}),
|
|
42
42
|
|
|
43
43
|
fallback: (_props: ImageDynamicProps) => ({
|
|
@@ -49,11 +49,11 @@ export const imageStyles = defineStyle('Image', (theme: Theme) => ({
|
|
|
49
49
|
display: 'flex' as const,
|
|
50
50
|
alignItems: 'center' as const,
|
|
51
51
|
justifyContent: 'center' as const,
|
|
52
|
-
backgroundColor: theme.colors['
|
|
53
|
-
color: theme.colors['
|
|
52
|
+
backgroundColor: theme.colors.pallet.gray?.['300'] ?? theme.colors.surface.tertiary,
|
|
53
|
+
color: theme.colors.pallet.gray?.['600'] ?? theme.colors.text.secondary,
|
|
54
54
|
}),
|
|
55
55
|
|
|
56
56
|
loadingIndicator: (_props: ImageDynamicProps) => ({
|
|
57
|
-
color: theme.colors['
|
|
57
|
+
color: theme.colors.pallet.gray?.['600'] ?? theme.colors.text.secondary,
|
|
58
58
|
}),
|
|
59
59
|
}));
|
package/src/Image/Image.web.tsx
CHANGED
|
@@ -50,15 +50,15 @@ const Image: React.FC<ImageProps> = ({
|
|
|
50
50
|
]);
|
|
51
51
|
|
|
52
52
|
const imageProps = getWebProps([
|
|
53
|
-
imageStyles.image,
|
|
53
|
+
imageStyles.image as any,
|
|
54
54
|
{
|
|
55
55
|
objectFit: objectFit,
|
|
56
56
|
borderRadius: borderRadius ? `${borderRadius}px` : undefined,
|
|
57
57
|
}
|
|
58
58
|
]);
|
|
59
59
|
|
|
60
|
-
const placeholderProps = getWebProps([imageStyles.placeholder]);
|
|
61
|
-
const fallbackProps = getWebProps([imageStyles.fallback]);
|
|
60
|
+
const placeholderProps = getWebProps([imageStyles.placeholder as any]);
|
|
61
|
+
const fallbackProps = getWebProps([imageStyles.fallback as any]);
|
|
62
62
|
|
|
63
63
|
return (
|
|
64
64
|
<div
|
package/src/List/List.native.tsx
CHANGED
|
@@ -64,9 +64,8 @@ const List = forwardRef<View, ListProps>(({
|
|
|
64
64
|
const processedChildren = childArray.map((child, index) => {
|
|
65
65
|
if (isValidElement(child)) {
|
|
66
66
|
return cloneElement(child, {
|
|
67
|
-
...child.props,
|
|
68
67
|
isLast: index === childArray.length - 1,
|
|
69
|
-
});
|
|
68
|
+
} as Record<string, unknown>);
|
|
70
69
|
}
|
|
71
70
|
return child;
|
|
72
71
|
});
|
package/src/List/List.web.tsx
CHANGED
|
@@ -52,9 +52,8 @@ const List: React.FC<ListProps> = ({
|
|
|
52
52
|
const processedChildren = childArray.map((child, index) => {
|
|
53
53
|
if (isValidElement(child)) {
|
|
54
54
|
return cloneElement(child, {
|
|
55
|
-
...child.props,
|
|
56
55
|
isLast: index === childArray.length - 1,
|
|
57
|
-
});
|
|
56
|
+
} as Record<string, unknown>);
|
|
58
57
|
}
|
|
59
58
|
return child;
|
|
60
59
|
});
|
|
@@ -10,9 +10,9 @@ const ListSection: React.FC<ListSectionProps> = ({
|
|
|
10
10
|
style,
|
|
11
11
|
testID,
|
|
12
12
|
}) => {
|
|
13
|
-
const sectionProps = getWebProps([listStyles.section, style as any]);
|
|
14
|
-
const titleProps = getWebProps([listStyles.sectionTitle]);
|
|
15
|
-
const contentProps = getWebProps([listStyles.sectionContent]);
|
|
13
|
+
const sectionProps = getWebProps([listStyles.section as any, style as any]);
|
|
14
|
+
const titleProps = getWebProps([listStyles.sectionTitle as any]);
|
|
15
|
+
const contentProps = getWebProps([listStyles.sectionContent as any]);
|
|
16
16
|
|
|
17
17
|
return (
|
|
18
18
|
<div {...sectionProps} data-testid={testID}>
|
package/src/Menu/Menu.web.tsx
CHANGED
|
@@ -5,7 +5,7 @@ import type { MenuProps } from './types';
|
|
|
5
5
|
import MenuItem from './MenuItem.web';
|
|
6
6
|
import useMergeRefs from '../hooks/useMergeRefs';
|
|
7
7
|
import { PositionedPortal } from '../internal/PositionedPortal';
|
|
8
|
-
import { getWebInteractiveAriaProps, generateAccessibilityId, MENU_KEYS } from '../utils/accessibility';
|
|
8
|
+
import { getWebInteractiveAriaProps, generateAccessibilityId, MENU_KEYS, matchesKey } from '../utils/accessibility';
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Dropdown menu for actions and navigation triggered by a button or element.
|
|
@@ -64,9 +64,7 @@ const Menu = forwardRef<HTMLDivElement, MenuProps>(({
|
|
|
64
64
|
|
|
65
65
|
// Keyboard navigation handler
|
|
66
66
|
const handleKeyDown = useCallback((e: React.KeyboardEvent) => {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
if (MENU_KEYS.close.includes(key)) {
|
|
67
|
+
if (matchesKey(e, MENU_KEYS.close)) {
|
|
70
68
|
e.preventDefault();
|
|
71
69
|
onOpenChange?.(false);
|
|
72
70
|
// Return focus to trigger
|
|
@@ -78,16 +76,16 @@ const Menu = forwardRef<HTMLDivElement, MenuProps>(({
|
|
|
78
76
|
|
|
79
77
|
let nextIndex = focusedIndex.current;
|
|
80
78
|
|
|
81
|
-
if (MENU_KEYS.next
|
|
79
|
+
if (matchesKey(e, MENU_KEYS.next)) {
|
|
82
80
|
e.preventDefault();
|
|
83
81
|
nextIndex = focusedIndex.current < enabledItems.length - 1 ? focusedIndex.current + 1 : 0;
|
|
84
|
-
} else if (MENU_KEYS.prev
|
|
82
|
+
} else if (matchesKey(e, MENU_KEYS.prev)) {
|
|
85
83
|
e.preventDefault();
|
|
86
84
|
nextIndex = focusedIndex.current > 0 ? focusedIndex.current - 1 : enabledItems.length - 1;
|
|
87
|
-
} else if (MENU_KEYS.first
|
|
85
|
+
} else if (matchesKey(e, MENU_KEYS.first)) {
|
|
88
86
|
e.preventDefault();
|
|
89
87
|
nextIndex = 0;
|
|
90
|
-
} else if (MENU_KEYS.last
|
|
88
|
+
} else if (matchesKey(e, MENU_KEYS.last)) {
|
|
91
89
|
e.preventDefault();
|
|
92
90
|
nextIndex = enabledItems.length - 1;
|
|
93
91
|
}
|
|
@@ -119,7 +117,7 @@ const Menu = forwardRef<HTMLDivElement, MenuProps>(({
|
|
|
119
117
|
|
|
120
118
|
const overlayProps = getWebProps([(menuStyles.overlay as any)({})]);
|
|
121
119
|
const menuProps = getWebProps([(menuStyles.menu as any)({}), style as any]);
|
|
122
|
-
const separatorProps = getWebProps([menuStyles.separator]);
|
|
120
|
+
const separatorProps = getWebProps([menuStyles.separator as any]);
|
|
123
121
|
|
|
124
122
|
const handleTriggerClick = () => {
|
|
125
123
|
onOpenChange?.(!open);
|
|
@@ -161,7 +159,7 @@ const Menu = forwardRef<HTMLDivElement, MenuProps>(({
|
|
|
161
159
|
|
|
162
160
|
<PositionedPortal
|
|
163
161
|
open={open}
|
|
164
|
-
anchor={triggerRef}
|
|
162
|
+
anchor={triggerRef as React.RefObject<HTMLElement>}
|
|
165
163
|
placement={placement}
|
|
166
164
|
offset={4}
|
|
167
165
|
onClickOutside={() => onOpenChange?.(false)}
|
|
@@ -67,7 +67,7 @@ const MenuItem = forwardRef<HTMLButtonElement, MenuItemProps>(({ item, onPress,
|
|
|
67
67
|
<button
|
|
68
68
|
{...itemProps}
|
|
69
69
|
ref={mergedRef}
|
|
70
|
-
style={
|
|
70
|
+
style={buttonResetStyles}
|
|
71
71
|
onClick={() => onPress(item)}
|
|
72
72
|
disabled={item.disabled}
|
|
73
73
|
role="menuitem"
|
|
@@ -49,7 +49,7 @@ const Popover = forwardRef<HTMLDivElement, PopoverProps>(({
|
|
|
49
49
|
popoverStyles.useVariants({});
|
|
50
50
|
|
|
51
51
|
const containerProps = getWebProps([(popoverStyles.container as any)({})]);
|
|
52
|
-
const contentProps = getWebProps([popoverStyles.content]);
|
|
52
|
+
const contentProps = getWebProps([popoverStyles.content as any]);
|
|
53
53
|
|
|
54
54
|
const mergedPopoverRef = useMergeRefs(ref, popoverRef);
|
|
55
55
|
|
|
@@ -78,7 +78,7 @@ const Pressable = forwardRef<HTMLDivElement, PressableProps>(({
|
|
|
78
78
|
id={id}
|
|
79
79
|
role={accessibilityRole}
|
|
80
80
|
tabIndex={disabled ? -1 : 0}
|
|
81
|
-
style={
|
|
81
|
+
style={baseStyle}
|
|
82
82
|
onMouseDown={handleMouseDown}
|
|
83
83
|
onMouseUp={handleMouseUp}
|
|
84
84
|
onMouseLeave={handleMouseUp} // Handle mouse leave as press out
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Progress styles using
|
|
2
|
+
* Progress styles using static styles with variants.
|
|
3
3
|
*/
|
|
4
4
|
import { StyleSheet } from 'react-native-unistyles';
|
|
5
|
-
import { defineStyle, ThemeStyleWrapper } from '@idealyst/theme';
|
|
5
|
+
import { defineStyle, ThemeStyleWrapper, CompoundVariants } from '@idealyst/theme';
|
|
6
6
|
import type { Theme as BaseTheme, Intent, Size } from '@idealyst/theme';
|
|
7
7
|
|
|
8
8
|
// Required: Unistyles must see StyleSheet usage in original source to process this file
|
|
@@ -11,26 +11,34 @@ void StyleSheet;
|
|
|
11
11
|
// Wrap theme for $iterator support
|
|
12
12
|
type Theme = ThemeStyleWrapper<BaseTheme>;
|
|
13
13
|
|
|
14
|
-
export type
|
|
15
|
-
size
|
|
16
|
-
intent
|
|
17
|
-
rounded
|
|
14
|
+
export type ProgressVariants = {
|
|
15
|
+
size: Size;
|
|
16
|
+
intent: Intent;
|
|
17
|
+
rounded: boolean;
|
|
18
18
|
};
|
|
19
19
|
|
|
20
|
+
// Create intent variants dynamically from theme
|
|
21
|
+
function createIntentVariants(theme: Theme) {
|
|
22
|
+
const variants: Record<string, object> = {};
|
|
23
|
+
for (const intent in theme.intents) {
|
|
24
|
+
variants[intent] = {};
|
|
25
|
+
}
|
|
26
|
+
return variants;
|
|
27
|
+
}
|
|
28
|
+
|
|
20
29
|
/**
|
|
21
|
-
* Progress styles with
|
|
30
|
+
* Progress styles with static styles and variants.
|
|
22
31
|
*/
|
|
23
32
|
export const progressStyles = defineStyle('Progress', (theme: Theme) => ({
|
|
24
|
-
container:
|
|
33
|
+
container: {
|
|
25
34
|
gap: 4 as const,
|
|
26
|
-
}
|
|
35
|
+
},
|
|
27
36
|
|
|
28
|
-
linearTrack:
|
|
37
|
+
linearTrack: {
|
|
29
38
|
backgroundColor: theme.colors.border.secondary,
|
|
30
39
|
overflow: 'hidden' as const,
|
|
31
40
|
position: 'relative' as const,
|
|
32
41
|
variants: {
|
|
33
|
-
// $iterator expands for each progress size
|
|
34
42
|
size: {
|
|
35
43
|
height: theme.sizes.$progress.linearHeight,
|
|
36
44
|
},
|
|
@@ -39,36 +47,60 @@ export const progressStyles = defineStyle('Progress', (theme: Theme) => ({
|
|
|
39
47
|
false: { borderRadius: 0 },
|
|
40
48
|
},
|
|
41
49
|
},
|
|
42
|
-
}
|
|
50
|
+
},
|
|
43
51
|
|
|
44
|
-
linearBar:
|
|
52
|
+
linearBar: {
|
|
45
53
|
height: '100%' as const,
|
|
46
|
-
backgroundColor: theme.intents[intent].primary,
|
|
47
54
|
variants: {
|
|
48
55
|
rounded: {
|
|
49
56
|
true: { borderRadius: 9999 },
|
|
50
57
|
false: { borderRadius: 0 },
|
|
51
58
|
},
|
|
59
|
+
intent: createIntentVariants(theme),
|
|
52
60
|
},
|
|
61
|
+
compoundVariants: (() => {
|
|
62
|
+
const cv: CompoundVariants<keyof ProgressVariants> = [];
|
|
63
|
+
for (const intent in theme.intents) {
|
|
64
|
+
cv.push({
|
|
65
|
+
intent,
|
|
66
|
+
styles: {
|
|
67
|
+
backgroundColor: theme.intents[intent as Intent].primary,
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
return cv;
|
|
72
|
+
})(),
|
|
53
73
|
_web: {
|
|
54
74
|
transition: 'width 0.3s ease' as const,
|
|
55
75
|
},
|
|
56
|
-
}
|
|
76
|
+
},
|
|
57
77
|
|
|
58
|
-
indeterminateBar:
|
|
78
|
+
indeterminateBar: {
|
|
59
79
|
position: 'absolute' as const,
|
|
60
80
|
height: '100%' as const,
|
|
61
81
|
width: '40%' as const,
|
|
62
|
-
backgroundColor: theme.intents[intent].primary,
|
|
63
82
|
variants: {
|
|
64
83
|
rounded: {
|
|
65
84
|
true: { borderRadius: 9999 },
|
|
66
85
|
false: { borderRadius: 0 },
|
|
67
86
|
},
|
|
87
|
+
intent: createIntentVariants(theme),
|
|
68
88
|
},
|
|
69
|
-
|
|
89
|
+
compoundVariants: (() => {
|
|
90
|
+
const cv: CompoundVariants<keyof ProgressVariants> = [];
|
|
91
|
+
for (const intent in theme.intents) {
|
|
92
|
+
cv.push({
|
|
93
|
+
intent,
|
|
94
|
+
styles: {
|
|
95
|
+
backgroundColor: theme.intents[intent as Intent].primary,
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
return cv;
|
|
100
|
+
})(),
|
|
101
|
+
},
|
|
70
102
|
|
|
71
|
-
circularContainer:
|
|
103
|
+
circularContainer: {
|
|
72
104
|
alignItems: 'center' as const,
|
|
73
105
|
justifyContent: 'center' as const,
|
|
74
106
|
position: 'relative' as const,
|
|
@@ -78,21 +110,35 @@ export const progressStyles = defineStyle('Progress', (theme: Theme) => ({
|
|
|
78
110
|
height: theme.sizes.$progress.circularSize,
|
|
79
111
|
},
|
|
80
112
|
},
|
|
81
|
-
}
|
|
113
|
+
},
|
|
82
114
|
|
|
83
|
-
circularTrack:
|
|
115
|
+
circularTrack: {
|
|
84
116
|
_web: {
|
|
85
117
|
stroke: theme.colors.border.secondary,
|
|
86
118
|
},
|
|
87
|
-
}
|
|
119
|
+
},
|
|
88
120
|
|
|
89
|
-
circularBar:
|
|
90
|
-
|
|
91
|
-
|
|
121
|
+
circularBar: {
|
|
122
|
+
variants: {
|
|
123
|
+
intent: createIntentVariants(theme),
|
|
92
124
|
},
|
|
93
|
-
|
|
125
|
+
compoundVariants: (() => {
|
|
126
|
+
const cv: CompoundVariants<keyof ProgressVariants> = [];
|
|
127
|
+
for (const intent in theme.intents) {
|
|
128
|
+
cv.push({
|
|
129
|
+
intent,
|
|
130
|
+
styles: {
|
|
131
|
+
_web: {
|
|
132
|
+
stroke: theme.intents[intent as Intent].primary,
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
return cv;
|
|
138
|
+
})(),
|
|
139
|
+
},
|
|
94
140
|
|
|
95
|
-
label:
|
|
141
|
+
label: {
|
|
96
142
|
color: theme.colors.text.primary,
|
|
97
143
|
textAlign: 'center' as const,
|
|
98
144
|
variants: {
|
|
@@ -100,9 +146,9 @@ export const progressStyles = defineStyle('Progress', (theme: Theme) => ({
|
|
|
100
146
|
fontSize: theme.sizes.$progress.labelFontSize,
|
|
101
147
|
},
|
|
102
148
|
},
|
|
103
|
-
}
|
|
149
|
+
},
|
|
104
150
|
|
|
105
|
-
circularLabel:
|
|
151
|
+
circularLabel: {
|
|
106
152
|
position: 'absolute' as const,
|
|
107
153
|
fontWeight: '600' as const,
|
|
108
154
|
color: theme.colors.text.primary,
|
|
@@ -111,5 +157,5 @@ export const progressStyles = defineStyle('Progress', (theme: Theme) => ({
|
|
|
111
157
|
fontSize: theme.sizes.$progress.circularLabelFontSize,
|
|
112
158
|
},
|
|
113
159
|
},
|
|
114
|
-
}
|
|
160
|
+
},
|
|
115
161
|
}));
|
|
@@ -23,21 +23,19 @@ const Progress: React.FC<ProgressProps> = ({
|
|
|
23
23
|
}) => {
|
|
24
24
|
const percentage = Math.min(Math.max((value / max) * 100, 0), 100);
|
|
25
25
|
|
|
26
|
-
// Apply variants (for size and rounded)
|
|
26
|
+
// Apply variants (for size, intent, and rounded)
|
|
27
27
|
progressStyles.useVariants({
|
|
28
28
|
size,
|
|
29
|
+
intent,
|
|
29
30
|
rounded,
|
|
30
31
|
});
|
|
31
32
|
|
|
32
|
-
// Compute dynamic styles with intent
|
|
33
|
-
const dynamicProps = { intent };
|
|
34
|
-
|
|
35
33
|
// Linear progress
|
|
36
|
-
const containerProps = getWebProps([
|
|
37
|
-
const trackProps = getWebProps([
|
|
38
|
-
const barProps = getWebProps([
|
|
39
|
-
const indeterminateProps = getWebProps([
|
|
40
|
-
const labelProps = getWebProps([progressStyles.label]);
|
|
34
|
+
const containerProps = getWebProps([progressStyles.container as any, style as any]);
|
|
35
|
+
const trackProps = getWebProps([progressStyles.linearTrack as any]);
|
|
36
|
+
const barProps = getWebProps([progressStyles.linearBar as any, { width: `${percentage}%` }]);
|
|
37
|
+
const indeterminateProps = getWebProps([progressStyles.indeterminateBar as any]);
|
|
38
|
+
const labelProps = getWebProps([progressStyles.label as any]);
|
|
41
39
|
|
|
42
40
|
const getCircularSize = () => {
|
|
43
41
|
if (size === 'sm') return 32;
|
|
@@ -53,13 +51,13 @@ const Progress: React.FC<ProgressProps> = ({
|
|
|
53
51
|
const strokeDashoffset = indeterminate ? circumference * 0.25 : circumference - (percentage / 100) * circumference;
|
|
54
52
|
|
|
55
53
|
const computedContainerProps = getWebProps([
|
|
56
|
-
progressStyles.circularContainer,
|
|
57
|
-
style,
|
|
54
|
+
progressStyles.circularContainer as any,
|
|
55
|
+
style as any,
|
|
58
56
|
{ display: 'inline-flex' }
|
|
59
57
|
]);
|
|
60
|
-
const
|
|
61
|
-
const trackColorProps = getWebProps([progressStyles.circularTrack]);
|
|
62
|
-
const barColorProps = getWebProps([
|
|
58
|
+
const circularLabelProps = getWebProps([progressStyles.circularLabel as any]);
|
|
59
|
+
const trackColorProps = getWebProps([progressStyles.circularTrack as any]);
|
|
60
|
+
const barColorProps = getWebProps([progressStyles.circularBar as any]);
|
|
63
61
|
|
|
64
62
|
return (
|
|
65
63
|
<div {...computedContainerProps} id={id} data-testid={testID}>
|
|
@@ -93,7 +91,7 @@ const Progress: React.FC<ProgressProps> = ({
|
|
|
93
91
|
/>
|
|
94
92
|
</svg>
|
|
95
93
|
{showLabel && (
|
|
96
|
-
<span {...
|
|
94
|
+
<span {...circularLabelProps}>
|
|
97
95
|
{label || `${Math.round(percentage)}%`}
|
|
98
96
|
</span>
|
|
99
97
|
)}
|
|
@@ -56,7 +56,7 @@ const SVGImage = forwardRef<HTMLDivElement, SVGImageProps>(({
|
|
|
56
56
|
|
|
57
57
|
// Use getWebProps to generate className and ref for web
|
|
58
58
|
const containerWebProps = getWebProps(containerStyleArray);
|
|
59
|
-
const imageWebProps = getWebProps(svgImageStyles.image);
|
|
59
|
+
const imageWebProps = getWebProps([svgImageStyles.image as any]);
|
|
60
60
|
|
|
61
61
|
// Apply custom color if provided
|
|
62
62
|
// Convert React Native resize modes to CSS object-fit values
|