@olympusoss/canvas 4.0.0 → 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 +108 -0
- package/package.json +14 -3
- 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/src/theme.ts +21 -0
- package/styles/canvas.css +128 -67
- package/tsconfig.json +4 -2
- package/src/cn.ts +0 -3
- package/styles/base.css +0 -17
- package/styles/components/alert.css +0 -66
- package/styles/components/app-shell.css +0 -46
- package/styles/components/avatar.css +0 -15
- package/styles/components/badge.css +0 -83
- package/styles/components/breadcrumb.css +0 -35
- package/styles/components/button-group.css +0 -23
- package/styles/components/button.css +0 -107
- package/styles/components/calendar.css +0 -73
- package/styles/components/card.css +0 -58
- package/styles/components/checkbox.css +0 -55
- package/styles/components/code-block.css +0 -18
- package/styles/components/combobox.css +0 -75
- package/styles/components/command.css +0 -94
- package/styles/components/data-table.css +0 -142
- package/styles/components/dialog.css +0 -72
- package/styles/components/dropdown.css +0 -54
- package/styles/components/empty-state.css +0 -17
- package/styles/components/field.css +0 -27
- package/styles/components/filter-panel.css +0 -58
- package/styles/components/form.css +0 -27
- package/styles/components/icon.css +0 -8
- package/styles/components/input-group.css +0 -45
- package/styles/components/input.css +0 -56
- package/styles/components/kbd.css +0 -15
- package/styles/components/page-header.css +0 -52
- package/styles/components/pagination.css +0 -48
- package/styles/components/popover.css +0 -14
- package/styles/components/radio.css +0 -28
- package/styles/components/row-menu.css +0 -69
- package/styles/components/section-card.css +0 -49
- package/styles/components/select.css +0 -57
- package/styles/components/separator.css +0 -32
- package/styles/components/sheet.css +0 -70
- package/styles/components/sidebar.css +0 -146
- package/styles/components/skeleton.css +0 -32
- package/styles/components/spinner.css +0 -26
- package/styles/components/stat-card.css +0 -71
- package/styles/components/stepper.css +0 -63
- package/styles/components/switch.css +0 -45
- package/styles/components/tabs.css +0 -40
- package/styles/components/textarea.css +0 -31
- package/styles/components/toast.css +0 -95
- package/styles/components/tooltip.css +0 -53
- package/styles/components/topbar.css +0 -24
- package/styles/components/typography.css +0 -105
- package/styles/patterns/backdrops.css +0 -35
- package/styles/patterns/density.css +0 -66
- package/styles/patterns/focus.css +0 -38
- 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 -106
- 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
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
# Empty States
|
|
2
|
+
|
|
3
|
+
Centered, calm, never blame the user. Always tell them what could be here, and ideally how to get there.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
<EmptyState
|
|
9
|
+
icon="🔍"
|
|
10
|
+
title="No results found"
|
|
11
|
+
description="Try adjusting your search filters."
|
|
12
|
+
bordered
|
|
13
|
+
/>
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Variants
|
|
17
|
+
|
|
18
|
+
### Variant - users
|
|
19
|
+
|
|
20
|
+
```tsx
|
|
21
|
+
<EmptyState
|
|
22
|
+
icon="👥"
|
|
23
|
+
title="No users"
|
|
24
|
+
description="Invite your first team member."
|
|
25
|
+
bordered
|
|
26
|
+
/>
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Variant - files
|
|
30
|
+
|
|
31
|
+
```tsx
|
|
32
|
+
<EmptyState
|
|
33
|
+
icon="📄"
|
|
34
|
+
title="No files"
|
|
35
|
+
description="Upload or drag files here."
|
|
36
|
+
bordered
|
|
37
|
+
/>
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Variant - activity
|
|
41
|
+
|
|
42
|
+
```tsx
|
|
43
|
+
<EmptyState
|
|
44
|
+
icon="📈"
|
|
45
|
+
title="No activity"
|
|
46
|
+
description="Events will appear as they happen."
|
|
47
|
+
bordered
|
|
48
|
+
/>
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Variant - notifications
|
|
52
|
+
|
|
53
|
+
```tsx
|
|
54
|
+
<EmptyState
|
|
55
|
+
icon="🔔"
|
|
56
|
+
title="All caught up"
|
|
57
|
+
description="No new notifications."
|
|
58
|
+
bordered
|
|
59
|
+
/>
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Variant - errors
|
|
63
|
+
|
|
64
|
+
```tsx
|
|
65
|
+
<EmptyState
|
|
66
|
+
icon="✅"
|
|
67
|
+
title="No errors"
|
|
68
|
+
description="Everything is running smoothly."
|
|
69
|
+
positive
|
|
70
|
+
bordered
|
|
71
|
+
/>
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Variant - all-clear
|
|
75
|
+
|
|
76
|
+
```tsx
|
|
77
|
+
<EmptyState
|
|
78
|
+
icon="✅"
|
|
79
|
+
title="All clear"
|
|
80
|
+
description="No locked accounts or pending reviews."
|
|
81
|
+
positive
|
|
82
|
+
bordered
|
|
83
|
+
/>
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Single action
|
|
87
|
+
|
|
88
|
+
```tsx
|
|
89
|
+
<EmptyState
|
|
90
|
+
icon="🔍"
|
|
91
|
+
title="No results found"
|
|
92
|
+
description="Try adjusting your search filters."
|
|
93
|
+
actionLabel="Create identity"
|
|
94
|
+
bordered
|
|
95
|
+
/>
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Inside a table
|
|
99
|
+
|
|
100
|
+
```tsx
|
|
101
|
+
<View style={{ overflow: "scroll", borderRadius: 8, borderWidth: 1, borderColor: tokens.border }}>
|
|
102
|
+
<View style={{ flexDirection: "row", borderBottomWidth: 1, borderColor: tokens.border }}>
|
|
103
|
+
<Text style={{ flexGrow: 1, flexShrink: 1, flexBasis: "0%", paddingHorizontal: 16, paddingVertical: 10, textAlign: "left", fontSize: 12, lineHeight: 16, fontWeight: "500", textTransform: "uppercase", letterSpacing: 0.4, color: tokens["muted-foreground"] }}>Name</Text>
|
|
104
|
+
<Text style={{ flexGrow: 1, flexShrink: 1, flexBasis: "0%", paddingHorizontal: 16, paddingVertical: 10, textAlign: "left", fontSize: 12, lineHeight: 16, fontWeight: "500", textTransform: "uppercase", letterSpacing: 0.4, color: tokens["muted-foreground"] }}>Email</Text>
|
|
105
|
+
<Text style={{ flexGrow: 1, flexShrink: 1, flexBasis: "0%", paddingHorizontal: 16, paddingVertical: 10, textAlign: "left", fontSize: 12, lineHeight: 16, fontWeight: "500", textTransform: "uppercase", letterSpacing: 0.4, color: tokens["muted-foreground"] }}>Role</Text>
|
|
106
|
+
<Text style={{ flexGrow: 1, flexShrink: 1, flexBasis: "0%", paddingHorizontal: 16, paddingVertical: 10, textAlign: "left", fontSize: 12, lineHeight: 16, fontWeight: "500", textTransform: "uppercase", letterSpacing: 0.4, color: tokens["muted-foreground"] }}>Status</Text>
|
|
107
|
+
</View>
|
|
108
|
+
<View style={{ paddingHorizontal: 16, paddingVertical: 40 }}>
|
|
109
|
+
<EmptyState bordered icon="🔍" title="No results found" description="Try adjusting your search filters." />
|
|
110
|
+
</View>
|
|
111
|
+
</View>
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Do & Don't
|
|
115
|
+
|
|
116
|
+
### search
|
|
117
|
+
|
|
118
|
+
**Do** — State the result neutrally and point at the lever the user can pull (filters, query).
|
|
119
|
+
|
|
120
|
+
```tsx
|
|
121
|
+
<EmptyState bordered icon="🔍" title="No results found" description="Try adjusting your search filters." />
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
**Don't** — Blaming the searcher for an empty result set makes a normal outcome feel like a mistake.
|
|
125
|
+
|
|
126
|
+
```tsx
|
|
127
|
+
<EmptyState bordered icon="🔍" title="Nothing matched" description="You searched for the wrong thing. Check your spelling and try again." />
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### users
|
|
131
|
+
|
|
132
|
+
**Do** — When the user can fix it, give them the one action that does (Invite, Create).
|
|
133
|
+
|
|
134
|
+
```tsx
|
|
135
|
+
<EmptyState bordered icon="👥" title="No users" description="Invite your first team member." actionLabel="Invite member" />
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
**Don't** — A dead-end that only restates the emptiness leaves the user with nowhere to go.
|
|
139
|
+
|
|
140
|
+
```tsx
|
|
141
|
+
<EmptyState bordered icon="👥" title="No users" description="There are no team members in this workspace." />
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### files
|
|
145
|
+
|
|
146
|
+
**Do** — Name the gesture that fills the empty space so the next step is obvious.
|
|
147
|
+
|
|
148
|
+
```tsx
|
|
149
|
+
<EmptyState bordered icon="📄" title="No files" description="Upload or drag files here." />
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
**Don't** — Generic 'nothing here' copy never tells the user how files would get into the folder.
|
|
153
|
+
|
|
154
|
+
```tsx
|
|
155
|
+
<EmptyState bordered icon="📄" title="Nothing here" description="This folder is empty." />
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### activity
|
|
159
|
+
|
|
160
|
+
**Do** — Keep a routinely-empty state calm: muted icon, reassuring copy, no urgency.
|
|
161
|
+
|
|
162
|
+
```tsx
|
|
163
|
+
<EmptyState bordered icon="📈" title="No activity" description="Events will appear as they happen." />
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
**Don't** — An alarming red icon turns a calm, expected empty feed into a false error.
|
|
167
|
+
|
|
168
|
+
```tsx
|
|
169
|
+
<View style={{ alignItems: "center", borderRadius: 8, borderWidth: 1, borderColor: tokens.border, paddingHorizontal: 24, paddingVertical: 32 }}>
|
|
170
|
+
<View style={{ marginBottom: 12, height: 48, width: 48, alignItems: "center", justifyContent: "center", borderRadius: 9999, backgroundColor: alpha(tokens.destructive, 0.1) }}>
|
|
171
|
+
<Text style={{ fontSize: 20, lineHeight: 28, color: tokens.destructive }}>⚠️</Text>
|
|
172
|
+
</View>
|
|
173
|
+
<Text style={{ textAlign: "center", fontSize: 16, lineHeight: 24, fontWeight: "600", color: tokens.foreground }}>No activity</Text>
|
|
174
|
+
<Text style={{ marginTop: 4, textAlign: "center", fontSize: 14, lineHeight: 20, color: tokens["muted-foreground"] }}>Events will appear as they happen.</Text>
|
|
175
|
+
</View>
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### notifications
|
|
179
|
+
|
|
180
|
+
**Do** — Celebrate a cleared queue: 'All caught up' reads as success, not absence.
|
|
181
|
+
|
|
182
|
+
```tsx
|
|
183
|
+
<EmptyState bordered icon="🔔" title="All caught up" description="No new notifications." />
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
**Don't** — Apologetic, sad-toned copy frames an inbox-zero win as a letdown.
|
|
187
|
+
|
|
188
|
+
```tsx
|
|
189
|
+
<EmptyState bordered icon="🔔" title="Nothing to see" description="You have no notifications. Sorry, it's quiet in here." />
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### errors
|
|
193
|
+
|
|
194
|
+
**Do** — Signal the good news with a green check: zero errors is a passing state, not an empty one.
|
|
195
|
+
|
|
196
|
+
```tsx
|
|
197
|
+
<EmptyState bordered positive icon="✅" title="No errors" description="Everything is running smoothly." />
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
**Don't** — A grey 'nothing found' disc reads as a failed query, not as a healthy, error-free system.
|
|
201
|
+
|
|
202
|
+
```tsx
|
|
203
|
+
<EmptyState bordered icon="🔍" title="No errors" description="Nothing was found." />
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### all-clear
|
|
207
|
+
|
|
208
|
+
**Do** — Reserve the green all-clear for genuinely empty queues: nothing locked, nothing waiting.
|
|
209
|
+
|
|
210
|
+
```tsx
|
|
211
|
+
<EmptyState bordered positive icon="✅" title="All clear" description="No locked accounts or pending reviews." />
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
**Don't** — A green all-clear over copy that admits work is pending hides the action the user must take.
|
|
215
|
+
|
|
216
|
+
```tsx
|
|
217
|
+
<EmptyState bordered positive icon="✅" title="No pending reviews" description="3 accounts are locked and waiting on you." />
|
|
218
|
+
```
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { type ViewStyle, type TextStyle } from "react-native";
|
|
2
|
+
import { type ColorTokens, palette, alpha } from "../../style/index.js";
|
|
3
|
+
|
|
4
|
+
// Co-located EmptyState styles. Layout-only fragments are static objects; the
|
|
5
|
+
// disc fill + glyph color read a color and so are functions of the active
|
|
6
|
+
// tokens. The `positive` tone paints the disc and glyph green from the fixed
|
|
7
|
+
// Tailwind palette (green-600), while the default tone stays on the semantic
|
|
8
|
+
// muted / muted-foreground tokens so it follows light/dark/glass.
|
|
9
|
+
|
|
10
|
+
export type Tone = "positive" | "default";
|
|
11
|
+
|
|
12
|
+
// The centered column. Bordered wraps it in a rounded, bordered card whose
|
|
13
|
+
// padding tightens under `compact`.
|
|
14
|
+
export const container: ViewStyle = { alignItems: "center" };
|
|
15
|
+
|
|
16
|
+
export const borderedBase: ViewStyle = { borderRadius: 8, borderWidth: 1 };
|
|
17
|
+
|
|
18
|
+
export function borderedSurface(tokens: ColorTokens): ViewStyle {
|
|
19
|
+
return { borderColor: tokens.border };
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Bordered card padding by density (compact tightens it for dense table cells).
|
|
23
|
+
export const borderedPad: Record<"compact" | "default", ViewStyle> = {
|
|
24
|
+
compact: { paddingHorizontal: 16, paddingVertical: 24 },
|
|
25
|
+
default: { paddingHorizontal: 24, paddingVertical: 32 },
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// The round 48x48 icon disc.
|
|
29
|
+
export const discBase: ViewStyle = {
|
|
30
|
+
marginBottom: 12,
|
|
31
|
+
height: 48,
|
|
32
|
+
width: 48,
|
|
33
|
+
alignItems: "center",
|
|
34
|
+
justifyContent: "center",
|
|
35
|
+
borderRadius: 9999,
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// Disc fill per tone: a 10% green wash when positive, the muted token otherwise.
|
|
39
|
+
export function discTone(tokens: ColorTokens, tone: Tone): ViewStyle {
|
|
40
|
+
return tone === "positive"
|
|
41
|
+
? { backgroundColor: alpha(palette["green-600"], 0.1) }
|
|
42
|
+
: { backgroundColor: tokens.muted };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// The glyph inside the disc (text-xl), colored per tone.
|
|
46
|
+
export const glyphBase: TextStyle = { fontSize: 20, lineHeight: 28 };
|
|
47
|
+
|
|
48
|
+
export function glyphTone(tokens: ColorTokens, tone: Tone): TextStyle {
|
|
49
|
+
return tone === "positive" ? { color: palette["green-600"] } : { color: tokens["muted-foreground"] };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Title: centered, semibold, foreground.
|
|
53
|
+
export function title(tokens: ColorTokens): TextStyle {
|
|
54
|
+
return { textAlign: "center", fontSize: 16, lineHeight: 24, fontWeight: "600", color: tokens.foreground };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Description: centered, muted, with a small inset above the title.
|
|
58
|
+
export function description(tokens: ColorTokens): TextStyle {
|
|
59
|
+
return { marginTop: 4, textAlign: "center", fontSize: 14, lineHeight: 20, color: tokens["muted-foreground"] };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// The action button's inset above it.
|
|
63
|
+
export const actionSpacing: ViewStyle = { marginTop: 16 };
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { View, Text, type StyleProp, type ViewStyle, useTheme } from "../../style/index.js";
|
|
2
|
+
import { Button } from "../../atoms/button/button.js";
|
|
3
|
+
import * as s from "./empty-state.styles.js";
|
|
4
|
+
|
|
5
|
+
// Empty state: a centered, calm column that explains why a region is empty and,
|
|
6
|
+
// when the user can act, offers the one action that fills it. Anatomy mirrors
|
|
7
|
+
// the docs entry: a round 48x48 icon disc, a 12px gap, a semibold title, a
|
|
8
|
+
// muted description, and an optional primary action button.
|
|
9
|
+
//
|
|
10
|
+
// Boolean-prop API: flat booleans grouped into axes, first-match precedence
|
|
11
|
+
// within an axis (mirrors Button's intentOf).
|
|
12
|
+
//
|
|
13
|
+
// - Tone axis: `positive` paints the disc and glyph green for states where
|
|
14
|
+
// emptiness is good news (no errors, all caught up). The default tone keeps
|
|
15
|
+
// the disc muted and the glyph muted-foreground for routinely-empty states.
|
|
16
|
+
// - Container axis: `bordered` wraps the column in a rounded, bordered card
|
|
17
|
+
// (used inside a SectionCard or a table cell). Omit for a bare column.
|
|
18
|
+
// - Density axis: `compact` tightens the card padding for dense table cells.
|
|
19
|
+
|
|
20
|
+
export interface EmptyStateProps {
|
|
21
|
+
/** A glyph (emoji or icon character) shown in the disc. */
|
|
22
|
+
icon?: string;
|
|
23
|
+
/** Short, neutral headline naming the empty result. */
|
|
24
|
+
title?: string;
|
|
25
|
+
/** One reassuring line, ideally pointing at the next step. */
|
|
26
|
+
description?: string;
|
|
27
|
+
/** When set, renders a primary action button below the description. */
|
|
28
|
+
actionLabel?: string;
|
|
29
|
+
/** Called when the action button is pressed. */
|
|
30
|
+
onAction?: () => void;
|
|
31
|
+
// Tone axis (pick one; default keeps the disc muted).
|
|
32
|
+
positive?: boolean;
|
|
33
|
+
// Container axis: wrap the column in a bordered card.
|
|
34
|
+
bordered?: boolean;
|
|
35
|
+
// Density axis (only affects the bordered card's padding).
|
|
36
|
+
compact?: boolean;
|
|
37
|
+
/** Escape hatch for layout/positioning composition (width, margins). */
|
|
38
|
+
style?: StyleProp<ViewStyle>;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Tone precedence: first match wins.
|
|
42
|
+
function isPositive(p: EmptyStateProps): boolean {
|
|
43
|
+
return !!p.positive;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function EmptyState(props: EmptyStateProps) {
|
|
47
|
+
const { icon, title, description, actionLabel, onAction, bordered, compact, style } = props;
|
|
48
|
+
const { tokens } = useTheme();
|
|
49
|
+
const tone: s.Tone = isPositive(props) ? "positive" : "default";
|
|
50
|
+
|
|
51
|
+
const container: StyleProp<ViewStyle> = [
|
|
52
|
+
s.container,
|
|
53
|
+
bordered ? s.borderedBase : null,
|
|
54
|
+
bordered ? s.borderedSurface(tokens) : null,
|
|
55
|
+
bordered ? s.borderedPad[compact ? "compact" : "default"] : null,
|
|
56
|
+
style,
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
<View style={container}>
|
|
61
|
+
{icon != null ? (
|
|
62
|
+
<View style={[s.discBase, s.discTone(tokens, tone)]}>
|
|
63
|
+
<Text style={[s.glyphBase, s.glyphTone(tokens, tone)]}>{icon}</Text>
|
|
64
|
+
</View>
|
|
65
|
+
) : null}
|
|
66
|
+
{title != null ? <Text style={s.title(tokens)}>{title}</Text> : null}
|
|
67
|
+
{description != null ? <Text style={s.description(tokens)}>{description}</Text> : null}
|
|
68
|
+
{actionLabel != null ? (
|
|
69
|
+
<View style={s.actionSpacing}>
|
|
70
|
+
<Button primary small onPress={onAction}>
|
|
71
|
+
{actionLabel}
|
|
72
|
+
</Button>
|
|
73
|
+
</View>
|
|
74
|
+
) : null}
|
|
75
|
+
</View>
|
|
76
|
+
);
|
|
77
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# Feeds
|
|
2
|
+
|
|
3
|
+
Vertical activity streams with icons and timestamps. Used for audit logs, change history, and notification lists.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
<Feed
|
|
9
|
+
connector
|
|
10
|
+
items={[
|
|
11
|
+
{ actor: "Rachel Chen", action: "approved the request", time: "2 hours ago" },
|
|
12
|
+
{ actor: "Ada Lovelace", action: "updated the description", time: "5 hours ago" },
|
|
13
|
+
{ actor: "System", action: "created the project", time: "3 days ago" }
|
|
14
|
+
]}
|
|
15
|
+
/>
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Variants
|
|
19
|
+
|
|
20
|
+
### Variant - avatar
|
|
21
|
+
|
|
22
|
+
```tsx
|
|
23
|
+
<Feed
|
|
24
|
+
avatar
|
|
25
|
+
items={[
|
|
26
|
+
{ actor: "Rachel Chen", action: "commented on the pull request", time: "2 hours ago" },
|
|
27
|
+
{ actor: "Ada Lovelace", action: "pushed 3 commits", time: "5 hours ago" },
|
|
28
|
+
{ actor: "Kevin Turner", action: "opened the pull request", time: "1 day ago" }
|
|
29
|
+
]}
|
|
30
|
+
/>
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Do & Don't
|
|
34
|
+
|
|
35
|
+
### Connector
|
|
36
|
+
|
|
37
|
+
**Do** — Drop the connector on the last item so the line terminates cleanly at the final event.
|
|
38
|
+
|
|
39
|
+
```tsx
|
|
40
|
+
<Feed connector items={[
|
|
41
|
+
{ actor: "Rachel Chen", action: "approved the request", time: "2 hours ago" },
|
|
42
|
+
{ actor: "System", action: "created the project", time: "3 days ago" }
|
|
43
|
+
]} />
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
**Don't** — Running the connector line past the final event leaves a dangling tail pointing at nothing.
|
|
47
|
+
|
|
48
|
+
```tsx
|
|
49
|
+
<View style={{ width: "100%", maxWidth: 420, borderRadius: 8, borderWidth: 1, borderColor: tokens.border, backgroundColor: tokens.card, overflow: "hidden", padding: 24 }}>
|
|
50
|
+
<View style={{ position: "relative", flexDirection: "row", gap: 12, paddingBottom: 24 }}>
|
|
51
|
+
<View style={{ position: "absolute", bottom: -24, left: 13, top: 28, width: 1, backgroundColor: tokens.border }} />
|
|
52
|
+
<View style={{ height: 28, width: 28, flexShrink: 0, alignItems: "center", justifyContent: "center", borderRadius: 9999, borderWidth: 1, borderColor: tokens.border, backgroundColor: tokens.card }}>
|
|
53
|
+
<Text style={{ fontSize: 12, lineHeight: 16, fontWeight: "500", color: tokens["muted-foreground"] }}>RC</Text>
|
|
54
|
+
</View>
|
|
55
|
+
<View style={{ flexGrow: 1, flexShrink: 1, flexBasis: "0%", paddingTop: 2 }}>
|
|
56
|
+
<Text style={{ fontSize: 14, lineHeight: 20 }}>
|
|
57
|
+
<Text style={{ fontWeight: "500" }}>Rachel Chen </Text>
|
|
58
|
+
<Text style={{ color: tokens["muted-foreground"] }}>approved the request</Text>
|
|
59
|
+
</Text>
|
|
60
|
+
<Text style={{ marginTop: 2, fontSize: 12, lineHeight: 16, color: tokens["muted-foreground"] }}>2 hours ago</Text>
|
|
61
|
+
</View>
|
|
62
|
+
</View>
|
|
63
|
+
<View style={{ position: "relative", flexDirection: "row", gap: 12 }}>
|
|
64
|
+
<View style={{ position: "absolute", bottom: -24, left: 13, top: 28, width: 1, backgroundColor: tokens.border }} />
|
|
65
|
+
<View style={{ height: 28, width: 28, flexShrink: 0, alignItems: "center", justifyContent: "center", borderRadius: 9999, borderWidth: 1, borderColor: tokens.border, backgroundColor: tokens.card }}>
|
|
66
|
+
<Text style={{ fontSize: 12, lineHeight: 16, fontWeight: "500", color: tokens["muted-foreground"] }}>SY</Text>
|
|
67
|
+
</View>
|
|
68
|
+
<View style={{ flexGrow: 1, flexShrink: 1, flexBasis: "0%", paddingTop: 2 }}>
|
|
69
|
+
<Text style={{ fontSize: 14, lineHeight: 20 }}>
|
|
70
|
+
<Text style={{ fontWeight: "500" }}>System </Text>
|
|
71
|
+
<Text style={{ color: tokens["muted-foreground"] }}>created the project</Text>
|
|
72
|
+
</Text>
|
|
73
|
+
<Text style={{ marginTop: 2, fontSize: 12, lineHeight: 16, color: tokens["muted-foreground"] }}>3 days ago</Text>
|
|
74
|
+
</View>
|
|
75
|
+
</View>
|
|
76
|
+
</View>
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Avatar
|
|
80
|
+
|
|
81
|
+
**Do** — Lead with the person's avatar, bold the actor, and keep the action plus a relative timestamp muted.
|
|
82
|
+
|
|
83
|
+
```tsx
|
|
84
|
+
<Feed avatar items={[
|
|
85
|
+
{ actor: "Ada Lovelace", action: "pushed 3 commits", time: "5 hours ago", avatar: "/ada-lovelace.jpg" }
|
|
86
|
+
]} />
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
**Don't** — An anonymous avatar with no actor name and no timestamp strips the row of the who and the when.
|
|
90
|
+
|
|
91
|
+
```tsx
|
|
92
|
+
<View style={{ width: "100%", maxWidth: 420, borderRadius: 8, borderWidth: 1, borderColor: tokens.border, backgroundColor: tokens.card, overflow: "hidden" }}>
|
|
93
|
+
<View style={{ flexDirection: "row", alignItems: "flex-start", gap: 12, paddingHorizontal: 20, paddingVertical: 16 }}>
|
|
94
|
+
<View style={{ height: 40, width: 40, flexShrink: 0, alignItems: "center", justifyContent: "center", overflow: "hidden", borderRadius: 9999, backgroundColor: tokens.muted }}>
|
|
95
|
+
<Text style={{ fontSize: 16, lineHeight: 24, fontWeight: "500", color: tokens["muted-foreground"] }}>?</Text>
|
|
96
|
+
</View>
|
|
97
|
+
<View style={{ minWidth: 0, flexGrow: 1, flexShrink: 1, flexBasis: "0%" }}>
|
|
98
|
+
<Text style={{ fontSize: 14, lineHeight: 20, color: tokens["muted-foreground"] }}>Pushed 3 commits</Text>
|
|
99
|
+
</View>
|
|
100
|
+
</View>
|
|
101
|
+
</View>
|
|
102
|
+
```
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { type ViewStyle, type TextStyle } from "react-native";
|
|
2
|
+
import { type ColorTokens } from "../../style/index.js";
|
|
3
|
+
|
|
4
|
+
// Co-located Feed styles. Layout-only fragments are static objects; anything that
|
|
5
|
+
// reads a color is a function of the active tokens (so the card surface, node
|
|
6
|
+
// fill, and divider follow light/dark and read as glass when the ThemeProvider's
|
|
7
|
+
// surface is "glass", since tokens.card is swapped translucent at the theming
|
|
8
|
+
// level). Grouped by axis: shared surface, the avatar lead, and the connector lead.
|
|
9
|
+
|
|
10
|
+
export type Lead = "connector" | "avatar";
|
|
11
|
+
|
|
12
|
+
// --- shared surface ---------------------------------------------------------
|
|
13
|
+
|
|
14
|
+
// The card surface framing the feed (mirrors the docs cardCls):
|
|
15
|
+
// w-full max-w-[560px] rounded-lg border border-border bg-card overflow-hidden.
|
|
16
|
+
export function cardSurface(tokens: ColorTokens): ViewStyle {
|
|
17
|
+
return {
|
|
18
|
+
width: "100%",
|
|
19
|
+
maxWidth: 560,
|
|
20
|
+
borderRadius: 8,
|
|
21
|
+
borderWidth: 1,
|
|
22
|
+
borderColor: tokens.border,
|
|
23
|
+
backgroundColor: tokens.card,
|
|
24
|
+
overflow: "hidden",
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// --- shared content column --------------------------------------------------
|
|
29
|
+
|
|
30
|
+
// The content column wrapper (flex-1).
|
|
31
|
+
export const contentColumn: ViewStyle = { flexGrow: 1, flexShrink: 1, flexBasis: "0%" };
|
|
32
|
+
|
|
33
|
+
// The outer line wrapping actor + action + target (text-sm).
|
|
34
|
+
export const lineText: TextStyle = { fontSize: 14, lineHeight: 20 };
|
|
35
|
+
|
|
36
|
+
// The actor name, rendered bold (text-sm font-medium text-foreground).
|
|
37
|
+
export function actorLabel(tokens: ColorTokens): TextStyle {
|
|
38
|
+
return { fontSize: 14, lineHeight: 20, fontWeight: "500", color: tokens.foreground };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// The action / target text, muted (text-sm text-muted-foreground).
|
|
42
|
+
export function actionLabel(tokens: ColorTokens): TextStyle {
|
|
43
|
+
return { fontSize: 14, lineHeight: 20, color: tokens["muted-foreground"] };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// The relative timestamp, muted and small (text-xs text-muted-foreground mt-0.5).
|
|
47
|
+
export function timeLabel(tokens: ColorTokens): TextStyle {
|
|
48
|
+
return { marginTop: 2, fontSize: 12, lineHeight: 16, color: tokens["muted-foreground"] };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// --- avatar lead ------------------------------------------------------------
|
|
52
|
+
|
|
53
|
+
// Avatar row base (flex-row items-start gap-3) + density-driven padding.
|
|
54
|
+
export const avatarRow: ViewStyle = { flexDirection: "row", alignItems: "flex-start", gap: 12 };
|
|
55
|
+
|
|
56
|
+
// Row padding by density: compact (px-5 py-3) vs default (px-5 py-4).
|
|
57
|
+
export function avatarRowPad(compact: boolean): ViewStyle {
|
|
58
|
+
return { paddingHorizontal: 20, paddingVertical: compact ? 12 : 16 };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// The hairline rule between avatar rows (border-b border-border); omitted on last.
|
|
62
|
+
export function avatarDivider(tokens: ColorTokens): ViewStyle {
|
|
63
|
+
return { borderBottomWidth: 1, borderColor: tokens.border };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// --- connector lead ---------------------------------------------------------
|
|
67
|
+
|
|
68
|
+
// Connector surface padding by density: compact (p-5) vs default (p-6).
|
|
69
|
+
export function connectorPad(compact: boolean): ViewStyle {
|
|
70
|
+
return { padding: compact ? 20 : 24 };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Connector row base (relative flex-row gap-3).
|
|
74
|
+
export const connectorRow: ViewStyle = { position: "relative", flexDirection: "row", gap: 12 };
|
|
75
|
+
|
|
76
|
+
// Inter-row vertical rhythm by density: compact (pb-4) vs default (pb-6).
|
|
77
|
+
export function connectorRowGap(compact: boolean): ViewStyle {
|
|
78
|
+
return { paddingBottom: compact ? 16 : 24 };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// The vertical connector line (absolute bottom-0 left-[13px] top-7 w-px bg-border):
|
|
82
|
+
// a 1px border-colored line under the node's horizontal center, dropped on the last row.
|
|
83
|
+
export function connectorLine(tokens: ColorTokens): ViewStyle {
|
|
84
|
+
return { position: "absolute", bottom: 0, left: 13, top: 28, width: 1, backgroundColor: tokens.border };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// The leading node for the connector variant (h-7 w-7 shrink-0 items-center
|
|
88
|
+
// justify-center rounded-full border border-border bg-card): a small bordered
|
|
89
|
+
// circle carrying the actor's initials, sized to sit on the connector line.
|
|
90
|
+
export function node(tokens: ColorTokens): ViewStyle {
|
|
91
|
+
return {
|
|
92
|
+
height: 28,
|
|
93
|
+
width: 28,
|
|
94
|
+
flexShrink: 0,
|
|
95
|
+
alignItems: "center",
|
|
96
|
+
justifyContent: "center",
|
|
97
|
+
borderRadius: 9999,
|
|
98
|
+
borderWidth: 1,
|
|
99
|
+
borderColor: tokens.border,
|
|
100
|
+
backgroundColor: tokens.card,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// The node's initials (text-xs font-medium text-muted-foreground).
|
|
105
|
+
export function nodeInitials(tokens: ColorTokens): TextStyle {
|
|
106
|
+
return { fontSize: 12, lineHeight: 16, fontWeight: "500", color: tokens["muted-foreground"] };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// The node's actor-less dot (h-1.5 w-1.5 rounded-full bg-muted-foreground).
|
|
110
|
+
export function nodeDot(tokens: ColorTokens): ViewStyle {
|
|
111
|
+
return { height: 6, width: 6, borderRadius: 9999, backgroundColor: tokens["muted-foreground"] };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// The connector content column (flex-1 pt-0.5): nudged down to align with the node.
|
|
115
|
+
export const connectorContentColumn: ViewStyle = {
|
|
116
|
+
flexGrow: 1,
|
|
117
|
+
flexShrink: 1,
|
|
118
|
+
flexBasis: "0%",
|
|
119
|
+
paddingTop: 2,
|
|
120
|
+
};
|