@idealyst/components 1.1.8 → 1.2.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.
Files changed (46) hide show
  1. package/package.json +3 -3
  2. package/plugin/web.js +280 -532
  3. package/src/Accordion/Accordion.web.tsx +1 -3
  4. package/src/Alert/Alert.web.tsx +3 -4
  5. package/src/Badge/Badge.web.tsx +8 -15
  6. package/src/Breadcrumb/Breadcrumb.web.tsx +4 -8
  7. package/src/Button/Button.native.tsx +14 -21
  8. package/src/Button/Button.styles.tsx +15 -0
  9. package/src/Button/Button.web.tsx +9 -19
  10. package/src/Checkbox/Checkbox.web.tsx +1 -2
  11. package/src/Chip/Chip.web.tsx +3 -5
  12. package/src/Dialog/Dialog.web.tsx +3 -3
  13. package/src/Dialog/types.ts +1 -1
  14. package/src/Icon/Icon.web.tsx +22 -17
  15. package/src/Icon/IconRegistry.native.ts +41 -0
  16. package/src/Icon/IconRegistry.ts +107 -0
  17. package/src/Icon/IconSvg/IconSvg.web.tsx +26 -5
  18. package/src/Icon/icon-resolver.ts +12 -43
  19. package/src/Icon/index.native.ts +2 -1
  20. package/src/Icon/index.ts +1 -0
  21. package/src/Icon/index.web.ts +1 -0
  22. package/src/Input/Input.styles.tsx +56 -83
  23. package/src/Input/Input.web.tsx +5 -8
  24. package/src/List/ListItem.native.tsx +6 -7
  25. package/src/List/ListItem.web.tsx +3 -3
  26. package/src/Menu/MenuItem.web.tsx +3 -5
  27. package/src/Screen/Screen.native.tsx +1 -1
  28. package/src/Screen/Screen.styles.tsx +3 -6
  29. package/src/Screen/Screen.web.tsx +1 -1
  30. package/src/Select/Select.styles.tsx +31 -48
  31. package/src/Select/Select.web.tsx +45 -33
  32. package/src/Slider/Slider.web.tsx +2 -4
  33. package/src/Switch/Switch.native.tsx +2 -2
  34. package/src/Switch/Switch.web.tsx +2 -3
  35. package/src/Table/Table.native.tsx +168 -65
  36. package/src/Table/Table.styles.tsx +26 -33
  37. package/src/Table/Table.web.tsx +169 -70
  38. package/src/Text/Text.web.tsx +1 -0
  39. package/src/TextArea/TextArea.native.tsx +21 -8
  40. package/src/TextArea/TextArea.styles.tsx +15 -27
  41. package/src/TextArea/TextArea.web.tsx +17 -6
  42. package/src/View/View.native.tsx +33 -3
  43. package/src/View/View.web.tsx +4 -21
  44. package/src/View/types.ts +31 -3
  45. package/src/examples/ButtonExamples.tsx +20 -0
  46. package/src/index.ts +1 -1
@@ -1,9 +1,135 @@
1
- import React, { useMemo } from 'react';
1
+ import React, { useMemo, ReactNode } from 'react';
2
2
  import { getWebProps } from 'react-native-unistyles/web';
3
3
  import { tableStyles } from './Table.styles';
4
- import type { TableProps, TableColumn } from './types';
4
+ import type { TableProps, TableColumn, TableType, TableSizeVariant, TableAlignVariant } from './types';
5
5
  import { getWebAriaProps } from '../utils/accessibility';
6
6
 
