@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
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
import React, { isValidElement, useState, useRef, useEffect } from 'react';
|
|
2
|
+
import { getWebProps } from 'react-native-unistyles/web';
|
|
3
|
+
import {
|
|
4
|
+
breadcrumbContainerStyles,
|
|
5
|
+
breadcrumbItemStyles,
|
|
6
|
+
breadcrumbSeparatorStyles,
|
|
7
|
+
breadcrumbEllipsisStyles,
|
|
8
|
+
breadcrumbMenuButtonStyles
|
|
9
|
+
} from './Breadcrumb.styles';
|
|
10
|
+
import type { BreadcrumbProps, BreadcrumbItem as BreadcrumbItemType } from './types';
|
|
11
|
+
import { IconSvg } from '../Icon/IconSvg/IconSvg.web';
|
|
12
|
+
import { resolveIconPath, isIconName } from '../Icon/icon-resolver';
|
|
13
|
+
import Menu from '../Menu/Menu.web';
|
|
14
|
+
import type { MenuItem } from '../Menu/types';
|
|
15
|
+
|
|
16
|
+
interface BreadcrumbItemProps {
|
|
17
|
+
item: BreadcrumbItemType;
|
|
18
|
+
isLast: boolean;
|
|
19
|
+
size: BreadcrumbProps['size'];
|
|
20
|
+
intent: BreadcrumbProps['intent'];
|
|
21
|
+
itemStyle?: BreadcrumbProps['itemStyle'];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const BreadcrumbItem: React.FC<BreadcrumbItemProps> = ({ item, isLast, size, intent, itemStyle }) => {
|
|
25
|
+
// Apply variants for this item only
|
|
26
|
+
breadcrumbItemStyles.useVariants({
|
|
27
|
+
size,
|
|
28
|
+
intent,
|
|
29
|
+
disabled: item.disabled || false,
|
|
30
|
+
isLast,
|
|
31
|
+
clickable: !!item.onPress && !item.disabled,
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const itemProps = getWebProps([breadcrumbItemStyles.item]);
|
|
35
|
+
const itemTextProps = getWebProps([breadcrumbItemStyles.itemText, itemStyle]);
|
|
36
|
+
const iconProps = getWebProps([breadcrumbItemStyles.icon]);
|
|
37
|
+
|
|
38
|
+
const handleClick = () => {
|
|
39
|
+
if (!item.disabled && item.onPress) {
|
|
40
|
+
item.onPress();
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const renderIcon = () => {
|
|
45
|
+
if (!item.icon) return null;
|
|
46
|
+
|
|
47
|
+
if (isIconName(item.icon)) {
|
|
48
|
+
const iconPath = resolveIconPath(item.icon);
|
|
49
|
+
return (
|
|
50
|
+
<IconSvg
|
|
51
|
+
path={iconPath}
|
|
52
|
+
{...iconProps}
|
|
53
|
+
aria-label={item.icon}
|
|
54
|
+
/>
|
|
55
|
+
);
|
|
56
|
+
} else if (isValidElement(item.icon)) {
|
|
57
|
+
return item.icon;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return null;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const content = (
|
|
64
|
+
<div {...itemProps}>
|
|
65
|
+
{item.icon && (
|
|
66
|
+
<span
|
|
67
|
+
{...iconProps}
|
|
68
|
+
style={{
|
|
69
|
+
display: 'inline-flex',
|
|
70
|
+
alignItems: 'center',
|
|
71
|
+
justifyContent: 'center',
|
|
72
|
+
}}
|
|
73
|
+
>
|
|
74
|
+
{renderIcon()}
|
|
75
|
+
</span>
|
|
76
|
+
)}
|
|
77
|
+
<span {...itemTextProps}>
|
|
78
|
+
{item.label}
|
|
79
|
+
</span>
|
|
80
|
+
</div>
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
if (item.onPress && !item.disabled) {
|
|
84
|
+
return (
|
|
85
|
+
<button
|
|
86
|
+
onClick={handleClick}
|
|
87
|
+
style={{
|
|
88
|
+
background: 'none',
|
|
89
|
+
border: 'none',
|
|
90
|
+
padding: 0,
|
|
91
|
+
cursor: 'pointer',
|
|
92
|
+
textDecoration: 'none',
|
|
93
|
+
}}
|
|
94
|
+
disabled={item.disabled}
|
|
95
|
+
aria-current={isLast ? 'page' : undefined}
|
|
96
|
+
>
|
|
97
|
+
{content}
|
|
98
|
+
</button>
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return (
|
|
103
|
+
<div aria-current={isLast ? 'page' : undefined}>
|
|
104
|
+
{content}
|
|
105
|
+
</div>
|
|
106
|
+
);
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
interface BreadcrumbSeparatorProps {
|
|
110
|
+
separator: React.ReactNode;
|
|
111
|
+
size: BreadcrumbProps['size'];
|
|
112
|
+
separatorStyle?: BreadcrumbProps['separatorStyle'];
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const BreadcrumbSeparator: React.FC<BreadcrumbSeparatorProps> = ({ separator, size, separatorStyle }) => {
|
|
116
|
+
breadcrumbSeparatorStyles.useVariants({ size });
|
|
117
|
+
const separatorProps = getWebProps([breadcrumbSeparatorStyles.separator, separatorStyle]);
|
|
118
|
+
|
|
119
|
+
return (
|
|
120
|
+
<span {...separatorProps} aria-hidden="true">
|
|
121
|
+
{separator}
|
|
122
|
+
</span>
|
|
123
|
+
);
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
interface BreadcrumbEllipsisProps {
|
|
127
|
+
size: BreadcrumbProps['size'];
|
|
128
|
+
intent: BreadcrumbProps['intent'];
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const BreadcrumbEllipsis: React.FC<BreadcrumbEllipsisProps> = ({ size, intent }) => {
|
|
132
|
+
breadcrumbEllipsisStyles.useVariants({ size });
|
|
133
|
+
const ellipsisProps = getWebProps([breadcrumbEllipsisStyles.ellipsis]);
|
|
134
|
+
const iconProps = getWebProps([breadcrumbEllipsisStyles.icon({ intent })]);
|
|
135
|
+
const ellipsisIconPath = resolveIconPath('dots-horizontal');
|
|
136
|
+
|
|
137
|
+
return (
|
|
138
|
+
<span {...ellipsisProps}>
|
|
139
|
+
<IconSvg
|
|
140
|
+
path={ellipsisIconPath}
|
|
141
|
+
{...iconProps}
|
|
142
|
+
aria-label="more items"
|
|
143
|
+
/>
|
|
144
|
+
</span>
|
|
145
|
+
);
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
const Breadcrumb: React.FC<BreadcrumbProps> = ({
|
|
149
|
+
items,
|
|
150
|
+
separator = '/',
|
|
151
|
+
maxItems,
|
|
152
|
+
intent = 'primary',
|
|
153
|
+
size = 'md',
|
|
154
|
+
style,
|
|
155
|
+
itemStyle,
|
|
156
|
+
separatorStyle,
|
|
157
|
+
testID,
|
|
158
|
+
responsive = false,
|
|
159
|
+
minVisibleItems = 3,
|
|
160
|
+
}) => {
|
|
161
|
+
const [menuOpen, setMenuOpen] = useState(false);
|
|
162
|
+
const containerProps = getWebProps([breadcrumbContainerStyles.container, style as any]);
|
|
163
|
+
const menuIconPath = resolveIconPath('dots-horizontal');
|
|
164
|
+
|
|
165
|
+
// Apply variants for menu button
|
|
166
|
+
breadcrumbMenuButtonStyles.useVariants({ size });
|
|
167
|
+
const menuButtonProps = getWebProps([breadcrumbMenuButtonStyles.button]);
|
|
168
|
+
const menuIconProps = getWebProps([breadcrumbMenuButtonStyles.icon({ intent })]);
|
|
169
|
+
|
|
170
|
+
// Handle responsive collapsing
|
|
171
|
+
let displayItems = items;
|
|
172
|
+
let collapsedItems: BreadcrumbItemType[] = [];
|
|
173
|
+
let showMenu = false;
|
|
174
|
+
let showEllipsis = false;
|
|
175
|
+
|
|
176
|
+
if (responsive && items.length > minVisibleItems) {
|
|
177
|
+
// Responsive mode: show first item + menu + last (minVisibleItems - 2) items
|
|
178
|
+
showMenu = true;
|
|
179
|
+
const lastItemCount = Math.max(1, minVisibleItems - 2);
|
|
180
|
+
displayItems = [
|
|
181
|
+
items[0],
|
|
182
|
+
...items.slice(-lastItemCount),
|
|
183
|
+
];
|
|
184
|
+
collapsedItems = items.slice(1, -(lastItemCount));
|
|
185
|
+
} else if (maxItems && items.length > maxItems) {
|
|
186
|
+
// Legacy truncation mode
|
|
187
|
+
showEllipsis = true;
|
|
188
|
+
const firstItems = items.slice(0, 1);
|
|
189
|
+
const lastItems = items.slice(-(maxItems - 1));
|
|
190
|
+
displayItems = [...firstItems, ...lastItems];
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Convert collapsed breadcrumb items to menu items
|
|
194
|
+
const menuItems: MenuItem[] = collapsedItems.map((item, index) => ({
|
|
195
|
+
id: `collapsed-${index}`,
|
|
196
|
+
label: item.label,
|
|
197
|
+
onClick: item.onPress,
|
|
198
|
+
disabled: item.disabled,
|
|
199
|
+
icon: isIconName(item.icon) ? item.icon : undefined,
|
|
200
|
+
}));
|
|
201
|
+
|
|
202
|
+
return (
|
|
203
|
+
<nav {...containerProps} aria-label="Breadcrumb" data-testid={testID}>
|
|
204
|
+
{displayItems.map((item, index) => {
|
|
205
|
+
const isLast = index === displayItems.length - 1;
|
|
206
|
+
const shouldShowEllipsis = showEllipsis && index === 1;
|
|
207
|
+
const shouldShowMenu = showMenu && index === 1;
|
|
208
|
+
|
|
209
|
+
return (
|
|
210
|
+
<React.Fragment key={index}>
|
|
211
|
+
{shouldShowEllipsis && (
|
|
212
|
+
<>
|
|
213
|
+
<BreadcrumbEllipsis size={size} intent={intent} />
|
|
214
|
+
<BreadcrumbSeparator separator={separator} size={size} separatorStyle={separatorStyle} />
|
|
215
|
+
</>
|
|
216
|
+
)}
|
|
217
|
+
|
|
218
|
+
{shouldShowMenu && (
|
|
219
|
+
<>
|
|
220
|
+
<Menu
|
|
221
|
+
items={menuItems}
|
|
222
|
+
open={menuOpen}
|
|
223
|
+
onOpenChange={setMenuOpen}
|
|
224
|
+
placement="bottom-start"
|
|
225
|
+
size={size}
|
|
226
|
+
>
|
|
227
|
+
<button
|
|
228
|
+
className={menuButtonProps.className}
|
|
229
|
+
style={{
|
|
230
|
+
background: 'none',
|
|
231
|
+
border: 'none',
|
|
232
|
+
cursor: 'pointer',
|
|
233
|
+
display: 'inline-flex',
|
|
234
|
+
alignItems: 'center',
|
|
235
|
+
justifyContent: 'center',
|
|
236
|
+
}}
|
|
237
|
+
aria-label="Show more breadcrumb items"
|
|
238
|
+
>
|
|
239
|
+
<IconSvg
|
|
240
|
+
path={menuIconPath}
|
|
241
|
+
{...menuIconProps}
|
|
242
|
+
aria-label="dots-horizontal"
|
|
243
|
+
/>
|
|
244
|
+
</button>
|
|
245
|
+
</Menu>
|
|
246
|
+
<BreadcrumbSeparator separator={separator} size={size} separatorStyle={separatorStyle} />
|
|
247
|
+
</>
|
|
248
|
+
)}
|
|
249
|
+
|
|
250
|
+
<BreadcrumbItem
|
|
251
|
+
item={item}
|
|
252
|
+
isLast={isLast}
|
|
253
|
+
size={size}
|
|
254
|
+
intent={intent}
|
|
255
|
+
itemStyle={itemStyle}
|
|
256
|
+
/>
|
|
257
|
+
|
|
258
|
+
{!isLast && (
|
|
259
|
+
<BreadcrumbSeparator separator={separator} size={size} separatorStyle={separatorStyle} />
|
|
260
|
+
)}
|
|
261
|
+
</React.Fragment>
|
|
262
|
+
);
|
|
263
|
+
})}
|
|
264
|
+
</nav>
|
|
265
|
+
);
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
export default Breadcrumb;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { StyleProp, ViewStyle, TextStyle } from 'react-native';
|
|
2
|
+
import type { IconName } from '../Icon/icon-types';
|
|
3
|
+
import { Size } from '@idealyst/theme';
|
|
4
|
+
|
|
5
|
+
// Component-specific type aliases for future extensibility
|
|
6
|
+
export type BreadcrumbIntentVariant = 'primary' | 'neutral';
|
|
7
|
+
export type BreadcrumbSizeVariant = Size;
|
|
8
|
+
|
|
9
|
+
export interface BreadcrumbItem {
|
|
10
|
+
/** Label text for the breadcrumb item */
|
|
11
|
+
label: string;
|
|
12
|
+
|
|
13
|
+
/** Optional icon to display before the label - can be an IconName or custom component */
|
|
14
|
+
icon?: IconName | React.ReactNode;
|
|
15
|
+
|
|
16
|
+
/** Click handler for the breadcrumb item */
|
|
17
|
+
onPress?: () => void;
|
|
18
|
+
|
|
19
|
+
/** Whether this item is disabled */
|
|
20
|
+
disabled?: boolean;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface BreadcrumbProps {
|
|
24
|
+
/** Array of breadcrumb items */
|
|
25
|
+
items: BreadcrumbItem[];
|
|
26
|
+
|
|
27
|
+
/** Custom separator between items (default: '/') */
|
|
28
|
+
separator?: React.ReactNode;
|
|
29
|
+
|
|
30
|
+
/** Maximum number of items to show before truncating */
|
|
31
|
+
maxItems?: number;
|
|
32
|
+
|
|
33
|
+
/** Intent color for links */
|
|
34
|
+
intent?: BreadcrumbIntentVariant;
|
|
35
|
+
|
|
36
|
+
/** Size of the breadcrumb text */
|
|
37
|
+
size?: BreadcrumbSizeVariant;
|
|
38
|
+
|
|
39
|
+
/** Custom container style */
|
|
40
|
+
style?: StyleProp<ViewStyle>;
|
|
41
|
+
|
|
42
|
+
/** Custom item style */
|
|
43
|
+
itemStyle?: StyleProp<ViewStyle>;
|
|
44
|
+
|
|
45
|
+
/** Custom separator style */
|
|
46
|
+
separatorStyle?: StyleProp<TextStyle>;
|
|
47
|
+
|
|
48
|
+
/** Test ID for testing */
|
|
49
|
+
testID?: string;
|
|
50
|
+
|
|
51
|
+
/** Enable responsive collapsing on narrow screens */
|
|
52
|
+
responsive?: boolean;
|
|
53
|
+
|
|
54
|
+
/** Minimum number of items to show before collapsing (default: 3) */
|
|
55
|
+
minVisibleItems?: number;
|
|
56
|
+
}
|
|
@@ -1,39 +1,90 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { TouchableOpacity, Text } from 'react-native';
|
|
1
|
+
import React, { isValidElement, forwardRef, ComponentRef } from 'react';
|
|
2
|
+
import { TouchableOpacity, Text, View } from 'react-native';
|
|
3
|
+
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
|
|
3
4
|
import { ButtonProps } from './types';
|
|
4
5
|
import { buttonStyles } from './Button.styles';
|
|
5
6
|
|
|
6
|
-
const Button
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
7
|
+
const Button = forwardRef<ComponentRef<typeof TouchableOpacity>, ButtonProps>((props, ref) => {
|
|
8
|
+
const {
|
|
9
|
+
children,
|
|
10
|
+
title,
|
|
11
|
+
onPress,
|
|
12
|
+
disabled = false,
|
|
13
|
+
type = 'contained',
|
|
14
|
+
intent = 'primary',
|
|
15
|
+
size = 'md',
|
|
16
|
+
leftIcon,
|
|
17
|
+
rightIcon,
|
|
18
|
+
style,
|
|
19
|
+
testID,
|
|
20
|
+
} = props;
|
|
21
|
+
|
|
22
|
+
// Compute dynamic styles
|
|
23
|
+
const buttonStyle = buttonStyles.button(size, intent, type, disabled);
|
|
24
|
+
const textStyle = buttonStyles.text(size, intent, type, disabled);
|
|
25
|
+
const iconStyle = buttonStyles.icon(size, intent, type);
|
|
26
|
+
|
|
27
|
+
// Map button size to icon size
|
|
28
|
+
const iconSizeMap = {
|
|
29
|
+
xs: 12,
|
|
30
|
+
sm: 14,
|
|
31
|
+
md: 16,
|
|
32
|
+
lg: 18,
|
|
33
|
+
xl: 20,
|
|
34
|
+
} as const;
|
|
35
|
+
const iconSize = iconSizeMap[size];
|
|
36
|
+
|
|
37
|
+
// Helper to render icon - uses the icon styles from buttonStyles
|
|
38
|
+
const renderIcon = (icon: string | React.ReactNode) => {
|
|
39
|
+
if (typeof icon === 'string') {
|
|
40
|
+
// Render MaterialCommunityIcons with explicit size prop
|
|
41
|
+
// The icon styles provide the correct color based on dynamic styles
|
|
42
|
+
return (
|
|
43
|
+
<MaterialCommunityIcons
|
|
44
|
+
name={icon}
|
|
45
|
+
size={iconSize}
|
|
46
|
+
style={iconStyle}
|
|
47
|
+
/>
|
|
48
|
+
);
|
|
49
|
+
} else if (isValidElement(icon)) {
|
|
50
|
+
// Render custom component as-is
|
|
51
|
+
return icon;
|
|
52
|
+
}
|
|
53
|
+
return null;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
// Use children if available, otherwise use title
|
|
57
|
+
const buttonContent = children || title;
|
|
58
|
+
|
|
59
|
+
// Determine if we need to wrap content in icon container
|
|
60
|
+
const hasIcons = leftIcon || rightIcon;
|
|
23
61
|
|
|
24
62
|
return (
|
|
25
63
|
<TouchableOpacity
|
|
64
|
+
ref={ref}
|
|
26
65
|
onPress={onPress}
|
|
27
66
|
disabled={disabled}
|
|
28
67
|
testID={testID}
|
|
29
68
|
activeOpacity={0.7}
|
|
30
|
-
style={[
|
|
69
|
+
style={[buttonStyle, style]}
|
|
31
70
|
>
|
|
32
|
-
|
|
33
|
-
{
|
|
34
|
-
|
|
71
|
+
{hasIcons ? (
|
|
72
|
+
<View style={buttonStyles.iconContainer}>
|
|
73
|
+
{leftIcon && renderIcon(leftIcon)}
|
|
74
|
+
<Text style={textStyle}>
|
|
75
|
+
{buttonContent}
|
|
76
|
+
</Text>
|
|
77
|
+
{rightIcon && renderIcon(rightIcon)}
|
|
78
|
+
</View>
|
|
79
|
+
) : (
|
|
80
|
+
<Text style={textStyle}>
|
|
81
|
+
{buttonContent}
|
|
82
|
+
</Text>
|
|
83
|
+
)}
|
|
35
84
|
</TouchableOpacity>
|
|
36
85
|
);
|
|
37
|
-
};
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
Button.displayName = 'Button';
|
|
38
89
|
|
|
39
90
|
export default Button;
|