@idealyst/components 1.1.8 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -3
- package/plugin/web.js +280 -532
- package/src/Accordion/Accordion.web.tsx +1 -3
- package/src/Alert/Alert.web.tsx +3 -4
- package/src/Badge/Badge.web.tsx +8 -15
- package/src/Breadcrumb/Breadcrumb.web.tsx +4 -8
- package/src/Button/Button.native.tsx +14 -21
- package/src/Button/Button.styles.tsx +15 -0
- package/src/Button/Button.web.tsx +9 -19
- package/src/Checkbox/Checkbox.web.tsx +1 -2
- package/src/Chip/Chip.web.tsx +3 -5
- package/src/Dialog/Dialog.web.tsx +3 -3
- package/src/Dialog/types.ts +1 -1
- package/src/Icon/Icon.web.tsx +22 -17
- package/src/Icon/IconRegistry.native.ts +41 -0
- package/src/Icon/IconRegistry.ts +107 -0
- package/src/Icon/IconSvg/IconSvg.web.tsx +26 -5
- package/src/Icon/icon-resolver.ts +12 -43
- package/src/Icon/index.native.ts +2 -1
- package/src/Icon/index.ts +1 -0
- package/src/Icon/index.web.ts +1 -0
- package/src/Input/Input.styles.tsx +56 -83
- package/src/Input/Input.web.tsx +5 -8
- package/src/List/ListItem.native.tsx +6 -7
- package/src/List/ListItem.web.tsx +3 -3
- package/src/Menu/MenuItem.web.tsx +3 -5
- package/src/Screen/Screen.native.tsx +1 -1
- package/src/Screen/Screen.styles.tsx +3 -6
- package/src/Screen/Screen.web.tsx +1 -1
- package/src/Select/Select.styles.tsx +31 -48
- package/src/Select/Select.web.tsx +45 -33
- package/src/Slider/Slider.web.tsx +2 -4
- package/src/Switch/Switch.native.tsx +2 -2
- package/src/Switch/Switch.web.tsx +2 -3
- package/src/Table/Table.native.tsx +168 -65
- package/src/Table/Table.styles.tsx +26 -33
- package/src/Table/Table.web.tsx +169 -70
- package/src/Text/Text.web.tsx +1 -0
- package/src/TextArea/TextArea.native.tsx +21 -8
- package/src/TextArea/TextArea.styles.tsx +15 -27
- package/src/TextArea/TextArea.web.tsx +17 -6
- package/src/View/View.native.tsx +33 -3
- package/src/View/View.web.tsx +4 -21
- package/src/View/types.ts +31 -3
- package/src/examples/ButtonExamples.tsx +20 -0
- package/src/index.ts +1 -1
|
@@ -3,7 +3,6 @@ import { getWebProps } from 'react-native-unistyles/web';
|
|
|
3
3
|
import { accordionStyles } from './Accordion.styles';
|
|
4
4
|
import type { AccordionProps, AccordionItem as AccordionItemType } from './types';
|
|
5
5
|
import { IconSvg } from '../Icon/IconSvg/IconSvg.web';
|
|
6
|
-
import { resolveIconPath } from '../Icon/icon-resolver';
|
|
7
6
|
import { getWebAriaProps, generateAccessibilityId, ACCORDION_KEYS } from '../utils/accessibility';
|
|
8
7
|
|
|
9
8
|
interface AccordionItemProps {
|
|
@@ -33,7 +32,6 @@ const AccordionItem: React.FC<AccordionItemProps> = ({
|
|
|
33
32
|
}) => {
|
|
34
33
|
const contentInnerRef = useRef<HTMLDivElement>(null);
|
|
35
34
|
const [contentHeight, setContentHeight] = useState(0);
|
|
36
|
-
const chevronIconPath = resolveIconPath('chevron-down');
|
|
37
35
|
|
|
38
36
|
// Apply item-specific variants (for size, expanded, disabled)
|
|
39
37
|
accordionStyles.useVariants({
|
|
@@ -86,7 +84,7 @@ const AccordionItem: React.FC<AccordionItemProps> = ({
|
|
|
86
84
|
<span {...iconProps}>
|
|
87
85
|
<IconSvg
|
|
88
86
|
style={{ width: 12, height: 12 }}
|
|
89
|
-
|
|
87
|
+
name="chevron-down"
|
|
90
88
|
aria-label="chevron-down"
|
|
91
89
|
/>
|
|
92
90
|
</span>
|
package/src/Alert/Alert.web.tsx
CHANGED
|
@@ -3,7 +3,7 @@ import { getWebProps } from 'react-native-unistyles/web';
|
|
|
3
3
|
import { alertStyles } from './Alert.styles';
|
|
4
4
|
import type { AlertProps } from './types';
|
|
5
5
|
import { IconSvg } from '../Icon/IconSvg/IconSvg.web';
|
|
6
|
-
import {
|
|
6
|
+
import { isIconName } from '../Icon/icon-resolver';
|
|
7
7
|
import useMergeRefs from '../hooks/useMergeRefs';
|
|
8
8
|
|
|
9
9
|
// Default icons for each intent
|
|
@@ -49,10 +49,9 @@ const Alert = forwardRef<HTMLDivElement, AlertProps>(({
|
|
|
49
49
|
if (!iconProp) return null;
|
|
50
50
|
|
|
51
51
|
if (isIconName(iconProp)) {
|
|
52
|
-
const iconPath = resolveIconPath(iconProp);
|
|
53
52
|
return (
|
|
54
53
|
<IconSvg
|
|
55
|
-
|
|
54
|
+
name={iconProp}
|
|
56
55
|
{...iconContainerProps}
|
|
57
56
|
aria-label={iconProp}
|
|
58
57
|
/>
|
|
@@ -106,7 +105,7 @@ const Alert = forwardRef<HTMLDivElement, AlertProps>(({
|
|
|
106
105
|
type="button"
|
|
107
106
|
>
|
|
108
107
|
<IconSvg
|
|
109
|
-
|
|
108
|
+
name="close"
|
|
110
109
|
{...closeIconProps}
|
|
111
110
|
aria-label="close"
|
|
112
111
|
/>
|
package/src/Badge/Badge.web.tsx
CHANGED
|
@@ -5,19 +5,13 @@ import { badgeStyles } from './Badge.styles';
|
|
|
5
5
|
import { IconSvg } from '../Icon/IconSvg/IconSvg.web';
|
|
6
6
|
import useMergeRefs from '../hooks/useMergeRefs';
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
interface InternalBadgeProps extends BadgeProps {
|
|
10
|
-
iconPath?: string;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
const Badge = forwardRef<HTMLSpanElement, BadgeProps>((props: InternalBadgeProps, ref) => {
|
|
8
|
+
const Badge = forwardRef<HTMLSpanElement, BadgeProps>((props, ref) => {
|
|
14
9
|
const {
|
|
15
10
|
children,
|
|
16
11
|
size = 'md',
|
|
17
12
|
type = 'filled',
|
|
18
13
|
color = 'blue',
|
|
19
14
|
icon,
|
|
20
|
-
iconPath,
|
|
21
15
|
style,
|
|
22
16
|
testID,
|
|
23
17
|
id,
|
|
@@ -27,7 +21,7 @@ const Badge = forwardRef<HTMLSpanElement, BadgeProps>((props: InternalBadgeProps
|
|
|
27
21
|
size,
|
|
28
22
|
type,
|
|
29
23
|
});
|
|
30
|
-
|
|
24
|
+
|
|
31
25
|
const badgeStyle = (badgeStyles.badge as any)({ color });
|
|
32
26
|
const contentStyle = badgeStyles.content;
|
|
33
27
|
const textStyle = (badgeStyles.text as any)({ color });
|
|
@@ -38,13 +32,12 @@ const Badge = forwardRef<HTMLSpanElement, BadgeProps>((props: InternalBadgeProps
|
|
|
38
32
|
const iconProps = getWebProps([badgeStyles.icon, textStyle]);
|
|
39
33
|
|
|
40
34
|
// Helper to render icon
|
|
41
|
-
const renderIcon = (iconProp: typeof icon
|
|
42
|
-
if (typeof iconProp === 'string'
|
|
43
|
-
// Render IconSvg
|
|
44
|
-
// Don't pass size prop - let the style control the dimensions entirely
|
|
35
|
+
const renderIcon = (iconProp: typeof icon) => {
|
|
36
|
+
if (typeof iconProp === 'string') {
|
|
37
|
+
// Render IconSvg with the icon name - registry lookup happens inside
|
|
45
38
|
return (
|
|
46
39
|
<IconSvg
|
|
47
|
-
|
|
40
|
+
name={iconProp}
|
|
48
41
|
{...iconProps}
|
|
49
42
|
aria-label={iconProp}
|
|
50
43
|
/>
|
|
@@ -85,7 +78,7 @@ const Badge = forwardRef<HTMLSpanElement, BadgeProps>((props: InternalBadgeProps
|
|
|
85
78
|
>
|
|
86
79
|
{hasIcon ? (
|
|
87
80
|
<span {...contentProps}>
|
|
88
|
-
{renderIcon(icon
|
|
81
|
+
{renderIcon(icon)}
|
|
89
82
|
<span {...textProps}>
|
|
90
83
|
{children}
|
|
91
84
|
</span>
|
|
@@ -101,4 +94,4 @@ const Badge = forwardRef<HTMLSpanElement, BadgeProps>((props: InternalBadgeProps
|
|
|
101
94
|
|
|
102
95
|
Badge.displayName = 'Badge';
|
|
103
96
|
|
|
104
|
-
export default Badge;
|
|
97
|
+
export default Badge;
|
|
@@ -3,7 +3,7 @@ import { getWebProps } from 'react-native-unistyles/web';
|
|
|
3
3
|
import { breadcrumbStyles } from './Breadcrumb.styles';
|
|
4
4
|
import type { BreadcrumbProps, BreadcrumbItem as BreadcrumbItemType } from './types';
|
|
5
5
|
import { IconSvg } from '../Icon/IconSvg/IconSvg.web';
|
|
6
|
-
import {
|
|
6
|
+
import { isIconName } from '../Icon/icon-resolver';
|
|
7
7
|
import Menu from '../Menu/Menu.web';
|
|
8
8
|
import type { MenuItem } from '../Menu/types';
|
|
9
9
|
|
|
@@ -48,11 +48,9 @@ const BreadcrumbItem: React.FC<BreadcrumbItemProps> = ({ item, isLast, size, int
|
|
|
48
48
|
if (!item.icon) return null;
|
|
49
49
|
|
|
50
50
|
if (isIconName(item.icon)) {
|
|
51
|
-
const iconPath = resolveIconPath(item.icon);
|
|
52
|
-
// IconSvg uses size="1em" by default, sized by container's fontSize from styles
|
|
53
51
|
return (
|
|
54
52
|
<IconSvg
|
|
55
|
-
|
|
53
|
+
name={item.icon}
|
|
56
54
|
aria-label={item.icon}
|
|
57
55
|
/>
|
|
58
56
|
);
|
|
@@ -138,12 +136,11 @@ const BreadcrumbEllipsis: React.FC<BreadcrumbEllipsisProps> = ({ size, intent })
|
|
|
138
136
|
const ellipsisIconStyle = (breadcrumbStyles.ellipsisIcon as any)({ intent });
|
|
139
137
|
const ellipsisProps = getWebProps([ellipsisStyle]);
|
|
140
138
|
const iconProps = getWebProps([ellipsisIconStyle]);
|
|
141
|
-
const ellipsisIconPath = resolveIconPath('dots-horizontal');
|
|
142
139
|
|
|
143
140
|
return (
|
|
144
141
|
<span {...ellipsisProps}>
|
|
145
142
|
<IconSvg
|
|
146
|
-
|
|
143
|
+
name="dots-horizontal"
|
|
147
144
|
{...iconProps}
|
|
148
145
|
aria-label="more items"
|
|
149
146
|
/>
|
|
@@ -173,7 +170,6 @@ const Breadcrumb: React.FC<BreadcrumbProps> = ({
|
|
|
173
170
|
const menuButtonIconStyle = (breadcrumbStyles.menuButtonIcon as any)({ intent });
|
|
174
171
|
|
|
175
172
|
const containerProps = getWebProps([containerStyle, style as any]);
|
|
176
|
-
const menuIconPath = resolveIconPath('dots-horizontal');
|
|
177
173
|
|
|
178
174
|
// Apply variants for menu button
|
|
179
175
|
breadcrumbStyles.useVariants({ size });
|
|
@@ -250,7 +246,7 @@ const Breadcrumb: React.FC<BreadcrumbProps> = ({
|
|
|
250
246
|
aria-label="Show more breadcrumb items"
|
|
251
247
|
>
|
|
252
248
|
<IconSvg
|
|
253
|
-
|
|
249
|
+
name="dots-horizontal"
|
|
254
250
|
{...menuIconProps}
|
|
255
251
|
aria-label="dots-horizontal"
|
|
256
252
|
/>
|
|
@@ -5,6 +5,7 @@ import Svg, { Defs, LinearGradient, Stop, Rect } from 'react-native-svg';
|
|
|
5
5
|
import { buttonStyles } from './Button.styles';
|
|
6
6
|
import { ButtonProps } from './types';
|
|
7
7
|
import { getNativeInteractiveAccessibilityProps } from '../utils/accessibility';
|
|
8
|
+
import { useUnistyles } from 'react-native-unistyles';
|
|
8
9
|
|
|
9
10
|
const Button = forwardRef<ComponentRef<typeof TouchableOpacity>, ButtonProps>((props, ref) => {
|
|
10
11
|
const {
|
|
@@ -71,24 +72,6 @@ const Button = forwardRef<ComponentRef<typeof TouchableOpacity>, ButtonProps>((p
|
|
|
71
72
|
} as const;
|
|
72
73
|
const iconSize = iconSizeMap[size];
|
|
73
74
|
|
|
74
|
-
// Helper to render icon - uses the icon styles from buttonStyles
|
|
75
|
-
const renderIcon = (icon: string | React.ReactNode) => {
|
|
76
|
-
if (typeof icon === 'string') {
|
|
77
|
-
// Render MaterialCommunityIcons with explicit size prop
|
|
78
|
-
// The icon styles provide the correct color based on dynamic styles
|
|
79
|
-
return (
|
|
80
|
-
<MaterialCommunityIcons
|
|
81
|
-
name={icon}
|
|
82
|
-
size={iconSize}
|
|
83
|
-
style={iconStyle}
|
|
84
|
-
/>
|
|
85
|
-
);
|
|
86
|
-
} else if (isValidElement(icon)) {
|
|
87
|
-
// Render custom component as-is
|
|
88
|
-
return icon;
|
|
89
|
-
}
|
|
90
|
-
return null;
|
|
91
|
-
};
|
|
92
75
|
|
|
93
76
|
// Use children if available, otherwise use title
|
|
94
77
|
const buttonContent = children || title;
|
|
@@ -199,12 +182,22 @@ const Button = forwardRef<ComponentRef<typeof TouchableOpacity>, ButtonProps>((p
|
|
|
199
182
|
<TouchableOpacity {...touchableProps as any}>
|
|
200
183
|
{renderGradientLayer()}
|
|
201
184
|
{hasIcons ? (
|
|
202
|
-
<View
|
|
203
|
-
{leftIcon &&
|
|
185
|
+
<View style={iconContainerStyle}>
|
|
186
|
+
{leftIcon &&
|
|
187
|
+
<MaterialCommunityIcons
|
|
188
|
+
name={leftIcon}
|
|
189
|
+
size={iconSize}
|
|
190
|
+
style={iconStyle}
|
|
191
|
+
/>}
|
|
204
192
|
<Text style={textStyle}>
|
|
205
193
|
{buttonContent}
|
|
206
194
|
</Text>
|
|
207
|
-
{rightIcon &&
|
|
195
|
+
{rightIcon &&
|
|
196
|
+
<MaterialCommunityIcons
|
|
197
|
+
name={rightIcon}
|
|
198
|
+
size={iconSize}
|
|
199
|
+
style={iconStyle}
|
|
200
|
+
/>}
|
|
208
201
|
</View>
|
|
209
202
|
) : (
|
|
210
203
|
<Text style={textStyle}>
|
|
@@ -68,6 +68,21 @@ export const buttonStyles = defineStyle('Button', (theme: Theme) => ({
|
|
|
68
68
|
},
|
|
69
69
|
variants: {
|
|
70
70
|
// $iterator expands for each button size
|
|
71
|
+
type: {
|
|
72
|
+
contained: {
|
|
73
|
+
backgroundColor: theme.intents[intent].primary,
|
|
74
|
+
borderColor: 'transparent',
|
|
75
|
+
},
|
|
76
|
+
outlined: {
|
|
77
|
+
backgroundColor: 'transparent',
|
|
78
|
+
borderColor: theme.intents[intent].primary,
|
|
79
|
+
},
|
|
80
|
+
text: {
|
|
81
|
+
backgroundColor: 'transparent',
|
|
82
|
+
borderColor: 'transparent',
|
|
83
|
+
borderWidth: 0,
|
|
84
|
+
}
|
|
85
|
+
},
|
|
71
86
|
size: {
|
|
72
87
|
paddingVertical: theme.sizes.$button.paddingVertical,
|
|
73
88
|
paddingHorizontal: theme.sizes.$button.paddingHorizontal,
|
|
@@ -6,14 +6,7 @@ import { IconSvg } from '../Icon/IconSvg/IconSvg.web';
|
|
|
6
6
|
import useMergeRefs from '../hooks/useMergeRefs';
|
|
7
7
|
import { getWebInteractiveAriaProps, generateAccessibilityId } from '../utils/accessibility';
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
interface InternalButtonProps extends ButtonProps {
|
|
11
|
-
leftIconPath?: string;
|
|
12
|
-
rightIconPath?: string;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const Button = forwardRef<HTMLButtonElement, ButtonProps>((props: InternalButtonProps, ref) => {
|
|
16
|
-
|
|
9
|
+
const Button = forwardRef<HTMLButtonElement, ButtonProps>((props, ref) => {
|
|
17
10
|
const {
|
|
18
11
|
title,
|
|
19
12
|
children,
|
|
@@ -25,8 +18,6 @@ const Button = forwardRef<HTMLButtonElement, ButtonProps>((props: InternalButton
|
|
|
25
18
|
gradient,
|
|
26
19
|
leftIcon,
|
|
27
20
|
rightIcon,
|
|
28
|
-
leftIconPath,
|
|
29
|
-
rightIconPath,
|
|
30
21
|
style,
|
|
31
22
|
testID,
|
|
32
23
|
id,
|
|
@@ -124,14 +115,13 @@ const Button = forwardRef<HTMLButtonElement, ButtonProps>((props: InternalButton
|
|
|
124
115
|
const iconStyleArray = [(buttonStyles.icon as any)(dynamicProps)];
|
|
125
116
|
const iconProps = getWebProps(iconStyleArray);
|
|
126
117
|
|
|
127
|
-
// Helper to render icon
|
|
128
|
-
const renderIcon = (icon: string | React.ReactNode
|
|
129
|
-
if (typeof icon === 'string'
|
|
130
|
-
// Render IconSvg
|
|
131
|
-
// Don't pass size - let the style control the dimensions
|
|
118
|
+
// Helper to render icon - now uses icon name directly
|
|
119
|
+
const renderIcon = (icon: string | React.ReactNode) => {
|
|
120
|
+
if (typeof icon === 'string') {
|
|
121
|
+
// Render IconSvg with the icon name - registry lookup happens inside
|
|
132
122
|
return (
|
|
133
123
|
<IconSvg
|
|
134
|
-
|
|
124
|
+
name={icon}
|
|
135
125
|
{...iconProps}
|
|
136
126
|
aria-label={icon}
|
|
137
127
|
/>
|
|
@@ -164,9 +154,9 @@ const Button = forwardRef<HTMLButtonElement, ButtonProps>((props: InternalButton
|
|
|
164
154
|
>
|
|
165
155
|
{hasIcons ? (
|
|
166
156
|
<div {...iconContainerProps}>
|
|
167
|
-
{leftIcon && renderIcon(leftIcon
|
|
157
|
+
{leftIcon && renderIcon(leftIcon)}
|
|
168
158
|
{buttonContent}
|
|
169
|
-
{rightIcon && renderIcon(rightIcon
|
|
159
|
+
{rightIcon && renderIcon(rightIcon)}
|
|
170
160
|
</div>
|
|
171
161
|
) : (
|
|
172
162
|
buttonContent
|
|
@@ -177,4 +167,4 @@ const Button = forwardRef<HTMLButtonElement, ButtonProps>((props: InternalButton
|
|
|
177
167
|
|
|
178
168
|
Button.displayName = 'Button';
|
|
179
169
|
|
|
180
|
-
export default Button;
|
|
170
|
+
export default Button;
|
|
@@ -3,7 +3,6 @@ import { getWebProps } from 'react-native-unistyles/web';
|
|
|
3
3
|
import { CheckboxProps } from './types';
|
|
4
4
|
import { checkboxStyles } from './Checkbox.styles';
|
|
5
5
|
import { IconSvg } from '../Icon/IconSvg/IconSvg.web';
|
|
6
|
-
import { resolveIconPath } from '../Icon/icon-resolver';
|
|
7
6
|
import useMergeRefs from '../hooks/useMergeRefs';
|
|
8
7
|
import { getWebSelectionAriaProps, generateAccessibilityId, combineIds } from '../utils/accessibility';
|
|
9
8
|
|
|
@@ -177,7 +176,7 @@ const Checkbox = forwardRef<HTMLDivElement, CheckboxProps>(({
|
|
|
177
176
|
<div {...checkboxProps}>
|
|
178
177
|
{(internalChecked || indeterminate) && (
|
|
179
178
|
<IconSvg
|
|
180
|
-
|
|
179
|
+
name={indeterminate ? 'minus' : 'check'}
|
|
181
180
|
{...checkmarkProps}
|
|
182
181
|
aria-label={indeterminate ? 'minus' : 'check'}
|
|
183
182
|
/>
|
package/src/Chip/Chip.web.tsx
CHANGED
|
@@ -3,7 +3,7 @@ import { getWebProps } from 'react-native-unistyles/web';
|
|
|
3
3
|
import { chipStyles } from './Chip.styles';
|
|
4
4
|
import type { ChipProps } from './types';
|
|
5
5
|
import { IconSvg } from '../Icon/IconSvg/IconSvg.web';
|
|
6
|
-
import {
|
|
6
|
+
import { isIconName } from '../Icon/icon-resolver';
|
|
7
7
|
import useMergeRefs from '../hooks/useMergeRefs';
|
|
8
8
|
|
|
9
9
|
const Chip = forwardRef<HTMLDivElement, ChipProps>(({
|
|
@@ -56,10 +56,9 @@ const Chip = forwardRef<HTMLDivElement, ChipProps>(({
|
|
|
56
56
|
if (!icon) return null;
|
|
57
57
|
|
|
58
58
|
if (isIconName(icon)) {
|
|
59
|
-
const iconPath = resolveIconPath(icon);
|
|
60
59
|
return (
|
|
61
60
|
<IconSvg
|
|
62
|
-
|
|
61
|
+
name={icon}
|
|
63
62
|
{...iconProps}
|
|
64
63
|
aria-label={icon}
|
|
65
64
|
/>
|
|
@@ -74,10 +73,9 @@ const Chip = forwardRef<HTMLDivElement, ChipProps>(({
|
|
|
74
73
|
// Helper to render delete icon
|
|
75
74
|
const renderDeleteIcon = () => {
|
|
76
75
|
if (isIconName(deleteIcon)) {
|
|
77
|
-
const iconPath = resolveIconPath(deleteIcon);
|
|
78
76
|
return (
|
|
79
77
|
<IconSvg
|
|
80
|
-
|
|
78
|
+
name={deleteIcon}
|
|
81
79
|
{...deleteIconProps}
|
|
82
80
|
aria-label={deleteIcon}
|
|
83
81
|
/>
|
|
@@ -13,7 +13,7 @@ const Dialog = forwardRef<HTMLDivElement, DialogProps>(({
|
|
|
13
13
|
title,
|
|
14
14
|
children,
|
|
15
15
|
size = 'md',
|
|
16
|
-
type = '
|
|
16
|
+
type = 'default',
|
|
17
17
|
showCloseButton = true,
|
|
18
18
|
closeOnBackdropClick = true,
|
|
19
19
|
closeOnEscapeKey = true,
|
|
@@ -144,8 +144,8 @@ const Dialog = forwardRef<HTMLDivElement, DialogProps>(({
|
|
|
144
144
|
: { opacity: 0, transform: 'scale(0.96) translateY(-4px)' }
|
|
145
145
|
]);
|
|
146
146
|
const headerProps = getWebProps([(dialogStyles.header as any)({})]);
|
|
147
|
-
const titleProps = getWebProps([dialogStyles.title]);
|
|
148
|
-
const closeButtonProps = getWebProps([dialogStyles.closeButton]);
|
|
147
|
+
const titleProps = getWebProps([(dialogStyles.title as any)({})]);
|
|
148
|
+
const closeButtonProps = getWebProps([(dialogStyles.closeButton as any)({})]);
|
|
149
149
|
const contentProps = getWebProps([(dialogStyles.content as any)({})]);
|
|
150
150
|
|
|
151
151
|
const mergedBackdropRef = useMergeRefs(ref, backdropProps.ref);
|
package/src/Dialog/types.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { InteractiveAccessibilityProps } from '../utils/accessibility';
|
|
|
5
5
|
|
|
6
6
|
// Component-specific type aliases for future extensibility
|
|
7
7
|
export type DialogSizeVariant = 'sm' | 'md' | 'lg' | 'fullscreen';
|
|
8
|
-
export type DialogType = '
|
|
8
|
+
export type DialogType = 'default' | 'alert' | 'confirmation';
|
|
9
9
|
export type DialogAnimationType = 'slide' | 'fade' | 'none';
|
|
10
10
|
|
|
11
11
|
export interface DialogProps extends BaseProps, InteractiveAccessibilityProps {
|
package/src/Icon/Icon.web.tsx
CHANGED
|
@@ -6,13 +6,9 @@ import { getWebProps } from 'react-native-unistyles/web';
|
|
|
6
6
|
import { useUnistyles } from 'react-native-unistyles';
|
|
7
7
|
import useMergeRefs from '../hooks/useMergeRefs';
|
|
8
8
|
import { getColorFromString, Intent, Color } from '@idealyst/theme';
|
|
9
|
+
import { IconRegistry } from './IconRegistry';
|
|
9
10
|
|
|
10
|
-
|
|
11
|
-
interface InternalIconProps extends IconProps {
|
|
12
|
-
path?: string; // Added by Babel plugin transformation
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const Icon = forwardRef<HTMLSpanElement, IconProps>((props: InternalIconProps, ref) => {
|
|
11
|
+
const Icon = forwardRef<HTMLSpanElement, IconProps>((props, ref) => {
|
|
16
12
|
const {
|
|
17
13
|
name,
|
|
18
14
|
size = 'md',
|
|
@@ -27,8 +23,16 @@ const Icon = forwardRef<HTMLSpanElement, IconProps>((props: InternalIconProps, r
|
|
|
27
23
|
|
|
28
24
|
const { theme } = useUnistyles();
|
|
29
25
|
|
|
30
|
-
//
|
|
31
|
-
const
|
|
26
|
+
// Look up the icon path from the registry
|
|
27
|
+
const path = IconRegistry.get(name);
|
|
28
|
+
|
|
29
|
+
// Warn in development if icon is not registered
|
|
30
|
+
if (!path && process.env.NODE_ENV !== 'production') {
|
|
31
|
+
console.warn(
|
|
32
|
+
`[Icon] Icon "${name}" is not registered. ` +
|
|
33
|
+
`Add it to the 'icons' array in your babel config, or ensure it's used in a way that static analysis can detect.`
|
|
34
|
+
);
|
|
35
|
+
}
|
|
32
36
|
|
|
33
37
|
// Compute size from theme
|
|
34
38
|
let iconSize: number;
|
|
@@ -52,7 +56,6 @@ const Icon = forwardRef<HTMLSpanElement, IconProps>((props: InternalIconProps, r
|
|
|
52
56
|
|
|
53
57
|
const mergedRef = useMergeRefs(ref, iconProps.ref);
|
|
54
58
|
|
|
55
|
-
// Use MDI React icon when path is provided (transformed by Babel plugin)
|
|
56
59
|
return (
|
|
57
60
|
<span
|
|
58
61
|
{...iconProps}
|
|
@@ -71,17 +74,19 @@ const Icon = forwardRef<HTMLSpanElement, IconProps>((props: InternalIconProps, r
|
|
|
71
74
|
color: iconColor,
|
|
72
75
|
}}
|
|
73
76
|
>
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
77
|
+
{path && (
|
|
78
|
+
<MdiIcon
|
|
79
|
+
path={path}
|
|
80
|
+
size="1em"
|
|
81
|
+
color="currentColor"
|
|
82
|
+
data-testid={testID}
|
|
83
|
+
aria-label={accessibilityLabel || name}
|
|
84
|
+
/>
|
|
85
|
+
)}
|
|
81
86
|
</span>
|
|
82
87
|
);
|
|
83
88
|
});
|
|
84
89
|
|
|
85
90
|
Icon.displayName = 'Icon';
|
|
86
91
|
|
|
87
|
-
export default Icon;
|
|
92
|
+
export default Icon;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Icon Registry stub for native platform
|
|
3
|
+
*
|
|
4
|
+
* On native, icons are handled by react-native-vector-icons which uses
|
|
5
|
+
* icon names directly. The registry is only used on web for SVG path lookup.
|
|
6
|
+
*
|
|
7
|
+
* This stub exists to prevent import errors when code is shared between platforms.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
class IconRegistryStub {
|
|
11
|
+
register(_name: string, _path: string): void {
|
|
12
|
+
// No-op on native
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
registerMany(_icons: Record<string, string>): void {
|
|
16
|
+
// No-op on native
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
get(_name: string): string | undefined {
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
has(_name: string): boolean {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
getRegisteredNames(): string[] {
|
|
28
|
+
return [];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
get size(): number {
|
|
32
|
+
return 0;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
get isInitialized(): boolean {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export const IconRegistry = new IconRegistryStub();
|
|
41
|
+
export { IconRegistryStub as IconRegistryClass };
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Icon Registry for web platform
|
|
3
|
+
*
|
|
4
|
+
* This registry stores SVG paths for icons that are populated at build time
|
|
5
|
+
* by the Babel plugin. At runtime, components look up icon paths by their
|
|
6
|
+
* canonical name (e.g., "home", "account-circle").
|
|
7
|
+
*
|
|
8
|
+
* The registry is populated by:
|
|
9
|
+
* 1. Static analysis - Babel plugin scans for icon names in JSX
|
|
10
|
+
* 2. Config icons - User specifies additional icons in babel config
|
|
11
|
+
*
|
|
12
|
+
* This approach:
|
|
13
|
+
* - Enables dynamic/variable icon names (if registered)
|
|
14
|
+
* - Tree-shakes unused icons (only registered icons are bundled)
|
|
15
|
+
* - Provides a single source of truth for icon resolution
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
type IconPath = string;
|
|
19
|
+
|
|
20
|
+
class IconRegistryClass {
|
|
21
|
+
private icons = new Map<string, IconPath>();
|
|
22
|
+
private initialized = false;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Register a single icon
|
|
26
|
+
* @internal Called by generated registration code
|
|
27
|
+
*/
|
|
28
|
+
register(name: string, path: IconPath): void {
|
|
29
|
+
// Normalize the name (strip mdi: prefix, lowercase)
|
|
30
|
+
const normalizedName = this.normalizeName(name);
|
|
31
|
+
this.icons.set(normalizedName, path);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Register multiple icons at once
|
|
36
|
+
* @internal Called by generated registration code
|
|
37
|
+
*/
|
|
38
|
+
registerMany(icons: Record<string, IconPath>): void {
|
|
39
|
+
Object.entries(icons).forEach(([name, path]) => {
|
|
40
|
+
this.register(name, path);
|
|
41
|
+
});
|
|
42
|
+
this.initialized = true;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Get an icon path by name
|
|
47
|
+
* Returns undefined if the icon is not registered
|
|
48
|
+
*/
|
|
49
|
+
get(name: string): IconPath | undefined {
|
|
50
|
+
const normalizedName = this.normalizeName(name);
|
|
51
|
+
return this.icons.get(normalizedName);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Check if an icon is registered
|
|
56
|
+
*/
|
|
57
|
+
has(name: string): boolean {
|
|
58
|
+
const normalizedName = this.normalizeName(name);
|
|
59
|
+
return this.icons.has(normalizedName);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Get all registered icon names
|
|
64
|
+
*/
|
|
65
|
+
getRegisteredNames(): string[] {
|
|
66
|
+
return Array.from(this.icons.keys());
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Get the count of registered icons
|
|
71
|
+
*/
|
|
72
|
+
get size(): number {
|
|
73
|
+
return this.icons.size;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Check if the registry has been initialized
|
|
78
|
+
*/
|
|
79
|
+
get isInitialized(): boolean {
|
|
80
|
+
return this.initialized;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Normalize icon name for consistent lookup
|
|
85
|
+
* - Strips "mdi:" prefix
|
|
86
|
+
* - Converts to lowercase for case-insensitive matching
|
|
87
|
+
*/
|
|
88
|
+
private normalizeName(name: string): string {
|
|
89
|
+
if (!name || typeof name !== 'string') {
|
|
90
|
+
return '';
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Strip mdi: prefix if present
|
|
94
|
+
let normalized = name.startsWith('mdi:') ? name.slice(4) : name;
|
|
95
|
+
|
|
96
|
+
// Lowercase for consistent lookup
|
|
97
|
+
normalized = normalized.toLowerCase();
|
|
98
|
+
|
|
99
|
+
return normalized;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Singleton instance
|
|
104
|
+
export const IconRegistry = new IconRegistryClass();
|
|
105
|
+
|
|
106
|
+
// Also export the class for testing purposes
|
|
107
|
+
export { IconRegistryClass };
|