7
+ // ============================================================================
8
+ // Sub-component Props
9
+ // ============================================================================
10
+
11
+ interface TRProps {
12
+ children: ReactNode;
13
+ size?: TableSizeVariant;
14
+ type?: TableType;
15
+ clickable?: boolean;
16
+ onClick?: () => void;
17
+ testID?: string;
18
+ }
19
+
20
+ interface THProps {
21
+ children: ReactNode;
22
+ size?: TableSizeVariant;
23
+ type?: TableType;
24
+ align?: TableAlignVariant;
25
+ width?: number | string;
26
+ accessibilitySort?: 'ascending' | 'descending' | 'none' | 'other';
27
+ }
28
+
29
+ interface TDProps {
30
+ children: ReactNode;
31
+ size?: TableSizeVariant;
32
+ type?: TableType;
33
+ align?: TableAlignVariant;
34
+ width?: number | string;
35
+ }
36
+
37
+ // ============================================================================
38
+ // TR Component
39
+ // ============================================================================
40
+
41
+ function TR({
42
+ children,
43
+ size = 'md',
44
+ type = 'standard',
45
+ clickable = false,
46
+ onClick,
47
+ testID,
48
+ }: TRProps) {
49
+ tableStyles.useVariants({
50
+ size,
51
+ type,
52
+ clickable,
53
+ });
54
+
55
+ const rowProps = getWebProps([(tableStyles.row as any)({})]);
56
+
57
+ return (
58
+ <tr
59
+ {...rowProps}
60
+ onClick={onClick}
61
+ data-testid={testID}
62
+ >
63
+ {children}
64
+ </tr>
65
+ );
66
+ }
67
+
68
+ // ============================================================================
69
+ // TH Component
70
+ // ============================================================================
71
+
72
+ function TH({
73
+ children,
74
+ size = 'md',
75
+ type = 'standard',
76
+ align = 'left',
77
+ width,
78
+ accessibilitySort,
79
+ }: THProps) {
80
+ tableStyles.useVariants({
81
+ size,
82
+ type,
83
+ align,
84
+ });
85
+
86
+ const headerCellProps = getWebProps([(tableStyles.headerCell as any)({})]);
87
+
88
+ return (
89
+ <th
90
+ {...headerCellProps}
91
+ scope="col"
92
+ aria-sort={accessibilitySort}
93
+ style={{ width }}
94
+ >
95
+ {children}
96
+ </th>
97
+ );
98
+ }
99
+
100
+ // ============================================================================
101
+ // TD Component
102
+ // ============================================================================
103
+
104
+ function TD({
105
+ children,
106
+ size = 'md',
107
+ type = 'standard',
108
+ align = 'left',
109
+ width,
110
+ }: TDProps) {
111
+ tableStyles.useVariants({
112
+ size,
113
+ type,
114
+ align,
115
+ });
116
+
117
+ const cellProps = getWebProps([(tableStyles.cell as any)({})]);
118
+
119
+ return (
120
+ <td
121
+ {...cellProps}
122
+ style={{ width }}
123
+ >
124
+ {children}
125
+ </td>
126
+ );
127
+ }
128
+
129
+ // ============================================================================
130
+ // Main Table Component
131
+ // ============================================================================
132
+
7
133
  function Table<T = any>({
8
134
  columns,
9
135
  data,
@@ -37,7 +163,8 @@ function Table<T = any>({
37
163
  accessibilityHidden,
38
164
  });
39
165
  }, [accessibilityLabel, accessibilityHint, accessibilityRole, accessibilityHidden]);
