@idealyst/components 1.3.2 → 1.3.4

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@idealyst/components",
3
- "version": "1.3.2",
3
+ "version": "1.3.4",
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.3.2",
59
+ "@idealyst/theme": "^1.3.4",
60
60
  "@mdi/js": ">=7.0.0",
61
61
  "@mdi/react": ">=1.0.0",
62
62
  "@react-native-vector-icons/common": ">=12.0.0",
@@ -111,8 +111,8 @@
111
111
  },
112
112
  "devDependencies": {
113
113
  "@idealyst/blur": "^1.2.40",
114
- "@idealyst/theme": "^1.3.2",
115
- "@idealyst/tooling": "^1.3.2",
114
+ "@idealyst/theme": "^1.3.4",
115
+ "@idealyst/tooling": "^1.3.4",
116
116
  "@mdi/react": "^1.6.1",
117
117
  "@types/react": "^19.1.0",
118
118
  "react": "^19.1.0",
@@ -17,34 +17,26 @@ interface BreadcrumbItemProps {
17
17
  }
18
18
 
19
19
  const BreadcrumbItem: React.FC<BreadcrumbItemProps> = ({ item, isLast, size, intent, itemStyle }) => {
20
- const isClickable = !!item.onPress && !item.disabled;
21
20
  const isDisabled = item.disabled || false;
22
21
 
23
- // Apply size variant
24
22
  breadcrumbStyles.useVariants({
25
23
  size,
26
- });
27
-
28
- // Get dynamic item text style
29
- const itemTextStyle = (breadcrumbStyles.itemText as any)({
30
24
  intent,
31
- isLast,
25
+ active: isLast,
32
26
  disabled: isDisabled,
33
- clickable: isClickable,
34
27
  });
35
28
 
36
- const iconStyle = (breadcrumbStyles.icon as any)({});
29
+ const iconSize = (breadcrumbStyles.icon as any).width || 16;
37
30
 
38
31
  const renderIcon = () => {
39
32
  if (!item.icon) return null;
40
33
 
41
34
  if (typeof item.icon === 'string') {
42
- const iconSize = iconStyle.width || 16;
43
35
  return (
44
36
  <Icon
45
37
  name={item.icon as IconName}
46
38
  size={iconSize}
47
- style={iconStyle}
39
+ style={breadcrumbStyles.icon}
48
40
  />
49
41
  );
50
42
  } else if (isValidElement(item.icon)) {
@@ -54,16 +46,14 @@ const BreadcrumbItem: React.FC<BreadcrumbItemProps> = ({ item, isLast, size, int
54
46
  return null;
55
47
  };
56
48
 
57
- const itemContainerStyle = (breadcrumbStyles.item as any)({});
58
-
59
49
  const content = (
60
- <View style={[itemContainerStyle, itemStyle]}>
61
- {item.icon && <View style={iconStyle}>{renderIcon()}</View>}
62
- <Text style={itemTextStyle}>{item.label}</Text>
50
+ <View style={[breadcrumbStyles.item, itemStyle]}>
51
+ {item.icon && <View style={breadcrumbStyles.icon}>{renderIcon()}</View>}
52
+ <Text style={breadcrumbStyles.itemText}>{item.label}</Text>
63
53
  </View>
64
54
  );
65
55
 
66
- if (isClickable) {
56
+ if (!!item.onPress && !item.disabled) {
67
57
  return (
68
58
  <Pressable
69
59
  onPress={item.onPress}
@@ -89,12 +79,12 @@ interface BreadcrumbSeparatorProps {
89
79
 
90
80
  const BreadcrumbSeparator: React.FC<BreadcrumbSeparatorProps> = ({ separator, size, separatorStyle }) => {
91
81
  breadcrumbStyles.useVariants({ size });
92
- const sepStyle = (breadcrumbStyles.separator as any)({});
93
82
 
94
83
  if (typeof separator === 'string') {
95
- return <Text style={[sepStyle, separatorStyle]}>{separator}</Text>;
84
+ return <Text style={[breadcrumbStyles.separator, separatorStyle]}>{separator}</Text>;
96
85
  }
97
- return <View style={[sepStyle, separatorStyle]}>{separator}</View>;
86
+
87
+ return <View style={[breadcrumbStyles.separatorIcon, separatorStyle]}>{separator}</View>;
98
88
  };
99
89
 
100
90
  interface BreadcrumbEllipsisProps {
@@ -103,13 +93,11 @@ interface BreadcrumbEllipsisProps {
103
93
  }
104
94
 
105
95
  const BreadcrumbEllipsis: React.FC<BreadcrumbEllipsisProps> = ({ size, intent }) => {
106
- breadcrumbStyles.useVariants({ size });
107
- const ellipsisStyle = (breadcrumbStyles.ellipsis as any)({});
108
- const iconStyle = (breadcrumbStyles.ellipsisIcon as any)({ intent });
96
+ breadcrumbStyles.useVariants({ size, intent });
109
97
 
110
98
  return (
111
- <View style={ellipsisStyle}>
112
- <Icon name="dots-horizontal" style={iconStyle} />
99
+ <View style={breadcrumbStyles.ellipsis}>
100
+ <Icon name="dots-horizontal" style={breadcrumbStyles.ellipsisIcon} />
113
101
  </View>
114
102
  );
115
103
  };
@@ -130,11 +118,7 @@ const Breadcrumb = forwardRef<IdealystElement, BreadcrumbProps>(({
130
118
  }, ref) => {
131
119
  const [menuOpen, setMenuOpen] = useState(false);
132
120
 
133
- // Apply variants
134
- breadcrumbStyles.useVariants({ size });
135
- const containerStyle = (breadcrumbStyles.container as any)({});
136
- const menuButtonStyle = (breadcrumbStyles.menuButton as any)({});
137
- const menuIconStyle = (breadcrumbStyles.menuButtonIcon as any)({ intent });
121
+ breadcrumbStyles.useVariants({ size, intent });
138
122
 
139
123
  // Handle responsive collapsing
140
124
  let displayItems = items;
@@ -172,7 +156,7 @@ const Breadcrumb = forwardRef<IdealystElement, BreadcrumbProps>(({
172
156
  <View
173
157
  ref={ref as any}
174
158
  nativeID={id}
175
- style={[containerStyle, style]}
159
+ style={[breadcrumbStyles.container, style]}
176
160
  testID={testID}
177
161
  accessibilityLabel="Breadcrumb"
178
162
  >
@@ -200,11 +184,11 @@ const Breadcrumb = forwardRef<IdealystElement, BreadcrumbProps>(({
200
184
  size={size}
201
185
  >
202
186
  <Pressable
203
- style={menuButtonStyle}
187
+ style={breadcrumbStyles.menuButton}
204
188
  accessibilityRole="button"
205
189
  accessibilityLabel="Show more breadcrumb items"
206
190
  >
207
- <Icon name="dots-horizontal" style={menuIconStyle} />
191
+ <Icon name="dots-horizontal" style={breadcrumbStyles.menuButtonIcon} />
208
192
  </Pressable>
209
193
  </Menu>
210
194
  <BreadcrumbSeparator separator={separator} size={size} separatorStyle={separatorStyle} />
@@ -1,9 +1,10 @@
1
1
  /**
2
- * Breadcrumb styles using defineStyle with $iterator expansion.
2
+ * Breadcrumb styles using defineStyle with static variants.
3
3
  */
4
4
  import { StyleSheet } from 'react-native-unistyles';
5
5
  import { defineStyle, ThemeStyleWrapper } from '@idealyst/theme';
6
- import type { Theme as BaseTheme, Size } from '@idealyst/theme';
6
+ import type { Theme as BaseTheme } from '@idealyst/theme';
7
+ import { ViewStyleSize } from '../utils/viewStyleProps';
7
8
 
8
9
  // Required: Unistyles must see StyleSheet usage in original source to process this file
9
10
  void StyleSheet;
@@ -11,59 +12,61 @@ void StyleSheet;
11
12
  // Wrap theme for $iterator support
12
13
  type Theme = ThemeStyleWrapper<BaseTheme>;
13
14
 
14
- type BreadcrumbIntent = 'primary' | 'neutral';
15
-
16
- export type BreadcrumbDynamicProps = {
17
- size?: Size;
18
- intent?: BreadcrumbIntent;
15
+ export type BreadcrumbVariants = {
16
+ size?: ViewStyleSize;
17
+ intent?: 'primary' | 'neutral';
18
+ active?: boolean;
19
19
  disabled?: boolean;
20
- isLast?: boolean;
21
- clickable?: boolean;
22
20
  };
23
21
 
24
22
  /**
25
- * Breadcrumb styles with intent and state handling.
23
+ * Breadcrumb styles with static variants.
26
24
  */
27
25
  export const breadcrumbStyles = defineStyle('Breadcrumb', (theme: Theme) => ({
28
- container: (_props: BreadcrumbDynamicProps) => ({
26
+ container: {
29
27
  display: 'flex' as const,
30
28
  flexDirection: 'row' as const,
31
29
  alignItems: 'center' as const,
32
30
  flexWrap: 'wrap' as const,
33
- gap: 8,
34
- }),
31
+ gap: 4,
32
+ },
35
33
 
36
- item: (_props: BreadcrumbDynamicProps) => ({
34
+ item: {
37
35
  display: 'flex' as const,
38
36
  flexDirection: 'row' as const,
39
37
  alignItems: 'center' as const,
40
38
  gap: 4,
41
- }),
42
-
43
- itemText: ({ intent = 'primary', isLast = false, disabled = false, clickable = true }: BreadcrumbDynamicProps) => {
44
- // Get color based on state - inline for Unistyles to trace
45
- const color = disabled
46
- ? theme.colors.text.secondary
47
- : isLast
48
- ? theme.colors.text.primary
49
- : clickable
50
- ? (intent === 'primary' ? theme.intents.primary.primary : theme.colors.text.secondary)
51
- : theme.colors.text.secondary;
39
+ opacity: 0.7,
40
+ variants: {
41
+ active: {
42
+ true: { opacity: 1 },
43
+ false: { _web: { _hover: { opacity: 1 } } },
44
+ },
45
+ disabled: {
46
+ true: { opacity: 0.5 },
47
+ false: {},
48
+ },
49
+ },
50
+ _web: {
51
+ transition: 'opacity 0.2s ease',
52
+ },
53
+ },
52
54
 
53
- return {
54
- color,
55
- opacity: disabled ? 0.5 : 1,
56
- variants: {
57
- // $iterator expands for each breadcrumb size
58
- size: {
59
- fontSize: theme.sizes.$breadcrumb.fontSize,
60
- lineHeight: theme.sizes.$breadcrumb.lineHeight,
61
- },
55
+ itemText: {
56
+ color: theme.colors.text.primary,
57
+ variants: {
58
+ size: {
59
+ fontSize: theme.sizes.$breadcrumb.fontSize,
60
+ lineHeight: theme.sizes.$breadcrumb.lineHeight,
61
+ },
62
+ disabled: {
63
+ true: { color: theme.colors.text.secondary },
64
+ false: {},
62
65
  },
63
- } as const;
66
+ },
64
67
  },
65
68
 
66
- icon: (_props: BreadcrumbDynamicProps) => ({
69
+ icon: {
67
70
  variants: {
68
71
  size: {
69
72
  width: theme.sizes.$breadcrumb.iconSize,
@@ -71,48 +74,72 @@ export const breadcrumbStyles = defineStyle('Breadcrumb', (theme: Theme) => ({
71
74
  fontSize: theme.sizes.$breadcrumb.iconSize,
72
75
  },
73
76
  },
74
- }),
77
+ },
75
78
 
76
- separator: (_props: BreadcrumbDynamicProps) => ({
79
+ separator: {
77
80
  color: theme.colors.text.tertiary,
81
+ opacity: 0.9,
78
82
  variants: {
79
83
  size: {
80
84
  fontSize: theme.sizes.$breadcrumb.fontSize,
81
85
  lineHeight: theme.sizes.$breadcrumb.lineHeight,
82
86
  },
83
87
  },
84
- }),
88
+ },
85
89
 
86
- ellipsis: (_props: BreadcrumbDynamicProps) => ({
90
+ separatorIcon: {
87
91
  display: 'flex' as const,
88
92
  alignItems: 'center' as const,
89
93
  justifyContent: 'center' as const,
90
- }),
94
+ color: theme.colors.text.tertiary,
95
+ opacity: 0.9,
96
+ variants: {
97
+ size: {
98
+ width: theme.sizes.$breadcrumb.iconSize,
99
+ height: theme.sizes.$breadcrumb.iconSize,
100
+ fontSize: theme.sizes.$breadcrumb.iconSize,
101
+ },
102
+ },
103
+ },
104
+
105
+ ellipsis: {
106
+ display: 'flex' as const,
107
+ alignItems: 'center' as const,
108
+ justifyContent: 'center' as const,
109
+ },
91
110
 
92
- ellipsisIcon: ({ intent = 'primary' }: BreadcrumbDynamicProps) => ({
93
- color: intent === 'primary' ? theme.intents.primary.primary : theme.colors.text.secondary,
111
+ ellipsisIcon: {
112
+ color: theme.colors.text.secondary,
94
113
  variants: {
95
114
  size: {
96
115
  width: theme.sizes.$breadcrumb.iconSize,
97
116
  height: theme.sizes.$breadcrumb.iconSize,
98
117
  fontSize: theme.sizes.$breadcrumb.iconSize,
99
118
  },
119
+ intent: {
120
+ primary: { color: theme.intents.primary.primary },
121
+ neutral: { color: theme.colors.text.secondary },
122
+ },
100
123
  },
101
- }),
124
+ },
102
125
 
103
- menuButton: (_props: BreadcrumbDynamicProps) => ({
126
+ menuButton: {
104
127
  paddingVertical: 4,
105
128
  paddingHorizontal: 8,
106
- }),
129
+ },
107
130
 
108
- menuButtonIcon: ({ intent = 'primary' }: BreadcrumbDynamicProps) => ({
109
- color: intent === 'primary' ? theme.intents.primary.primary : theme.colors.text.secondary,
131
+ menuButtonIcon: {
132
+ color: theme.colors.text.secondary,
110
133
  variants: {
111
134
  size: {
112
135
  width: theme.sizes.$breadcrumb.iconSize,
113
136
  height: theme.sizes.$breadcrumb.iconSize,
114
137
  fontSize: theme.sizes.$breadcrumb.iconSize,
115
138
  },
139
+ intent: {
140
+ primary: { color: theme.intents.primary.primary },
141
+ neutral: { color: theme.colors.text.secondary },
142
+ },
116
143
  },
117
- }),
144
+ },
118
145
  }));
@@ -19,24 +19,16 @@ const BreadcrumbItem: React.FC<BreadcrumbItemProps> = ({ item, isLast, size, int
19
19
  const isClickable = !!item.onPress && !item.disabled;
20
20
  const isDisabled = item.disabled || false;
21
21
 
22
- // Apply size variant
23
22
  breadcrumbStyles.useVariants({
24
23
  size,
25
- });
26
-
27
- // Get dynamic styles - call as functions for theme reactivity
28
- const itemStyle_ = (breadcrumbStyles.item as any)({});
29
- const itemTextStyle = (breadcrumbStyles.itemText as any)({
30
24
  intent,
31
- isLast,
25
+ active: isLast,
32
26
  disabled: isDisabled,
33
- clickable: isClickable,
34
27
  });
35
- const iconStyle = (breadcrumbStyles.icon as any)({});
36
28
 
37
- const itemProps = getWebProps([itemStyle_]);
38
- const itemTextProps = getWebProps([itemTextStyle, itemStyle]);
39
- const iconProps = getWebProps([iconStyle]);
29
+ const itemProps = getWebProps([breadcrumbStyles.item]);
30
+ const itemTextProps = getWebProps([breadcrumbStyles.itemText, itemStyle]);
31
+ const iconProps = getWebProps([breadcrumbStyles.icon]);
40
32
 
41
33
  const handleClick = () => {
42
34
  if (!item.disabled && item.onPress) {
@@ -61,48 +53,60 @@ const BreadcrumbItem: React.FC<BreadcrumbItemProps> = ({ item, isLast, size, int
61
53
  return null;
62
54
  };
63
55
 
64
- const content = (
65
- <div {...itemProps}>
66
- {item.icon && (
67
- <span
68
- {...iconProps}
69
- style={{
70
- display: 'inline-flex',
71
- alignItems: 'center',
72
- justifyContent: 'center',
73
- }}
74
- >
75
- {renderIcon()}
76
- </span>
77
- )}
78
- <span {...itemTextProps}>
79
- {item.label}
80
- </span>
81
- </div>
82
- );
83
-
84
56
  if (isClickable) {
85
57
  return (
86
58
  <button
59
+ {...itemProps}
87
60
  onClick={handleClick}
61
+ disabled={isDisabled}
62
+ aria-current={isLast ? 'page' : undefined}
88
63
  style={{
89
64
  background: 'none',
90
65
  border: 'none',
91
66
  padding: 0,
67
+ margin: 0,
92
68
  cursor: 'pointer',
93
69
  textDecoration: 'none',
70
+ font: 'inherit',
71
+ color: 'inherit',
94
72
  }}
95
- disabled={isDisabled}
96
- aria-current={isLast ? 'page' : undefined}
97
73
  >
98
- {content}
74
+ {item.icon && (
75
+ <span
76
+ {...iconProps}
77
+ style={{
78
+ display: 'inline-flex',
79
+ alignItems: 'center',
80
+ justifyContent: 'center',
81
+ }}
82
+ >
83
+ {renderIcon()}
84
+ </span>
85
+ )}
86
+ <span {...itemTextProps}>
87
+ {item.label}
88
+ </span>
99
89
  </button>
100
90
  );
101
91
  }
102
92
 
103
93
  return (
104
- <div aria-current={isLast ? 'page' : undefined}>
105
- {content}
94
+ <div {...itemProps} aria-current={isLast ? 'page' : undefined}>
95
+ {item.icon && (
96
+ <span
97
+ {...iconProps}
98
+ style={{
99
+ display: 'inline-flex',
100
+ alignItems: 'center',
101
+ justifyContent: 'center',
102
+ }}
103
+ >
104
+ {renderIcon()}
105
+ </span>
106
+ )}
107
+ <span {...itemTextProps}>
108
+ {item.label}
109
+ </span>
106
110
  </div>
107
111
  );
108
112
  };
@@ -115,11 +119,20 @@ interface BreadcrumbSeparatorProps {
115
119
 
116
120
  const BreadcrumbSeparator: React.FC<BreadcrumbSeparatorProps> = ({ separator, size, separatorStyle }) => {
117
121
  breadcrumbStyles.useVariants({ size });
118
- const separatorStyle_ = (breadcrumbStyles.separator as any)({});
119
- const separatorProps = getWebProps([separatorStyle_, separatorStyle]);
122
+ const isTextSeparator = typeof separator === 'string';
123
+
124
+ if (isTextSeparator) {
125
+ const separatorProps = getWebProps([breadcrumbStyles.separator, separatorStyle]);
126
+ return (
127
+ <span {...separatorProps} aria-hidden="true">
128
+ {separator}
129
+ </span>
130
+ );
131
+ }
120
132
 
133
+ const separatorIconProps = getWebProps([breadcrumbStyles.separatorIcon, separatorStyle]);
121
134
  return (
122
- <span {...separatorProps} aria-hidden="true">
135
+ <span {...separatorIconProps} aria-hidden="true">
123
136
  {separator}
124
137
  </span>
125
138
  );
@@ -131,11 +144,9 @@ interface BreadcrumbEllipsisProps {
131
144
  }
132
145
 
133
146
  const BreadcrumbEllipsis: React.FC<BreadcrumbEllipsisProps> = ({ size, intent }) => {
134
- breadcrumbStyles.useVariants({ size });
135
- const ellipsisStyle = (breadcrumbStyles.ellipsis as any)({});
136
- const ellipsisIconStyle = (breadcrumbStyles.ellipsisIcon as any)({ intent });
137
- const ellipsisProps = getWebProps([ellipsisStyle]);
138
- const iconProps = getWebProps([ellipsisIconStyle]);
147
+ breadcrumbStyles.useVariants({ size, intent });
148
+ const ellipsisProps = getWebProps([breadcrumbStyles.ellipsis]);
149
+ const iconProps = getWebProps([breadcrumbStyles.ellipsisIcon]);
139
150
 
140
151
  return (
141
152
  <span {...ellipsisProps}>
@@ -168,17 +179,10 @@ const Breadcrumb: React.FC<BreadcrumbProps> = ({
168
179
  }) => {
169
180
  const [menuOpen, setMenuOpen] = useState(false);
170
181
 
171
- // Get dynamic styles - call as functions for theme reactivity
172
- const containerStyle = (breadcrumbStyles.container as any)({});
173
- const menuButtonStyle = (breadcrumbStyles.menuButton as any)({});
174
- const menuButtonIconStyle = (breadcrumbStyles.menuButtonIcon as any)({ intent });
175
-
176
- const containerProps = getWebProps([containerStyle, style as any]);
177
-
178
- // Apply variants for menu button
179
- breadcrumbStyles.useVariants({ size });
180
- const menuButtonProps = getWebProps([menuButtonStyle]);
181
- const menuIconProps = getWebProps([menuButtonIconStyle]);
182
+ breadcrumbStyles.useVariants({ size, intent });
183
+ const containerProps = getWebProps([breadcrumbStyles.container, style as any]);
184
+ const menuButtonProps = getWebProps([breadcrumbStyles.menuButton]);
185
+ const menuIconProps = getWebProps([breadcrumbStyles.menuButtonIcon]);
182
186
 
183
187
  // Handle responsive collapsing
184
188
  let displayItems = items;
@@ -81,6 +81,14 @@ export const buttonStyles = defineStyle('Button', (theme: Theme) => ({
81
81
  backgroundColor: 'transparent',
82
82
  borderColor: 'transparent',
83
83
  borderWidth: 0,
84
+ _web: {
85
+ _hover: {
86
+ backgroundColor: theme.colors.surface.secondary,
87
+ },
88
+ _active: {
89
+ backgroundColor: theme.colors.surface.tertiary,
90
+ },
91
+ },
84
92
  }
85
93
  },
86
94
  size: {
@@ -76,6 +76,14 @@ export const iconButtonStyles = defineStyle('IconButton', (theme: Theme) => ({
76
76
  backgroundColor: 'transparent',
77
77
  borderColor: 'transparent',
78
78
  borderWidth: 0,
79
+ _web: {
80
+ _hover: {
81
+ backgroundColor: theme.colors.surface.secondary,
82
+ },
83
+ _active: {
84
+ backgroundColor: theme.colors.surface.tertiary,
85
+ },
86
+ },
79
87
  }
80
88
  },
81
89
  // Size variants - circular so width equals height
@@ -0,0 +1,11 @@
1
+ /**
2
+ * IconButton Documentation Sample Props
3
+ */
4
+
5
+ import type { SampleProps } from '@idealyst/tooling';
6
+
7
+ export const sampleProps: SampleProps = {
8
+ props: {
9
+ icon: 'heart',
10
+ },
11
+ };