@idealyst/components 1.0.83 → 1.0.84
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/CLAUDE.md +199 -232
- package/README.md +5 -5
- package/package.json +20 -2
- package/plugin/README.md +272 -0
- package/plugin/test-cases.jsx +112 -0
- package/plugin/web-legacy.js +320 -0
- package/plugin/web.js +422 -124
- package/src/Accordion/Accordion.native.tsx +182 -0
- package/src/Accordion/Accordion.styles.tsx +260 -0
- package/src/Accordion/Accordion.web.tsx +147 -0
- package/src/Accordion/index.native.tsx +3 -0
- package/src/Accordion/index.ts +3 -0
- package/src/Accordion/index.web.tsx +3 -0
- package/src/Accordion/types.ts +23 -0
- package/src/ActivityIndicator/ActivityIndicator.native.tsx +17 -12
- package/src/ActivityIndicator/ActivityIndicator.styles.tsx +83 -109
- package/src/ActivityIndicator/ActivityIndicator.web.tsx +23 -17
- package/src/ActivityIndicator/index.ts +5 -2
- package/src/ActivityIndicator/index.web.ts +5 -2
- package/src/ActivityIndicator/types.ts +15 -10
- package/src/Alert/Alert.native.tsx +113 -0
- package/src/Alert/Alert.styles.tsx +304 -0
- package/src/Alert/Alert.web.tsx +123 -0
- package/src/Alert/index.native.ts +5 -0
- package/src/Alert/index.ts +5 -0
- package/src/Alert/index.web.ts +5 -0
- package/src/Alert/types.ts +21 -0
- package/src/Avatar/Avatar.native.tsx +8 -6
- package/src/Avatar/Avatar.styles.tsx +64 -58
- package/src/Avatar/Avatar.web.tsx +13 -8
- package/src/Avatar/index.ts +5 -2
- package/src/Avatar/index.web.ts +5 -2
- package/src/Avatar/types.ts +19 -13
- package/src/Badge/Badge.native.tsx +59 -14
- package/src/Badge/Badge.styles.tsx +125 -139
- package/src/Badge/Badge.web.tsx +72 -16
- package/src/Badge/index.ts +5 -2
- package/src/Badge/index.web.ts +5 -2
- package/src/Badge/types.ts +23 -11
- package/src/Breadcrumb/Breadcrumb.native.tsx +225 -0
- package/src/Breadcrumb/Breadcrumb.styles.tsx +234 -0
- package/src/Breadcrumb/Breadcrumb.web.tsx +268 -0
- package/src/Breadcrumb/index.native.ts +5 -0
- package/src/Breadcrumb/index.ts +5 -0
- package/src/Breadcrumb/index.web.ts +5 -0
- package/src/Breadcrumb/types.ts +56 -0
- package/src/Button/Button.native.tsx +75 -24
- package/src/Button/Button.styles.tsx +248 -205
- package/src/Button/Button.web.tsx +82 -25
- package/src/Button/index.ts +5 -5
- package/src/Button/index.web.ts +5 -3
- package/src/Button/types.ts +32 -15
- package/src/Card/Card.native.tsx +14 -11
- package/src/Card/Card.styles.tsx +146 -220
- package/src/Card/Card.web.tsx +20 -21
- package/src/Card/index.ts +5 -5
- package/src/Card/index.web.ts +5 -3
- package/src/Card/types.ts +24 -17
- package/src/Checkbox/Checkbox.native.tsx +24 -34
- package/src/Checkbox/Checkbox.styles.tsx +223 -275
- package/src/Checkbox/Checkbox.web.tsx +30 -37
- package/src/Checkbox/index.ts +5 -5
- package/src/Checkbox/index.web.ts +5 -3
- package/src/Checkbox/types.ts +26 -20
- package/src/Chip/Chip.native.tsx +126 -0
- package/src/Chip/Chip.styles.tsx +138 -0
- package/src/Chip/Chip.web.tsx +154 -0
- package/src/Chip/index.native.ts +5 -0
- package/src/Chip/index.ts +5 -0
- package/src/Chip/index.web.ts +5 -0
- package/src/Chip/types.ts +51 -0
- package/src/Dialog/Dialog.native.tsx +65 -12
- package/src/Dialog/Dialog.styles.tsx +154 -136
- package/src/Dialog/Dialog.web.tsx +16 -11
- package/src/Dialog/index.ts +5 -2
- package/src/Dialog/index.web.ts +5 -2
- package/src/Dialog/types.ts +22 -16
- package/src/Divider/Divider.native.tsx +19 -14
- package/src/Divider/Divider.styles.tsx +273 -595
- package/src/Divider/Divider.web.tsx +19 -12
- package/src/Divider/index.ts +5 -5
- package/src/Divider/index.web.ts +5 -3
- package/src/Divider/types.ts +28 -19
- package/src/Icon/Icon.native.tsx +17 -24
- package/src/Icon/Icon.styles.tsx +64 -48
- package/src/Icon/Icon.web.tsx +14 -11
- package/src/Icon/IconSvg/IconSvg.native.tsx +42 -0
- package/src/Icon/IconSvg/IconSvg.web.tsx +40 -0
- package/src/Icon/IconSvg/index.native.ts +1 -0
- package/src/Icon/IconSvg/index.ts +1 -0
- package/src/Icon/icon-resolver.native.ts +27 -0
- package/src/Icon/icon-resolver.ts +70 -0
- package/src/Icon/index.ts +5 -5
- package/src/Icon/index.web.ts +5 -3
- package/src/Icon/types.ts +17 -11
- package/src/Image/Image.native.tsx +86 -0
- package/src/Image/Image.styles.tsx +57 -0
- package/src/Image/Image.web.tsx +92 -0
- package/src/Image/index.native.ts +5 -0
- package/src/Image/index.ts +5 -0
- package/src/Image/types.ts +21 -0
- package/src/Input/Input.native.tsx +103 -26
- package/src/Input/Input.styles.tsx +240 -177
- package/src/Input/Input.web.tsx +141 -38
- package/src/Input/index.ts +5 -5
- package/src/Input/index.web.ts +5 -3
- package/src/Input/types.ts +43 -20
- package/src/List/List.native.tsx +56 -0
- package/src/List/List.styles.tsx +257 -0
- package/src/List/List.web.tsx +43 -0
- package/src/List/ListContext.tsx +16 -0
- package/src/List/ListItem.native.tsx +111 -0
- package/src/List/ListItem.web.tsx +110 -0
- package/src/List/ListSection.native.tsx +31 -0
- package/src/List/ListSection.web.tsx +33 -0
- package/src/List/index.native.tsx +5 -0
- package/src/List/index.ts +5 -0
- package/src/List/index.web.tsx +5 -0
- package/src/List/types.ts +42 -0
- package/src/Menu/Menu.native.tsx +150 -0
- package/src/Menu/Menu.styles.tsx +185 -0
- package/src/Menu/Menu.web.tsx +99 -0
- package/src/Menu/MenuItem.native.tsx +66 -0
- package/src/Menu/MenuItem.styles.tsx +119 -0
- package/src/Menu/MenuItem.web.tsx +67 -0
- package/src/Menu/index.native.ts +3 -0
- package/src/Menu/index.ts +3 -0
- package/src/Menu/index.web.ts +3 -0
- package/src/Menu/types.ts +30 -0
- package/src/Popover/Popover.native.tsx +102 -32
- package/src/Popover/Popover.styles.tsx +100 -67
- package/src/Popover/Popover.web.tsx +36 -260
- package/src/Popover/index.ts +5 -2
- package/src/Popover/index.web.ts +5 -2
- package/src/Popover/types.ts +14 -13
- package/src/Pressable/Pressable.native.tsx +7 -6
- package/src/Pressable/Pressable.web.tsx +8 -6
- package/src/Pressable/index.ts +5 -2
- package/src/Pressable/index.web.ts +5 -2
- package/src/Pressable/types.ts +11 -10
- package/src/Progress/Progress.native.tsx +179 -0
- package/src/Progress/Progress.styles.tsx +164 -0
- package/src/Progress/Progress.web.tsx +144 -0
- package/src/Progress/index.native.ts +1 -0
- package/src/Progress/index.ts +5 -0
- package/src/Progress/index.web.ts +5 -0
- package/src/Progress/types.ts +21 -0
- package/src/RadioButton/RadioButton.native.tsx +88 -0
- package/src/RadioButton/RadioButton.styles.tsx +163 -0
- package/src/RadioButton/RadioButton.web.tsx +85 -0
- package/src/RadioButton/RadioGroup.native.tsx +43 -0
- package/src/RadioButton/RadioGroup.web.tsx +49 -0
- package/src/RadioButton/index.native.ts +2 -0
- package/src/RadioButton/index.ts +2 -0
- package/src/RadioButton/index.web.ts +2 -0
- package/src/RadioButton/types.ts +29 -0
- package/src/SVGImage/SVGImage.native.tsx +9 -7
- package/src/SVGImage/SVGImage.styles.tsx +63 -55
- package/src/SVGImage/SVGImage.web.tsx +16 -13
- package/src/SVGImage/index.ts +5 -5
- package/src/SVGImage/index.web.ts +5 -2
- package/src/SVGImage/types.ts +7 -3
- package/src/Screen/Screen.native.tsx +43 -17
- package/src/Screen/Screen.styles.tsx +58 -54
- package/src/Screen/Screen.web.tsx +11 -5
- package/src/Screen/index.ts +5 -2
- package/src/Screen/index.web.ts +5 -2
- package/src/Screen/types.ts +23 -9
- package/src/Select/Select.native.tsx +140 -63
- package/src/Select/Select.styles.tsx +312 -302
- package/src/Select/Select.web.tsx +156 -316
- package/src/Select/index.ts +5 -2
- package/src/Select/index.web.ts +5 -2
- package/src/Select/types.ts +13 -7
- package/src/Skeleton/Skeleton.native.tsx +139 -0
- package/src/Skeleton/Skeleton.styles.tsx +59 -0
- package/src/Skeleton/Skeleton.web.tsx +112 -0
- package/src/Skeleton/index.native.ts +4 -0
- package/src/Skeleton/index.ts +5 -0
- package/src/Skeleton/index.web.ts +5 -0
- package/src/Skeleton/types.ts +75 -0
- package/src/Slider/Slider.native.tsx +248 -0
- package/src/Slider/Slider.styles.tsx +241 -0
- package/src/Slider/Slider.web.tsx +226 -0
- package/src/Slider/index.native.ts +3 -0
- package/src/Slider/index.ts +5 -0
- package/src/Slider/index.web.ts +5 -0
- package/src/Slider/types.ts +31 -0
- package/src/Switch/Switch.native.tsx +131 -0
- package/src/Switch/Switch.styles.tsx +169 -0
- package/src/Switch/Switch.web.tsx +121 -0
- package/src/Switch/index.native.ts +3 -0
- package/src/Switch/index.ts +5 -0
- package/src/Switch/index.web.ts +5 -0
- package/src/Switch/types.ts +21 -0
- package/src/TabBar/TabBar.native.tsx +142 -0
- package/src/TabBar/TabBar.styles.tsx +399 -0
- package/src/TabBar/TabBar.web.tsx +205 -0
- package/src/TabBar/index.native.tsx +3 -0
- package/src/TabBar/index.ts +3 -0
- package/src/TabBar/index.web.tsx +3 -0
- package/src/TabBar/types.ts +26 -0
- package/src/Table/Table.native.tsx +122 -0
- package/src/Table/Table.styles.tsx +283 -0
- package/src/Table/Table.web.tsx +112 -0
- package/src/Table/index.native.tsx +3 -0
- package/src/Table/index.ts +3 -0
- package/src/Table/index.web.tsx +3 -0
- package/src/Table/types.ts +28 -0
- package/src/Text/Text.native.tsx +12 -11
- package/src/Text/Text.styles.tsx +76 -64
- package/src/Text/Text.web.tsx +14 -9
- package/src/Text/index.ts +5 -5
- package/src/Text/index.web.ts +5 -3
- package/src/Text/types.ts +20 -13
- package/src/TextArea/TextArea.native.tsx +134 -0
- package/src/TextArea/TextArea.styles.tsx +175 -0
- package/src/TextArea/TextArea.web.tsx +156 -0
- package/src/TextArea/index.native.ts +3 -0
- package/src/TextArea/index.ts +3 -0
- package/src/TextArea/index.web.ts +3 -0
- package/src/TextArea/types.ts +30 -0
- package/src/Tooltip/Tooltip.native.tsx +165 -0
- package/src/Tooltip/Tooltip.styles.tsx +73 -0
- package/src/Tooltip/Tooltip.web.tsx +87 -0
- package/src/Tooltip/index.native.ts +3 -0
- package/src/Tooltip/index.ts +3 -0
- package/src/Tooltip/types.ts +18 -0
- package/src/Video/Video.native.tsx +105 -0
- package/src/Video/Video.styles.tsx +39 -0
- package/src/Video/Video.web.tsx +115 -0
- package/src/Video/index.native.ts +5 -0
- package/src/Video/index.ts +5 -0
- package/src/Video/types.ts +29 -0
- package/src/View/View.native.tsx +9 -14
- package/src/View/View.styles.tsx +101 -93
- package/src/View/View.web.tsx +16 -17
- package/src/View/index.ts +5 -5
- package/src/View/index.web.ts +5 -3
- package/src/View/types.ts +29 -21
- package/src/examples/AccordionExamples.tsx +126 -0
- package/src/examples/AlertExamples.tsx +280 -0
- package/src/examples/AvatarExamples.tsx +23 -23
- package/src/examples/BadgeExamples.tsx +109 -41
- package/src/examples/BreadcrumbExamples.tsx +312 -0
- package/src/examples/ButtonExamples.tsx +160 -33
- package/src/examples/CardExamples.tsx +40 -40
- package/src/examples/CheckboxExamples.tsx +12 -12
- package/src/examples/ChipExamples.tsx +197 -0
- package/src/examples/DialogExamples.tsx +22 -22
- package/src/examples/DividerExamples.tsx +49 -49
- package/src/examples/IconExamples.tsx +270 -54
- package/src/examples/ImageExamples.tsx +174 -0
- package/src/examples/InputExamples.tsx +75 -17
- package/src/examples/ListExamples.tsx +288 -0
- package/src/examples/MenuExamples.tsx +144 -0
- package/src/examples/PopoverExamples.tsx +69 -73
- package/src/examples/ProgressExamples.tsx +137 -0
- package/src/examples/RadioButtonExamples.tsx +161 -0
- package/src/examples/SVGImageExamples.tsx +19 -17
- package/src/examples/ScreenExamples.tsx +31 -31
- package/src/examples/SelectExamples.tsx +67 -67
- package/src/examples/SkeletonExamples.tsx +206 -0
- package/src/examples/SliderExamples.tsx +200 -0
- package/src/examples/SwitchExamples.tsx +182 -0
- package/src/examples/TabBarExamples.tsx +143 -0
- package/src/examples/TableExamples.tsx +280 -0
- package/src/examples/TextAreaExamples.tsx +173 -0
- package/src/examples/TextExamples.tsx +28 -32
- package/src/examples/ThemeExtensionExamples.tsx +10 -10
- package/src/examples/TooltipExamples.tsx +126 -0
- package/src/examples/VideoExamples.tsx +144 -0
- package/src/examples/ViewExamples.tsx +64 -56
- package/src/examples/index.ts +17 -3
- package/src/hooks/useMergeRefs.ts +16 -0
- package/src/hooks/useSmartPosition.native.ts +169 -0
- package/src/index.native.ts +80 -9
- package/src/index.ts +71 -1
- package/src/internal/BoundedModalContent.native.tsx +58 -0
- package/src/internal/PositionedPortal.tsx +254 -0
- package/src/internal/SafeAreaDebugOverlay.native.tsx +173 -0
- package/src/unistyles.d.ts +6 -0
- package/src/utils/buildSizeVariants.ts +16 -0
- package/src/utils/deepMerge.ts +43 -0
- package/src/utils/positionUtils.native.ts +280 -0
- package/src/utils/styleHelpers.ts +48 -0
- package/LLM-ACCESS-GUIDE.md +0 -143
- package/src/ActivityIndicator/README.md +0 -132
- package/src/Avatar/README.md +0 -139
- package/src/Badge/README.md +0 -170
- package/src/Button/Button.types.ts +0 -12
- package/src/Button/README.md +0 -262
- package/src/Card/README.md +0 -258
- package/src/Checkbox/README.md +0 -102
- package/src/Dialog/README.md +0 -210
- package/src/Divider/README.md +0 -108
- package/src/Icon/README.md +0 -81
- package/src/Input/README.md +0 -100
- package/src/SVGImage/README.md +0 -209
- package/src/Screen/README.md +0 -86
- package/src/Select/README.md +0 -166
- package/src/Text/README.md +0 -94
- package/src/View/README.md +0 -107
- package/src/examples/AllExamples.tsx +0 -88
- package/src/examples/README.md +0 -136
- package/src/examples/ValidationExamples.tsx +0 -95
- package/src/examples/extendedTheme.ts +0 -329
- package/src/theme/breakpoints.ts +0 -8
- package/src/theme/colorResolver.ts +0 -218
- package/src/theme/colors.ts +0 -315
- package/src/theme/defaultThemes.ts +0 -326
- package/src/theme/index.ts +0 -188
- package/src/theme/themeBuilder.ts +0 -602
- package/src/theme/unistyles.d.ts +0 -6
- package/src/theme/variantHelpers.ts +0 -584
- package/src/theme/variants.ts +0 -56
package/src/SVGImage/types.ts
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
|
+
import { Intent } from '@idealyst/theme';
|
|
1
2
|
import React from 'react';
|
|
2
3
|
import { ViewProps } from 'react-native';
|
|
3
4
|
import { SvgProps } from 'react-native-svg';
|
|
4
|
-
|
|
5
|
+
|
|
6
|
+
// Component-specific type aliases for future extensibility
|
|
7
|
+
export type SVGImageIntentVariant = Intent;
|
|
8
|
+
export type SVGImageResizeMode = 'contain' | 'cover' | 'stretch';
|
|
5
9
|
|
|
6
10
|
export interface SVGImageProps extends Omit<ViewProps, 'children'> {
|
|
7
11
|
source: string | { uri: string } | React.FC<SvgProps>;
|
|
@@ -9,6 +13,6 @@ export interface SVGImageProps extends Omit<ViewProps, 'children'> {
|
|
|
9
13
|
height?: number | string;
|
|
10
14
|
size?: number | string;
|
|
11
15
|
color?: string;
|
|
12
|
-
intent?:
|
|
13
|
-
resizeMode?:
|
|
16
|
+
intent?: SVGImageIntentVariant;
|
|
17
|
+
resizeMode?: SVGImageResizeMode;
|
|
14
18
|
}
|
|
@@ -1,41 +1,67 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { View as RNView, ScrollView as RNScrollView } from 'react-native';
|
|
1
|
+
import React, { forwardRef } from 'react';
|
|
2
|
+
import { View as RNView, ScrollView as RNScrollView, SafeAreaView } from 'react-native';
|
|
3
3
|
import { ScreenProps } from './types';
|
|
4
4
|
import { screenStyles } from './Screen.styles';
|
|
5
5
|
|
|
6
|
-
const Screen
|
|
6
|
+
const Screen = forwardRef<RNView | RNScrollView, ScreenProps>(({
|
|
7
7
|
children,
|
|
8
8
|
background = 'primary',
|
|
9
9
|
padding = 'md',
|
|
10
|
-
safeArea =
|
|
10
|
+
safeArea = true,
|
|
11
11
|
scrollable = true,
|
|
12
|
+
contentInset,
|
|
12
13
|
style,
|
|
13
14
|
testID,
|
|
14
|
-
}) => {
|
|
15
|
+
}, ref) => {
|
|
16
|
+
|
|
15
17
|
screenStyles.useVariants({
|
|
16
18
|
background,
|
|
17
19
|
padding,
|
|
18
20
|
safeArea,
|
|
19
21
|
});
|
|
20
22
|
|
|
21
|
-
const screenStyleArray = [
|
|
22
|
-
screenStyles.screen,
|
|
23
|
-
style,
|
|
24
|
-
];
|
|
25
|
-
|
|
26
23
|
if (scrollable) {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
24
|
+
// For ScrollView, flex: 1 goes on the ScrollView style
|
|
25
|
+
// Background and padding go on contentContainerStyle (without flex: 1)
|
|
26
|
+
const scrollViewStyle = [{ flex: 1 }, style];
|
|
27
|
+
|
|
28
|
+
const contentContainerStyleArray = [
|
|
29
|
+
screenStyles.screenContent,
|
|
30
|
+
contentInset ? {
|
|
31
|
+
paddingTop: contentInset.top,
|
|
32
|
+
paddingBottom: contentInset.bottom,
|
|
33
|
+
paddingLeft: contentInset.left,
|
|
34
|
+
paddingRight: contentInset.right,
|
|
35
|
+
} : undefined,
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<RNScrollView
|
|
40
|
+
ref={ref as any}
|
|
41
|
+
style={scrollViewStyle}
|
|
42
|
+
contentContainerStyle={contentContainerStyleArray}
|
|
43
|
+
testID={testID}
|
|
44
|
+
>
|
|
45
|
+
{children}
|
|
46
|
+
</RNScrollView>
|
|
31
47
|
);
|
|
32
48
|
}
|
|
33
49
|
|
|
34
|
-
|
|
35
|
-
|
|
50
|
+
const containerStyle = [screenStyles.screen, style];
|
|
51
|
+
|
|
52
|
+
const view = (
|
|
53
|
+
<RNView ref={ref as any} style={containerStyle} testID={testID}>
|
|
36
54
|
{children}
|
|
37
55
|
</RNView>
|
|
38
56
|
);
|
|
39
|
-
|
|
57
|
+
|
|
58
|
+
if (safeArea) {
|
|
59
|
+
return <SafeAreaView style={{ flex: 1 }}>{view}</SafeAreaView>;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return view;
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
Screen.displayName = 'Screen';
|
|
40
66
|
|
|
41
67
|
export default Screen;
|
|
@@ -1,60 +1,64 @@
|
|
|
1
1
|
import { StyleSheet } from 'react-native-unistyles';
|
|
2
|
+
import { Theme } from '@idealyst/theme';
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
backgroundColor: theme.colors.surface.
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
4
|
+
function generateBackgroundVariants(theme: Theme) {
|
|
5
|
+
return {
|
|
6
|
+
primary: { backgroundColor: theme.colors.surface.primary },
|
|
7
|
+
secondary: { backgroundColor: theme.colors.surface.secondary },
|
|
8
|
+
tertiary: { backgroundColor: theme.colors.surface.tertiary },
|
|
9
|
+
inverse: { backgroundColor: theme.colors.surface.inverse },
|
|
10
|
+
'inverse-secondary': { backgroundColor: theme.colors.surface['inverse-secondary'] },
|
|
11
|
+
'inverse-tertiary': { backgroundColor: theme.colors.surface['inverse-tertiary'] },
|
|
12
|
+
transparent: { backgroundColor: 'transparent' },
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function generatePaddingVariants() {
|
|
17
|
+
return {
|
|
18
|
+
none: { padding: 0 },
|
|
19
|
+
xs: { padding: 4 },
|
|
20
|
+
sm: { padding: 8 },
|
|
21
|
+
md: { padding: 16 },
|
|
22
|
+
lg: { padding: 24 },
|
|
23
|
+
xl: { padding: 32 },
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Styles are inlined here instead of in @idealyst/theme because Unistyles' Babel
|
|
28
|
+
// transform on native cannot resolve function calls to extract variant structures.
|
|
29
|
+
// @ts-ignore - TS language server needs restart to pick up theme structure changes
|
|
30
|
+
export const screenStyles = StyleSheet.create((theme: Theme) => {
|
|
31
|
+
return {
|
|
32
|
+
screen: {
|
|
33
|
+
flex: 1,
|
|
34
|
+
backgroundColor: theme.colors.surface.primary,
|
|
35
|
+
variants: {
|
|
36
|
+
background: generateBackgroundVariants(theme),
|
|
37
|
+
padding: generatePaddingVariants(),
|
|
38
|
+
safeArea: {
|
|
39
|
+
true: {},
|
|
40
|
+
false: {},
|
|
41
|
+
}
|
|
39
42
|
},
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
paddingTop: 0,
|
|
47
|
-
paddingBottom: 0,
|
|
48
|
-
},
|
|
43
|
+
_web: {
|
|
44
|
+
overflow: 'auto',
|
|
45
|
+
display: 'flex',
|
|
46
|
+
flexDirection: 'column',
|
|
47
|
+
minHeight: '100%',
|
|
48
|
+
boxSizing: 'border-box',
|
|
49
49
|
},
|
|
50
50
|
},
|
|
51
|
-
//
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
51
|
+
// Content style for ScrollView - no flex: 1 so content can grow
|
|
52
|
+
screenContent: {
|
|
53
|
+
backgroundColor: theme.colors.surface.primary,
|
|
54
|
+
variants: {
|
|
55
|
+
background: generateBackgroundVariants(theme),
|
|
56
|
+
padding: generatePaddingVariants(),
|
|
57
|
+
safeArea: {
|
|
58
|
+
true: {},
|
|
59
|
+
false: {},
|
|
60
|
+
}
|
|
61
|
+
},
|
|
58
62
|
},
|
|
59
|
-
}
|
|
60
|
-
})
|
|
63
|
+
};
|
|
64
|
+
});
|
|
@@ -1,16 +1,17 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { forwardRef } from 'react';
|
|
2
2
|
import { getWebProps } from 'react-native-unistyles/web';
|
|
3
3
|
import { ScreenProps } from './types';
|
|
4
4
|
import { screenStyles } from './Screen.styles';
|
|
5
|
+
import useMergeRefs from '../hooks/useMergeRefs';
|
|
5
6
|
|
|
6
|
-
const Screen
|
|
7
|
+
const Screen = forwardRef<HTMLDivElement, ScreenProps>(({
|
|
7
8
|
children,
|
|
8
9
|
background = 'primary',
|
|
9
10
|
padding = 'md',
|
|
10
11
|
safeArea = false,
|
|
11
12
|
style,
|
|
12
13
|
testID,
|
|
13
|
-
}) => {
|
|
14
|
+
}, ref) => {
|
|
14
15
|
screenStyles.useVariants({
|
|
15
16
|
background,
|
|
16
17
|
padding,
|
|
@@ -18,16 +19,21 @@ const Screen: React.FC<ScreenProps> = ({
|
|
|
18
19
|
});
|
|
19
20
|
|
|
20
21
|
// Use getWebProps to generate className and ref for web
|
|
21
|
-
const webProps = getWebProps([screenStyles.screen, style]);
|
|
22
|
+
const webProps = getWebProps([screenStyles.screen, style as any]);
|
|
23
|
+
|
|
24
|
+
const mergedRef = useMergeRefs(ref, webProps.ref);
|
|
22
25
|
|
|
23
26
|
return (
|
|
24
27
|
<div
|
|
25
28
|
{...webProps}
|
|
29
|
+
ref={mergedRef}
|
|
26
30
|
data-testid={testID}
|
|
27
31
|
>
|
|
28
32
|
{children}
|
|
29
33
|
</div>
|
|
30
34
|
);
|
|
31
|
-
};
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
Screen.displayName = 'Screen';
|
|
32
38
|
|
|
33
39
|
export default Screen;
|
package/src/Screen/index.ts
CHANGED
package/src/Screen/index.web.ts
CHANGED
package/src/Screen/types.ts
CHANGED
|
@@ -1,31 +1,45 @@
|
|
|
1
|
-
import { ReactNode } from 'react';
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import type { StyleProp, ViewStyle } from 'react-native';
|
|
3
|
+
import { Size, Surface } from '@idealyst/theme';
|
|
2
4
|
|
|
3
5
|
export interface ScreenProps {
|
|
4
6
|
/**
|
|
5
7
|
* The content to display inside the screen
|
|
6
8
|
*/
|
|
7
9
|
children?: ReactNode;
|
|
8
|
-
|
|
10
|
+
|
|
9
11
|
/**
|
|
10
12
|
* Background variant - controls the background color
|
|
11
13
|
*/
|
|
12
|
-
background?:
|
|
13
|
-
|
|
14
|
+
background?: Surface | 'transparent';
|
|
15
|
+
|
|
14
16
|
/**
|
|
15
17
|
* Screen padding variant
|
|
16
18
|
*/
|
|
17
|
-
padding?:
|
|
18
|
-
|
|
19
|
+
padding?: Size | 'none';
|
|
20
|
+
|
|
19
21
|
/**
|
|
20
22
|
* Safe area padding for mobile devices
|
|
21
23
|
*/
|
|
22
24
|
safeArea?: boolean;
|
|
23
|
-
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Content inset padding for scrollable content (mobile only)
|
|
28
|
+
* Adds padding to the scroll view's content container
|
|
29
|
+
* Useful for adding safe area insets or additional spacing
|
|
30
|
+
*/
|
|
31
|
+
contentInset?: {
|
|
32
|
+
top?: number;
|
|
33
|
+
bottom?: number;
|
|
34
|
+
left?: number;
|
|
35
|
+
right?: number;
|
|
36
|
+
};
|
|
37
|
+
|
|
24
38
|
/**
|
|
25
39
|
* Additional styles (platform-specific)
|
|
26
40
|
*/
|
|
27
|
-
style?:
|
|
28
|
-
|
|
41
|
+
style?: StyleProp<ViewStyle>;
|
|
42
|
+
|
|
29
43
|
/**
|
|
30
44
|
* Test ID for testing
|
|
31
45
|
*/
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useState, useRef } from 'react';
|
|
1
|
+
import React, { useState, useRef, forwardRef } from 'react';
|
|
2
2
|
import {
|
|
3
3
|
View,
|
|
4
4
|
Text,
|
|
@@ -10,10 +10,14 @@ import {
|
|
|
10
10
|
Platform,
|
|
11
11
|
Animated,
|
|
12
12
|
} from 'react-native';
|
|
13
|
+
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
|
|
13
14
|
import { SelectProps, SelectOption } from './types';
|
|
14
15
|
import { selectStyles } from './Select.styles';
|
|
16
|
+
import { BoundedModalContent } from '../internal/BoundedModalContent.native';
|
|
17
|
+
import { useSmartPosition } from '../hooks/useSmartPosition.native';
|
|
18
|
+
import useMergeRefs from '../hooks/useMergeRefs';
|
|
15
19
|
|
|
16
|
-
const Select
|
|
20
|
+
const Select = forwardRef<View, SelectProps>(({
|
|
17
21
|
options,
|
|
18
22
|
value,
|
|
19
23
|
onValueChange,
|
|
@@ -22,9 +26,9 @@ const Select: React.FC<SelectProps> = ({
|
|
|
22
26
|
error = false,
|
|
23
27
|
helperText,
|
|
24
28
|
label,
|
|
25
|
-
|
|
29
|
+
type = 'outlined',
|
|
26
30
|
intent = 'neutral',
|
|
27
|
-
size = '
|
|
31
|
+
size = 'md',
|
|
28
32
|
searchable = false,
|
|
29
33
|
filterOption,
|
|
30
34
|
presentationMode = 'dropdown',
|
|
@@ -32,11 +36,27 @@ const Select: React.FC<SelectProps> = ({
|
|
|
32
36
|
style,
|
|
33
37
|
testID,
|
|
34
38
|
accessibilityLabel,
|
|
35
|
-
}) => {
|
|
39
|
+
}, ref) => {
|
|
36
40
|
const [isOpen, setIsOpen] = useState(false);
|
|
37
41
|
const [searchTerm, setSearchTerm] = useState('');
|
|
38
42
|
const scaleAnim = useRef(new Animated.Value(0)).current;
|
|
39
43
|
|
|
44
|
+
const {
|
|
45
|
+
position: dropdownPosition,
|
|
46
|
+
size: dropdownSize,
|
|
47
|
+
isPositioned,
|
|
48
|
+
anchorRef: triggerRef,
|
|
49
|
+
measureAndPosition,
|
|
50
|
+
handleLayout: handleDropdownLayout,
|
|
51
|
+
reset: resetPosition,
|
|
52
|
+
} = useSmartPosition({
|
|
53
|
+
placement: 'bottom-start',
|
|
54
|
+
offset: 4,
|
|
55
|
+
maxHeight,
|
|
56
|
+
matchWidth: true,
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const mergedTriggerRef = useMergeRefs(ref, triggerRef);
|
|
40
60
|
const selectedOption = options.find(option => option.value === value);
|
|
41
61
|
|
|
42
62
|
// Filter options based on search term
|
|
@@ -51,9 +71,8 @@ const Select: React.FC<SelectProps> = ({
|
|
|
51
71
|
|
|
52
72
|
// Apply styles with variants
|
|
53
73
|
selectStyles.useVariants({
|
|
54
|
-
|
|
74
|
+
type,
|
|
55
75
|
size,
|
|
56
|
-
intent,
|
|
57
76
|
disabled,
|
|
58
77
|
error,
|
|
59
78
|
focused: isOpen,
|
|
@@ -65,8 +84,13 @@ const Select: React.FC<SelectProps> = ({
|
|
|
65
84
|
if (Platform.OS === 'ios' && presentationMode === 'actionSheet') {
|
|
66
85
|
showIOSActionSheet();
|
|
67
86
|
} else {
|
|
87
|
+
// Measure and position dropdown
|
|
88
|
+
measureAndPosition();
|
|
89
|
+
|
|
90
|
+
// Open the modal (it will be invisible with opacity 0 until positioned)
|
|
68
91
|
setIsOpen(true);
|
|
69
92
|
setSearchTerm('');
|
|
93
|
+
|
|
70
94
|
// Animate dropdown appearance
|
|
71
95
|
Animated.spring(scaleAnim, {
|
|
72
96
|
toValue: 1,
|
|
@@ -89,7 +113,7 @@ const Select: React.FC<SelectProps> = ({
|
|
|
89
113
|
title: label || 'Select an option',
|
|
90
114
|
message: helperText,
|
|
91
115
|
},
|
|
92
|
-
(buttonIndex) => {
|
|
116
|
+
(buttonIndex: number) => {
|
|
93
117
|
if (buttonIndex !== cancelButtonIndex && buttonIndex < options.length) {
|
|
94
118
|
const selectedOption = options[buttonIndex];
|
|
95
119
|
if (!selectedOption.disabled) {
|
|
@@ -115,6 +139,7 @@ const Select: React.FC<SelectProps> = ({
|
|
|
115
139
|
friction: 20,
|
|
116
140
|
}).start(() => {
|
|
117
141
|
setIsOpen(false);
|
|
142
|
+
resetPosition();
|
|
118
143
|
setSearchTerm('');
|
|
119
144
|
});
|
|
120
145
|
};
|
|
@@ -123,32 +148,95 @@ const Select: React.FC<SelectProps> = ({
|
|
|
123
148
|
setSearchTerm(text);
|
|
124
149
|
};
|
|
125
150
|
|
|
126
|
-
const renderChevron = () =>
|
|
127
|
-
|
|
128
|
-
<
|
|
129
|
-
|
|
130
|
-
|
|
151
|
+
const renderChevron = () => {
|
|
152
|
+
return (
|
|
153
|
+
<View style={selectStyles.chevron}>
|
|
154
|
+
<MaterialCommunityIcons
|
|
155
|
+
name="chevron-down"
|
|
156
|
+
style={selectStyles.chevron}
|
|
157
|
+
/>
|
|
158
|
+
</View>
|
|
159
|
+
);
|
|
160
|
+
};
|
|
131
161
|
|
|
132
|
-
const
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
162
|
+
const renderTriggerIcon = (icon: any) => {
|
|
163
|
+
if (!icon) return null;
|
|
164
|
+
|
|
165
|
+
// If it's a string, render as MaterialCommunityIcons
|
|
166
|
+
if (typeof icon === 'string') {
|
|
167
|
+
const iconStyle = selectStyles.icon;
|
|
168
|
+
return (
|
|
169
|
+
<View style={iconStyle}>
|
|
170
|
+
<MaterialCommunityIcons
|
|
171
|
+
style={iconStyle}
|
|
172
|
+
name={icon}
|
|
173
|
+
/>
|
|
174
|
+
</View>
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Otherwise render the React element directly
|
|
179
|
+
return <View style={selectStyles.icon}>{icon}</View>;
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
const renderOptionIcon = (icon: any) => {
|
|
183
|
+
if (!icon) return null;
|
|
184
|
+
|
|
185
|
+
// If it's a string, render as MaterialCommunityIcons
|
|
186
|
+
if (typeof icon === 'string') {
|
|
187
|
+
const iconStyle = selectStyles.optionIcon;
|
|
188
|
+
return (
|
|
189
|
+
<View style={iconStyle}>
|
|
190
|
+
<MaterialCommunityIcons
|
|
191
|
+
style={iconStyle}
|
|
192
|
+
name={icon}
|
|
193
|
+
/>
|
|
194
|
+
</View>
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Otherwise render the React element directly
|
|
199
|
+
return <View style={selectStyles.optionIcon}>{icon}</View>;
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
const renderDropdown = () => {
|
|
203
|
+
if (!isOpen) return null;
|
|
204
|
+
|
|
205
|
+
// Show dropdown only after it has been measured AND positioned
|
|
206
|
+
const isMeasured = dropdownSize.height > 0;
|
|
207
|
+
const shouldShow = isMeasured && isPositioned;
|
|
208
|
+
|
|
209
|
+
return (
|
|
210
|
+
<Modal
|
|
211
|
+
visible={true}
|
|
212
|
+
transparent
|
|
213
|
+
animationType="none"
|
|
214
|
+
onRequestClose={closeDropdown}
|
|
215
|
+
>
|
|
216
|
+
<Pressable
|
|
217
|
+
style={selectStyles.overlay}
|
|
218
|
+
onPress={closeDropdown}
|
|
219
|
+
>
|
|
220
|
+
<BoundedModalContent
|
|
221
|
+
top={dropdownPosition.top}
|
|
222
|
+
left={dropdownPosition.left}
|
|
223
|
+
width={dropdownPosition.width}
|
|
224
|
+
maxHeight={maxHeight}
|
|
151
225
|
>
|
|
226
|
+
<Animated.View
|
|
227
|
+
style={[
|
|
228
|
+
selectStyles.dropdown,
|
|
229
|
+
{
|
|
230
|
+
position: 'relative', // Override absolute positioning from styles
|
|
231
|
+
top: 0, // Override top: '100%' from styles
|
|
232
|
+
left: 0, // Override left from styles
|
|
233
|
+
right: undefined, // Remove right constraint
|
|
234
|
+
transform: [{ scale: scaleAnim }],
|
|
235
|
+
opacity: shouldShow ? 1 : 0, // Hide until measured AND positioned
|
|
236
|
+
}
|
|
237
|
+
]}
|
|
238
|
+
onLayout={handleDropdownLayout}
|
|
239
|
+
>
|
|
152
240
|
{searchable && (
|
|
153
241
|
<View style={selectStyles.searchContainer}>
|
|
154
242
|
<TextInput
|
|
@@ -161,16 +249,11 @@ const Select: React.FC<SelectProps> = ({
|
|
|
161
249
|
</View>
|
|
162
250
|
)}
|
|
163
251
|
|
|
164
|
-
<ScrollView
|
|
165
|
-
{
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
selected: isSelected,
|
|
170
|
-
disabled: option.disabled,
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
return (
|
|
252
|
+
<ScrollView
|
|
253
|
+
style={selectStyles.optionsList}
|
|
254
|
+
showsVerticalScrollIndicator={true}
|
|
255
|
+
>
|
|
256
|
+
{filteredOptions.map((option) => (
|
|
174
257
|
<Pressable
|
|
175
258
|
key={option.value}
|
|
176
259
|
style={selectStyles.option}
|
|
@@ -179,11 +262,7 @@ const Select: React.FC<SelectProps> = ({
|
|
|
179
262
|
android_ripple={{ color: 'rgba(0, 0, 0, 0.1)' }}
|
|
180
263
|
>
|
|
181
264
|
<View style={selectStyles.optionContent}>
|
|
182
|
-
{option.icon
|
|
183
|
-
<View style={selectStyles.optionIcon}>
|
|
184
|
-
{option.icon}
|
|
185
|
-
</View>
|
|
186
|
-
)}
|
|
265
|
+
{renderOptionIcon(option.icon)}
|
|
187
266
|
<Text
|
|
188
267
|
style={[
|
|
189
268
|
selectStyles.optionText,
|
|
@@ -194,8 +273,7 @@ const Select: React.FC<SelectProps> = ({
|
|
|
194
273
|
</Text>
|
|
195
274
|
</View>
|
|
196
275
|
</Pressable>
|
|
197
|
-
)
|
|
198
|
-
})}
|
|
276
|
+
))}
|
|
199
277
|
|
|
200
278
|
{filteredOptions.length === 0 && (
|
|
201
279
|
<View style={selectStyles.option}>
|
|
@@ -205,14 +283,15 @@ const Select: React.FC<SelectProps> = ({
|
|
|
205
283
|
</View>
|
|
206
284
|
)}
|
|
207
285
|
</ScrollView>
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
286
|
+
</Animated.View>
|
|
287
|
+
</BoundedModalContent>
|
|
288
|
+
</Pressable>
|
|
289
|
+
</Modal>
|
|
290
|
+
);
|
|
291
|
+
};
|
|
213
292
|
|
|
214
293
|
return (
|
|
215
|
-
<View style={[selectStyles.container, style]} testID={testID}>
|
|
294
|
+
<View ref={ref} style={[selectStyles.container, style]} testID={testID}>
|
|
216
295
|
{label && (
|
|
217
296
|
<Text style={selectStyles.label}>
|
|
218
297
|
{label}
|
|
@@ -220,7 +299,8 @@ const Select: React.FC<SelectProps> = ({
|
|
|
220
299
|
)}
|
|
221
300
|
|
|
222
301
|
<Pressable
|
|
223
|
-
|
|
302
|
+
ref={mergedTriggerRef}
|
|
303
|
+
style={selectStyles.trigger({ type, intent })}
|
|
224
304
|
onPress={handleTriggerPress}
|
|
225
305
|
disabled={disabled}
|
|
226
306
|
accessibilityLabel={accessibilityLabel || label}
|
|
@@ -232,11 +312,7 @@ const Select: React.FC<SelectProps> = ({
|
|
|
232
312
|
android_ripple={{ color: 'rgba(0, 0, 0, 0.1)' }}
|
|
233
313
|
>
|
|
234
314
|
<View style={selectStyles.triggerContent}>
|
|
235
|
-
{selectedOption?.icon
|
|
236
|
-
<View style={selectStyles.icon}>
|
|
237
|
-
{selectedOption.icon}
|
|
238
|
-
</View>
|
|
239
|
-
)}
|
|
315
|
+
{renderTriggerIcon(selectedOption?.icon)}
|
|
240
316
|
<Text
|
|
241
317
|
style={[
|
|
242
318
|
selectedOption ? selectStyles.triggerText : selectStyles.placeholder,
|
|
@@ -257,7 +333,6 @@ const Select: React.FC<SelectProps> = ({
|
|
|
257
333
|
<Text
|
|
258
334
|
style={[
|
|
259
335
|
selectStyles.helperText,
|
|
260
|
-
error && selectStyles.helperText.variants?.error?.true,
|
|
261
336
|
]}
|
|
262
337
|
>
|
|
263
338
|
{helperText}
|
|
@@ -265,6 +340,8 @@ const Select: React.FC<SelectProps> = ({
|
|
|
265
340
|
)}
|
|
266
341
|
</View>
|
|
267
342
|
);
|
|
268
|
-
};
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
Select.displayName = 'Select';
|
|
269
346
|
|
|
270
347
|
export default Select;
|