40
- // Apply variants
166
+
167
+ // Apply variants for container
41
168
  tableStyles.useVariants({
42
169
  type,
43
170
  size,
@@ -51,7 +178,7 @@ function Table<T = any>({
51
178
  });
52
179
 
53
180
  const containerProps = getWebProps([(tableStyles.container as any)({}), style as any]);
54
- const tableProps = getWebProps([tableStyles.table]);
181
+ const tableProps = getWebProps([(tableStyles.table as any)({})]);
55
182
 
56
183
  // Helper to get cell value
57
184
  const getCellValue = (column: TableColumn<T>, row: T, rowIndex: number) => {
@@ -67,79 +194,51 @@ function Table<T = any>({
67
194
  return (
68
195
  <div {...containerProps} {...ariaProps} id={id} data-testid={testID}>
69
196
  <table {...tableProps} role="table">
70
- <thead {...getWebProps([tableStyles.thead])}>
197
+ <thead {...getWebProps([(tableStyles.thead as any)({})])}>
71
198
  <tr>
72
- {columns.map((column) => {
73
- tableStyles.useVariants({
74
- size,
75
- align: column.align || 'left',
76
- type,
77
- });
78
-
79
- const headerCellProps = getWebProps([tableStyles.headerCell]);
80
-
81
- return (
82
- <th
83
- key={column.key}
84
- {...headerCellProps}
85
- scope="col"
86
- aria-sort={column.accessibilitySort}
87
- style={{
88
- width: column.width,
89
- }}
90
- >
91
- {column.title}
92
- </th>
93
- );
94
- })}
199
+ {columns.map((column) => (
200
+ <TH
201
+ key={column.key}
202
+ size={size}
203
+ type={type}
204
+ align={column.align}
205
+ width={column.width}
206
+ accessibilitySort={column.accessibilitySort}
207
+ >
208
+ {column.title}
209
+ </TH>
210
+ ))}
95
211
  </tr>
96
212
  </thead>
97
- <tbody {...getWebProps([tableStyles.tbody])}>
98
- {data.map((row, rowIndex) => {
99
- tableStyles.useVariants({
100
- size,
101
- align: 'left',
102
- type,
103
- clickable: isClickable,
104
- });
105
-
106
- const rowProps = getWebProps([tableStyles.row]);
107
-
108
- return (
109
- <tr
110
- key={rowIndex}
111
- {...rowProps}
112
- onClick={() => onRowPress?.(row, rowIndex)}
113
- data-testid={`${testID}-row-${rowIndex}`}
114
- >
115
- {columns.map((column) => {
116
- tableStyles.useVariants({
117
- size,
118
- align: column.align || 'left',
119
- type,
120
- });
121
-
122
- const cellProps = getWebProps([tableStyles.cell]);
123
-
124
- return (
125
- <td
126
- key={column.key}
127
- {...cellProps}
128
- style={{
129
- width: column.width,
130
- }}
131
- >
132
- {getCellValue(column, row, rowIndex)}
133
- </td>
134
- );
135
- })}
136
- </tr>
137
- );
138
- })}
213
+ <tbody {...getWebProps([(tableStyles.tbody as any)({})])}>
214
+ {data.map((row, rowIndex) => (
215
+ <TR
216
+ key={rowIndex}
217
+ size={size}
218
+ type={type}
219
+ clickable={isClickable}
220
+ onClick={() => onRowPress?.(row, rowIndex)}
221
+ testID={testID ? `${testID}-row-${rowIndex}` : undefined}
222
+ >
223
+ {columns.map((column) => (
224
+ <TD
225
+ key={column.key}
226
+ size={size}
227
+ type={type}
228
+ align={column.align}
229
+ width={column.width}
230
+ >
231
+ {getCellValue(column, row, rowIndex)}
232
+ </TD>
233
+ ))}
234
+ </TR>
235
+ ))}
139
236
  </tbody>
140
237
  </table>
141
238
  </div>
142
239
  );
143
240
  }
144
241
 
242
+
243
+
145
244
  export default Table;
@@ -10,6 +10,7 @@ const Text = forwardRef<HTMLSpanElement, TextProps>(({
10
10
  weight,
11
11
  color = 'primary',
12
12
  align = 'left',
13
+ pre = false,
13
14
  // Spacing variants from TextSpacingStyleProps
14
15
  gap,
15
16
  padding,
@@ -129,20 +129,33 @@ const TextArea = forwardRef<TextInput, TextAreaProps>(({
129
129
 
130
130
  const showFooter = (error || helperText) || (showCharacterCount && maxLength);
131
131
 
132
+ // Get dynamic styles - call as functions for theme reactivity
133
+ const containerStyleComputed = (textAreaStyles.container as any)({});
134
+ const labelStyleComputed = (textAreaStyles.label as any)({ disabled });
135
+ const textareaContainerStyleComputed = (textAreaStyles.textareaContainer as any)({});
136
+ const textareaStyleComputed = (textAreaStyles.textarea as any)({ intent, disabled, hasError });
137
+ const footerStyleComputed = (textAreaStyles.footer as any)({});
138
+ const helperTextStyleComputed = (textAreaStyles.helperText as any)({ hasError });
139
+ const characterCountStyleComputed = (textAreaStyles.characterCount as any)({
140
+ isNearLimit: maxLength ? value.length >= maxLength * 0.9 : false,
141
+ isAtLimit: maxLength ? value.length >= maxLength : false,
142
+ });
143
+
132
144
  return (
133
- <View nativeID={id} style={[textAreaStyles.container, style]} testID={testID}>
145
+ <View nativeID={id} style={[containerStyleComputed, style]} testID={testID}>
134
146
  {label && (
135
- <Text style={textAreaStyles.label}>{label}</Text>
147
+ <Text style={labelStyleComputed}>{label}</Text>
136
148
  )}
137
149
 
138
- <View style={textAreaStyles.textareaContainer}>
150
+ <View style={textareaContainerStyleComputed}>
139
151
  <TextInput
140
152
  ref={ref}
141
153
  {...nativeA11yProps}
142
154
  style={[
143
- textAreaStyles.textarea({ intent, disabled, hasError }),
155
+ textareaStyleComputed,
144
156
  {
145
157
  textAlignVertical: 'top',
158
+ backgroundColor: 'transparent',
146
159
  },
147
160
  maxHeight && { maxHeight },
148
161
  { height: autoGrow ? contentHeight : rows * 24 },
@@ -161,22 +174,22 @@ const TextArea = forwardRef<TextInput, TextAreaProps>(({
161
174
  </View>
162
175
 
163
176
  {showFooter && (
164
- <View style={textAreaStyles.footer}>
177
+ <View style={footerStyleComputed}>
165
178
  <View style={{ flex: 1 }}>
166
179
  {error && (
167
- <Text style={textAreaStyles.helperText}>
180
+ <Text style={helperTextStyleComputed}>
168
181
  {error}
169
182
  </Text>
170
183
  )}
171
184
  {!error && helperText && (
172
- <Text style={textAreaStyles.helperText}>
185
+ <Text style={helperTextStyleComputed}>
173
186
  {helperText}
174
187
  </Text>
175
188
  )}
176
189
  </View>
177
190
 
178
191
  {showCharacterCount && maxLength && (
179
- <Text style={textAreaStyles.characterCount}>
192
+ <Text style={characterCountStyleComputed}>
180
193
  {value.length}/{maxLength}
181
194
  </Text>
182
195
  )}
@@ -35,6 +35,9 @@ export const textAreaStyles = defineStyle('TextArea', (theme: Theme) => ({
35
35
  display: 'flex' as const,
36
36
  flexDirection: 'column' as const,
37
37
  gap: 4,
38
+ borderWidth: 1,
39
+ borderColor: theme.colors.border.primary,
40
+ borderRadius: theme.radii.md,
38
41
  variants: {
39
42
  margin: {
40
43
  margin: theme.sizes.$view.padding,
@@ -52,38 +55,25 @@ export const textAreaStyles = defineStyle('TextArea', (theme: Theme) => ({
52
55
  fontSize: 14,
53
56
  fontWeight: '500' as const,
54
57
  color: theme.colors.text.primary,
55
- opacity: disabled ? 0.5 : 1,
56
58
  }),
57
59
 
58
60
  textareaContainer: (_props: TextAreaDynamicProps) => ({
59
61
  position: 'relative' as const,
60
- }),
61
-
62
- textarea: ({ intent = 'primary', disabled = false, hasError = false, resize = 'none' }: TextAreaDynamicProps) => {
63
- const intentValue = theme.intents[intent];
64
- const errorColor = theme.intents.error.primary;
65
-
66
- // Get border color based on state
67
- let borderColor = theme.colors.border.primary;
68
- if (hasError) {
69
- borderColor = errorColor;
70
- } else if (intent === 'success' || intent === 'warning') {
71
- borderColor = intentValue.primary;
62
+ variants: {
63
+ disabled: {
64
+ true: {
65
+ opacity: 1,
66
+ },
67
+ false: {
68
+ opacity: 0.8,
69
+ },
70
+ },
72
71
  }
72
+ }),
73
73
 
74
- // Get web-specific styles
75
- const webFocusStyles = hasError
76
- ? { borderColor: errorColor, boxShadow: `0 0 0 2px ${errorColor}33` }
77
- : { borderColor: intentValue.primary, boxShadow: `0 0 0 2px ${intentValue.primary}33` };
78
-
79
- return {
74
+ textarea: ({ disabled = false, resize = 'none' }: TextAreaDynamicProps) => ({
80
75
  width: '100%',
81
76
  color: theme.colors.text.primary,
82
- backgroundColor: disabled ? theme.colors.surface.secondary : theme.colors.surface.primary,
83
- borderWidth: 1,
84
- borderStyle: 'solid' as const,
85
- borderColor,
86
- borderRadius: 8,
87
77
  opacity: disabled ? 0.5 : 1,
88
78
  variants: {
89
79
  size: {
@@ -101,10 +91,8 @@ export const textAreaStyles = defineStyle('TextArea', (theme: Theme) => ({
101
91
  overflowY: 'hidden',
102
92
  cursor: disabled ? 'not-allowed' : 'text',
103
93
  resize: resize,
104
- _focus: disabled ? {} : webFocusStyles,
105
94
  },
106
- } as const;
107
- },
95
+ }),
108
96
 
109
97
  helperText: ({ hasError = false }: TextAreaDynamicProps) => ({
110
98
  fontSize: 12,
@@ -127,12 +127,23 @@ const TextArea = forwardRef<HTMLDivElement, TextAreaProps>(({
127
127
  marginHorizontal,
128
128
  });
129
129
 
130
- const containerProps = getWebProps([textAreaStyles.container, style as any]);
131
- const labelProps = getWebProps([textAreaStyles.label]);
132
- const textareaContainerProps = getWebProps([textAreaStyles.textareaContainer]);
133
- const footerProps = getWebProps([textAreaStyles.footer]);
134
- const helperTextProps = getWebProps([textAreaStyles.helperText]);
135
- const characterCountProps = getWebProps([textAreaStyles.characterCount]);
130
+ // Get dynamic styles - call as functions for theme reactivity
131
+ const containerStyle = (textAreaStyles.container as any)({});
132
+ const labelStyle = (textAreaStyles.label as any)({ disabled });
133
+ const textareaContainerStyle = (textAreaStyles.textareaContainer as any)({});
134
+ const footerStyle = (textAreaStyles.footer as any)({});
135
+ const helperTextStyle = (textAreaStyles.helperText as any)({ hasError });
136
+ const characterCountStyle = (textAreaStyles.characterCount as any)({
137
+ isNearLimit: maxLength ? value.length >= maxLength * 0.9 : false,
138
+ isAtLimit: maxLength ? value.length >= maxLength : false,
139
+ });
140
+
141
+ const containerProps = getWebProps([containerStyle, style as any]);
142
+ const labelProps = getWebProps([labelStyle]);
143
+ const textareaContainerProps = getWebProps([textareaContainerStyle]);
144
+ const footerProps = getWebProps([footerStyle]);
145
+ const helperTextProps = getWebProps([helperTextStyle]);
146
+ const characterCountProps = getWebProps([characterCountStyle]);
136
147
 
137
148
  const adjustHeight = () => {
138
149
  if (!autoGrow || !textareaRef.current) return;
@@ -1,8 +1,27 @@
1
1
  import React, { forwardRef } from 'react';
2
- import { View as RNView, ScrollView as RNScrollView, ViewStyle } from 'react-native';
2
+ import { View as RNView, ScrollView as RNScrollView, ViewStyle, StyleSheet } from 'react-native';
3
+ import { useResponsiveStyle, ResponsiveStyle } from '@idealyst/theme';
3
4
  import { ViewProps } from './types';
4
5
  import { viewStyles } from './View.styles';
5
6
 
7
+ /**
8
+ * Check if a style object contains any responsive values
9
+ */
10
+ function hasResponsiveValues(style: any): style is ResponsiveStyle {
11
+ if (!style || typeof style !== 'object' || Array.isArray(style)) return false;
12
+ for (const key in style) {
13
+ const value = style[key];
14
+ if (value && typeof value === 'object' && !Array.isArray(value) && !('$$typeof' in value)) {
15
+ // Check if it looks like a breakpoint map (has breakpoint-like keys)
16
+ const keys = Object.keys(value);
17
+ if (keys.some(k => ['xs', 'sm', 'md', 'lg', 'xl'].includes(k))) {
18
+ return true;
19
+ }
20
+ }
21
+ }
22
+ return false;
23
+ }
24
+
6
25
  const View = forwardRef<RNView | RNScrollView, ViewProps>(({
7
26
  children,
8
27
  background = 'transparent',
@@ -50,11 +69,22 @@ const View = forwardRef<RNView | RNScrollView, ViewProps>(({
50
69
  if (borderWidth !== undefined) overrideStyles.borderWidth = borderWidth;
51
70
  if (borderColor) overrideStyles.borderColor = borderColor;
52
71
 
72
+ // Flatten style array if needed and check for responsive values
73
+ const flattenedStyle = Array.isArray(style) ? StyleSheet.flatten(style) : style;
74
+
75
+ // Resolve responsive values if present (this hook is reactive to breakpoint changes)
76
+ const resolvedStyle = useResponsiveStyle(
77
+ hasResponsiveValues(flattenedStyle) ? flattenedStyle : {}
78
+ );
79
+
80
+ // Use resolved style if responsive, otherwise use original
81
+ const finalStyle = hasResponsiveValues(flattenedStyle) ? resolvedStyle : style;
82
+
53
83
  if (scrollable) {
54
84
  return (
55
85
  <RNScrollView
56
86
  ref={ref as any}
57
- style={[viewStyle, { flex: 1 }, overrideStyles, style]}
87
+ style={[viewStyle, { flex: 1 }, overrideStyles, finalStyle]}
58
88
  contentContainerStyle={[viewStyle, overrideStyles]}
59
89
  testID={testID}
60
90
  nativeID={id}
@@ -65,7 +95,7 @@ const View = forwardRef<RNView | RNScrollView, ViewProps>(({
65
95
  }
66
96
 
67
97
  return (
68
- <RNView ref={ref as any} style={[viewStyle, overrideStyles, style]} testID={testID} nativeID={id}>
98
+ <RNView ref={ref as any} style={[viewStyle, overrideStyles, finalStyle]} testID={testID} nativeID={id}>
69
99
  {children}
70
100
  </RNView>
71
101
  );
@@ -1,6 +1,6 @@
1
1
  import React, { forwardRef, useMemo } from 'react';
2
- import { StyleSheet } from 'react-native';
3
2
  import { getWebProps } from 'react-native-unistyles/web';
3
+ import { useResponsiveStyle } from '@idealyst/theme';
4
4
  import { ViewProps } from './types';
5
5
  import { viewStyles } from './View.styles';
6
6
  import useMergeRefs from '../hooks/useMergeRefs';
@@ -41,32 +41,15 @@ const View = forwardRef<HTMLDivElement, ViewProps>(({
41
41
  marginHorizontal,
42
42
  });
43
43
 
44
- // Create dynamic styles based on custom props (overrides variants)
45
- const dynamicStyles: any = {};
46
-
47
- if (backgroundColor) dynamicStyles.backgroundColor = backgroundColor;
48
- if (borderRadius !== undefined) dynamicStyles.borderRadius = borderRadius;
49
- if (borderWidth !== undefined) dynamicStyles.borderWidth = borderWidth;
50
- if (borderColor) dynamicStyles.borderColor = borderColor;
51
-
52
- // Flatten style array to object (HTML divs don't support style arrays)
53
- const flattenedStyle = useMemo(() => {
54
- if (!style) return undefined;
55
- if (Array.isArray(style)) {
56
- return StyleSheet.flatten(style);
57
- }
58
- return style;
59
- }, [style]);
60
-
61
44
  // Call style as function to get theme-reactive styles
62
45
  /** @ts-ignore */
63
- const webProps = getWebProps([(viewStyles.view as any)({}), dynamicStyles]);
64
-
46
+ const webProps = getWebProps((viewStyles.view as any)({}));
47
+
65
48
  const mergedRef = useMergeRefs(ref, webProps.ref);
66
49
 
67
50
  return (
68
51
  <div
69
- style={flattenedStyle as any}
52
+ style={style as any}
70
53
  {...webProps}
71
54
  ref={mergedRef}
72
55
  id={id}
package/src/View/types.ts CHANGED
@@ -1,8 +1,28 @@
1
- import { Size, Surface } from '@idealyst/theme';
1
+ import { Size, Surface, ResponsiveStyle } from '@idealyst/theme';
2
2
  import type { ReactNode } from 'react';
3
3
  import type { StyleProp, ViewStyle } from 'react-native';
4
4
  import { ContainerStyleProps } from '../utils/viewStyleProps';
5
5
 
6
+ /**
7
+ * Style prop type that accepts both regular styles and responsive styles.
8
+ * Responsive styles allow breakpoint-based values:
9
+ * @example
10
+ * ```tsx
11
+ * // Regular style
12
+ * <View style={{ flexDirection: 'column' }} />
13
+ *
14
+ * // Responsive style
15
+ * <View style={{ flexDirection: { xs: 'column', md: 'row' } }} />
16
+ *
17
+ * // Style array (native only)
18
+ * <View style={[styles.container, { padding: 10 }]} />
19
+ *
20
+ * // Web-only CSS properties
21
+ * <View style={{ display: 'inline-block' }} />
22
+ * ```
23
+ */
24
+ export type ViewStyleProp = StyleProp<ViewStyle> | ResponsiveStyle | React.CSSProperties;
25
+
6
26
  // Component-specific type aliases for future extensibility
7
27
  export type ViewBackgroundVariant = Surface | 'transparent';
8
28
  export type ViewRadiusVariant = Size | 'none';
@@ -50,9 +70,17 @@ export interface ViewProps extends ContainerStyleProps {
50
70
  borderColor?: string;
51
71
 
52
72
  /**
53
- * Additional styles (platform-specific)
73
+ * Additional styles. Supports responsive values for any property.
74
+ * @example
75
+ * ```tsx
76
+ * // Responsive flexDirection
77
+ * <View style={{ flexDirection: { xs: 'column', md: 'row' } }} />
78
+ *
79
+ * // Mix responsive and static values
80
+ * <View style={{ padding: { xs: 8, lg: 16 }, backgroundColor: '#fff' }} />
81
+ * ```
54
82
  */
55
- style?: React.CSSProperties | StyleProp<ViewStyle>;
83
+ style?: ViewStyleProp;
56
84
 
57
85
  /**
58
86
  * Enable scrollable content (uses ScrollView on native, overflow:auto on web)
@@ -1,11 +1,14 @@
1
1
  import React from 'react';
2
2
  import { Screen, View, Button, Text } from '@idealyst/components';
3
+ import { useUnistyles } from 'react-native-unistyles';
3
4
 
4
5
  export const ButtonExamples = () => {
5
6
  const handlePress = (buttonType: string) => {
6
7
  console.log(`Button pressed: ${buttonType}`);
7
8
  };
8
9
 
10
+ const { theme } = useUnistyles()
11
+
9
12
  return (
10
13
  <Screen background="primary">
11
14
  <View gap="xl">
@@ -41,6 +44,23 @@ export const ButtonExamples = () => {
41
44
  </View>
42
45
  </View>
43
46
 
47
+ {/* Show all intents in theme (including extended intents) */}
48
+ <View gap="md">
49
+ <Text typography="subtitle1">All Intents</Text>
50
+ <View style={{ flexDirection: 'row', gap: 12, flexWrap: 'wrap' }}>
51
+ { (Object.keys(theme.intents) as Array<keyof typeof theme.intents>).map((intent) => (
52
+ <Button
53
+ key={intent}
54
+ type="contained"
55
+ intent={intent}
56
+ onPress={() => handlePress(`intent-${intent}`)}
57
+ >
58
+ {intent.charAt(0).toUpperCase() + intent.slice(1)}
59
+ </Button>
60
+ )) }
61
+ </View>
62
+ </View>
63
+
44
64
  {/* Button Sizes */}
45
65
  <View gap="md">
46
66
  <Text typography="subtitle1">Sizes</Text>
package/src/index.ts CHANGED
@@ -40,7 +40,7 @@ export * from './Avatar/types';
40
40
  export { default as Screen } from './Screen';
41
41
  export * from './Screen/types';
42
42
 
43
- export { default as Icon } from './Icon';
43
+ export { default as Icon, IconRegistry } from './Icon';
44
44
  export * from './Icon/types';
45
45
 
46
46
  export { default as SVGImage } from './SVGImage';