@idealyst/components 1.0.82 → 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 +25 -7
- 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 +347 -0
- package/src/Select/Select.styles.tsx +335 -0
- package/src/Select/Select.web.tsx +276 -0
- package/src/Select/index.native.ts +2 -0
- package/src/Select/index.ts +5 -0
- package/src/Select/index.web.ts +5 -0
- package/src/Select/types.ts +124 -0
- 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 +423 -0
- 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 +18 -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 +75 -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/Text/README.md +0 -94
- package/src/View/README.md +0 -107
- package/src/examples/AllExamples.tsx +0 -84
- 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
|
@@ -1,14 +1,17 @@
|
|
|
1
|
-
import React, { useState, useEffect } from 'react';
|
|
1
|
+
import React, { useState, useEffect, forwardRef } from 'react';
|
|
2
2
|
import { getWebProps } from 'react-native-unistyles/web';
|
|
3
3
|
import { CheckboxProps } from './types';
|
|
4
|
-
import { checkboxStyles
|
|
4
|
+
import { checkboxStyles } from './Checkbox.styles';
|
|
5
|
+
import { IconSvg } from '../Icon/IconSvg/IconSvg.web';
|
|
6
|
+
import { resolveIconPath } from '../Icon/icon-resolver';
|
|
7
|
+
import useMergeRefs from '../hooks/useMergeRefs';
|
|
5
8
|
|
|
6
|
-
const Checkbox
|
|
9
|
+
const Checkbox = forwardRef<HTMLDivElement, CheckboxProps>(({
|
|
7
10
|
checked = false,
|
|
8
11
|
indeterminate = false,
|
|
9
12
|
disabled = false,
|
|
10
13
|
onCheckedChange,
|
|
11
|
-
size = '
|
|
14
|
+
size = 'md',
|
|
12
15
|
intent = 'primary',
|
|
13
16
|
variant = 'default',
|
|
14
17
|
label,
|
|
@@ -19,54 +22,38 @@ const Checkbox: React.FC<CheckboxProps> = ({
|
|
|
19
22
|
required = false,
|
|
20
23
|
error,
|
|
21
24
|
helperText,
|
|
22
|
-
}) => {
|
|
25
|
+
}, ref) => {
|
|
23
26
|
const [internalChecked, setInternalChecked] = useState(checked);
|
|
24
|
-
|
|
27
|
+
|
|
25
28
|
useEffect(() => {
|
|
26
29
|
setInternalChecked(checked);
|
|
27
30
|
}, [checked]);
|
|
28
31
|
|
|
29
32
|
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
30
33
|
if (disabled) return;
|
|
31
|
-
|
|
34
|
+
|
|
32
35
|
const newChecked = event.target.checked;
|
|
33
36
|
setInternalChecked(newChecked);
|
|
34
37
|
onCheckedChange?.(newChecked);
|
|
35
38
|
};
|
|
36
39
|
|
|
37
|
-
// Apply variants
|
|
40
|
+
// Apply variants
|
|
38
41
|
checkboxStyles.useVariants({
|
|
39
42
|
size,
|
|
40
|
-
|
|
41
|
-
variant: variant as any,
|
|
43
|
+
type: variant as any,
|
|
42
44
|
checked: internalChecked,
|
|
43
45
|
disabled,
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
// Apply variants for label
|
|
47
|
-
checkboxLabelStyles.useVariants({
|
|
48
|
-
size,
|
|
49
|
-
disabled,
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
// Apply variants for checkmark
|
|
53
|
-
checkboxCheckmarkStyles.useVariants({
|
|
54
|
-
size,
|
|
55
|
-
visible: internalChecked,
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
// Apply variants for helper text
|
|
59
|
-
checkboxHelperStyles.useVariants({
|
|
60
|
-
error: !!error,
|
|
46
|
+
visible: internalChecked || indeterminate,
|
|
47
|
+
error: Boolean(error),
|
|
61
48
|
});
|
|
62
49
|
|
|
63
50
|
// Create style arrays
|
|
64
|
-
const wrapperStyleArray = [checkboxStyles.wrapper, style];
|
|
51
|
+
const wrapperStyleArray = [checkboxStyles.wrapper, style as any];
|
|
65
52
|
const containerStyleArray = [checkboxStyles.container];
|
|
66
|
-
const checkboxStyleArray = [checkboxStyles.checkbox];
|
|
67
|
-
const labelStyleArray = [
|
|
68
|
-
const helperTextStyleArray = [
|
|
69
|
-
const checkmarkStyleArray = [
|
|
53
|
+
const checkboxStyleArray = [checkboxStyles.checkbox({ intent })];
|
|
54
|
+
const labelStyleArray = [checkboxStyles.label];
|
|
55
|
+
const helperTextStyleArray = [checkboxStyles.helperText];
|
|
56
|
+
const checkmarkStyleArray = [checkboxStyles.checkmark];
|
|
70
57
|
|
|
71
58
|
// Generate web props
|
|
72
59
|
const wrapperProps = getWebProps(wrapperStyleArray);
|
|
@@ -79,8 +66,10 @@ const Checkbox: React.FC<CheckboxProps> = ({
|
|
|
79
66
|
const labelContent = children || label;
|
|
80
67
|
const displayHelperText = error || helperText;
|
|
81
68
|
|
|
69
|
+
const mergedRef = useMergeRefs(ref, wrapperProps.ref);
|
|
70
|
+
|
|
82
71
|
return (
|
|
83
|
-
<div {...wrapperProps}>
|
|
72
|
+
<div {...wrapperProps} ref={mergedRef}>
|
|
84
73
|
<label {...containerProps}>
|
|
85
74
|
<div style={{ position: 'relative' }}>
|
|
86
75
|
<input
|
|
@@ -107,9 +96,11 @@ const Checkbox: React.FC<CheckboxProps> = ({
|
|
|
107
96
|
/>
|
|
108
97
|
<div {...checkboxProps}>
|
|
109
98
|
{(internalChecked || indeterminate) && (
|
|
110
|
-
<
|
|
111
|
-
{indeterminate ? '
|
|
112
|
-
|
|
99
|
+
<IconSvg
|
|
100
|
+
path={resolveIconPath(indeterminate ? 'minus' : 'check')}
|
|
101
|
+
{...checkmarkProps}
|
|
102
|
+
aria-label={indeterminate ? 'minus' : 'check'}
|
|
103
|
+
/>
|
|
113
104
|
)}
|
|
114
105
|
</div>
|
|
115
106
|
</div>
|
|
@@ -126,6 +117,8 @@ const Checkbox: React.FC<CheckboxProps> = ({
|
|
|
126
117
|
)}
|
|
127
118
|
</div>
|
|
128
119
|
);
|
|
129
|
-
};
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
Checkbox.displayName = 'Checkbox';
|
|
130
123
|
|
|
131
124
|
export default Checkbox;
|
package/src/Checkbox/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
export {
|
|
5
|
-
export * from './types';
|
|
1
|
+
import CheckboxComponent from './Checkbox.web';
|
|
2
|
+
|
|
3
|
+
export default CheckboxComponent;
|
|
4
|
+
export { CheckboxComponent as Checkbox };
|
|
5
|
+
export * from './types';
|
package/src/Checkbox/types.ts
CHANGED
|
@@ -1,77 +1,83 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import type {
|
|
1
|
+
import { Intent, Size } from '@idealyst/theme';
|
|
2
|
+
import type { ReactNode } from 'react';
|
|
3
|
+
import type { StyleProp, ViewStyle } from 'react-native';
|
|
4
|
+
|
|
5
|
+
// Component-specific type aliases for future extensibility
|
|
6
|
+
export type CheckboxIntentVariant = Intent;
|
|
7
|
+
export type CheckboxSizeVariant = Size;
|
|
8
|
+
export type CheckboxVariant = 'default' | 'outlined';
|
|
3
9
|
|
|
4
10
|
export interface CheckboxProps {
|
|
5
11
|
/**
|
|
6
12
|
* Whether the checkbox is checked
|
|
7
13
|
*/
|
|
8
14
|
checked?: boolean;
|
|
9
|
-
|
|
15
|
+
|
|
10
16
|
/**
|
|
11
17
|
* Whether the checkbox is in an indeterminate state
|
|
12
18
|
*/
|
|
13
19
|
indeterminate?: boolean;
|
|
14
|
-
|
|
20
|
+
|
|
15
21
|
/**
|
|
16
22
|
* Whether the checkbox is disabled
|
|
17
23
|
*/
|
|
18
24
|
disabled?: boolean;
|
|
19
|
-
|
|
25
|
+
|
|
20
26
|
/**
|
|
21
27
|
* Called when the checkbox state changes
|
|
22
28
|
*/
|
|
23
29
|
onCheckedChange?: (checked: boolean) => void;
|
|
24
|
-
|
|
30
|
+
|
|
25
31
|
/**
|
|
26
32
|
* The size of the checkbox
|
|
27
33
|
*/
|
|
28
|
-
size?:
|
|
29
|
-
|
|
34
|
+
size?: CheckboxSizeVariant;
|
|
35
|
+
|
|
30
36
|
/**
|
|
31
37
|
* The intent/color scheme of the checkbox
|
|
32
38
|
*/
|
|
33
|
-
intent?:
|
|
34
|
-
|
|
39
|
+
intent?: CheckboxIntentVariant;
|
|
40
|
+
|
|
35
41
|
/**
|
|
36
42
|
* The visual style variant of the checkbox
|
|
37
43
|
*/
|
|
38
|
-
variant?:
|
|
39
|
-
|
|
44
|
+
variant?: CheckboxVariant;
|
|
45
|
+
|
|
40
46
|
/**
|
|
41
47
|
* Label text to display next to the checkbox
|
|
42
48
|
*/
|
|
43
49
|
label?: string;
|
|
44
|
-
|
|
50
|
+
|
|
45
51
|
/**
|
|
46
52
|
* Custom label content (ReactNode)
|
|
47
53
|
*/
|
|
48
54
|
children?: ReactNode;
|
|
49
|
-
|
|
55
|
+
|
|
50
56
|
/**
|
|
51
57
|
* Additional styles (platform-specific)
|
|
52
58
|
*/
|
|
53
|
-
style?:
|
|
54
|
-
|
|
59
|
+
style?: StyleProp<ViewStyle>;
|
|
60
|
+
|
|
55
61
|
/**
|
|
56
62
|
* Test ID for testing
|
|
57
63
|
*/
|
|
58
64
|
testID?: string;
|
|
59
|
-
|
|
65
|
+
|
|
60
66
|
/**
|
|
61
67
|
* Accessibility label
|
|
62
68
|
*/
|
|
63
69
|
accessibilityLabel?: string;
|
|
64
|
-
|
|
70
|
+
|
|
65
71
|
/**
|
|
66
72
|
* Whether the checkbox is required
|
|
67
73
|
*/
|
|
68
74
|
required?: boolean;
|
|
69
|
-
|
|
75
|
+
|
|
70
76
|
/**
|
|
71
77
|
* Error message to display
|
|
72
78
|
*/
|
|
73
79
|
error?: string;
|
|
74
|
-
|
|
80
|
+
|
|
75
81
|
/**
|
|
76
82
|
* Helper text to display
|
|
77
83
|
*/
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import React, { isValidElement, forwardRef, ComponentRef } from 'react';
|
|
2
|
+
import { Pressable, Text, View } from 'react-native';
|
|
3
|
+
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
|
|
4
|
+
import { chipStyles } from './Chip.styles';
|
|
5
|
+
import { isIconName } from '../Icon/icon-resolver';
|
|
6
|
+
import type { ChipProps } from './types';
|
|
7
|
+
|
|
8
|
+
const Chip = forwardRef<ComponentRef<typeof Pressable>, ChipProps>(({
|
|
9
|
+
label,
|
|
10
|
+
type = 'filled',
|
|
11
|
+
intent = 'primary',
|
|
12
|
+
size = 'md',
|
|
13
|
+
icon,
|
|
14
|
+
deletable = false,
|
|
15
|
+
onDelete,
|
|
16
|
+
selectable = false,
|
|
17
|
+
selected = false,
|
|
18
|
+
onPress,
|
|
19
|
+
disabled = false,
|
|
20
|
+
style,
|
|
21
|
+
testID,
|
|
22
|
+
}, ref) => {
|
|
23
|
+
const handlePress = () => {
|
|
24
|
+
if (disabled) return;
|
|
25
|
+
if (onPress) {
|
|
26
|
+
onPress();
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const handleDelete = () => {
|
|
31
|
+
if (disabled) return;
|
|
32
|
+
if (onDelete) {
|
|
33
|
+
onDelete();
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
// Compute actual selected state
|
|
38
|
+
const isSelected = selectable ? selected : false;
|
|
39
|
+
|
|
40
|
+
// Compute dynamic styles
|
|
41
|
+
const containerStyle = chipStyles.container(size, intent, type, isSelected, disabled);
|
|
42
|
+
const labelStyle = chipStyles.label(size, intent, type, isSelected);
|
|
43
|
+
const iconStyle = chipStyles.icon(size, intent, type, isSelected);
|
|
44
|
+
const deleteButtonStyle = chipStyles.deleteButton(size);
|
|
45
|
+
const deleteIconStyle = chipStyles.deleteIcon(size, intent, type, isSelected);
|
|
46
|
+
|
|
47
|
+
// Map chip size to icon size
|
|
48
|
+
const iconSize = size === 'sm' ? 12 : size === 'md' ? 14 : 16;
|
|
49
|
+
const deleteIconSize = size === 'sm' ? 10 : size === 'md' ? 11 : 12;
|
|
50
|
+
|
|
51
|
+
// Helper to render icon
|
|
52
|
+
const renderIcon = () => {
|
|
53
|
+
if (!icon) return null;
|
|
54
|
+
|
|
55
|
+
if (typeof icon === 'string' && isIconName(icon)) {
|
|
56
|
+
return (
|
|
57
|
+
<View style={iconStyle}>
|
|
58
|
+
<MaterialCommunityIcons
|
|
59
|
+
name={icon}
|
|
60
|
+
size={iconSize}
|
|
61
|
+
style={iconStyle}
|
|
62
|
+
/>
|
|
63
|
+
</View>
|
|
64
|
+
);
|
|
65
|
+
} else if (isValidElement(icon)) {
|
|
66
|
+
return icon;
|
|
67
|
+
}
|
|
68
|
+
return null;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const isClickable = (onPress && !disabled) || (selectable && !disabled);
|
|
72
|
+
|
|
73
|
+
const innerContent = (
|
|
74
|
+
<>
|
|
75
|
+
{icon && renderIcon()}
|
|
76
|
+
|
|
77
|
+
<Text style={labelStyle}>{label}</Text>
|
|
78
|
+
|
|
79
|
+
{deletable && onDelete && (
|
|
80
|
+
<Pressable
|
|
81
|
+
style={deleteButtonStyle}
|
|
82
|
+
onPress={handleDelete}
|
|
83
|
+
disabled={disabled}
|
|
84
|
+
hitSlop={8}
|
|
85
|
+
accessibilityLabel="Delete"
|
|
86
|
+
accessibilityRole="button"
|
|
87
|
+
>
|
|
88
|
+
<MaterialCommunityIcons
|
|
89
|
+
name="close"
|
|
90
|
+
size={deleteIconSize}
|
|
91
|
+
style={deleteIconStyle}
|
|
92
|
+
/>
|
|
93
|
+
</Pressable>
|
|
94
|
+
)}
|
|
95
|
+
</>
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
if (isClickable) {
|
|
99
|
+
return (
|
|
100
|
+
<Pressable
|
|
101
|
+
ref={ref}
|
|
102
|
+
onPress={handlePress}
|
|
103
|
+
disabled={disabled}
|
|
104
|
+
accessibilityRole="button"
|
|
105
|
+
accessibilityState={{
|
|
106
|
+
disabled,
|
|
107
|
+
selected: selectable ? selected : undefined,
|
|
108
|
+
}}
|
|
109
|
+
>
|
|
110
|
+
<View style={[containerStyle, style]} testID={testID}>
|
|
111
|
+
{innerContent}
|
|
112
|
+
</View>
|
|
113
|
+
</Pressable>
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return (
|
|
118
|
+
<View ref={ref} style={[containerStyle, style]} testID={testID}>
|
|
119
|
+
{innerContent}
|
|
120
|
+
</View>
|
|
121
|
+
);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
Chip.displayName = 'Chip';
|
|
125
|
+
|
|
126
|
+
export default Chip;
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { StyleSheet } from 'react-native-unistyles';
|
|
2
|
+
import { Theme, Intent} from '@idealyst/theme';
|
|
3
|
+
|
|
4
|
+
type ChipSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl';
|
|
5
|
+
type ChipType = 'filled' | 'outlined' | 'soft';
|
|
6
|
+
type ChipIntent = Intent;
|
|
7
|
+
|
|
8
|
+
// Styles are inlined here instead of in @idealyst/theme because Unistyles' Babel
|
|
9
|
+
// transform on native cannot resolve function calls to extract variant structures.
|
|
10
|
+
export const chipStyles = StyleSheet.create((theme: Theme) => {
|
|
11
|
+
return {
|
|
12
|
+
container: (size: ChipSize, intent: ChipIntent, type: ChipType, selected: boolean, disabled: boolean) => {
|
|
13
|
+
const intentValue = theme.intents[intent];
|
|
14
|
+
const sizeValue = theme.sizes.chip[size];
|
|
15
|
+
|
|
16
|
+
// Compute colors based on type and selected state
|
|
17
|
+
let backgroundColor: string;
|
|
18
|
+
let borderColor: string;
|
|
19
|
+
let borderWidth: number;
|
|
20
|
+
|
|
21
|
+
if (type === 'filled') {
|
|
22
|
+
borderWidth = 1;
|
|
23
|
+
backgroundColor = selected ? intentValue.contrast : intentValue.primary;
|
|
24
|
+
borderColor = selected ? intentValue.primary : 'transparent';
|
|
25
|
+
} else if (type === 'outlined') {
|
|
26
|
+
borderWidth = 1;
|
|
27
|
+
backgroundColor = selected ? intentValue.primary : 'transparent';
|
|
28
|
+
borderColor = intentValue.primary;
|
|
29
|
+
} else { // soft
|
|
30
|
+
borderWidth = 0;
|
|
31
|
+
backgroundColor = selected ? intentValue.primary : intentValue.light;
|
|
32
|
+
borderColor = 'transparent';
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return {
|
|
36
|
+
display: 'flex' as const,
|
|
37
|
+
flexDirection: 'row' as const,
|
|
38
|
+
alignItems: 'center' as const,
|
|
39
|
+
justifyContent: 'center' as const,
|
|
40
|
+
gap: 4,
|
|
41
|
+
paddingHorizontal: sizeValue.paddingHorizontal as number,
|
|
42
|
+
paddingVertical: sizeValue.paddingVertical as number,
|
|
43
|
+
minHeight: sizeValue.minHeight as number,
|
|
44
|
+
borderRadius: sizeValue.borderRadius as number,
|
|
45
|
+
backgroundColor,
|
|
46
|
+
borderColor,
|
|
47
|
+
borderWidth,
|
|
48
|
+
borderStyle: borderWidth > 0 ? ('solid' as const) : undefined,
|
|
49
|
+
opacity: disabled ? 0.5 : 1,
|
|
50
|
+
} as const;
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
label: (size: ChipSize, intent: ChipIntent, type: ChipType, selected: boolean) => {
|
|
54
|
+
const intentValue = theme.intents[intent];
|
|
55
|
+
const sizeValue = theme.sizes.chip[size];
|
|
56
|
+
|
|
57
|
+
// Compute color based on type and selected state
|
|
58
|
+
let color: string;
|
|
59
|
+
|
|
60
|
+
if (type === 'filled') {
|
|
61
|
+
color = selected ? intentValue.primary : intentValue.contrast;
|
|
62
|
+
} else if (type === 'outlined') {
|
|
63
|
+
color = selected ? theme.colors.text.inverse : intentValue.primary;
|
|
64
|
+
} else { // soft
|
|
65
|
+
color = selected ? theme.colors.text.inverse : intentValue.dark;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
fontFamily: 'inherit',
|
|
70
|
+
fontWeight: '500' as const,
|
|
71
|
+
fontSize: sizeValue.fontSize as number,
|
|
72
|
+
lineHeight: sizeValue.lineHeight as number,
|
|
73
|
+
color,
|
|
74
|
+
} as const;
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
icon: (size: ChipSize, intent: ChipIntent, type: ChipType, selected: boolean) => {
|
|
78
|
+
const intentValue = theme.intents[intent];
|
|
79
|
+
const sizeValue = theme.sizes.chip[size];
|
|
80
|
+
|
|
81
|
+
// Compute color based on type and selected state (same logic as label)
|
|
82
|
+
let color: string;
|
|
83
|
+
|
|
84
|
+
if (type === 'filled') {
|
|
85
|
+
color = selected ? intentValue.primary : intentValue.contrast;
|
|
86
|
+
} else if (type === 'outlined') {
|
|
87
|
+
color = selected ? theme.colors.text.inverse : intentValue.primary;
|
|
88
|
+
} else { // soft
|
|
89
|
+
color = selected ? theme.colors.text.inverse : intentValue.dark;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return {
|
|
93
|
+
display: 'flex' as const,
|
|
94
|
+
alignItems: 'center' as const,
|
|
95
|
+
justifyContent: 'center' as const,
|
|
96
|
+
width: sizeValue.iconSize as number,
|
|
97
|
+
height: sizeValue.iconSize as number,
|
|
98
|
+
color,
|
|
99
|
+
} as const;
|
|
100
|
+
},
|
|
101
|
+
|
|
102
|
+
deleteButton: (size: ChipSize) => {
|
|
103
|
+
const sizeValue = theme.sizes.chip[size];
|
|
104
|
+
|
|
105
|
+
return {
|
|
106
|
+
display: 'flex' as const,
|
|
107
|
+
alignItems: 'center' as const,
|
|
108
|
+
justifyContent: 'center' as const,
|
|
109
|
+
padding: 0,
|
|
110
|
+
marginLeft: 4,
|
|
111
|
+
borderRadius: 12,
|
|
112
|
+
width: sizeValue.iconSize as number,
|
|
113
|
+
height: sizeValue.iconSize as number,
|
|
114
|
+
} as const;
|
|
115
|
+
},
|
|
116
|
+
|
|
117
|
+
deleteIcon: (size: ChipSize, intent: ChipIntent, type: ChipType, selected: boolean) => {
|
|
118
|
+
const intentValue = theme.intents[intent];
|
|
119
|
+
const sizeValue = theme.sizes.chip[size];
|
|
120
|
+
|
|
121
|
+
// Compute color based on type and selected state (same logic as label/icon)
|
|
122
|
+
let color: string;
|
|
123
|
+
|
|
124
|
+
if (type === 'filled') {
|
|
125
|
+
color = selected ? intentValue.primary : intentValue.contrast;
|
|
126
|
+
} else if (type === 'outlined') {
|
|
127
|
+
color = selected ? theme.colors.text.inverse : intentValue.primary;
|
|
128
|
+
} else { // soft
|
|
129
|
+
color = selected ? theme.colors.text.inverse : intentValue.dark;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return {
|
|
133
|
+
fontSize: sizeValue.iconSize as number,
|
|
134
|
+
color,
|
|
135
|
+
} as const;
|
|
136
|
+
},
|
|
137
|
+
} as const;
|
|
138
|
+
});
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import React, { isValidElement, forwardRef } from 'react';
|
|
2
|
+
import { getWebProps } from 'react-native-unistyles/web';
|
|
3
|
+
import { chipStyles } from './Chip.styles';
|
|
4
|
+
import type { ChipProps } from './types';
|
|
5
|
+
import { IconSvg } from '../Icon/IconSvg/IconSvg.web';
|
|
6
|
+
import { resolveIconPath, isIconName } from '../Icon/icon-resolver';
|
|
7
|
+
import useMergeRefs from '../hooks/useMergeRefs';
|
|
8
|
+
|
|
9
|
+
const Chip = forwardRef<HTMLDivElement, ChipProps>(({
|
|
10
|
+
label,
|
|
11
|
+
type = 'filled',
|
|
12
|
+
intent = 'primary',
|
|
13
|
+
size = 'md',
|
|
14
|
+
icon,
|
|
15
|
+
deletable = false,
|
|
16
|
+
onDelete,
|
|
17
|
+
deleteIcon = 'close',
|
|
18
|
+
selectable = false,
|
|
19
|
+
selected = false,
|
|
20
|
+
onPress,
|
|
21
|
+
disabled = false,
|
|
22
|
+
style,
|
|
23
|
+
testID,
|
|
24
|
+
}, ref) => {
|
|
25
|
+
// Compute actual selected state
|
|
26
|
+
const isSelected = selectable ? selected : false;
|
|
27
|
+
|
|
28
|
+
// Compute dynamic styles
|
|
29
|
+
const containerProps = getWebProps([chipStyles.container(size, intent, type, isSelected, disabled), style as any]);
|
|
30
|
+
const labelProps = getWebProps([chipStyles.label(size, intent, type, isSelected)]);
|
|
31
|
+
const iconProps = getWebProps([chipStyles.icon(size, intent, type, isSelected)]);
|
|
32
|
+
const deleteButtonProps = getWebProps([chipStyles.deleteButton(size)]);
|
|
33
|
+
const deleteIconProps = getWebProps([chipStyles.deleteIcon(size, intent, type, isSelected)]);
|
|
34
|
+
|
|
35
|
+
const handleClick = () => {
|
|
36
|
+
if (disabled) return;
|
|
37
|
+
if (onPress) {
|
|
38
|
+
onPress();
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const handleDelete = (e: React.MouseEvent) => {
|
|
43
|
+
e.stopPropagation();
|
|
44
|
+
if (disabled) return;
|
|
45
|
+
if (onDelete) {
|
|
46
|
+
onDelete();
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
// Helper to render icon
|
|
51
|
+
const renderIcon = () => {
|
|
52
|
+
if (!icon) return null;
|
|
53
|
+
|
|
54
|
+
if (isIconName(icon)) {
|
|
55
|
+
const iconPath = resolveIconPath(icon);
|
|
56
|
+
return (
|
|
57
|
+
<IconSvg
|
|
58
|
+
path={iconPath}
|
|
59
|
+
{...iconProps}
|
|
60
|
+
aria-label={icon}
|
|
61
|
+
/>
|
|
62
|
+
);
|
|
63
|
+
} else if (isValidElement(icon)) {
|
|
64
|
+
return icon;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return null;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
// Helper to render delete icon
|
|
71
|
+
const renderDeleteIcon = () => {
|
|
72
|
+
if (isIconName(deleteIcon)) {
|
|
73
|
+
const iconPath = resolveIconPath(deleteIcon);
|
|
74
|
+
return (
|
|
75
|
+
<IconSvg
|
|
76
|
+
path={iconPath}
|
|
77
|
+
{...deleteIconProps}
|
|
78
|
+
aria-label={deleteIcon}
|
|
79
|
+
/>
|
|
80
|
+
);
|
|
81
|
+
} else if (isValidElement(deleteIcon)) {
|
|
82
|
+
return deleteIcon;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return null;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const isClickable = (onPress && !disabled) || (selectable && !disabled);
|
|
89
|
+
|
|
90
|
+
const mergedRef = useMergeRefs(ref, containerProps.ref);
|
|
91
|
+
|
|
92
|
+
return (
|
|
93
|
+
<div
|
|
94
|
+
{...containerProps}
|
|
95
|
+
ref={mergedRef}
|
|
96
|
+
style={{
|
|
97
|
+
cursor: isClickable ? 'pointer' : 'default',
|
|
98
|
+
userSelect: 'none',
|
|
99
|
+
}}
|
|
100
|
+
onClick={handleClick}
|
|
101
|
+
data-testid={testID}
|
|
102
|
+
role={isClickable ? 'button' : undefined}
|
|
103
|
+
aria-disabled={disabled}
|
|
104
|
+
aria-pressed={selectable ? selected : undefined}
|
|
105
|
+
>
|
|
106
|
+
{icon && (
|
|
107
|
+
<span
|
|
108
|
+
{...iconProps}
|
|
109
|
+
style={{
|
|
110
|
+
display: 'inline-flex',
|
|
111
|
+
alignItems: 'center',
|
|
112
|
+
justifyContent: 'center',
|
|
113
|
+
}}
|
|
114
|
+
>
|
|
115
|
+
{renderIcon()}
|
|
116
|
+
</span>
|
|
117
|
+
)}
|
|
118
|
+
|
|
119
|
+
<span
|
|
120
|
+
{...labelProps}
|
|
121
|
+
style={{
|
|
122
|
+
display: 'inline-flex',
|
|
123
|
+
alignItems: 'center',
|
|
124
|
+
}}
|
|
125
|
+
>
|
|
126
|
+
{label}
|
|
127
|
+
</span>
|
|
128
|
+
|
|
129
|
+
{deletable && onDelete && (
|
|
130
|
+
<button
|
|
131
|
+
{...deleteButtonProps}
|
|
132
|
+
style={{
|
|
133
|
+
background: 'transparent',
|
|
134
|
+
border: 'none',
|
|
135
|
+
cursor: disabled ? 'default' : 'pointer',
|
|
136
|
+
display: 'flex',
|
|
137
|
+
alignItems: 'center',
|
|
138
|
+
justifyContent: 'center',
|
|
139
|
+
}}
|
|
140
|
+
onClick={handleDelete}
|
|
141
|
+
disabled={disabled}
|
|
142
|
+
aria-label="Delete"
|
|
143
|
+
type="button"
|
|
144
|
+
>
|
|
145
|
+
{renderDeleteIcon()}
|
|
146
|
+
</button>
|
|
147
|
+
)}
|
|
148
|
+
</div>
|
|
149
|
+
);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
Chip.displayName = 'Chip';
|
|
153
|
+
|
|
154
|
+
export default Chip;
|