@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.
- package/package.json +3 -3
- package/plugin/web.js +280 -532
- package/src/Accordion/Accordion.web.tsx +1 -3
- package/src/Alert/Alert.web.tsx +3 -4
- package/src/Badge/Badge.web.tsx +8 -15
- package/src/Breadcrumb/Breadcrumb.web.tsx +4 -8
- package/src/Button/Button.native.tsx +14 -21
- package/src/Button/Button.styles.tsx +15 -0
- package/src/Button/Button.web.tsx +9 -19
- package/src/Checkbox/Checkbox.web.tsx +1 -2
- package/src/Chip/Chip.web.tsx +3 -5
- package/src/Dialog/Dialog.web.tsx +3 -3
- package/src/Dialog/types.ts +1 -1
- package/src/Icon/Icon.web.tsx +22 -17
- package/src/Icon/IconRegistry.native.ts +41 -0
- package/src/Icon/IconRegistry.ts +107 -0
- package/src/Icon/IconSvg/IconSvg.web.tsx +26 -5
- package/src/Icon/icon-resolver.ts +12 -43
- package/src/Icon/index.native.ts +2 -1
- package/src/Icon/index.ts +1 -0
- package/src/Icon/index.web.ts +1 -0
- package/src/Input/Input.styles.tsx +56 -83
- package/src/Input/Input.web.tsx +5 -8
- package/src/List/ListItem.native.tsx +6 -7
- package/src/List/ListItem.web.tsx +3 -3
- package/src/Menu/MenuItem.web.tsx +3 -5
- package/src/Screen/Screen.native.tsx +1 -1
- package/src/Screen/Screen.styles.tsx +3 -6
- package/src/Screen/Screen.web.tsx +1 -1
- package/src/Select/Select.styles.tsx +31 -48
- package/src/Select/Select.web.tsx +45 -33
- package/src/Slider/Slider.web.tsx +2 -4
- package/src/Switch/Switch.native.tsx +2 -2
- package/src/Switch/Switch.web.tsx +2 -3
- package/src/Table/Table.native.tsx +168 -65
- package/src/Table/Table.styles.tsx +26 -33
- package/src/Table/Table.web.tsx +169 -70
- package/src/Text/Text.web.tsx +1 -0
- package/src/TextArea/TextArea.native.tsx +21 -8
- package/src/TextArea/TextArea.styles.tsx +15 -27
- package/src/TextArea/TextArea.web.tsx +17 -6
- package/src/View/View.native.tsx +33 -3
- package/src/View/View.web.tsx +4 -21
- package/src/View/types.ts +31 -3
- package/src/examples/ButtonExamples.tsx +20 -0
- package/src/index.ts +1 -1
package/src/Table/Table.web.tsx
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
type
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
type
|
|
103
|
-
clickable
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
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;
|
package/src/Text/Text.web.tsx
CHANGED
|
@@ -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={[
|
|
145
|
+
<View nativeID={id} style={[containerStyleComputed, style]} testID={testID}>
|
|
134
146
|
{label && (
|
|
135
|
-
<Text style={
|
|
147
|
+
<Text style={labelStyleComputed}>{label}</Text>
|
|
136
148
|
)}
|
|
137
149
|
|
|
138
|
-
<View style={
|
|
150
|
+
<View style={textareaContainerStyleComputed}>
|
|
139
151
|
<TextInput
|
|
140
152
|
ref={ref}
|
|
141
153
|
{...nativeA11yProps}
|
|
142
154
|
style={[
|
|
143
|
-
|
|
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={
|
|
177
|
+
<View style={footerStyleComputed}>
|
|
165
178
|
<View style={{ flex: 1 }}>
|
|
166
179
|
{error && (
|
|
167
|
-
<Text style={
|
|
180
|
+
<Text style={helperTextStyleComputed}>
|
|
168
181
|
{error}
|
|
169
182
|
</Text>
|
|
170
183
|
)}
|
|
171
184
|
{!error && helperText && (
|
|
172
|
-
<Text style={
|
|
185
|
+
<Text style={helperTextStyleComputed}>
|
|
173
186
|
{helperText}
|
|
174
187
|
</Text>
|
|
175
188
|
)}
|
|
176
189
|
</View>
|
|
177
190
|
|
|
178
191
|
{showCharacterCount && maxLength && (
|
|
179
|
-
<Text style={
|
|
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
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
131
|
-
const
|
|
132
|
-
const
|
|
133
|
-
const
|
|
134
|
-
const
|
|
135
|
-
const
|
|
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;
|
package/src/View/View.native.tsx
CHANGED
|
@@ -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,
|
|
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,
|
|
98
|
+
<RNView ref={ref as any} style={[viewStyle, overrideStyles, finalStyle]} testID={testID} nativeID={id}>
|
|
69
99
|
{children}
|
|
70
100
|
</RNView>
|
|
71
101
|
);
|
package/src/View/View.web.tsx
CHANGED
|
@@ -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(
|
|
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={
|
|
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
|
|
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?:
|
|
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';
|