@olympusoss/canvas 3.2.1 → 5.0.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/README.md +75 -65
- package/package.json +11 -5
- package/src/atoms/avatar/avatar.md +185 -0
- package/src/atoms/avatar/avatar.styles.ts +48 -0
- package/src/atoms/avatar/avatar.tsx +99 -0
- package/src/atoms/badge/badge.md +237 -0
- package/src/atoms/badge/badge.styles.ts +79 -0
- package/src/atoms/badge/badge.tsx +86 -0
- package/src/atoms/breadcrumb/breadcrumb.md +233 -0
- package/src/atoms/breadcrumb/breadcrumb.styles.ts +40 -0
- package/src/atoms/breadcrumb/breadcrumb.tsx +130 -0
- package/src/atoms/button/button.android.tsx +6 -0
- package/src/atoms/button/button.ios.tsx +6 -0
- package/src/atoms/button/button.md +184 -0
- package/src/atoms/button/button.shared.tsx +79 -0
- package/src/atoms/button/button.styles.ts +152 -0
- package/src/atoms/button/button.tsx +6 -0
- package/src/atoms/button-group/button-group.android.tsx +6 -0
- package/src/atoms/button-group/button-group.ios.tsx +6 -0
- package/src/atoms/button-group/button-group.md +120 -0
- package/src/atoms/button-group/button-group.shared.tsx +398 -0
- package/src/atoms/button-group/button-group.styles.ts +483 -0
- package/src/atoms/button-group/button-group.tsx +6 -0
- package/src/atoms/checkbox/checkbox.android.tsx +6 -0
- package/src/atoms/checkbox/checkbox.ios.tsx +6 -0
- package/src/atoms/checkbox/checkbox.md +150 -0
- package/src/atoms/checkbox/checkbox.shared.tsx +103 -0
- package/src/atoms/checkbox/checkbox.styles.ts +106 -0
- package/src/atoms/checkbox/checkbox.tsx +6 -0
- package/src/atoms/combobox/combobox.android.tsx +6 -0
- package/src/atoms/combobox/combobox.ios.tsx +6 -0
- package/src/atoms/combobox/combobox.md +213 -0
- package/src/atoms/combobox/combobox.shared.tsx +160 -0
- package/src/atoms/combobox/combobox.styles.ts +270 -0
- package/src/atoms/combobox/combobox.tsx +6 -0
- package/src/atoms/divider/divider.md +140 -0
- package/src/atoms/divider/divider.styles.ts +35 -0
- package/src/atoms/divider/divider.tsx +67 -0
- package/src/atoms/dropdown/dropdown.android.tsx +6 -0
- package/src/atoms/dropdown/dropdown.ios.tsx +6 -0
- package/src/atoms/dropdown/dropdown.md +221 -0
- package/src/atoms/dropdown/dropdown.shared.tsx +190 -0
- package/src/atoms/dropdown/dropdown.styles.ts +233 -0
- package/src/atoms/dropdown/dropdown.tsx +6 -0
- package/src/atoms/icon/icon.md +131 -0
- package/src/atoms/icon/icon.styles.ts +30 -0
- package/src/atoms/icon/icon.tsx +328 -0
- package/src/atoms/index.ts +24 -0
- package/src/atoms/input/input.android.tsx +6 -0
- package/src/atoms/input/input.ios.tsx +6 -0
- package/src/atoms/input/input.md +118 -0
- package/src/atoms/input/input.shared.tsx +203 -0
- package/src/atoms/input/input.styles.ts +286 -0
- package/src/atoms/input/input.tsx +6 -0
- package/src/atoms/kbd/kbd.md +91 -0
- package/src/atoms/kbd/kbd.styles.ts +33 -0
- package/src/atoms/kbd/kbd.tsx +27 -0
- package/src/atoms/listbox/listbox.md +177 -0
- package/src/atoms/listbox/listbox.styles.ts +60 -0
- package/src/atoms/listbox/listbox.tsx +113 -0
- package/src/atoms/pagination/pagination.android.tsx +6 -0
- package/src/atoms/pagination/pagination.ios.tsx +6 -0
- package/src/atoms/pagination/pagination.md +133 -0
- package/src/atoms/pagination/pagination.shared.tsx +289 -0
- package/src/atoms/pagination/pagination.styles.ts +245 -0
- package/src/atoms/pagination/pagination.tsx +6 -0
- package/src/atoms/popover/popover.android.tsx +8 -0
- package/src/atoms/popover/popover.ios.tsx +6 -0
- package/src/atoms/popover/popover.md +87 -0
- package/src/atoms/popover/popover.shared.tsx +124 -0
- package/src/atoms/popover/popover.styles.ts +144 -0
- package/src/atoms/popover/popover.tsx +6 -0
- package/src/atoms/radio/radio.android.tsx +6 -0
- package/src/atoms/radio/radio.ios.tsx +6 -0
- package/src/atoms/radio/radio.md +173 -0
- package/src/atoms/radio/radio.shared.tsx +98 -0
- package/src/atoms/radio/radio.styles.ts +109 -0
- package/src/atoms/radio/radio.tsx +6 -0
- package/src/atoms/select/select.android.tsx +6 -0
- package/src/atoms/select/select.ios.tsx +6 -0
- package/src/atoms/select/select.md +156 -0
- package/src/atoms/select/select.shared.tsx +143 -0
- package/src/atoms/select/select.styles.ts +310 -0
- package/src/atoms/select/select.tsx +6 -0
- package/src/atoms/skeleton/skeleton.md +135 -0
- package/src/atoms/skeleton/skeleton.styles.ts +117 -0
- package/src/atoms/skeleton/skeleton.tsx +145 -0
- package/src/atoms/spinner/spinner.android.tsx +7 -0
- package/src/atoms/spinner/spinner.ios.tsx +7 -0
- package/src/atoms/spinner/spinner.md +94 -0
- package/src/atoms/spinner/spinner.shared.tsx +92 -0
- package/src/atoms/spinner/spinner.styles.tsx +115 -0
- package/src/atoms/spinner/spinner.tsx +7 -0
- package/src/atoms/switch/switch.android.tsx +6 -0
- package/src/atoms/switch/switch.ios.tsx +6 -0
- package/src/atoms/switch/switch.md +91 -0
- package/src/atoms/switch/switch.shared.tsx +97 -0
- package/src/atoms/switch/switch.styles.ts +79 -0
- package/src/atoms/switch/switch.tsx +6 -0
- package/src/atoms/textarea/textarea.android.tsx +6 -0
- package/src/atoms/textarea/textarea.ios.tsx +6 -0
- package/src/atoms/textarea/textarea.md +140 -0
- package/src/atoms/textarea/textarea.shared.tsx +74 -0
- package/src/atoms/textarea/textarea.styles.ts +116 -0
- package/src/atoms/textarea/textarea.tsx +6 -0
- package/src/atoms/tooltip/tooltip.android.tsx +6 -0
- package/src/atoms/tooltip/tooltip.ios.tsx +7 -0
- package/src/atoms/tooltip/tooltip.md +122 -0
- package/src/atoms/tooltip/tooltip.shared.tsx +113 -0
- package/src/atoms/tooltip/tooltip.styles.ts +113 -0
- package/src/atoms/tooltip/tooltip.tsx +6 -0
- package/src/atoms/typography/typography.md +330 -0
- package/src/atoms/typography/typography.styles.ts +95 -0
- package/src/atoms/typography/typography.tsx +76 -0
- package/src/index.ts +12 -2
- package/src/molecules/action-panels/action-panels.md +133 -0
- package/src/molecules/action-panels/action-panels.styles.ts +39 -0
- package/src/molecules/action-panels/action-panels.tsx +113 -0
- package/src/molecules/alert/alert.md +119 -0
- package/src/molecules/alert/alert.styles.ts +88 -0
- package/src/molecules/alert/alert.tsx +74 -0
- package/src/molecules/alert-dialog/alert-dialog.android.tsx +6 -0
- package/src/molecules/alert-dialog/alert-dialog.ios.tsx +6 -0
- package/src/molecules/alert-dialog/alert-dialog.md +177 -0
- package/src/molecules/alert-dialog/alert-dialog.shared.tsx +187 -0
- package/src/molecules/alert-dialog/alert-dialog.styles.ts +248 -0
- package/src/molecules/alert-dialog/alert-dialog.tsx +6 -0
- package/src/molecules/card/card.md +190 -0
- package/src/molecules/card/card.styles.ts +67 -0
- package/src/molecules/card/card.tsx +176 -0
- package/src/molecules/code-block/code-block.md +159 -0
- package/src/molecules/code-block/code-block.styles.ts +167 -0
- package/src/molecules/code-block/code-block.tsx +176 -0
- package/src/molecules/description-lists/description-lists.md +129 -0
- package/src/molecules/description-lists/description-lists.styles.ts +102 -0
- package/src/molecules/description-lists/description-lists.tsx +133 -0
- package/src/molecules/empty-state/empty-state.md +218 -0
- package/src/molecules/empty-state/empty-state.styles.ts +63 -0
- package/src/molecules/empty-state/empty-state.tsx +77 -0
- package/src/molecules/feeds/feeds.md +102 -0
- package/src/molecules/feeds/feeds.styles.ts +120 -0
- package/src/molecules/feeds/feeds.tsx +167 -0
- package/src/molecules/field/field.md +117 -0
- package/src/molecules/field/field.styles.ts +85 -0
- package/src/molecules/field/field.tsx +175 -0
- package/src/molecules/fieldset/fieldset.md +141 -0
- package/src/molecules/fieldset/fieldset.styles.ts +79 -0
- package/src/molecules/fieldset/fieldset.tsx +182 -0
- package/src/molecules/form/form.md +137 -0
- package/src/molecules/form/form.styles.ts +39 -0
- package/src/molecules/form/form.tsx +246 -0
- package/src/molecules/grid-lists/grid-lists.md +114 -0
- package/src/molecules/grid-lists/grid-lists.styles.ts +79 -0
- package/src/molecules/grid-lists/grid-lists.tsx +157 -0
- package/src/molecules/index.ts +16 -0
- package/src/molecules/media-objects/media-objects.md +87 -0
- package/src/molecules/media-objects/media-objects.styles.ts +94 -0
- package/src/molecules/media-objects/media-objects.tsx +128 -0
- package/src/molecules/stacked-lists/stacked-lists.md +116 -0
- package/src/molecules/stacked-lists/stacked-lists.styles.ts +111 -0
- package/src/molecules/stacked-lists/stacked-lists.tsx +195 -0
- package/src/molecules/stats/stats.md +166 -0
- package/src/molecules/stats/stats.styles.ts +91 -0
- package/src/molecules/stats/stats.tsx +88 -0
- package/src/organisms/calendar/calendar.android.tsx +6 -0
- package/src/organisms/calendar/calendar.ios.tsx +6 -0
- package/src/organisms/calendar/calendar.md +114 -0
- package/src/organisms/calendar/calendar.shared.tsx +146 -0
- package/src/organisms/calendar/calendar.styles.ts +315 -0
- package/src/organisms/calendar/calendar.tsx +6 -0
- package/src/organisms/charts/charts.md +326 -0
- package/src/organisms/charts/charts.styles.ts +135 -0
- package/src/organisms/charts/charts.tsx +124 -0
- package/src/organisms/command/command.md +117 -0
- package/src/organisms/command/command.styles.ts +179 -0
- package/src/organisms/command/command.tsx +164 -0
- package/src/organisms/data-table/data-table.md +182 -0
- package/src/organisms/data-table/data-table.styles.ts +103 -0
- package/src/organisms/data-table/data-table.tsx +105 -0
- package/src/organisms/dialog/dialog.android.tsx +6 -0
- package/src/organisms/dialog/dialog.ios.tsx +6 -0
- package/src/organisms/dialog/dialog.md +271 -0
- package/src/organisms/dialog/dialog.shared.tsx +230 -0
- package/src/organisms/dialog/dialog.styles.ts +272 -0
- package/src/organisms/dialog/dialog.tsx +6 -0
- package/src/organisms/filter-panel/filter-panel.md +116 -0
- package/src/organisms/filter-panel/filter-panel.styles.ts +83 -0
- package/src/organisms/filter-panel/filter-panel.tsx +91 -0
- package/src/organisms/index.ts +13 -0
- package/src/organisms/navbars/navbars.android.tsx +6 -0
- package/src/organisms/navbars/navbars.ios.tsx +6 -0
- package/src/organisms/navbars/navbars.md +144 -0
- package/src/organisms/navbars/navbars.shared.tsx +137 -0
- package/src/organisms/navbars/navbars.styles.ts +251 -0
- package/src/organisms/navbars/navbars.tsx +6 -0
- package/src/organisms/overlays/overlays.android.tsx +6 -0
- package/src/organisms/overlays/overlays.ios.tsx +6 -0
- package/src/organisms/overlays/overlays.md +123 -0
- package/src/organisms/overlays/overlays.shared.tsx +175 -0
- package/src/organisms/overlays/overlays.styles.ts +309 -0
- package/src/organisms/overlays/overlays.tsx +6 -0
- package/src/organisms/row-menu/row-menu.android.tsx +6 -0
- package/src/organisms/row-menu/row-menu.ios.tsx +6 -0
- package/src/organisms/row-menu/row-menu.md +102 -0
- package/src/organisms/row-menu/row-menu.shared.tsx +105 -0
- package/src/organisms/row-menu/row-menu.styles.ts +262 -0
- package/src/organisms/row-menu/row-menu.tsx +6 -0
- package/src/organisms/sidebar/sidebar.android.tsx +6 -0
- package/src/organisms/sidebar/sidebar.ios.tsx +6 -0
- package/src/organisms/sidebar/sidebar.md +188 -0
- package/src/organisms/sidebar/sidebar.shared.tsx +167 -0
- package/src/organisms/sidebar/sidebar.styles.ts +262 -0
- package/src/organisms/sidebar/sidebar.tsx +6 -0
- package/src/organisms/stepper/stepper.android.tsx +6 -0
- package/src/organisms/stepper/stepper.ios.tsx +6 -0
- package/src/organisms/stepper/stepper.md +150 -0
- package/src/organisms/stepper/stepper.shared.tsx +158 -0
- package/src/organisms/stepper/stepper.styles.ts +280 -0
- package/src/organisms/stepper/stepper.tsx +6 -0
- package/src/organisms/tabs/tabs.android.tsx +6 -0
- package/src/organisms/tabs/tabs.ios.tsx +6 -0
- package/src/organisms/tabs/tabs.md +127 -0
- package/src/organisms/tabs/tabs.shared.tsx +281 -0
- package/src/organisms/tabs/tabs.styles.ts +398 -0
- package/src/organisms/tabs/tabs.tsx +6 -0
- package/src/style/color.ts +17 -0
- package/src/style/index.ts +14 -0
- package/src/style/primitives.ts +26 -0
- package/src/style/responsive.ts +45 -0
- package/src/style/shadow.ts +21 -0
- package/src/style/theme.tsx +56 -0
- package/src/style/tokens.ts +487 -0
- package/styles/canvas.css +127 -74
- package/tsconfig.json +4 -2
- package/src/cn.ts +0 -3
- package/styles/atoms/avatar.css +0 -22
- package/styles/atoms/badge.css +0 -83
- package/styles/atoms/breadcrumb.css +0 -35
- package/styles/atoms/button-group.css +0 -23
- package/styles/atoms/button.css +0 -107
- package/styles/atoms/checkbox.css +0 -55
- package/styles/atoms/combobox.css +0 -76
- package/styles/atoms/dropdown.css +0 -54
- package/styles/atoms/icon.css +0 -8
- package/styles/atoms/input-group.css +0 -45
- package/styles/atoms/input.css +0 -56
- package/styles/atoms/kbd.css +0 -15
- package/styles/atoms/pagination.css +0 -48
- package/styles/atoms/popover.css +0 -14
- package/styles/atoms/radio.css +0 -28
- package/styles/atoms/select.css +0 -57
- package/styles/atoms/separator.css +0 -32
- package/styles/atoms/skeleton.css +0 -32
- package/styles/atoms/spinner.css +0 -26
- package/styles/atoms/switch.css +0 -45
- package/styles/atoms/textarea.css +0 -31
- package/styles/atoms/tooltip.css +0 -53
- package/styles/atoms/typography.css +0 -105
- package/styles/base.css +0 -17
- package/styles/molecules/alert.css +0 -66
- package/styles/molecules/card.css +0 -58
- package/styles/molecules/code-block.css +0 -18
- package/styles/molecules/empty-state.css +0 -17
- package/styles/molecules/field.css +0 -27
- package/styles/molecules/form.css +0 -27
- package/styles/molecules/page-header.css +0 -52
- package/styles/molecules/section-card.css +0 -49
- package/styles/molecules/stat-card.css +0 -71
- package/styles/molecules/toast.css +0 -95
- package/styles/organisms/app-shell.css +0 -46
- package/styles/organisms/calendar.css +0 -73
- package/styles/organisms/command.css +0 -95
- package/styles/organisms/data-table.css +0 -142
- package/styles/organisms/dialog.css +0 -72
- package/styles/organisms/filter-panel.css +0 -58
- package/styles/organisms/row-menu.css +0 -69
- package/styles/organisms/sheet.css +0 -70
- package/styles/organisms/sidebar.css +0 -146
- package/styles/organisms/stepper.css +0 -63
- package/styles/organisms/tabs.css +0 -40
- package/styles/organisms/topbar.css +0 -24
- package/styles/patterns/backdrops.css +0 -35
- package/styles/patterns/density.css +0 -66
- package/styles/patterns/focus.css +0 -22
- package/styles/patterns/glass.css +0 -85
- package/styles/patterns/high-contrast.css +0 -70
- package/styles/patterns/reduced-motion.css +0 -12
- package/styles/patterns/scrollbar.css +0 -10
- package/styles/reset.css +0 -89
- package/styles/tokens/colors.css +0 -108
- package/styles/tokens/motion.css +0 -33
- package/styles/tokens/radius.css +0 -10
- package/styles/tokens/shadows.css +0 -35
- package/styles/tokens/spacing.css +0 -19
- package/styles/tokens/typography.css +0 -6
- package/styles/tokens/z-index.css +0 -12
- package/styles/utilities/display.css +0 -66
- package/styles/utilities/flexbox.css +0 -240
- package/styles/utilities/gap.css +0 -288
- package/styles/utilities/grid.css +0 -138
- package/styles/utilities/position.css +0 -78
- package/styles/utilities/sizing.css +0 -138
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# Icons
|
|
2
|
+
|
|
3
|
+
Lucide-style outline. 1.75 stroke width, rounded caps. Inherits currentColor, so the same icon adapts to any context: set the color on the parent.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
<Icon shield size={24} />
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Variants
|
|
12
|
+
|
|
13
|
+
### View - set
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
<Icon set />
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### Color - primary
|
|
20
|
+
|
|
21
|
+
```tsx
|
|
22
|
+
<Icon shield size={24} primary />
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### Color - destructive
|
|
26
|
+
|
|
27
|
+
```tsx
|
|
28
|
+
<Icon shield size={24} destructive />
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Color - muted
|
|
32
|
+
|
|
33
|
+
```tsx
|
|
34
|
+
<Icon shield size={24} muted />
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Do & Don't
|
|
38
|
+
|
|
39
|
+
### Stroke coherence
|
|
40
|
+
|
|
41
|
+
**Do** — One outline style at 1.75 stroke across the whole set.
|
|
42
|
+
|
|
43
|
+
```tsx
|
|
44
|
+
<View style={{ flexDirection: "row", alignItems: "center", gap: 20 }}>
|
|
45
|
+
<Icon home size={28} />
|
|
46
|
+
<Icon search size={28} />
|
|
47
|
+
<Icon bell size={28} />
|
|
48
|
+
</View>
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
**Don't** — Mixed stroke weights and a stray filled glyph make a set look incoherent.
|
|
52
|
+
|
|
53
|
+
```tsx
|
|
54
|
+
<View style={{ flexDirection: "row", alignItems: "center", gap: 20 }}>
|
|
55
|
+
<Icon home muted size={20} />
|
|
56
|
+
<Icon search destructive size={34} />
|
|
57
|
+
<Icon bell primary size={28} />
|
|
58
|
+
</View>
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### foreground
|
|
62
|
+
|
|
63
|
+
**Do** — Leave stroke as currentColor and set text-foreground on the parent so it follows light and dark.
|
|
64
|
+
|
|
65
|
+
```tsx
|
|
66
|
+
<Icon mail size={28} />
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
**Don't** — Hard-coding a hex stroke pins the icon to one theme; it stays black on a dark surface and disappears.
|
|
70
|
+
|
|
71
|
+
```tsx
|
|
72
|
+
<View style={{ flexDirection: "row", alignItems: "center", justifyContent: "center", borderRadius: 6, backgroundColor: tokens.foreground, padding: 12 }}>
|
|
73
|
+
<Icon mail size={28} />
|
|
74
|
+
</View>
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### primary
|
|
78
|
+
|
|
79
|
+
**Do** — Reserve text-primary for the one active or selected icon; keep the rest muted.
|
|
80
|
+
|
|
81
|
+
```tsx
|
|
82
|
+
<View style={{ flexDirection: "row", alignItems: "center", gap: 20 }}>
|
|
83
|
+
<Icon home muted size={22} />
|
|
84
|
+
<Icon star primary size={22} />
|
|
85
|
+
<Icon settings muted size={22} />
|
|
86
|
+
</View>
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
**Don't** — Painting a whole toolbar primary spends the accent on everything, so nothing reads as emphasized.
|
|
90
|
+
|
|
91
|
+
```tsx
|
|
92
|
+
<View style={{ flexDirection: "row", alignItems: "center", gap: 20 }}>
|
|
93
|
+
<Icon home primary size={22} />
|
|
94
|
+
<Icon search primary size={22} />
|
|
95
|
+
<Icon settings primary size={22} />
|
|
96
|
+
</View>
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### destructive
|
|
100
|
+
|
|
101
|
+
**Do** — Keep text-destructive for genuinely destructive actions like delete, so red always means consequence.
|
|
102
|
+
|
|
103
|
+
```tsx
|
|
104
|
+
<Icon trash destructive size={28} />
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
**Don't** — A red download icon implies danger on a perfectly safe action and trains users to ignore the warning color.
|
|
108
|
+
|
|
109
|
+
```tsx
|
|
110
|
+
<Icon download destructive size={28} />
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### muted
|
|
114
|
+
|
|
115
|
+
**Do** — Use text-muted-foreground for secondary, inline hint icons where its color matches the helper text.
|
|
116
|
+
|
|
117
|
+
```tsx
|
|
118
|
+
<View style={{ flexDirection: "row", alignItems: "center", gap: 6 }}>
|
|
119
|
+
<Icon info muted size={16} />
|
|
120
|
+
<Text style={{ fontSize: 14, lineHeight: 20, color: tokens["muted-foreground"] }}>Optional, used only for recovery</Text>
|
|
121
|
+
</View>
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
**Don't** — A muted icon inside a solid primary button reads as disabled and clashes with the high-contrast label.
|
|
125
|
+
|
|
126
|
+
```tsx
|
|
127
|
+
<Pressable style={{ flexDirection: "row", alignItems: "center", justifyContent: "center", gap: 8, borderRadius: 6, backgroundColor: tokens.primary, paddingHorizontal: 16, paddingVertical: 8 }}>
|
|
128
|
+
<Icon plus muted size={16} />
|
|
129
|
+
<Text style={{ fontSize: 14, lineHeight: 20, fontWeight: "500", color: tokens["primary-foreground"] }}>New project</Text>
|
|
130
|
+
</Pressable>
|
|
131
|
+
```
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type ViewStyle, type TextStyle } from "react-native";
|
|
2
|
+
import { type ColorTokens } from "../../style/index.js";
|
|
3
|
+
|
|
4
|
+
// Co-located Icon styles. A single glyph is a bare SVG with no surrounding box,
|
|
5
|
+
// so it has no fragments here; these cover the `set` gallery only. Layout-only
|
|
6
|
+
// parts are static objects; the label color reads the active tokens (so it
|
|
7
|
+
// follows light/dark).
|
|
8
|
+
|
|
9
|
+
// The gallery grid: full width, glyphs flowing left-to-right and wrapping.
|
|
10
|
+
// (w-full flex-row flex-wrap)
|
|
11
|
+
export const setGrid: ViewStyle = {
|
|
12
|
+
width: "100%",
|
|
13
|
+
flexDirection: "row",
|
|
14
|
+
flexWrap: "wrap",
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// One gallery cell: a fixed-width column centering the glyph over its label.
|
|
18
|
+
// (items-center gap-1.5 rounded-lg px-1 py-2.5; the 80px width stays inline.)
|
|
19
|
+
export const setCell: ViewStyle = {
|
|
20
|
+
alignItems: "center",
|
|
21
|
+
gap: 6,
|
|
22
|
+
borderRadius: 8,
|
|
23
|
+
paddingHorizontal: 4,
|
|
24
|
+
paddingVertical: 10,
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
// The cell label color. (text-muted-foreground; the 10px size stays inline.)
|
|
28
|
+
export function setLabel(tokens: ColorTokens): TextStyle {
|
|
29
|
+
return { color: tokens["muted-foreground"] };
|
|
30
|
+
}
|
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
import Svg, { Circle, Ellipse, Line, Path, Polygon, Polyline, Rect } from "react-native-svg";
|
|
2
|
+
import { View, Text, useTheme, type ColorTokens, type StyleProp, type ViewStyle } from "../../style/index.js";
|
|
3
|
+
import * as s from "./icon.styles.js";
|
|
4
|
+
|
|
5
|
+
// Icon: a Lucide-style outline glyph rendered with react-native-svg, so it draws
|
|
6
|
+
// crisply on native and web and inherits color the same way everywhere. Stroke is
|
|
7
|
+
// 1.75 with rounded caps/joins; the glyph paints in a single theme color that the
|
|
8
|
+
// caller picks via a boolean color prop (foreground by default).
|
|
9
|
+
//
|
|
10
|
+
// Boolean-prop API (the prop name is the value):
|
|
11
|
+
//
|
|
12
|
+
// <Icon shield /> the shield glyph, foreground, 24px
|
|
13
|
+
// <Icon search primary /> the search glyph, primary color
|
|
14
|
+
// <Icon trash destructive /> the trash glyph, destructive color
|
|
15
|
+
// <Icon set /> the whole gallery, each glyph labeled by name
|
|
16
|
+
//
|
|
17
|
+
// Axes (pass at most one per axis; first match wins):
|
|
18
|
+
// - Name: one boolean per glyph (activity, bell, search, shield, …). Default shield.
|
|
19
|
+
// - Color: primary, primaryForeground, destructive, muted. Default foreground.
|
|
20
|
+
// (primaryForeground is the contrast color for a glyph on a primary surface.)
|
|
21
|
+
// Dimensions/layout (orthogonal): `size` (px, single glyph) and `set` (gallery).
|
|
22
|
+
|
|
23
|
+
type Shape =
|
|
24
|
+
| { t: "path"; d: string }
|
|
25
|
+
| { t: "circle"; cx: number; cy: number; r: number }
|
|
26
|
+
| { t: "line"; x1: number; y1: number; x2: number; y2: number }
|
|
27
|
+
| { t: "polyline"; points: string }
|
|
28
|
+
| { t: "polygon"; points: string }
|
|
29
|
+
| { t: "rect"; x: number; y: number; width: number; height: number; rx?: number; ry?: number }
|
|
30
|
+
| { t: "ellipse"; cx: number; cy: number; rx: number; ry: number };
|
|
31
|
+
|
|
32
|
+
// The curated set, in gallery order. Each glyph is an array of SVG primitives on
|
|
33
|
+
// the 0 0 24 24 viewBox, transcribed from the Lucide outline source.
|
|
34
|
+
const ICONS: Record<string, Shape[]> = {
|
|
35
|
+
activity: [{ t: "polyline", points: "22 12 18 12 15 21 9 3 6 12 2 12" }],
|
|
36
|
+
alertTriangle: [
|
|
37
|
+
{ t: "path", d: "m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z" },
|
|
38
|
+
{ t: "line", x1: 12, y1: 9, x2: 12, y2: 13 },
|
|
39
|
+
{ t: "line", x1: 12, y1: 17, x2: 12.01, y2: 17 },
|
|
40
|
+
],
|
|
41
|
+
archive: [
|
|
42
|
+
{ t: "rect", x: 2, y: 7, width: 20, height: 14, rx: 2, ry: 2 },
|
|
43
|
+
{ t: "path", d: "M16 3v4M8 3v4" },
|
|
44
|
+
],
|
|
45
|
+
bell: [
|
|
46
|
+
{ t: "path", d: "M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9" },
|
|
47
|
+
{ t: "path", d: "M13.73 21a2 2 0 0 1-3.46 0" },
|
|
48
|
+
],
|
|
49
|
+
calendar: [
|
|
50
|
+
{ t: "rect", x: 3, y: 4, width: 18, height: 18, rx: 2, ry: 2 },
|
|
51
|
+
{ t: "line", x1: 16, y1: 2, x2: 16, y2: 6 },
|
|
52
|
+
{ t: "line", x1: 8, y1: 2, x2: 8, y2: 6 },
|
|
53
|
+
{ t: "line", x1: 3, y1: 10, x2: 21, y2: 10 },
|
|
54
|
+
],
|
|
55
|
+
check: [{ t: "polyline", points: "20 6 9 17 4 12" }],
|
|
56
|
+
chevronDown: [{ t: "path", d: "m6 9 6 6 6-6" }],
|
|
57
|
+
chevronLeft: [{ t: "path", d: "m15 18-6-6 6-6" }],
|
|
58
|
+
chevronRight: [{ t: "path", d: "m9 18 6-6-6-6" }],
|
|
59
|
+
code: [
|
|
60
|
+
{ t: "polyline", points: "16 18 22 12 16 6" },
|
|
61
|
+
{ t: "polyline", points: "8 6 2 12 8 18" },
|
|
62
|
+
],
|
|
63
|
+
copy: [
|
|
64
|
+
{ t: "rect", x: 9, y: 9, width: 13, height: 13, rx: 2, ry: 2 },
|
|
65
|
+
{ t: "path", d: "M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" },
|
|
66
|
+
],
|
|
67
|
+
database: [
|
|
68
|
+
{ t: "ellipse", cx: 12, cy: 5, rx: 9, ry: 3 },
|
|
69
|
+
{ t: "path", d: "M21 12c0 1.66-4 3-9 3s-9-1.34-9-3" },
|
|
70
|
+
{ t: "path", d: "M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5" },
|
|
71
|
+
],
|
|
72
|
+
download: [
|
|
73
|
+
{ t: "path", d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" },
|
|
74
|
+
{ t: "polyline", points: "7 10 12 15 17 10" },
|
|
75
|
+
{ t: "line", x1: 12, y1: 15, x2: 12, y2: 3 },
|
|
76
|
+
],
|
|
77
|
+
eye: [
|
|
78
|
+
{ t: "path", d: "M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z" },
|
|
79
|
+
{ t: "circle", cx: 12, cy: 12, r: 3 },
|
|
80
|
+
],
|
|
81
|
+
file: [
|
|
82
|
+
{ t: "path", d: "M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z" },
|
|
83
|
+
{ t: "polyline", points: "13 2 13 9 20 9" },
|
|
84
|
+
],
|
|
85
|
+
filter: [{ t: "polygon", points: "22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3" }],
|
|
86
|
+
globe: [
|
|
87
|
+
{ t: "circle", cx: 12, cy: 12, r: 10 },
|
|
88
|
+
{ t: "line", x1: 2, y1: 12, x2: 22, y2: 12 },
|
|
89
|
+
{ t: "path", d: "M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z" },
|
|
90
|
+
],
|
|
91
|
+
home: [
|
|
92
|
+
{ t: "path", d: "m3 9 9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" },
|
|
93
|
+
{ t: "polyline", points: "9 22 9 12 15 12 15 22" },
|
|
94
|
+
],
|
|
95
|
+
info: [
|
|
96
|
+
{ t: "circle", cx: 12, cy: 12, r: 10 },
|
|
97
|
+
{ t: "line", x1: 12, y1: 16, x2: 12, y2: 12 },
|
|
98
|
+
{ t: "line", x1: 12, y1: 8, x2: 12.01, y2: 8 },
|
|
99
|
+
],
|
|
100
|
+
key: [{ t: "path", d: "m21 2-2 2m-7.61 7.61a5.5 5.5 0 1 1-7.778 7.778 5.5 5.5 0 0 1 7.777-7.777zm0 0L15.5 7.5m0 0 3 3L22 7l-3-3m-3.5 3.5L19 4" }],
|
|
101
|
+
lock: [
|
|
102
|
+
{ t: "rect", x: 3, y: 11, width: 18, height: 11, rx: 2, ry: 2 },
|
|
103
|
+
{ t: "path", d: "M7 11V7a5 5 0 0 1 10 0v4" },
|
|
104
|
+
],
|
|
105
|
+
mail: [
|
|
106
|
+
{ t: "path", d: "M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z" },
|
|
107
|
+
{ t: "polyline", points: "22,6 12,13 2,6" },
|
|
108
|
+
],
|
|
109
|
+
plus: [
|
|
110
|
+
{ t: "line", x1: 12, y1: 5, x2: 12, y2: 19 },
|
|
111
|
+
{ t: "line", x1: 5, y1: 12, x2: 19, y2: 12 },
|
|
112
|
+
],
|
|
113
|
+
search: [
|
|
114
|
+
{ t: "circle", cx: 11, cy: 11, r: 8 },
|
|
115
|
+
{ t: "line", x1: 21, y1: 21, x2: 16.65, y2: 16.65 },
|
|
116
|
+
],
|
|
117
|
+
settings: [
|
|
118
|
+
{ t: "circle", cx: 12, cy: 12, r: 3 },
|
|
119
|
+
{
|
|
120
|
+
t: "path",
|
|
121
|
+
d: "M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z",
|
|
122
|
+
},
|
|
123
|
+
],
|
|
124
|
+
shield: [{ t: "path", d: "M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" }],
|
|
125
|
+
star: [{ t: "polygon", points: "12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2" }],
|
|
126
|
+
trash: [
|
|
127
|
+
{ t: "polyline", points: "3 6 5 6 21 6" },
|
|
128
|
+
{ t: "path", d: "M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" },
|
|
129
|
+
],
|
|
130
|
+
upload: [
|
|
131
|
+
{ t: "path", d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" },
|
|
132
|
+
{ t: "polyline", points: "17 8 12 3 7 8" },
|
|
133
|
+
{ t: "line", x1: 12, y1: 3, x2: 12, y2: 15 },
|
|
134
|
+
],
|
|
135
|
+
user: [
|
|
136
|
+
{ t: "path", d: "M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2" },
|
|
137
|
+
{ t: "circle", cx: 12, cy: 7, r: 4 },
|
|
138
|
+
],
|
|
139
|
+
users: [
|
|
140
|
+
{ t: "path", d: "M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2" },
|
|
141
|
+
{ t: "circle", cx: 9, cy: 7, r: 4 },
|
|
142
|
+
{ t: "path", d: "M23 21v-2a4 4 0 0 0-3-3.87" },
|
|
143
|
+
{ t: "path", d: "M16 3.13a4 4 0 0 1 0 7.75" },
|
|
144
|
+
],
|
|
145
|
+
x: [
|
|
146
|
+
{ t: "line", x1: 18, y1: 6, x2: 6, y2: 18 },
|
|
147
|
+
{ t: "line", x1: 6, y1: 6, x2: 18, y2: 18 },
|
|
148
|
+
],
|
|
149
|
+
zap: [{ t: "polygon", points: "13 2 3 14 12 14 11 22 21 10 12 10 13 2" }],
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
// Gallery order + the short label shown under each glyph in the set view.
|
|
153
|
+
const NAMES: { key: string; label: string }[] = [
|
|
154
|
+
{ key: "activity", label: "activity" },
|
|
155
|
+
{ key: "alertTriangle", label: "alert-tri" },
|
|
156
|
+
{ key: "archive", label: "archive" },
|
|
157
|
+
{ key: "bell", label: "bell" },
|
|
158
|
+
{ key: "calendar", label: "calendar" },
|
|
159
|
+
{ key: "check", label: "check" },
|
|
160
|
+
{ key: "chevronDown", label: "chevron-down" },
|
|
161
|
+
{ key: "chevronLeft", label: "chevron-left" },
|
|
162
|
+
{ key: "chevronRight", label: "chevron-right" },
|
|
163
|
+
{ key: "code", label: "code" },
|
|
164
|
+
{ key: "copy", label: "copy" },
|
|
165
|
+
{ key: "database", label: "database" },
|
|
166
|
+
{ key: "download", label: "download" },
|
|
167
|
+
{ key: "eye", label: "eye" },
|
|
168
|
+
{ key: "file", label: "file" },
|
|
169
|
+
{ key: "filter", label: "filter" },
|
|
170
|
+
{ key: "globe", label: "globe" },
|
|
171
|
+
{ key: "home", label: "home" },
|
|
172
|
+
{ key: "info", label: "info" },
|
|
173
|
+
{ key: "key", label: "key" },
|
|
174
|
+
{ key: "lock", label: "lock" },
|
|
175
|
+
{ key: "mail", label: "mail" },
|
|
176
|
+
{ key: "plus", label: "plus" },
|
|
177
|
+
{ key: "search", label: "search" },
|
|
178
|
+
{ key: "settings", label: "settings" },
|
|
179
|
+
{ key: "shield", label: "shield" },
|
|
180
|
+
{ key: "star", label: "star" },
|
|
181
|
+
{ key: "trash", label: "trash" },
|
|
182
|
+
{ key: "upload", label: "upload" },
|
|
183
|
+
{ key: "user", label: "user" },
|
|
184
|
+
{ key: "users", label: "users" },
|
|
185
|
+
{ key: "x", label: "x" },
|
|
186
|
+
{ key: "zap", label: "zap" },
|
|
187
|
+
];
|
|
188
|
+
|
|
189
|
+
export interface IconProps {
|
|
190
|
+
// Name axis: one boolean per glyph (pass one; first match wins, default shield).
|
|
191
|
+
activity?: boolean;
|
|
192
|
+
alertTriangle?: boolean;
|
|
193
|
+
archive?: boolean;
|
|
194
|
+
bell?: boolean;
|
|
195
|
+
calendar?: boolean;
|
|
196
|
+
check?: boolean;
|
|
197
|
+
chevronDown?: boolean;
|
|
198
|
+
chevronLeft?: boolean;
|
|
199
|
+
chevronRight?: boolean;
|
|
200
|
+
code?: boolean;
|
|
201
|
+
copy?: boolean;
|
|
202
|
+
database?: boolean;
|
|
203
|
+
download?: boolean;
|
|
204
|
+
eye?: boolean;
|
|
205
|
+
file?: boolean;
|
|
206
|
+
filter?: boolean;
|
|
207
|
+
globe?: boolean;
|
|
208
|
+
home?: boolean;
|
|
209
|
+
info?: boolean;
|
|
210
|
+
key?: boolean;
|
|
211
|
+
lock?: boolean;
|
|
212
|
+
mail?: boolean;
|
|
213
|
+
plus?: boolean;
|
|
214
|
+
search?: boolean;
|
|
215
|
+
settings?: boolean;
|
|
216
|
+
shield?: boolean;
|
|
217
|
+
star?: boolean;
|
|
218
|
+
trash?: boolean;
|
|
219
|
+
upload?: boolean;
|
|
220
|
+
user?: boolean;
|
|
221
|
+
users?: boolean;
|
|
222
|
+
x?: boolean;
|
|
223
|
+
zap?: boolean;
|
|
224
|
+
// Color axis: pass one (default foreground). First match wins.
|
|
225
|
+
primary?: boolean;
|
|
226
|
+
/** Contrast color for a glyph on a primary surface (e.g. a primary button). */
|
|
227
|
+
primaryForeground?: boolean;
|
|
228
|
+
destructive?: boolean;
|
|
229
|
+
muted?: boolean;
|
|
230
|
+
// Single-glyph size in px (default 24).
|
|
231
|
+
size?: number;
|
|
232
|
+
// Render the whole gallery instead of a single glyph.
|
|
233
|
+
set?: boolean;
|
|
234
|
+
/** Escape hatch for layout/positioning composition (margins, alignment). */
|
|
235
|
+
style?: StyleProp<ViewStyle>;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// First-match name precedence; defaults to shield (the demo glyph).
|
|
239
|
+
function nameOf(p: IconProps): string {
|
|
240
|
+
for (const { key } of NAMES) {
|
|
241
|
+
if ((p as Record<string, unknown>)[key]) return key;
|
|
242
|
+
}
|
|
243
|
+
return "shield";
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// First-match color precedence; defaults to foreground.
|
|
247
|
+
function strokeOf(p: IconProps, tokens: ColorTokens): string {
|
|
248
|
+
if (p.primary) return tokens.primary;
|
|
249
|
+
if (p.primaryForeground) return tokens["primary-foreground"];
|
|
250
|
+
if (p.destructive) return tokens.destructive;
|
|
251
|
+
if (p.muted) return tokens["muted-foreground"];
|
|
252
|
+
return tokens.foreground;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function renderShape(sh: Shape, k: number) {
|
|
256
|
+
switch (sh.t) {
|
|
257
|
+
case "path":
|
|
258
|
+
return <Path key={k} d={sh.d} />;
|
|
259
|
+
case "circle":
|
|
260
|
+
return <Circle key={k} cx={sh.cx} cy={sh.cy} r={sh.r} />;
|
|
261
|
+
case "line":
|
|
262
|
+
return <Line key={k} x1={sh.x1} y1={sh.y1} x2={sh.x2} y2={sh.y2} />;
|
|
263
|
+
case "polyline":
|
|
264
|
+
return <Polyline key={k} points={sh.points} />;
|
|
265
|
+
case "polygon":
|
|
266
|
+
return <Polygon key={k} points={sh.points} />;
|
|
267
|
+
case "rect":
|
|
268
|
+
return <Rect key={k} x={sh.x} y={sh.y} width={sh.width} height={sh.height} rx={sh.rx} ry={sh.ry} />;
|
|
269
|
+
case "ellipse":
|
|
270
|
+
return <Ellipse key={k} cx={sh.cx} cy={sh.cy} rx={sh.rx} ry={sh.ry} />;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// One glyph: an SVG that sets the shared presentation attributes (stroke, weight,
|
|
275
|
+
// caps) on the root; the primitive children inherit them.
|
|
276
|
+
function Glyph({
|
|
277
|
+
shapes,
|
|
278
|
+
size,
|
|
279
|
+
stroke,
|
|
280
|
+
style,
|
|
281
|
+
}: {
|
|
282
|
+
shapes: Shape[];
|
|
283
|
+
size: number;
|
|
284
|
+
stroke: string;
|
|
285
|
+
style?: StyleProp<ViewStyle>;
|
|
286
|
+
}) {
|
|
287
|
+
return (
|
|
288
|
+
<Svg
|
|
289
|
+
width={size}
|
|
290
|
+
height={size}
|
|
291
|
+
viewBox="0 0 24 24"
|
|
292
|
+
fill="none"
|
|
293
|
+
stroke={stroke}
|
|
294
|
+
strokeWidth={1.75}
|
|
295
|
+
strokeLinecap="round"
|
|
296
|
+
strokeLinejoin="round"
|
|
297
|
+
style={style}
|
|
298
|
+
>
|
|
299
|
+
{shapes.map((sh, i) => renderShape(sh, i))}
|
|
300
|
+
</Svg>
|
|
301
|
+
);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
export function Icon(props: IconProps) {
|
|
305
|
+
const { tokens } = useTheme();
|
|
306
|
+
|
|
307
|
+
if (props.set) {
|
|
308
|
+
return (
|
|
309
|
+
<View style={[s.setGrid, props.style]}>
|
|
310
|
+
{NAMES.map(({ key, label }) => (
|
|
311
|
+
<View key={key} style={[s.setCell, { width: 80 }]}>
|
|
312
|
+
<Glyph shapes={ICONS[key]} size={20} stroke={tokens.foreground} />
|
|
313
|
+
<Text style={[s.setLabel(tokens), { fontSize: 10 }]}>{label}</Text>
|
|
314
|
+
</View>
|
|
315
|
+
))}
|
|
316
|
+
</View>
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
return (
|
|
321
|
+
<Glyph
|
|
322
|
+
shapes={ICONS[nameOf(props)]}
|
|
323
|
+
size={props.size ?? 24}
|
|
324
|
+
stroke={strokeOf(props, tokens)}
|
|
325
|
+
style={props.style}
|
|
326
|
+
/>
|
|
327
|
+
);
|
|
328
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// Atoms: the React Native UI kit components at the atoms atomic level.
|
|
2
|
+
export * from "./avatar/avatar.js";
|
|
3
|
+
export * from "./badge/badge.js";
|
|
4
|
+
export * from "./breadcrumb/breadcrumb.js";
|
|
5
|
+
export * from "./button/button.js";
|
|
6
|
+
export * from "./button-group/button-group.js";
|
|
7
|
+
export * from "./checkbox/checkbox.js";
|
|
8
|
+
export * from "./combobox/combobox.js";
|
|
9
|
+
export * from "./divider/divider.js";
|
|
10
|
+
export * from "./dropdown/dropdown.js";
|
|
11
|
+
export * from "./icon/icon.js";
|
|
12
|
+
export * from "./input/input.js";
|
|
13
|
+
export * from "./kbd/kbd.js";
|
|
14
|
+
export * from "./listbox/listbox.js";
|
|
15
|
+
export * from "./pagination/pagination.js";
|
|
16
|
+
export * from "./popover/popover.js";
|
|
17
|
+
export * from "./radio/radio.js";
|
|
18
|
+
export * from "./select/select.js";
|
|
19
|
+
export * from "./skeleton/skeleton.js";
|
|
20
|
+
export * from "./spinner/spinner.js";
|
|
21
|
+
export * from "./switch/switch.js";
|
|
22
|
+
export * from "./textarea/textarea.js";
|
|
23
|
+
export * from "./tooltip/tooltip.js";
|
|
24
|
+
export * from "./typography/typography.js";
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { createInput } from "./input.shared.js";
|
|
2
|
+
import { androidSkin } from "./input.styles.js";
|
|
3
|
+
|
|
4
|
+
// Material 3 Input. Metro resolves this file on Android; the docs import it for preview.
|
|
5
|
+
export const Input = createInput(androidSkin);
|
|
6
|
+
export type { InputProps } from "./input.shared.js";
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { createInput } from "./input.shared.js";
|
|
2
|
+
import { iosSkin } from "./input.styles.js";
|
|
3
|
+
|
|
4
|
+
// iOS (HIG) Input. Metro resolves this file on iOS; the docs import it for preview.
|
|
5
|
+
export const Input = createInput(iosSkin);
|
|
6
|
+
export type { InputProps } from "./input.shared.js";
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# Inputs & Forms
|
|
2
|
+
|
|
3
|
+
The Input component is a React Native text field with semantic boolean props (`error`, `small`, `large`, `block`, `disabled`), plus prefix/suffix addons and overlaid icons; `multiline` turns it into a textarea. Select and the search field share its look, and Field and Form compose a label, the control, and helper text.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
<Input placeholder="rachel.chen@example.com" style={{ maxWidth: 320 }} />
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Variants
|
|
12
|
+
|
|
13
|
+
### Control - number
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
<Input placeholder="1024" style={{ maxWidth: 320 }} />
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### Control - select
|
|
20
|
+
|
|
21
|
+
```tsx
|
|
22
|
+
<View style={{ maxWidth: 320, flexDirection: "column", gap: 6 }}>
|
|
23
|
+
<Text style={{ fontSize: 14, lineHeight: 20, fontWeight: "500", color: tokens.foreground }}>Status</Text>
|
|
24
|
+
<Select value="Active" options={["Active", "Inactive", "Pending"]} />
|
|
25
|
+
<Text style={{ fontSize: 12, lineHeight: 16, color: tokens["muted-foreground"] }}>We'll use this for account recovery.</Text>
|
|
26
|
+
</View>
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Control - textarea
|
|
30
|
+
|
|
31
|
+
```tsx
|
|
32
|
+
<Input multiline placeholder="Describe the change" style={{ maxWidth: 320 }} />
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### State - error
|
|
36
|
+
|
|
37
|
+
```tsx
|
|
38
|
+
<Input error placeholder="rachel.chen@example.com" style={{ maxWidth: 320 }} />
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### State - disabled
|
|
42
|
+
|
|
43
|
+
```tsx
|
|
44
|
+
<Input disabled placeholder="rachel.chen@example.com" style={{ maxWidth: 320 }} />
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### State - readonly
|
|
48
|
+
|
|
49
|
+
```tsx
|
|
50
|
+
<Input readOnly placeholder="rachel.chen@example.com" style={{ maxWidth: 320 }} />
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Do & Don't
|
|
54
|
+
|
|
55
|
+
### text
|
|
56
|
+
|
|
57
|
+
**Do** — Pair every field with a persistent .label above the control.
|
|
58
|
+
|
|
59
|
+
```tsx
|
|
60
|
+
<Field label="Email" placeholder="ada@acme.dev" style={{ maxWidth: 320 }} />
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**Don't** — A placeholder is not a label; it vanishes the moment the user types and screen readers may skip it.
|
|
64
|
+
|
|
65
|
+
```tsx
|
|
66
|
+
<Input placeholder="Email" style={{ maxWidth: 320 }} />
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### number
|
|
70
|
+
|
|
71
|
+
**Do** — Use type="number" with inputmode and park the unit in a .input-addon so the value stays purely numeric.
|
|
72
|
+
|
|
73
|
+
```tsx
|
|
74
|
+
<View style={{ maxWidth: 320 }}>
|
|
75
|
+
<Text style={{ marginBottom: 6, fontSize: 14, lineHeight: 20, fontWeight: "500", color: tokens.foreground }}>Storage</Text>
|
|
76
|
+
<Input value="1024" suffix="GB" />
|
|
77
|
+
</View>
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**Don't** — A plain text field lets users type the unit into the value, breaking parsing and validation.
|
|
81
|
+
|
|
82
|
+
```tsx
|
|
83
|
+
<Field label="Storage" value="1024 GB" style={{ maxWidth: 320 }} />
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### select
|
|
87
|
+
|
|
88
|
+
**Do** — Reserve a select for picking one of several mutually exclusive options; use a switch or radios for two.
|
|
89
|
+
|
|
90
|
+
```tsx
|
|
91
|
+
<Select label="Status" options={["Active", "Inactive", "Pending", "Archived"]} value="Active" style={{ maxWidth: 320 }} />
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
**Don't** — A select for a single on/off choice buries a one-tap decision behind a dropdown.
|
|
95
|
+
|
|
96
|
+
```tsx
|
|
97
|
+
<Select label="Email notifications" options={["On", "Off"]} value="On" style={{ maxWidth: 320 }} />
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### textarea
|
|
101
|
+
|
|
102
|
+
**Do** — Give a textarea a min-height for several lines and resize-y so it can grow with the content.
|
|
103
|
+
|
|
104
|
+
```tsx
|
|
105
|
+
<View style={{ maxWidth: 320 }}>
|
|
106
|
+
<Text style={{ marginBottom: 6, fontSize: 14, lineHeight: 20, fontWeight: "500", color: tokens.foreground }}>Notes</Text>
|
|
107
|
+
<Textarea rows={4} value="Describe the change in enough detail that a teammate could follow it…" />
|
|
108
|
+
</View>
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
**Don't** — A one-line, resize-none textarea clips multi-line input so users cannot review what they wrote.
|
|
112
|
+
|
|
113
|
+
```tsx
|
|
114
|
+
<View style={{ maxWidth: 320 }}>
|
|
115
|
+
<Text style={{ marginBottom: 6, fontSize: 14, lineHeight: 20, fontWeight: "500", color: tokens.foreground }}>Notes</Text>
|
|
116
|
+
<TextInput multiline value="Describe the change in enough detail that a teammate could follow it…" style={{ height: 36, width: "100%", borderRadius: 6, borderWidth: 1, borderColor: tokens.input, backgroundColor: tokens.background, paddingHorizontal: 12, paddingVertical: 4, fontSize: 14, lineHeight: 20, color: tokens.foreground, overflow: "hidden" }} />
|
|
117
|
+
</View>
|
|
118
|
+
```
|