@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,177 @@
|
|
|
1
|
+
# Listboxes
|
|
2
|
+
|
|
3
|
+
A custom (non-native) select: single or multi-select, optional avatars or icons per option, and a checkmark on the chosen items. Reach for it when a native select can't show rich options; prefer a native select for simple short lists.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
<Listbox
|
|
9
|
+
items={[
|
|
10
|
+
{ label: "Backend", selected: true },
|
|
11
|
+
{ label: "Frontend", selected: false },
|
|
12
|
+
{ label: "Design", selected: false },
|
|
13
|
+
{ label: "Platform", selected: false },
|
|
14
|
+
{ label: "Security", selected: false }
|
|
15
|
+
]}
|
|
16
|
+
bordered
|
|
17
|
+
/>
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Variants
|
|
21
|
+
|
|
22
|
+
### Mode - multi
|
|
23
|
+
|
|
24
|
+
```tsx
|
|
25
|
+
<Listbox
|
|
26
|
+
items={[
|
|
27
|
+
{ label: "Backend", selected: true },
|
|
28
|
+
{ label: "Frontend", selected: false },
|
|
29
|
+
{ label: "Design", selected: true },
|
|
30
|
+
{ label: "Platform", selected: false },
|
|
31
|
+
{ label: "Security", selected: false }
|
|
32
|
+
]}
|
|
33
|
+
multi
|
|
34
|
+
bordered
|
|
35
|
+
/>
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Size - sm
|
|
39
|
+
|
|
40
|
+
```tsx
|
|
41
|
+
<Listbox
|
|
42
|
+
items={[
|
|
43
|
+
{ label: "Backend", selected: true },
|
|
44
|
+
{ label: "Frontend", selected: false },
|
|
45
|
+
{ label: "Design", selected: false },
|
|
46
|
+
{ label: "Platform", selected: false },
|
|
47
|
+
{ label: "Security", selected: false }
|
|
48
|
+
]}
|
|
49
|
+
bordered
|
|
50
|
+
small
|
|
51
|
+
/>
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Size - lg
|
|
55
|
+
|
|
56
|
+
```tsx
|
|
57
|
+
<Listbox
|
|
58
|
+
items={[
|
|
59
|
+
{ label: "Backend", selected: true },
|
|
60
|
+
{ label: "Frontend", selected: false },
|
|
61
|
+
{ label: "Design", selected: false },
|
|
62
|
+
{ label: "Platform", selected: false },
|
|
63
|
+
{ label: "Security", selected: false }
|
|
64
|
+
]}
|
|
65
|
+
bordered
|
|
66
|
+
large
|
|
67
|
+
/>
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Avatars
|
|
71
|
+
|
|
72
|
+
```tsx
|
|
73
|
+
<Listbox
|
|
74
|
+
items={[
|
|
75
|
+
{ label: "Rachel Chen", detail: "rachel@acme.io", selected: true },
|
|
76
|
+
{ label: "Ada Lovelace", detail: "ada@acme.io", selected: false },
|
|
77
|
+
{ label: "Kevin Turner", detail: "kevin@acme.io", selected: false },
|
|
78
|
+
{ label: "Linus Berg", detail: "linus@acme.io", selected: false }
|
|
79
|
+
]}
|
|
80
|
+
bordered
|
|
81
|
+
/>
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Disabled
|
|
85
|
+
|
|
86
|
+
```tsx
|
|
87
|
+
<Listbox
|
|
88
|
+
items={[
|
|
89
|
+
{ label: "Backend", selected: true },
|
|
90
|
+
{ label: "Frontend", selected: false },
|
|
91
|
+
{ label: "Design", selected: false },
|
|
92
|
+
{ label: "Platform", selected: false },
|
|
93
|
+
{ label: "Security", selected: false }
|
|
94
|
+
]}
|
|
95
|
+
bordered
|
|
96
|
+
disabled
|
|
97
|
+
/>
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Do & Don't
|
|
101
|
+
|
|
102
|
+
### Prefer a native select for simple lists
|
|
103
|
+
|
|
104
|
+
**Do** — For short, plain lists a native select is lighter, accessible, and uses the platform picker on mobile.
|
|
105
|
+
|
|
106
|
+
```tsx
|
|
107
|
+
<Select open value="Yes" options={["Yes", "No"]} style={{ width: 192 }} />
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
**Don't** — A custom listbox for two short options is heavier than it needs to be and worse on mobile.
|
|
111
|
+
|
|
112
|
+
```tsx
|
|
113
|
+
<Listbox bordered style={{ width: 192 }} items={[
|
|
114
|
+
{ label: "Yes", selected: true },
|
|
115
|
+
{ label: "No" }
|
|
116
|
+
]} />
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### single
|
|
120
|
+
|
|
121
|
+
**Do** — Show exactly one checkmark, mirror it in the trigger value, and close the panel on pick.
|
|
122
|
+
|
|
123
|
+
```tsx
|
|
124
|
+
<Listbox bordered style={{ width: 224 }} items={[
|
|
125
|
+
{ label: "Backend", selected: true },
|
|
126
|
+
{ label: "Frontend" },
|
|
127
|
+
{ label: "Design" },
|
|
128
|
+
{ label: "Platform" }
|
|
129
|
+
]} />
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
**Don't** — Single-select with two checkmarks lies about state: only one option can be the value.
|
|
133
|
+
|
|
134
|
+
```tsx
|
|
135
|
+
<Listbox bordered style={{ width: 224 }} items={[
|
|
136
|
+
{ label: "Backend", selected: true },
|
|
137
|
+
{ label: "Frontend", selected: true },
|
|
138
|
+
{ label: "Design" },
|
|
139
|
+
{ label: "Platform" }
|
|
140
|
+
]} />
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### multi
|
|
144
|
+
|
|
145
|
+
**Do** — Keep the panel open, toggle each option's own checkmark, and summarize the count in the trigger.
|
|
146
|
+
|
|
147
|
+
```tsx
|
|
148
|
+
<View style={{ width: 224, gap: 4 }}>
|
|
149
|
+
<View style={{ flexDirection: "row", alignItems: "center", justifyContent: "space-between", borderRadius: 6, borderWidth: 1, borderColor: tokens.input, backgroundColor: tokens.background, paddingHorizontal: 12, height: 36 }}>
|
|
150
|
+
<Text style={{ fontSize: 14, lineHeight: 20, color: tokens.foreground }}>3 selected</Text>
|
|
151
|
+
<Text style={{ fontSize: 14, lineHeight: 20, color: tokens["muted-foreground"] }}>▾</Text>
|
|
152
|
+
</View>
|
|
153
|
+
<Listbox multi bordered items={[
|
|
154
|
+
{ label: "Backend", selected: true },
|
|
155
|
+
{ label: "Frontend", selected: true },
|
|
156
|
+
{ label: "Design" },
|
|
157
|
+
{ label: "Platform", selected: true }
|
|
158
|
+
]} />
|
|
159
|
+
</View>
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
**Don't** — Don't close on each pick or echo only the last choice: multi-select needs to keep all selections visible.
|
|
163
|
+
|
|
164
|
+
```tsx
|
|
165
|
+
<View style={{ width: 224, gap: 4 }}>
|
|
166
|
+
<View style={{ flexDirection: "row", alignItems: "center", justifyContent: "space-between", borderRadius: 6, borderWidth: 1, borderColor: tokens.input, backgroundColor: tokens.background, paddingHorizontal: 12, height: 36 }}>
|
|
167
|
+
<Text style={{ fontSize: 14, lineHeight: 20, color: tokens.foreground }}>Backend</Text>
|
|
168
|
+
<Text style={{ fontSize: 14, lineHeight: 20, color: tokens["muted-foreground"] }}>▾</Text>
|
|
169
|
+
</View>
|
|
170
|
+
<Listbox multi bordered items={[
|
|
171
|
+
{ label: "Backend", selected: true },
|
|
172
|
+
{ label: "Frontend", selected: true },
|
|
173
|
+
{ label: "Design" },
|
|
174
|
+
{ label: "Platform", selected: true }
|
|
175
|
+
]} />
|
|
176
|
+
</View>
|
|
177
|
+
```
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { type ViewStyle, type TextStyle } from "react-native";
|
|
2
|
+
import { type ColorTokens } from "../../style/index.js";
|
|
3
|
+
|
|
4
|
+
// Co-located Listbox styles. Layout-only fragments are static objects; anything
|
|
5
|
+
// that reads a color is a function of the active tokens (so the bordered surface,
|
|
6
|
+
// the selected/press fill, and the label/detail colors follow light/dark and the
|
|
7
|
+
// glass surface via tokens.popover/accent). The component resolves its mode and
|
|
8
|
+
// size axes and spreads these.
|
|
9
|
+
|
|
10
|
+
export type Mode = "single" | "multi";
|
|
11
|
+
export type Size = "small" | "medium" | "large";
|
|
12
|
+
|
|
13
|
+
// A bordered container reads as a popover surface: rounded card, hairline border,
|
|
14
|
+
// popover fill (translucent under glass), and a 4px inset so rows don't touch the
|
|
15
|
+
// edge.
|
|
16
|
+
export function containerBordered(tokens: ColorTokens): ViewStyle {
|
|
17
|
+
return { borderRadius: 6, borderWidth: 1, borderColor: tokens.border, backgroundColor: tokens.popover, padding: 4 };
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Each row: a horizontal Pressable with a leading control, the label/detail
|
|
21
|
+
// stack, and (added by the component) a subtle press-state fill. Size adds the
|
|
22
|
+
// vertical padding.
|
|
23
|
+
export const rowBase: ViewStyle = { flexDirection: "row", alignItems: "center", gap: 8, borderRadius: 2 };
|
|
24
|
+
|
|
25
|
+
// Per-row padding by size: small reads like the legacy h-8 trigger, medium like
|
|
26
|
+
// h-9, large like h-10.
|
|
27
|
+
export const rowSize: Record<Size, ViewStyle> = {
|
|
28
|
+
small: { paddingHorizontal: 8, paddingVertical: 6 },
|
|
29
|
+
medium: { paddingHorizontal: 8, paddingVertical: 8 },
|
|
30
|
+
large: { paddingHorizontal: 8, paddingVertical: 10 },
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
// The accent fill used for a selected single-select row and the press state.
|
|
34
|
+
export function rowSelected(tokens: ColorTokens): ViewStyle {
|
|
35
|
+
return { backgroundColor: tokens.accent };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Single-select checkmark column: a fixed-width gutter reserved on every row so
|
|
39
|
+
// labels stay aligned whether or not the row is selected.
|
|
40
|
+
export function checkmark(tokens: ColorTokens): TextStyle {
|
|
41
|
+
return { width: 16, fontSize: 14, lineHeight: 20, fontWeight: "500", color: tokens.foreground };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// The label/detail stack grows to fill the remaining row width.
|
|
45
|
+
export const textStack: ViewStyle = { flexGrow: 1, flexShrink: 1, flexBasis: "0%" };
|
|
46
|
+
|
|
47
|
+
// Label type per size (small is smaller; medium and large share the body size).
|
|
48
|
+
const LABEL_TYPE: Record<Size, TextStyle> = {
|
|
49
|
+
small: { fontSize: 12, lineHeight: 16 },
|
|
50
|
+
medium: { fontSize: 14, lineHeight: 20 },
|
|
51
|
+
large: { fontSize: 14, lineHeight: 20 },
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export function label(tokens: ColorTokens, size: Size): TextStyle {
|
|
55
|
+
return { color: tokens.foreground, ...LABEL_TYPE[size] };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function detail(tokens: ColorTokens): TextStyle {
|
|
59
|
+
return { fontSize: 12, lineHeight: 16, color: tokens["muted-foreground"] };
|
|
60
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { View, Pressable, Text, useTheme, type StyleProp, type ViewStyle } from "../../style/index.js";
|
|
2
|
+
import { Checkbox } from "../checkbox/checkbox.js";
|
|
3
|
+
import * as s from "./listbox.styles.js";
|
|
4
|
+
import { type Mode, type Size } from "./listbox.styles.js";
|
|
5
|
+
|
|
6
|
+
// An inline, selectable list of options rendered directly (not a popover). Each
|
|
7
|
+
// row is a Pressable. Two selection modes, mutually exclusive:
|
|
8
|
+
//
|
|
9
|
+
// 1. Single-select (default): the chosen row is filled with the accent and shows
|
|
10
|
+
// a leading checkmark ("✓"); at most one row is the value.
|
|
11
|
+
// 2. Multi-select (`multi`): every row carries a leading Checkbox reflecting its
|
|
12
|
+
// own selected state, and any number of rows may be selected at once.
|
|
13
|
+
//
|
|
14
|
+
// Boolean-prop API: one boolean per option, grouped by axis, first-match
|
|
15
|
+
// precedence within an axis (mirrors Button's intentOf). `multi` switches the
|
|
16
|
+
// selection mode; `bordered` wraps the list in a rounded popover-surface card.
|
|
17
|
+
|
|
18
|
+
export interface ListboxItem {
|
|
19
|
+
/** The option's primary text. */
|
|
20
|
+
label: string;
|
|
21
|
+
/** Optional secondary text shown under the label in a muted tone. */
|
|
22
|
+
detail?: string;
|
|
23
|
+
/** Whether this option is currently selected. */
|
|
24
|
+
selected?: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface ListboxProps {
|
|
28
|
+
/** The options to render, top to bottom. */
|
|
29
|
+
items: ListboxItem[];
|
|
30
|
+
/** Multi-select: each row gets a leading Checkbox instead of a single ✓. */
|
|
31
|
+
multi?: boolean;
|
|
32
|
+
/** Wrap the list in a rounded, bordered popover-surface card. */
|
|
33
|
+
bordered?: boolean;
|
|
34
|
+
// Size (pick one; default is medium).
|
|
35
|
+
/** Tighter rows with smaller text. */
|
|
36
|
+
small?: boolean;
|
|
37
|
+
/** Taller rows. */
|
|
38
|
+
large?: boolean;
|
|
39
|
+
/** Dim the list and block selection. */
|
|
40
|
+
disabled?: boolean;
|
|
41
|
+
/** Fired with the pressed option's index. */
|
|
42
|
+
onSelect?: (index: number) => void;
|
|
43
|
+
/** Escape hatch for layout/positioning composition (mainly width). */
|
|
44
|
+
style?: StyleProp<ViewStyle>;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Selection mode precedence when more than one axis prop is passed: first match
|
|
48
|
+
// wins. Only `multi` competes here; the default is single-select.
|
|
49
|
+
function modeOf(p: ListboxProps): Mode {
|
|
50
|
+
if (p.multi) return "multi";
|
|
51
|
+
return "single";
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Size precedence when more than one is passed: first match wins (small over
|
|
55
|
+
// large). The default is medium.
|
|
56
|
+
function sizeOf(p: ListboxProps): Size {
|
|
57
|
+
if (p.small) return "small";
|
|
58
|
+
if (p.large) return "large";
|
|
59
|
+
return "medium";
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function Listbox(props: ListboxProps) {
|
|
63
|
+
const { items, bordered, disabled, onSelect, style } = props;
|
|
64
|
+
const mode = modeOf(props);
|
|
65
|
+
const size = sizeOf(props);
|
|
66
|
+
const { tokens } = useTheme();
|
|
67
|
+
|
|
68
|
+
const container: StyleProp<ViewStyle> = [
|
|
69
|
+
bordered ? s.containerBordered(tokens) : null,
|
|
70
|
+
disabled ? { opacity: 0.5 } : null,
|
|
71
|
+
style,
|
|
72
|
+
];
|
|
73
|
+
|
|
74
|
+
return (
|
|
75
|
+
<View style={container} accessibilityRole="list">
|
|
76
|
+
{items.map((item, index) => {
|
|
77
|
+
const selected = !!item.selected;
|
|
78
|
+
// Single-select fills the chosen row; multi-select leaves the row plain
|
|
79
|
+
// and reflects state in the leading Checkbox instead.
|
|
80
|
+
const rowBase: StyleProp<ViewStyle> = [
|
|
81
|
+
s.rowBase,
|
|
82
|
+
s.rowSize[size],
|
|
83
|
+
mode === "single" && selected ? s.rowSelected(tokens) : null,
|
|
84
|
+
];
|
|
85
|
+
|
|
86
|
+
return (
|
|
87
|
+
<Pressable
|
|
88
|
+
key={index}
|
|
89
|
+
// The press fill (the old `active:bg-accent`) is the accent, applied
|
|
90
|
+
// only when the list is enabled.
|
|
91
|
+
style={({ pressed }) => [rowBase, !disabled && pressed ? s.rowSelected(tokens) : null]}
|
|
92
|
+
onPress={disabled ? undefined : () => onSelect?.(index)}
|
|
93
|
+
disabled={disabled}
|
|
94
|
+
accessibilityRole={mode === "multi" ? "checkbox" : "menuitem"}
|
|
95
|
+
accessibilityState={{ selected, disabled: !!disabled }}
|
|
96
|
+
>
|
|
97
|
+
{mode === "multi" ? (
|
|
98
|
+
<Checkbox checked={selected} />
|
|
99
|
+
) : (
|
|
100
|
+
// Reserve the checkmark column on every row so labels stay aligned
|
|
101
|
+
// whether or not the row is selected.
|
|
102
|
+
<Text style={s.checkmark(tokens)}>{selected ? "✓" : ""}</Text>
|
|
103
|
+
)}
|
|
104
|
+
<View style={s.textStack}>
|
|
105
|
+
<Text style={s.label(tokens, size)}>{item.label}</Text>
|
|
106
|
+
{item.detail != null ? <Text style={s.detail(tokens)}>{item.detail}</Text> : null}
|
|
107
|
+
</View>
|
|
108
|
+
</Pressable>
|
|
109
|
+
);
|
|
110
|
+
})}
|
|
111
|
+
</View>
|
|
112
|
+
);
|
|
113
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { createPagination } from "./pagination.shared.js";
|
|
2
|
+
import { androidSkin } from "./pagination.styles.js";
|
|
3
|
+
|
|
4
|
+
// Material 3 Pagination. Metro resolves this file on Android; the docs import it for preview.
|
|
5
|
+
export const Pagination = createPagination(androidSkin);
|
|
6
|
+
export type { PaginationProps } from "./pagination.shared.js";
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { createPagination } from "./pagination.shared.js";
|
|
2
|
+
import { iosSkin } from "./pagination.styles.js";
|
|
3
|
+
|
|
4
|
+
// iOS (HIG page controls) Pagination. Metro resolves this file on iOS; the docs import it for preview.
|
|
5
|
+
export const Pagination = createPagination(iosSkin);
|
|
6
|
+
export type { PaginationProps } from "./pagination.shared.js";
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# Pagination
|
|
2
|
+
|
|
3
|
+
Page-of-N navigation for tables and lists.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
<Pagination page={2} total={12} compact pageSize={10} pageSizes={[10, 25, 50]} />
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Variants
|
|
12
|
+
|
|
13
|
+
### Variant - numbered
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
<Pagination page={2} total={12} pageSize={10} pageSizes={[10, 25, 50]} />
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### Variant - with-size
|
|
20
|
+
|
|
21
|
+
```tsx
|
|
22
|
+
<Pagination page={2} total={12} withSize pageSize={10} pageSizes={[10, 25, 50]} />
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Do & Don't
|
|
26
|
+
|
|
27
|
+
### compact
|
|
28
|
+
|
|
29
|
+
**Do** — Pair the buttons with a "Showing X–Y of N" range so position and total are always visible.
|
|
30
|
+
|
|
31
|
+
```tsx
|
|
32
|
+
<Pagination compact page={2} total={12} />
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
**Don't** — Bare Previous/Next with no range label leaves the user unable to tell where they are or how much is left.
|
|
36
|
+
|
|
37
|
+
```tsx
|
|
38
|
+
<View style={{ flexDirection: "row", alignItems: "center", justifyContent: "flex-end", gap: 4 }}>
|
|
39
|
+
<Pressable style={{ flexDirection: "row", alignItems: "center", justifyContent: "center", height: 36, minWidth: 36, paddingHorizontal: 10, borderRadius: 6, borderWidth: 1, borderColor: tokens.input, backgroundColor: tokens.background }} accessibilityRole="button" accessibilityLabel="Previous page">
|
|
40
|
+
<Text style={{ fontSize: 14, lineHeight: 20, fontWeight: "500", color: tokens.foreground }}>‹</Text>
|
|
41
|
+
</Pressable>
|
|
42
|
+
<Pressable style={{ flexDirection: "row", alignItems: "center", justifyContent: "center", height: 36, minWidth: 36, paddingHorizontal: 10, borderRadius: 6, borderWidth: 1, borderColor: tokens.input, backgroundColor: tokens.background }} accessibilityRole="button" accessibilityLabel="Next page">
|
|
43
|
+
<Text style={{ fontSize: 14, lineHeight: 20, fontWeight: "500", color: tokens.foreground }}>›</Text>
|
|
44
|
+
</Pressable>
|
|
45
|
+
</View>
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### numbered
|
|
49
|
+
|
|
50
|
+
**Do** — Truncate the middle with an ellipsis; keep first, last, and a window around the current page.
|
|
51
|
+
|
|
52
|
+
```tsx
|
|
53
|
+
<Pagination page={2} total={12} />
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
**Don't** — Rendering every page number overflows and stops being scannable past a handful.
|
|
57
|
+
|
|
58
|
+
```tsx
|
|
59
|
+
<View style={{ flexDirection: "row", alignItems: "center", gap: 4 }}>
|
|
60
|
+
<Pressable style={{ flexDirection: "row", alignItems: "center", justifyContent: "center", height: 36, minWidth: 36, paddingHorizontal: 10, borderRadius: 6, borderWidth: 1, borderColor: tokens.input, backgroundColor: tokens.background, opacity: 0.5 }} accessibilityRole="button" accessibilityLabel="Previous page">
|
|
61
|
+
<Text style={{ fontSize: 14, lineHeight: 20, fontWeight: "500", color: tokens.foreground }}>‹</Text>
|
|
62
|
+
</Pressable>
|
|
63
|
+
<Pressable style={{ flexDirection: "row", alignItems: "center", justifyContent: "center", height: 36, minWidth: 36, paddingHorizontal: 10, borderRadius: 6, borderWidth: 1, borderColor: tokens.primary, backgroundColor: tokens.primary }} accessibilityRole="button" accessibilityLabel="Page 1">
|
|
64
|
+
<Text style={{ fontSize: 14, lineHeight: 20, fontWeight: "500", color: tokens["primary-foreground"] }}>1</Text>
|
|
65
|
+
</Pressable>
|
|
66
|
+
<Pressable style={{ flexDirection: "row", alignItems: "center", justifyContent: "center", height: 36, minWidth: 36, paddingHorizontal: 10, borderRadius: 6, borderWidth: 1, borderColor: tokens.input, backgroundColor: tokens.background }} accessibilityRole="button" accessibilityLabel="Page 2">
|
|
67
|
+
<Text style={{ fontSize: 14, lineHeight: 20, fontWeight: "500", color: tokens.foreground }}>2</Text>
|
|
68
|
+
</Pressable>
|
|
69
|
+
<Pressable style={{ flexDirection: "row", alignItems: "center", justifyContent: "center", height: 36, minWidth: 36, paddingHorizontal: 10, borderRadius: 6, borderWidth: 1, borderColor: tokens.input, backgroundColor: tokens.background }} accessibilityRole="button" accessibilityLabel="Page 3">
|
|
70
|
+
<Text style={{ fontSize: 14, lineHeight: 20, fontWeight: "500", color: tokens.foreground }}>3</Text>
|
|
71
|
+
</Pressable>
|
|
72
|
+
<Pressable style={{ flexDirection: "row", alignItems: "center", justifyContent: "center", height: 36, minWidth: 36, paddingHorizontal: 10, borderRadius: 6, borderWidth: 1, borderColor: tokens.input, backgroundColor: tokens.background }} accessibilityRole="button" accessibilityLabel="Page 4">
|
|
73
|
+
<Text style={{ fontSize: 14, lineHeight: 20, fontWeight: "500", color: tokens.foreground }}>4</Text>
|
|
74
|
+
</Pressable>
|
|
75
|
+
<Pressable style={{ flexDirection: "row", alignItems: "center", justifyContent: "center", height: 36, minWidth: 36, paddingHorizontal: 10, borderRadius: 6, borderWidth: 1, borderColor: tokens.input, backgroundColor: tokens.background }} accessibilityRole="button" accessibilityLabel="Page 5">
|
|
76
|
+
<Text style={{ fontSize: 14, lineHeight: 20, fontWeight: "500", color: tokens.foreground }}>5</Text>
|
|
77
|
+
</Pressable>
|
|
78
|
+
<Pressable style={{ flexDirection: "row", alignItems: "center", justifyContent: "center", height: 36, minWidth: 36, paddingHorizontal: 10, borderRadius: 6, borderWidth: 1, borderColor: tokens.input, backgroundColor: tokens.background }} accessibilityRole="button" accessibilityLabel="Page 6">
|
|
79
|
+
<Text style={{ fontSize: 14, lineHeight: 20, fontWeight: "500", color: tokens.foreground }}>6</Text>
|
|
80
|
+
</Pressable>
|
|
81
|
+
<Pressable style={{ flexDirection: "row", alignItems: "center", justifyContent: "center", height: 36, minWidth: 36, paddingHorizontal: 10, borderRadius: 6, borderWidth: 1, borderColor: tokens.input, backgroundColor: tokens.background }} accessibilityRole="button" accessibilityLabel="Page 7">
|
|
82
|
+
<Text style={{ fontSize: 14, lineHeight: 20, fontWeight: "500", color: tokens.foreground }}>7</Text>
|
|
83
|
+
</Pressable>
|
|
84
|
+
<Pressable style={{ flexDirection: "row", alignItems: "center", justifyContent: "center", height: 36, minWidth: 36, paddingHorizontal: 10, borderRadius: 6, borderWidth: 1, borderColor: tokens.input, backgroundColor: tokens.background }} accessibilityRole="button" accessibilityLabel="Page 8">
|
|
85
|
+
<Text style={{ fontSize: 14, lineHeight: 20, fontWeight: "500", color: tokens.foreground }}>8</Text>
|
|
86
|
+
</Pressable>
|
|
87
|
+
<Pressable style={{ flexDirection: "row", alignItems: "center", justifyContent: "center", height: 36, minWidth: 36, paddingHorizontal: 10, borderRadius: 6, borderWidth: 1, borderColor: tokens.input, backgroundColor: tokens.background }} accessibilityRole="button" accessibilityLabel="Page 9">
|
|
88
|
+
<Text style={{ fontSize: 14, lineHeight: 20, fontWeight: "500", color: tokens.foreground }}>9</Text>
|
|
89
|
+
</Pressable>
|
|
90
|
+
<Pressable style={{ flexDirection: "row", alignItems: "center", justifyContent: "center", height: 36, minWidth: 36, paddingHorizontal: 10, borderRadius: 6, borderWidth: 1, borderColor: tokens.input, backgroundColor: tokens.background }} accessibilityRole="button" accessibilityLabel="Page 10">
|
|
91
|
+
<Text style={{ fontSize: 14, lineHeight: 20, fontWeight: "500", color: tokens.foreground }}>10</Text>
|
|
92
|
+
</Pressable>
|
|
93
|
+
<Pressable style={{ flexDirection: "row", alignItems: "center", justifyContent: "center", height: 36, minWidth: 36, paddingHorizontal: 10, borderRadius: 6, borderWidth: 1, borderColor: tokens.input, backgroundColor: tokens.background }} accessibilityRole="button" accessibilityLabel="Page 11">
|
|
94
|
+
<Text style={{ fontSize: 14, lineHeight: 20, fontWeight: "500", color: tokens.foreground }}>11</Text>
|
|
95
|
+
</Pressable>
|
|
96
|
+
<Pressable style={{ flexDirection: "row", alignItems: "center", justifyContent: "center", height: 36, minWidth: 36, paddingHorizontal: 10, borderRadius: 6, borderWidth: 1, borderColor: tokens.input, backgroundColor: tokens.background }} accessibilityRole="button" accessibilityLabel="Page 12">
|
|
97
|
+
<Text style={{ fontSize: 14, lineHeight: 20, fontWeight: "500", color: tokens.foreground }}>12</Text>
|
|
98
|
+
</Pressable>
|
|
99
|
+
<Pressable style={{ flexDirection: "row", alignItems: "center", justifyContent: "center", height: 36, minWidth: 36, paddingHorizontal: 10, borderRadius: 6, borderWidth: 1, borderColor: tokens.input, backgroundColor: tokens.background }} accessibilityRole="button" accessibilityLabel="Next page">
|
|
100
|
+
<Text style={{ fontSize: 14, lineHeight: 20, fontWeight: "500", color: tokens.foreground }}>›</Text>
|
|
101
|
+
</Pressable>
|
|
102
|
+
</View>
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### with-size
|
|
106
|
+
|
|
107
|
+
**Do** — Show "Page X of N" beside the size selector and reset to page 1 when the size changes.
|
|
108
|
+
|
|
109
|
+
```tsx
|
|
110
|
+
<Pagination withSize page={2} total={12} pageSize={10} pageSizes={[10, 25, 50]} />
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
**Don't** — Offering a page-size selector without a page indicator hides which page the new size landed on.
|
|
114
|
+
|
|
115
|
+
```tsx
|
|
116
|
+
<View style={{ flexDirection: "row", alignItems: "center", gap: 16 }}>
|
|
117
|
+
<View style={{ flexDirection: "row", alignItems: "center", gap: 8 }}>
|
|
118
|
+
<Text style={{ fontSize: 14, lineHeight: 20, color: tokens["muted-foreground"] }}>Rows per page</Text>
|
|
119
|
+
<Pressable style={{ flexDirection: "row", alignItems: "center", justifyContent: "space-between", gap: 4, height: 36, minWidth: 36, paddingHorizontal: 10, borderRadius: 6, borderWidth: 1, borderColor: tokens.input, backgroundColor: tokens.background }} accessibilityRole="button" accessibilityLabel="Rows per page">
|
|
120
|
+
<Text style={{ fontSize: 14, lineHeight: 20, fontWeight: "500", color: tokens.foreground }}>10</Text>
|
|
121
|
+
<Text style={{ fontSize: 14, lineHeight: 20, color: tokens["muted-foreground"] }}>▾</Text>
|
|
122
|
+
</Pressable>
|
|
123
|
+
</View>
|
|
124
|
+
<View style={{ flexDirection: "row", alignItems: "center", gap: 4 }}>
|
|
125
|
+
<Pressable style={{ flexDirection: "row", alignItems: "center", justifyContent: "center", height: 36, minWidth: 36, paddingHorizontal: 10, borderRadius: 6, borderWidth: 1, borderColor: tokens.input, backgroundColor: tokens.background, opacity: 0.5 }} accessibilityRole="button" accessibilityLabel="Previous page">
|
|
126
|
+
<Text style={{ fontSize: 14, lineHeight: 20, fontWeight: "500", color: tokens.foreground }}>‹</Text>
|
|
127
|
+
</Pressable>
|
|
128
|
+
<Pressable style={{ flexDirection: "row", alignItems: "center", justifyContent: "center", height: 36, minWidth: 36, paddingHorizontal: 10, borderRadius: 6, borderWidth: 1, borderColor: tokens.input, backgroundColor: tokens.background }} accessibilityRole="button" accessibilityLabel="Next page">
|
|
129
|
+
<Text style={{ fontSize: 14, lineHeight: 20, fontWeight: "500", color: tokens.foreground }}>›</Text>
|
|
130
|
+
</Pressable>
|
|
131
|
+
</View>
|
|
132
|
+
</View>
|
|
133
|
+
```
|