@idealyst/components 1.1.6 → 1.1.7
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 +8 -3
- package/src/Accordion/Accordion.native.tsx +15 -9
- package/src/Accordion/Accordion.styles.tsx +193 -168
- package/src/Accordion/Accordion.web.tsx +12 -7
- package/src/ActivityIndicator/ActivityIndicator.native.tsx +3 -2
- package/src/ActivityIndicator/ActivityIndicator.styles.tsx +22 -11
- package/src/ActivityIndicator/ActivityIndicator.web.tsx +2 -2
- package/src/Alert/Alert.native.tsx +11 -10
- package/src/Alert/Alert.styles.tsx +162 -253
- package/src/Alert/Alert.web.tsx +6 -10
- package/src/Avatar/Avatar.native.tsx +5 -2
- package/src/Avatar/Avatar.styles.tsx +48 -18
- package/src/Avatar/Avatar.web.tsx +2 -2
- package/src/Badge/Badge.native.tsx +2 -2
- package/src/Badge/Badge.styles.tsx +37 -16
- package/src/Badge/Badge.web.tsx +6 -6
- package/src/Breadcrumb/Breadcrumb.native.tsx +12 -5
- package/src/Breadcrumb/Breadcrumb.styles.tsx +59 -58
- package/src/Breadcrumb/Breadcrumb.web.tsx +13 -6
- package/src/Button/Button.native.tsx +39 -14
- package/src/Button/Button.styles.tsx +106 -208
- package/src/Button/Button.web.tsx +10 -8
- package/src/Card/Card.native.tsx +14 -6
- package/src/Card/Card.styles.tsx +64 -62
- package/src/Card/Card.web.tsx +5 -4
- package/src/Checkbox/Checkbox.native.tsx +7 -3
- package/src/Checkbox/Checkbox.styles.tsx +49 -25
- package/src/Checkbox/Checkbox.web.tsx +3 -3
- package/src/Chip/Chip.native.tsx +5 -5
- package/src/Chip/Chip.styles.tsx +71 -21
- package/src/Chip/Chip.web.tsx +5 -5
- package/src/Dialog/Dialog.native.tsx +10 -4
- package/src/Dialog/Dialog.styles.tsx +130 -90
- package/src/Dialog/Dialog.web.tsx +4 -4
- package/src/Divider/Divider.native.tsx +29 -42
- package/src/Divider/Divider.styles.tsx +138 -242
- package/src/Divider/Divider.web.tsx +17 -14
- package/src/Icon/Icon.native.tsx +11 -3
- package/src/Icon/Icon.styles.tsx +10 -4
- package/src/Image/Image.styles.tsx +53 -37
- package/src/Input/Input.native.tsx +6 -7
- package/src/Input/Input.styles.tsx +194 -174
- package/src/Input/Input.web.tsx +5 -8
- package/src/Link/Link.native.tsx +4 -1
- package/src/List/List.styles.tsx +79 -105
- package/src/List/ListItem.native.tsx +5 -3
- package/src/List/ListItem.web.tsx +4 -3
- package/src/Menu/Menu.native.tsx +1 -1
- package/src/Menu/Menu.styles.tsx +53 -37
- package/src/Menu/Menu.web.tsx +2 -2
- package/src/Menu/MenuItem.native.tsx +5 -3
- package/src/Menu/MenuItem.styles.tsx +68 -69
- package/src/Menu/MenuItem.web.tsx +16 -3
- package/src/Popover/Popover.native.tsx +1 -1
- package/src/Popover/Popover.styles.tsx +40 -29
- package/src/Popover/Popover.web.tsx +1 -1
- package/src/Pressable/Pressable.native.tsx +3 -1
- package/src/Pressable/Pressable.styles.tsx +20 -13
- package/src/Pressable/Pressable.web.tsx +1 -1
- package/src/Progress/Progress.native.tsx +15 -6
- package/src/Progress/Progress.styles.tsx +125 -85
- package/src/Progress/Progress.web.tsx +10 -9
- package/src/RadioButton/RadioButton.native.tsx +8 -3
- package/src/RadioButton/RadioButton.styles.tsx +44 -37
- package/src/RadioButton/RadioButton.web.tsx +3 -3
- package/src/SVGImage/SVGImage.styles.tsx +28 -16
- package/src/Screen/Screen.native.tsx +23 -13
- package/src/Screen/Screen.styles.tsx +57 -46
- package/src/Screen/Screen.web.tsx +1 -1
- package/src/Select/Select.native.tsx +11 -5
- package/src/Select/Select.styles.tsx +72 -52
- package/src/Select/Select.web.tsx +5 -5
- package/src/Skeleton/Skeleton.styles.tsx +26 -14
- package/src/Slider/Slider.native.tsx +9 -5
- package/src/Slider/Slider.styles.tsx +59 -48
- package/src/Slider/Slider.web.tsx +5 -5
- package/src/Switch/Switch.native.tsx +6 -2
- package/src/Switch/Switch.styles.tsx +46 -19
- package/src/Switch/Switch.web.tsx +4 -4
- package/src/TabBar/TabBar.native.tsx +23 -31
- package/src/TabBar/TabBar.styles.tsx +215 -371
- package/src/TabBar/TabBar.web.tsx +21 -33
- package/src/Table/Table.native.tsx +1 -1
- package/src/Table/Table.styles.tsx +11 -4
- package/src/Table/Table.web.tsx +1 -1
- package/src/Text/Text.native.tsx +3 -4
- package/src/Text/Text.styles.tsx +7 -1
- package/src/Text/Text.web.tsx +1 -1
- package/src/TextArea/TextArea.styles.tsx +90 -58
- package/src/Tooltip/Tooltip.native.tsx +2 -2
- package/src/Tooltip/Tooltip.styles.tsx +21 -12
- package/src/Tooltip/Tooltip.web.tsx +2 -2
- package/src/Video/Video.styles.tsx +39 -23
- package/src/View/View.native.tsx +4 -2
- package/src/View/View.styles.tsx +33 -22
- package/src/View/View.web.tsx +13 -2
- package/src/extensions/applyExtension.ts +210 -0
- package/src/extensions/extendComponent.ts +377 -0
- package/src/extensions/index.ts +102 -0
- package/src/extensions/types.ts +497 -0
- package/src/globals.ts +16 -0
- package/src/index.native.ts +4 -0
- package/src/index.ts +28 -0
- package/src/utils/deepMerge.ts +54 -2
package/src/List/List.styles.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { StyleSheet } from 'react-native-unistyles';
|
|
2
|
-
import { Theme
|
|
2
|
+
import { Theme } from '@idealyst/theme';
|
|
3
3
|
import { buildSizeVariants } from '../utils/buildSizeVariants';
|
|
4
4
|
import {
|
|
5
5
|
buildGapVariants,
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
buildMarginHorizontalVariants,
|
|
12
12
|
} from '../utils/buildViewStyleVariants';
|
|
13
13
|
import { ListSizeVariant, ListType } from './types';
|
|
14
|
+
import { applyExtensions } from '../extensions/applyExtension';
|
|
14
15
|
|
|
15
16
|
type ListVariants = {
|
|
16
17
|
type: ListType;
|
|
@@ -47,97 +48,76 @@ function createContainerTypeVariants(theme: Theme) {
|
|
|
47
48
|
}
|
|
48
49
|
|
|
49
50
|
|
|
51
|
+
type ItemDynamicProps = {
|
|
52
|
+
type?: ListType;
|
|
53
|
+
disabled?: boolean;
|
|
54
|
+
clickable?: boolean;
|
|
55
|
+
};
|
|
56
|
+
|
|
50
57
|
/**
|
|
51
|
-
*
|
|
58
|
+
* Get item hover styles based on disabled and clickable state
|
|
52
59
|
*/
|
|
53
|
-
function
|
|
54
|
-
|
|
55
|
-
{
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
},
|
|
65
|
-
{
|
|
66
|
-
disabled: true,
|
|
67
|
-
styles: {
|
|
68
|
-
_web: {
|
|
69
|
-
_hover: {
|
|
70
|
-
backgroundColor: 'transparent',
|
|
71
|
-
borderRadius: 0,
|
|
72
|
-
},
|
|
73
|
-
},
|
|
74
|
-
},
|
|
75
|
-
},
|
|
76
|
-
{
|
|
77
|
-
clickable: false,
|
|
78
|
-
styles: {
|
|
79
|
-
_web: {
|
|
80
|
-
_hover: {
|
|
81
|
-
backgroundColor: 'transparent',
|
|
82
|
-
borderRadius: 0,
|
|
83
|
-
},
|
|
84
|
-
},
|
|
85
|
-
},
|
|
86
|
-
},
|
|
87
|
-
];
|
|
60
|
+
function getItemHoverStyles(theme: Theme, disabled: boolean, clickable: boolean) {
|
|
61
|
+
if (disabled || !clickable) {
|
|
62
|
+
return {
|
|
63
|
+
backgroundColor: 'transparent',
|
|
64
|
+
borderRadius: 0,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
backgroundColor: theme.colors.surface.secondary,
|
|
69
|
+
borderRadius: 4,
|
|
70
|
+
};
|
|
88
71
|
}
|
|
89
72
|
|
|
90
73
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
},
|
|
74
|
+
// Container style creator for extension support
|
|
75
|
+
function createContainerStyles(theme: Theme) {
|
|
76
|
+
return () => ({
|
|
77
|
+
display: 'flex' as const,
|
|
78
|
+
flexDirection: 'column' as const,
|
|
79
|
+
width: '100%',
|
|
80
|
+
variants: {
|
|
81
|
+
type: createContainerTypeVariants(theme),
|
|
82
|
+
scrollable: {
|
|
83
|
+
true: {
|
|
84
|
+
_web: {
|
|
85
|
+
overflow: 'auto',
|
|
104
86
|
},
|
|
105
|
-
false: {},
|
|
106
87
|
},
|
|
107
|
-
|
|
108
|
-
gap: buildGapVariants(theme),
|
|
109
|
-
padding: buildPaddingVariants(theme),
|
|
110
|
-
paddingVertical: buildPaddingVerticalVariants(theme),
|
|
111
|
-
paddingHorizontal: buildPaddingHorizontalVariants(theme),
|
|
112
|
-
margin: buildMarginVariants(theme),
|
|
113
|
-
marginVertical: buildMarginVerticalVariants(theme),
|
|
114
|
-
marginHorizontal: buildMarginHorizontalVariants(theme),
|
|
88
|
+
false: {},
|
|
115
89
|
},
|
|
116
|
-
|
|
117
|
-
|
|
90
|
+
// Spacing variants from ContainerStyleProps
|
|
91
|
+
gap: buildGapVariants(theme),
|
|
92
|
+
padding: buildPaddingVariants(theme),
|
|
93
|
+
paddingVertical: buildPaddingVerticalVariants(theme),
|
|
94
|
+
paddingHorizontal: buildPaddingHorizontalVariants(theme),
|
|
95
|
+
margin: buildMarginVariants(theme),
|
|
96
|
+
marginVertical: buildMarginVerticalVariants(theme),
|
|
97
|
+
marginHorizontal: buildMarginHorizontalVariants(theme),
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Item style creator for extension support
|
|
103
|
+
function createItemStyles(theme: Theme) {
|
|
104
|
+
return ({ type = 'default', disabled = false, clickable = true }: ItemDynamicProps) => {
|
|
105
|
+
const hoverStyles = getItemHoverStyles(theme, disabled, clickable);
|
|
106
|
+
return {
|
|
118
107
|
display: 'flex',
|
|
119
108
|
flexDirection: 'row',
|
|
120
109
|
alignItems: 'center',
|
|
121
110
|
backgroundColor: 'transparent',
|
|
122
111
|
textAlign: 'left',
|
|
112
|
+
borderBottomWidth: type === 'divided' ? 1 : 0,
|
|
113
|
+
borderBottomStyle: type === 'divided' ? 'solid' as const : undefined,
|
|
114
|
+
borderBottomColor: type === 'divided' ? theme.colors.border.primary : undefined,
|
|
123
115
|
variants: {
|
|
124
116
|
size: buildSizeVariants(theme, 'list', (size) => ({
|
|
125
117
|
paddingVertical: size.paddingVertical,
|
|
126
118
|
paddingHorizontal: size.paddingHorizontal,
|
|
127
119
|
minHeight: size.minHeight,
|
|
128
120
|
})),
|
|
129
|
-
type: {
|
|
130
|
-
default: {},
|
|
131
|
-
bordered: {},
|
|
132
|
-
divided: {
|
|
133
|
-
borderBottomWidth: 1,
|
|
134
|
-
borderBottomStyle: 'solid',
|
|
135
|
-
borderBottomColor: theme.colors.border.primary,
|
|
136
|
-
_web: {
|
|
137
|
-
borderBottom: `1px solid ${theme.colors.border.primary}`,
|
|
138
|
-
},
|
|
139
|
-
},
|
|
140
|
-
},
|
|
141
121
|
active: {
|
|
142
122
|
true: {
|
|
143
123
|
backgroundColor: theme.colors.surface.secondary,
|
|
@@ -155,36 +135,30 @@ export const listStyles = StyleSheet.create((theme: Theme) => {
|
|
|
155
135
|
},
|
|
156
136
|
false: {},
|
|
157
137
|
},
|
|
158
|
-
disabled: {
|
|
159
|
-
true: {
|
|
160
|
-
opacity: 0.5,
|
|
161
|
-
_web: {
|
|
162
|
-
cursor: 'not-allowed',
|
|
163
|
-
},
|
|
164
|
-
},
|
|
165
|
-
false: {},
|
|
166
|
-
},
|
|
167
|
-
clickable: {
|
|
168
|
-
true: {},
|
|
169
|
-
false: {
|
|
170
|
-
_web: {
|
|
171
|
-
cursor: 'default',
|
|
172
|
-
},
|
|
173
|
-
},
|
|
174
|
-
},
|
|
175
138
|
} as const,
|
|
176
|
-
|
|
139
|
+
opacity: disabled ? 0.5 : 1,
|
|
177
140
|
_web: {
|
|
178
141
|
border: 'none',
|
|
179
|
-
cursor: 'pointer',
|
|
142
|
+
cursor: disabled ? 'not-allowed' : (clickable ? 'pointer' : 'default'),
|
|
180
143
|
outline: 'none',
|
|
181
144
|
transition: 'background-color 0.2s ease, border-color 0.2s ease',
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
borderRadius: 4,
|
|
185
|
-
},
|
|
145
|
+
borderBottom: type === 'divided' ? `1px solid ${theme.colors.border.primary}` : undefined,
|
|
146
|
+
_hover: hoverStyles,
|
|
186
147
|
},
|
|
187
|
-
} as const
|
|
148
|
+
} as const;
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export const listStyles = StyleSheet.create((theme: Theme) => {
|
|
153
|
+
// Apply extensions to main visual elements
|
|
154
|
+
const extended = applyExtensions('List', theme, {
|
|
155
|
+
container: createContainerStyles(theme),
|
|
156
|
+
item: createItemStyles(theme),
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
return {
|
|
160
|
+
...extended,
|
|
161
|
+
// Minor utility styles (not extended)
|
|
188
162
|
itemContent: {
|
|
189
163
|
display: 'flex',
|
|
190
164
|
flexDirection: 'row',
|
|
@@ -222,17 +196,17 @@ export const listStyles = StyleSheet.create((theme: Theme) => {
|
|
|
222
196
|
true: {
|
|
223
197
|
color: theme.colors.text.secondary,
|
|
224
198
|
},
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
199
|
+
false: {},
|
|
200
|
+
},
|
|
201
|
+
selected: {
|
|
202
|
+
true: {
|
|
203
|
+
color: theme.intents.primary.primary,
|
|
204
|
+
fontWeight: '600',
|
|
205
|
+
},
|
|
206
|
+
false: {},
|
|
231
207
|
},
|
|
232
|
-
false: {},
|
|
233
208
|
},
|
|
234
209
|
},
|
|
235
|
-
},
|
|
236
210
|
trailing: {
|
|
237
211
|
display: 'flex',
|
|
238
212
|
alignItems: 'center',
|
|
@@ -51,15 +51,17 @@ const ListItem = forwardRef<ComponentRef<typeof View> | ComponentRef<typeof Pres
|
|
|
51
51
|
const effectiveSize = size ?? listContext.size ?? 'md';
|
|
52
52
|
const effectiveVariant = listContext.type ?? 'default';
|
|
53
53
|
|
|
54
|
-
// Apply
|
|
54
|
+
// Apply variants (for size, active, selected, disabled on label)
|
|
55
55
|
listStyles.useVariants({
|
|
56
56
|
size: effectiveSize,
|
|
57
57
|
active,
|
|
58
58
|
selected,
|
|
59
59
|
disabled,
|
|
60
|
-
clickable: isClickable,
|
|
61
60
|
});
|
|
62
61
|
|
|
62
|
+
// Get dynamic item style with type, disabled, and clickable props
|
|
63
|
+
const itemStyle = (listStyles.item as any)({ type: effectiveVariant, disabled, clickable: isClickable });
|
|
64
|
+
|
|
63
65
|
// Resolve icon color - check intents first, then color palette
|
|
64
66
|
const resolvedIconColor = (() => {
|
|
65
67
|
if (!iconColor) return undefined;
|
|
@@ -117,7 +119,7 @@ const ListItem = forwardRef<ComponentRef<typeof View> | ComponentRef<typeof Pres
|
|
|
117
119
|
);
|
|
118
120
|
|
|
119
121
|
const indentStyle = indent > 0 ? { paddingLeft: indent * 16 } : {};
|
|
120
|
-
const combinedStyle = [
|
|
122
|
+
const combinedStyle = [itemStyle, indentStyle, style];
|
|
121
123
|
|
|
122
124
|
if (isClickable) {
|
|
123
125
|
return (
|
|
@@ -32,16 +32,17 @@ const ListItem: React.FC<ListItemProps> = ({
|
|
|
32
32
|
const effectiveSize = size ?? listContext.size ?? 'md';
|
|
33
33
|
const effectiveVariant = listContext.type ?? 'default';
|
|
34
34
|
|
|
35
|
-
// Apply
|
|
35
|
+
// Apply variants (for size, active, selected, disabled on label)
|
|
36
36
|
listStyles.useVariants({
|
|
37
37
|
size: effectiveSize,
|
|
38
38
|
active,
|
|
39
39
|
selected,
|
|
40
40
|
disabled,
|
|
41
|
-
clickable: isClickable,
|
|
42
41
|
});
|
|
43
42
|
|
|
44
|
-
|
|
43
|
+
// Get dynamic item style with type, disabled, and clickable props
|
|
44
|
+
const itemStyle = (listStyles.item as any)({ type: effectiveVariant, disabled, clickable: isClickable });
|
|
45
|
+
const itemProps = getWebProps([itemStyle, style]);
|
|
45
46
|
const labelProps = getWebProps([listStyles.label]);
|
|
46
47
|
const leadingProps = getWebProps([listStyles.leading]);
|
|
47
48
|
const trailingProps = getWebProps([listStyles.trailing]);
|
package/src/Menu/Menu.native.tsx
CHANGED
package/src/Menu/Menu.styles.tsx
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { StyleSheet } from 'react-native-unistyles';
|
|
2
|
-
import { Theme, StylesheetStyles, CompoundVariants, Intent, Size} from '@idealyst/theme';
|
|
2
|
+
import { Theme, StylesheetStyles, CompoundVariants, Intent, Size } from '@idealyst/theme';
|
|
3
3
|
import { buildSizeVariants } from '../utils/buildSizeVariants';
|
|
4
|
+
import { applyExtensions } from '../extensions/applyExtension';
|
|
4
5
|
|
|
5
6
|
type MenuSize = Size;
|
|
6
7
|
type MenuIntent = Intent;
|
|
@@ -88,9 +89,46 @@ function createLabelSizeVariants(theme: Theme) {
|
|
|
88
89
|
}));
|
|
89
90
|
}
|
|
90
91
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
92
|
+
// Main element style creators (for extension support)
|
|
93
|
+
function createOverlayStyles(theme: Theme) {
|
|
94
|
+
return () => ({
|
|
95
|
+
backgroundColor: 'transparent',
|
|
96
|
+
_web: {
|
|
97
|
+
position: 'fixed' as const,
|
|
98
|
+
top: 0,
|
|
99
|
+
left: 0,
|
|
100
|
+
right: 0,
|
|
101
|
+
bottom: 0,
|
|
102
|
+
zIndex: 999,
|
|
103
|
+
},
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function createMenuStyles(theme: Theme) {
|
|
108
|
+
return () => ({
|
|
109
|
+
position: 'absolute' as const,
|
|
110
|
+
zIndex: 1000,
|
|
111
|
+
backgroundColor: theme.colors.surface.primary,
|
|
112
|
+
borderWidth: 1,
|
|
113
|
+
borderStyle: 'solid' as const,
|
|
114
|
+
borderColor: theme.colors.border.primary,
|
|
115
|
+
borderRadius: 8,
|
|
116
|
+
minWidth: 120,
|
|
117
|
+
maxWidth: 400,
|
|
118
|
+
padding: 4,
|
|
119
|
+
display: 'flex' as const,
|
|
120
|
+
flexDirection: 'column' as const,
|
|
121
|
+
_web: {
|
|
122
|
+
border: `1px solid ${theme.colors.border.primary}`,
|
|
123
|
+
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.15)',
|
|
124
|
+
width: 'fit-content',
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function createItemStyles(theme: Theme) {
|
|
130
|
+
return ({ intent }: Partial<MenuVariants>) => {
|
|
131
|
+
const hoverStyles = getItemHoverStyles(theme, intent ?? 'neutral');
|
|
94
132
|
return {
|
|
95
133
|
flexDirection: 'row',
|
|
96
134
|
alignItems: 'center',
|
|
@@ -122,49 +160,27 @@ const createItemStyles = (theme: Theme) => {
|
|
|
122
160
|
},
|
|
123
161
|
...hoverStyles,
|
|
124
162
|
} as const;
|
|
125
|
-
}
|
|
163
|
+
};
|
|
126
164
|
}
|
|
127
165
|
|
|
128
166
|
// Styles are inlined here instead of in @idealyst/theme because Unistyles' Babel transform on native cannot resolve function calls to extract variant structures.
|
|
129
167
|
export const menuStyles = StyleSheet.create((theme: Theme) => {
|
|
168
|
+
// Apply extensions to main visual elements
|
|
169
|
+
const extended = applyExtensions('Menu', theme, {
|
|
170
|
+
overlay: createOverlayStyles(theme),
|
|
171
|
+
menu: createMenuStyles(theme),
|
|
172
|
+
item: createItemStyles(theme),
|
|
173
|
+
});
|
|
174
|
+
|
|
130
175
|
return {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
_web: {
|
|
134
|
-
position: 'fixed' as const,
|
|
135
|
-
top: 0,
|
|
136
|
-
left: 0,
|
|
137
|
-
right: 0,
|
|
138
|
-
bottom: 0,
|
|
139
|
-
zIndex: 999,
|
|
140
|
-
}
|
|
141
|
-
} as const,
|
|
142
|
-
menu: {
|
|
143
|
-
position: 'absolute',
|
|
144
|
-
zIndex: 1000,
|
|
145
|
-
backgroundColor: theme.colors.surface.primary,
|
|
146
|
-
borderWidth: 1,
|
|
147
|
-
borderStyle: 'solid',
|
|
148
|
-
borderColor: theme.colors.border.primary,
|
|
149
|
-
borderRadius: 8,
|
|
150
|
-
minWidth: 120,
|
|
151
|
-
maxWidth: 400,
|
|
152
|
-
padding: 4,
|
|
153
|
-
display: 'flex',
|
|
154
|
-
flexDirection: 'column',
|
|
155
|
-
_web: {
|
|
156
|
-
border: `1px solid ${theme.colors.border.primary}`,
|
|
157
|
-
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.15)',
|
|
158
|
-
width: 'fit-content',
|
|
159
|
-
},
|
|
160
|
-
} as const,
|
|
176
|
+
...extended,
|
|
177
|
+
// Minor utility styles (not extended)
|
|
161
178
|
separator: {
|
|
162
179
|
height: 1,
|
|
163
180
|
backgroundColor: theme.colors.border.primary,
|
|
164
181
|
marginTop: 4,
|
|
165
182
|
marginBottom: 4,
|
|
166
183
|
},
|
|
167
|
-
item: createItemStyles(theme),
|
|
168
184
|
icon: {
|
|
169
185
|
alignItems: 'center',
|
|
170
186
|
justifyContent: 'center',
|
|
@@ -181,5 +197,5 @@ export const menuStyles = StyleSheet.create((theme: Theme) => {
|
|
|
181
197
|
size: createLabelSizeVariants(theme),
|
|
182
198
|
} as const,
|
|
183
199
|
} as const,
|
|
184
|
-
}
|
|
200
|
+
};
|
|
185
201
|
});
|
package/src/Menu/Menu.web.tsx
CHANGED
|
@@ -113,8 +113,8 @@ const Menu = forwardRef<HTMLDivElement, MenuProps>(({
|
|
|
113
113
|
size,
|
|
114
114
|
});
|
|
115
115
|
|
|
116
|
-
const overlayProps = getWebProps([menuStyles.overlay]);
|
|
117
|
-
const menuProps = getWebProps([menuStyles.menu, style as any]);
|
|
116
|
+
const overlayProps = getWebProps([(menuStyles.overlay as any)({})]);
|
|
117
|
+
const menuProps = getWebProps([(menuStyles.menu as any)({}), style as any]);
|
|
118
118
|
const separatorProps = getWebProps([menuStyles.separator]);
|
|
119
119
|
|
|
120
120
|
const handleTriggerClick = () => {
|
|
@@ -13,13 +13,15 @@ interface MenuItemProps {
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
const MenuItem = forwardRef<ComponentRef<typeof Pressable>, MenuItemProps>(({ item, onPress, size = 'md', testID }, ref) => {
|
|
16
|
-
// Initialize styles with useVariants
|
|
16
|
+
// Initialize styles with useVariants (for size and disabled)
|
|
17
17
|
menuItemStyles.useVariants({
|
|
18
18
|
size,
|
|
19
19
|
disabled: Boolean(item.disabled),
|
|
20
|
-
intent: item.intent || 'neutral',
|
|
21
20
|
});
|
|
22
21
|
|
|
22
|
+
// Compute dynamic item style with intent
|
|
23
|
+
const itemStyle = (menuItemStyles.item as any)({ intent: item.intent || 'neutral' });
|
|
24
|
+
|
|
23
25
|
const renderIcon = () => {
|
|
24
26
|
if (!item.icon) return null;
|
|
25
27
|
|
|
@@ -39,7 +41,7 @@ const MenuItem = forwardRef<ComponentRef<typeof Pressable>, MenuItemProps>(({ it
|
|
|
39
41
|
return (
|
|
40
42
|
<Pressable
|
|
41
43
|
ref={ref}
|
|
42
|
-
style={
|
|
44
|
+
style={itemStyle}
|
|
43
45
|
onPress={() => onPress(item)}
|
|
44
46
|
disabled={item.disabled}
|
|
45
47
|
accessibilityRole="menuitem"
|
|
@@ -1,27 +1,48 @@
|
|
|
1
1
|
import { StyleSheet } from 'react-native-unistyles';
|
|
2
|
-
import { Theme } from '@idealyst/theme';
|
|
2
|
+
import { Theme, Intent } from '@idealyst/theme';
|
|
3
3
|
import { buildSizeVariants } from '../utils/buildSizeVariants';
|
|
4
|
+
import { applyExtensions } from '../extensions/applyExtension';
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
type MenuItemDynamicProps = {
|
|
7
|
+
intent?: Intent;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Get hover styles for menu item based on intent
|
|
12
|
+
*/
|
|
13
|
+
function getItemHoverStyles(theme: Theme, intent: Intent) {
|
|
14
|
+
if (intent === 'neutral') {
|
|
15
|
+
return {
|
|
16
|
+
_web: {
|
|
17
|
+
_hover: {
|
|
18
|
+
backgroundColor: theme.colors.surface.secondary,
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
} as const;
|
|
22
|
+
}
|
|
23
|
+
const intentValue = theme.intents[intent];
|
|
8
24
|
return {
|
|
9
|
-
|
|
25
|
+
_web: {
|
|
26
|
+
_hover: {
|
|
27
|
+
backgroundColor: intentValue.light,
|
|
28
|
+
color: intentValue.primary,
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
} as const;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Create dynamic item styles
|
|
36
|
+
*/
|
|
37
|
+
function createItemStyles(theme: Theme) {
|
|
38
|
+
return ({ intent = 'neutral' }: MenuItemDynamicProps) => {
|
|
39
|
+
const hoverStyles = getItemHoverStyles(theme, intent);
|
|
40
|
+
return {
|
|
10
41
|
flexDirection: 'row',
|
|
11
42
|
alignItems: 'center',
|
|
12
43
|
backgroundColor: 'transparent',
|
|
13
44
|
borderRadius: 4,
|
|
14
45
|
minHeight: 44,
|
|
15
|
-
_web: {
|
|
16
|
-
cursor: 'pointer',
|
|
17
|
-
border: 'none',
|
|
18
|
-
outline: 'none',
|
|
19
|
-
transition: 'background-color 0.2s ease',
|
|
20
|
-
textAlign: 'left',
|
|
21
|
-
_hover: {
|
|
22
|
-
backgroundColor: theme.colors.surface.secondary,
|
|
23
|
-
},
|
|
24
|
-
},
|
|
25
46
|
variants: {
|
|
26
47
|
size: buildSizeVariants(theme, 'menu', (size) => ({
|
|
27
48
|
paddingVertical: size.paddingVertical,
|
|
@@ -32,67 +53,42 @@ export const menuItemStyles = StyleSheet.create((theme: Theme) => {
|
|
|
32
53
|
opacity: 0.5,
|
|
33
54
|
_web: {
|
|
34
55
|
cursor: 'not-allowed',
|
|
35
|
-
},
|
|
36
|
-
},
|
|
37
|
-
false: {},
|
|
38
|
-
},
|
|
39
|
-
intent: {
|
|
40
|
-
primary: {
|
|
41
|
-
_web: {
|
|
42
|
-
_hover: {
|
|
43
|
-
backgroundColor: theme.intents.primary.light,
|
|
44
|
-
color: theme.intents.primary.primary,
|
|
45
|
-
},
|
|
46
|
-
},
|
|
47
|
-
},
|
|
48
|
-
neutral: {},
|
|
49
|
-
success: {
|
|
50
|
-
_web: {
|
|
51
|
-
_hover: {
|
|
52
|
-
backgroundColor: theme.intents.success.light,
|
|
53
|
-
color: theme.intents.success.primary,
|
|
54
|
-
},
|
|
55
|
-
},
|
|
56
|
-
},
|
|
57
|
-
error: {
|
|
58
|
-
_web: {
|
|
59
56
|
_hover: {
|
|
60
|
-
backgroundColor:
|
|
61
|
-
color: theme.intents.error.primary,
|
|
62
|
-
},
|
|
63
|
-
},
|
|
64
|
-
},
|
|
65
|
-
warning: {
|
|
66
|
-
_web: {
|
|
67
|
-
_hover: {
|
|
68
|
-
backgroundColor: theme.intents.warning.light,
|
|
69
|
-
color: theme.intents.warning.primary,
|
|
70
|
-
},
|
|
71
|
-
},
|
|
72
|
-
},
|
|
73
|
-
info: {
|
|
74
|
-
_web: {
|
|
75
|
-
_hover: {
|
|
76
|
-
backgroundColor: theme.intents.info.light,
|
|
77
|
-
color: theme.intents.info.primary,
|
|
57
|
+
backgroundColor: 'transparent',
|
|
78
58
|
},
|
|
79
59
|
},
|
|
80
60
|
},
|
|
61
|
+
false: {},
|
|
81
62
|
},
|
|
82
63
|
},
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
64
|
+
_web: {
|
|
65
|
+
display: 'flex',
|
|
66
|
+
width: '100%',
|
|
67
|
+
cursor: 'pointer',
|
|
68
|
+
border: 'none',
|
|
69
|
+
borderWidth: 0,
|
|
70
|
+
outline: 'none',
|
|
71
|
+
transition: 'background-color 0.2s ease',
|
|
72
|
+
textAlign: 'left',
|
|
73
|
+
_hover: {
|
|
74
|
+
backgroundColor: theme.colors.surface.secondary,
|
|
93
75
|
},
|
|
94
|
-
|
|
95
|
-
|
|
76
|
+
},
|
|
77
|
+
...hoverStyles,
|
|
78
|
+
} as const;
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Styles are inlined here instead of in @idealyst/theme because Unistyles' Babel transform on native cannot resolve function calls to extract variant structures.
|
|
83
|
+
export const menuItemStyles = StyleSheet.create((theme: Theme) => {
|
|
84
|
+
// Apply extensions to main visual elements
|
|
85
|
+
const extended = applyExtensions('MenuItem', theme, {
|
|
86
|
+
item: createItemStyles(theme),
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
...extended,
|
|
91
|
+
// Minor utility styles (not extended)
|
|
96
92
|
icon: {
|
|
97
93
|
alignItems: 'center',
|
|
98
94
|
justifyContent: 'center',
|
|
@@ -105,6 +101,9 @@ export const menuItemStyles = StyleSheet.create((theme: Theme) => {
|
|
|
105
101
|
fontSize: size.iconSize,
|
|
106
102
|
}))
|
|
107
103
|
},
|
|
104
|
+
_web: {
|
|
105
|
+
display: 'flex',
|
|
106
|
+
},
|
|
108
107
|
},
|
|
109
108
|
label: {
|
|
110
109
|
flex: 1,
|
|
@@ -14,14 +14,15 @@ interface MenuItemProps {
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
const MenuItem = forwardRef<HTMLButtonElement, MenuItemProps>(({ item, onPress, size = 'md', testID }, ref) => {
|
|
17
|
-
// Initialize styles with useVariants
|
|
17
|
+
// Initialize styles with useVariants (for size and disabled)
|
|
18
18
|
menuItemStyles.useVariants({
|
|
19
19
|
size,
|
|
20
20
|
disabled: Boolean(item.disabled),
|
|
21
|
-
intent: item.intent || 'neutral',
|
|
22
21
|
});
|
|
23
22
|
|
|
24
|
-
|
|
23
|
+
// Compute dynamic item style with intent
|
|
24
|
+
const itemStyle = (menuItemStyles.item as any)({ intent: item.intent || 'neutral' });
|
|
25
|
+
const itemProps = getWebProps([itemStyle]);
|
|
25
26
|
const iconProps = getWebProps([menuItemStyles.icon]);
|
|
26
27
|
const labelProps = getWebProps([menuItemStyles.label]);
|
|
27
28
|
|
|
@@ -48,10 +49,22 @@ const MenuItem = forwardRef<HTMLButtonElement, MenuItemProps>(({ item, onPress,
|
|
|
48
49
|
// Merge refs
|
|
49
50
|
const mergedRef = useMergeRefs(ref, itemProps.ref);
|
|
50
51
|
|
|
52
|
+
// Button reset styles that must be applied directly
|
|
53
|
+
const buttonResetStyles: React.CSSProperties = {
|
|
54
|
+
display: 'flex',
|
|
55
|
+
width: '100%',
|
|
56
|
+
border: 'none',
|
|
57
|
+
outline: 'none',
|
|
58
|
+
cursor: item.disabled ? 'not-allowed' : 'pointer',
|
|
59
|
+
background: 'transparent',
|
|
60
|
+
textAlign: 'left',
|
|
61
|
+
};
|
|
62
|
+
|
|
51
63
|
return (
|
|
52
64
|
<button
|
|
53
65
|
{...itemProps}
|
|
54
66
|
ref={mergedRef}
|
|
67
|
+
style={{ ...buttonResetStyles, ...itemProps.style }}
|
|
55
68
|
onClick={() => onPress(item)}
|
|
56
69
|
disabled={item.disabled}
|
|
57
70
|
role="menuitem"
|