@studio-piot/native-ui 0.1.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/LICENSE +15 -0
- package/README.md +91 -0
- package/dist/components/Button/Button.d.ts +16 -0
- package/dist/components/Button/Button.d.ts.map +1 -0
- package/dist/components/Button/Button.js +44 -0
- package/dist/components/Button/Button.js.map +1 -0
- package/dist/components/Button/buttonRadius.d.ts +3 -0
- package/dist/components/Button/buttonRadius.d.ts.map +1 -0
- package/dist/components/Button/buttonRadius.js +17 -0
- package/dist/components/Button/buttonRadius.js.map +1 -0
- package/dist/components/Button/index.d.ts +3 -0
- package/dist/components/Button/index.d.ts.map +1 -0
- package/dist/components/Button/index.js +8 -0
- package/dist/components/Button/index.js.map +1 -0
- package/dist/components/Field/FieldText.d.ts +3 -0
- package/dist/components/Field/FieldText.d.ts.map +1 -0
- package/dist/components/Field/FieldText.js +9 -0
- package/dist/components/Field/FieldText.js.map +1 -0
- package/dist/components/Field/FieldTextarea.d.ts +3 -0
- package/dist/components/Field/FieldTextarea.d.ts.map +1 -0
- package/dist/components/Field/FieldTextarea.js +10 -0
- package/dist/components/Field/FieldTextarea.js.map +1 -0
- package/dist/components/Field/fieldChrome.d.ts +10 -0
- package/dist/components/Field/fieldChrome.d.ts.map +1 -0
- package/dist/components/Field/fieldChrome.js +100 -0
- package/dist/components/Field/fieldChrome.js.map +1 -0
- package/dist/components/Field/fieldThemeRoles.d.ts +13 -0
- package/dist/components/Field/fieldThemeRoles.d.ts.map +1 -0
- package/dist/components/Field/fieldThemeRoles.js +15 -0
- package/dist/components/Field/fieldThemeRoles.js.map +1 -0
- package/dist/components/Field/index.d.ts +6 -0
- package/dist/components/Field/index.d.ts.map +1 -0
- package/dist/components/Field/index.js +10 -0
- package/dist/components/Field/index.js.map +1 -0
- package/dist/components/Field/resolveFieldBorder.d.ts +16 -0
- package/dist/components/Field/resolveFieldBorder.d.ts.map +1 -0
- package/dist/components/Field/resolveFieldBorder.js +20 -0
- package/dist/components/Field/resolveFieldBorder.js.map +1 -0
- package/dist/components/Field/resolveFieldColor.d.ts +7 -0
- package/dist/components/Field/resolveFieldColor.d.ts.map +1 -0
- package/dist/components/Field/resolveFieldColor.js +21 -0
- package/dist/components/Field/resolveFieldColor.js.map +1 -0
- package/dist/components/Field/types.d.ts +46 -0
- package/dist/components/Field/types.d.ts.map +1 -0
- package/dist/components/Field/types.js +3 -0
- package/dist/components/Field/types.js.map +1 -0
- package/dist/components/Field/useResolvedFieldColor.d.ts +2 -0
- package/dist/components/Field/useResolvedFieldColor.d.ts.map +1 -0
- package/dist/components/Field/useResolvedFieldColor.js +15 -0
- package/dist/components/Field/useResolvedFieldColor.js.map +1 -0
- package/dist/components/Form/Form.d.ts +3 -0
- package/dist/components/Form/Form.d.ts.map +1 -0
- package/dist/components/Form/Form.js +100 -0
- package/dist/components/Form/Form.js.map +1 -0
- package/dist/components/Form/FormCloseButton.d.ts +4 -0
- package/dist/components/Form/FormCloseButton.d.ts.map +1 -0
- package/dist/components/Form/FormCloseButton.js +17 -0
- package/dist/components/Form/FormCloseButton.js.map +1 -0
- package/dist/components/Form/index.d.ts +4 -0
- package/dist/components/Form/index.d.ts.map +1 -0
- package/dist/components/Form/index.js +8 -0
- package/dist/components/Form/index.js.map +1 -0
- package/dist/components/Form/resolveThemeColor.d.ts +11 -0
- package/dist/components/Form/resolveThemeColor.d.ts.map +1 -0
- package/dist/components/Form/resolveThemeColor.js +21 -0
- package/dist/components/Form/resolveThemeColor.js.map +1 -0
- package/dist/components/Form/types.d.ts +30 -0
- package/dist/components/Form/types.d.ts.map +1 -0
- package/dist/components/Form/types.js +3 -0
- package/dist/components/Form/types.js.map +1 -0
- package/dist/components/Form/useResolvedThemeColor.d.ts +2 -0
- package/dist/components/Form/useResolvedThemeColor.d.ts.map +1 -0
- package/dist/components/Form/useResolvedThemeColor.js +15 -0
- package/dist/components/Form/useResolvedThemeColor.js.map +1 -0
- package/dist/components/Form/withAlpha.d.ts +2 -0
- package/dist/components/Form/withAlpha.d.ts.map +1 -0
- package/dist/components/Form/withAlpha.js +12 -0
- package/dist/components/Form/withAlpha.js.map +1 -0
- package/dist/components/LinkBlocks/LinkBlocks.d.ts +3 -0
- package/dist/components/LinkBlocks/LinkBlocks.d.ts.map +1 -0
- package/dist/components/LinkBlocks/LinkBlocks.js +75 -0
- package/dist/components/LinkBlocks/LinkBlocks.js.map +1 -0
- package/dist/components/LinkBlocks/filterGroups.d.ts +3 -0
- package/dist/components/LinkBlocks/filterGroups.d.ts.map +1 -0
- package/dist/components/LinkBlocks/filterGroups.js +7 -0
- package/dist/components/LinkBlocks/filterGroups.js.map +1 -0
- package/dist/components/LinkBlocks/index.d.ts +4 -0
- package/dist/components/LinkBlocks/index.d.ts.map +1 -0
- package/dist/components/LinkBlocks/index.js +8 -0
- package/dist/components/LinkBlocks/index.js.map +1 -0
- package/dist/components/LinkBlocks/types.d.ts +26 -0
- package/dist/components/LinkBlocks/types.d.ts.map +1 -0
- package/dist/components/LinkBlocks/types.js +3 -0
- package/dist/components/LinkBlocks/types.js.map +1 -0
- package/dist/components/RadioField/RadioField.d.ts +3 -0
- package/dist/components/RadioField/RadioField.d.ts.map +1 -0
- package/dist/components/RadioField/RadioField.js +76 -0
- package/dist/components/RadioField/RadioField.js.map +1 -0
- package/dist/components/RadioField/RadioMark.d.ts +7 -0
- package/dist/components/RadioField/RadioMark.d.ts.map +1 -0
- package/dist/components/RadioField/RadioMark.js +32 -0
- package/dist/components/RadioField/RadioMark.js.map +1 -0
- package/dist/components/RadioField/index.d.ts +4 -0
- package/dist/components/RadioField/index.d.ts.map +1 -0
- package/dist/components/RadioField/index.js +8 -0
- package/dist/components/RadioField/index.js.map +1 -0
- package/dist/components/RadioField/radioThemeRoles.d.ts +11 -0
- package/dist/components/RadioField/radioThemeRoles.d.ts.map +1 -0
- package/dist/components/RadioField/radioThemeRoles.js +13 -0
- package/dist/components/RadioField/radioThemeRoles.js.map +1 -0
- package/dist/components/RadioField/types.d.ts +24 -0
- package/dist/components/RadioField/types.d.ts.map +1 -0
- package/dist/components/RadioField/types.js +3 -0
- package/dist/components/RadioField/types.js.map +1 -0
- package/dist/components/Select/Select.d.ts +3 -0
- package/dist/components/Select/Select.d.ts.map +1 -0
- package/dist/components/Select/Select.js +137 -0
- package/dist/components/Select/Select.js.map +1 -0
- package/dist/components/Select/SelectCloseButton.d.ts +4 -0
- package/dist/components/Select/SelectCloseButton.d.ts.map +1 -0
- package/dist/components/Select/SelectCloseButton.js +17 -0
- package/dist/components/Select/SelectCloseButton.js.map +1 -0
- package/dist/components/Select/index.d.ts +6 -0
- package/dist/components/Select/index.d.ts.map +1 -0
- package/dist/components/Select/index.js +14 -0
- package/dist/components/Select/index.js.map +1 -0
- package/dist/components/Select/resolveSelectDetentIndex.d.ts +9 -0
- package/dist/components/Select/resolveSelectDetentIndex.d.ts.map +1 -0
- package/dist/components/Select/resolveSelectDetentIndex.js +16 -0
- package/dist/components/Select/resolveSelectDetentIndex.js.map +1 -0
- package/dist/components/Select/selectThemeRoles.d.ts +12 -0
- package/dist/components/Select/selectThemeRoles.d.ts.map +1 -0
- package/dist/components/Select/selectThemeRoles.js +14 -0
- package/dist/components/Select/selectThemeRoles.js.map +1 -0
- package/dist/components/Select/types.d.ts +35 -0
- package/dist/components/Select/types.d.ts.map +1 -0
- package/dist/components/Select/types.js +3 -0
- package/dist/components/Select/types.js.map +1 -0
- package/dist/components/index.d.ts +7 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/index.js +27 -0
- package/dist/components/index.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +66 -0
- package/dist/index.js.map +1 -0
- package/dist/layout/index.d.ts +2 -0
- package/dist/layout/index.d.ts.map +1 -0
- package/dist/layout/index.js +6 -0
- package/dist/layout/index.js.map +1 -0
- package/dist/layout/screenPadding.d.ts +3 -0
- package/dist/layout/screenPadding.d.ts.map +1 -0
- package/dist/layout/screenPadding.js +6 -0
- package/dist/layout/screenPadding.js.map +1 -0
- package/dist/theme/ThemeProvider.d.ts +11 -0
- package/dist/theme/ThemeProvider.d.ts.map +1 -0
- package/dist/theme/ThemeProvider.js +11 -0
- package/dist/theme/ThemeProvider.js.map +1 -0
- package/dist/theme/index.d.ts +6 -0
- package/dist/theme/index.d.ts.map +1 -0
- package/dist/theme/index.js +12 -0
- package/dist/theme/index.js.map +1 -0
- package/dist/theme/resolveColorScheme.d.ts +2 -0
- package/dist/theme/resolveColorScheme.d.ts.map +1 -0
- package/dist/theme/resolveColorScheme.js +7 -0
- package/dist/theme/resolveColorScheme.js.map +1 -0
- package/dist/theme/types.d.ts +8 -0
- package/dist/theme/types.d.ts.map +1 -0
- package/dist/theme/types.js +3 -0
- package/dist/theme/types.js.map +1 -0
- package/dist/theme/useColorScheme.d.ts +2 -0
- package/dist/theme/useColorScheme.d.ts.map +1 -0
- package/dist/theme/useColorScheme.js +16 -0
- package/dist/theme/useColorScheme.js.map +1 -0
- package/dist/theme/useThemeColor.d.ts +5 -0
- package/dist/theme/useThemeColor.d.ts.map +1 -0
- package/dist/theme/useThemeColor.js +25 -0
- package/dist/theme/useThemeColor.js.map +1 -0
- package/docs/components/button.md +162 -0
- package/docs/components/field.md +128 -0
- package/docs/components/form.md +86 -0
- package/docs/components/link-blocks.md +120 -0
- package/docs/components/radio-field.md +100 -0
- package/docs/components/select.md +59 -0
- package/docs/superpowers/plans/2026-09-24-field.md +583 -0
- package/docs/superpowers/plans/2026-09-24-link-blocks.md +414 -0
- package/docs/superpowers/plans/2026-09-24-theme-provider-button.md +493 -0
- package/docs/superpowers/plans/2026-09-25-radio-field.md +516 -0
- package/docs/superpowers/plans/2026-09-25-select.md +22 -0
- package/docs/superpowers/specs/2026-09-24-field-design.md +205 -0
- package/docs/superpowers/specs/2026-09-24-link-blocks-design.md +167 -0
- package/docs/superpowers/specs/2026-09-24-theme-provider-button-design.md +178 -0
- package/docs/superpowers/specs/2026-09-25-radio-field-design.md +136 -0
- package/docs/superpowers/specs/2026-09-25-select-design.md +164 -0
- package/package.json +75 -0
- package/src/components/Button/Button.tsx +97 -0
- package/src/components/Button/buttonRadius.test.ts +16 -0
- package/src/components/Button/buttonRadius.ts +24 -0
- package/src/components/Button/index.ts +2 -0
- package/src/components/Field/FieldText.tsx +6 -0
- package/src/components/Field/FieldTextarea.tsx +18 -0
- package/src/components/Field/fieldChrome.tsx +219 -0
- package/src/components/Field/fieldThemeRoles.ts +13 -0
- package/src/components/Field/index.ts +5 -0
- package/src/components/Field/resolveFieldBorder.test.ts +76 -0
- package/src/components/Field/resolveFieldBorder.ts +40 -0
- package/src/components/Field/resolveFieldColor.test.ts +92 -0
- package/src/components/Field/resolveFieldColor.ts +26 -0
- package/src/components/Field/types.ts +53 -0
- package/src/components/Field/useResolvedFieldColor.ts +15 -0
- package/src/components/Form/Form.tsx +205 -0
- package/src/components/Form/FormCloseButton.tsx +19 -0
- package/src/components/Form/index.ts +3 -0
- package/src/components/Form/resolveThemeColor.ts +24 -0
- package/src/components/Form/types.ts +30 -0
- package/src/components/Form/useResolvedThemeColor.ts +15 -0
- package/src/components/Form/withAlpha.test.ts +13 -0
- package/src/components/Form/withAlpha.ts +9 -0
- package/src/components/LinkBlocks/LinkBlocks.tsx +165 -0
- package/src/components/LinkBlocks/filterGroups.test.ts +37 -0
- package/src/components/LinkBlocks/filterGroups.ts +7 -0
- package/src/components/LinkBlocks/index.ts +7 -0
- package/src/components/LinkBlocks/types.ts +28 -0
- package/src/components/RadioField/RadioField.tsx +168 -0
- package/src/components/RadioField/RadioMark.tsx +54 -0
- package/src/components/RadioField/index.ts +3 -0
- package/src/components/RadioField/radioThemeRoles.test.ts +15 -0
- package/src/components/RadioField/radioThemeRoles.ts +11 -0
- package/src/components/RadioField/types.ts +25 -0
- package/src/components/Select/Select.tsx +279 -0
- package/src/components/Select/SelectCloseButton.tsx +19 -0
- package/src/components/Select/index.ts +13 -0
- package/src/components/Select/resolveSelectDetentIndex.test.ts +20 -0
- package/src/components/Select/resolveSelectDetentIndex.ts +16 -0
- package/src/components/Select/selectThemeRoles.ts +12 -0
- package/src/components/Select/types.ts +39 -0
- package/src/components/index.ts +42 -0
- package/src/index.ts +50 -0
- package/src/layout/index.ts +1 -0
- package/src/layout/screenPadding.ts +2 -0
- package/src/theme/ThemeProvider.tsx +20 -0
- package/src/theme/index.ts +5 -0
- package/src/theme/resolveColorScheme.test.ts +16 -0
- package/src/theme/resolveColorScheme.ts +5 -0
- package/src/theme/types.ts +5 -0
- package/src/theme/useColorScheme.ts +13 -0
- package/src/theme/useThemeColor.ts +25 -0
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# Form — usage
|
|
2
|
+
|
|
3
|
+
Full-screen modal form shell: keyboard-aware scroll, floating title bar with Cancel, sticky submit footer. Colours default from `ThemeProvider`. Apps own form fields (e.g. `FieldText`) and can swap Cancel via a slot.
|
|
4
|
+
|
|
5
|
+
For `ThemeProvider` and `useThemeColor`, see [theme provider and Button usage](./button.md).
|
|
6
|
+
|
|
7
|
+
## Peer dependencies
|
|
8
|
+
|
|
9
|
+
`Form` needs these installed in the app (same versions you already use for Expo):
|
|
10
|
+
|
|
11
|
+
- `react-native-keyboard-controller`
|
|
12
|
+
- `react-native-safe-area-context`
|
|
13
|
+
|
|
14
|
+
Wrap the app (or the form route) with `KeyboardProvider` from `react-native-keyboard-controller` and a safe-area provider.
|
|
15
|
+
|
|
16
|
+
## Theme roles
|
|
17
|
+
|
|
18
|
+
| Usage | Theme role |
|
|
19
|
+
|---|---|
|
|
20
|
+
| Screen / fade ground | `background` |
|
|
21
|
+
| Title | `text` |
|
|
22
|
+
| Footer ground | `surface` |
|
|
23
|
+
| Submit fill | `buttonBackground` |
|
|
24
|
+
| Submit label | `buttonLabel` |
|
|
25
|
+
|
|
26
|
+
## Basic usage
|
|
27
|
+
|
|
28
|
+
```tsx
|
|
29
|
+
import { Form, FieldText } from "@studio-piot/native-ui";
|
|
30
|
+
|
|
31
|
+
export function NewItemScreen({ onClose, onSave }: Props) {
|
|
32
|
+
return (
|
|
33
|
+
<Form title="New item" onClose={onClose} onSubmit={onSave}>
|
|
34
|
+
<FieldText label="Name" value={name} onChangeText={setName} />
|
|
35
|
+
</Form>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Close button slot
|
|
41
|
+
|
|
42
|
+
Pass `closeButton` to replace the default plain Cancel (e.g. Expo liquid-glass Cancel in the app):
|
|
43
|
+
|
|
44
|
+
```tsx
|
|
45
|
+
import { Form } from "@studio-piot/native-ui";
|
|
46
|
+
import { FormCloseButton } from "@/components/FormCloseButton";
|
|
47
|
+
|
|
48
|
+
<Form
|
|
49
|
+
title="New activity"
|
|
50
|
+
onClose={() => router.back()}
|
|
51
|
+
onSubmit={save}
|
|
52
|
+
closeButton={<FormCloseButton onPress={() => router.back()} />}
|
|
53
|
+
>
|
|
54
|
+
{children}
|
|
55
|
+
</Form>
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
The default package `FormCloseButton` is a themed text Cancel. Export it if you want that control outside `Form`.
|
|
59
|
+
|
|
60
|
+
## Props
|
|
61
|
+
|
|
62
|
+
| Prop | Type | Default | Notes |
|
|
63
|
+
|---|---|---|---|
|
|
64
|
+
| `title` | `string` | — | Required. Header title |
|
|
65
|
+
| `children` | `ReactNode` | — | Required. Scroll body |
|
|
66
|
+
| `onClose` | `() => void` | — | Required. Wired to default Cancel |
|
|
67
|
+
| `onSubmit` | `() => void` | — | Required. Sticky Save |
|
|
68
|
+
| `submitLabel` | `string` | `"Save"` | |
|
|
69
|
+
| `submitting` | `boolean` | `false` | Loading on submit |
|
|
70
|
+
| `submitDisabled` | `boolean` | `false` | |
|
|
71
|
+
| `closeButton` | `ReactNode` | package Cancel | Slot for app Cancel |
|
|
72
|
+
| `submitRadius` | `ButtonRadius` | `"lg"` | Submit button radius |
|
|
73
|
+
| `paddingHorizontal` | `number` | `16` | Shared inset for header, content, and footer. Override per screen if needed. |
|
|
74
|
+
| `backgroundColor` | `string` | theme `background` | Optional override |
|
|
75
|
+
| `titleColor` | `string` | theme `text` | Optional override |
|
|
76
|
+
| `footerBackgroundColor` | `string` | theme `surface` | Optional override |
|
|
77
|
+
| `buttonBackgroundColor` | `string` | theme `buttonBackground` | Optional override |
|
|
78
|
+
| `buttonLabelColor` | `string` | theme `buttonLabel` | Optional override |
|
|
79
|
+
|
|
80
|
+
## Imports
|
|
81
|
+
|
|
82
|
+
```ts
|
|
83
|
+
import { Form, FormCloseButton, type FormProps } from "@studio-piot/native-ui";
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Header, scroll content, and footer share one horizontal inset (default `16`). Override with `paddingHorizontal` when a screen needs a different gutter; apps keep their own layout tokens and do not import the package default.
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# LinkBlocks — usage
|
|
2
|
+
|
|
3
|
+
Config-driven groups of tappable rows (optional icon, label, trailing chevron). Section titles sit above each rounded surface. Your app owns colours, icons, and navigation.
|
|
4
|
+
|
|
5
|
+
For the design contract, see [LinkBlocks design](../superpowers/specs/2026-09-24-link-blocks-design.md).
|
|
6
|
+
|
|
7
|
+
For `ThemeProvider` and `useThemeColor`, see [theme provider and Button usage](./button.md).
|
|
8
|
+
|
|
9
|
+
## LinkBlocks
|
|
10
|
+
|
|
11
|
+
`LinkBlocks` is presentational. Pass colour strings and a `blocks` config. Full width of its parent is fixed. The package does not import Expo Router, Linking, or icon libraries — wire `onPress` and optional `icon` nodes in the app.
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
import { LinkBlocks, useThemeColor } from "@studio-piot/native-ui";
|
|
15
|
+
import Ionicons from "@expo/vector-icons/Ionicons";
|
|
16
|
+
import { router } from "expo-router";
|
|
17
|
+
|
|
18
|
+
export function SettingsLinks() {
|
|
19
|
+
const surface = useThemeColor("surface");
|
|
20
|
+
const text = useThemeColor("text");
|
|
21
|
+
const textSecondary = useThemeColor("textSecondary");
|
|
22
|
+
const border = useThemeColor("border");
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<LinkBlocks
|
|
26
|
+
surfaceColor={surface}
|
|
27
|
+
labelColor={text}
|
|
28
|
+
dividerColor={border}
|
|
29
|
+
chevronColor={textSecondary}
|
|
30
|
+
titleColor={textSecondary}
|
|
31
|
+
blocks={[
|
|
32
|
+
{
|
|
33
|
+
key: "settings",
|
|
34
|
+
title: "Settings",
|
|
35
|
+
items: [
|
|
36
|
+
{
|
|
37
|
+
key: "units",
|
|
38
|
+
label: "Units of Measure",
|
|
39
|
+
icon: <Ionicons name="options-outline" size={22} color={text} />,
|
|
40
|
+
onPress: () => router.push("/settings/units"),
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
key: "notifications",
|
|
44
|
+
label: "Notification Settings",
|
|
45
|
+
icon: (
|
|
46
|
+
<Ionicons name="notifications-outline" size={22} color={text} />
|
|
47
|
+
),
|
|
48
|
+
onPress: () => router.push("/settings/notifications"),
|
|
49
|
+
},
|
|
50
|
+
],
|
|
51
|
+
},
|
|
52
|
+
]}
|
|
53
|
+
/>
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
On a grey page background, pass a lighter `surface` (e.g. white). On a white page, choose a contrasting surface — the component does not compute “lighter than background”.
|
|
59
|
+
|
|
60
|
+
### Data shape
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
type LinkBlockItem = {
|
|
64
|
+
key: string;
|
|
65
|
+
label: string;
|
|
66
|
+
onPress: () => void;
|
|
67
|
+
icon?: React.ReactNode;
|
|
68
|
+
disabled?: boolean;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
type LinkBlockGroup = {
|
|
72
|
+
key: string;
|
|
73
|
+
title?: string;
|
|
74
|
+
items: LinkBlockItem[];
|
|
75
|
+
};
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
- `key` on groups and items is for stable React lists; you choose the string.
|
|
79
|
+
- A group renders only when `items` is non-empty after filtering. Empty groups are skipped (no surface, no orphan title).
|
|
80
|
+
- If any item in a group has an `icon`, all rows in that group reserve a leading icon column so labels align.
|
|
81
|
+
|
|
82
|
+
### Props
|
|
83
|
+
|
|
84
|
+
| Prop | Type | Default | Notes |
|
|
85
|
+
|---|---|---|---|
|
|
86
|
+
| `blocks` | `LinkBlockGroup[]` | — | Required |
|
|
87
|
+
| `surfaceColor` | `string` | — | Required. Rounded container fill |
|
|
88
|
+
| `labelColor` | `string` | — | Required. Row label |
|
|
89
|
+
| `dividerColor` | `string` | — | Required. Hairline between rows |
|
|
90
|
+
| `chevronColor` | `string` | — | Required. Trailing chevron |
|
|
91
|
+
| `titleColor` | `string` | — | Section title; falls back to `labelColor` when omitted |
|
|
92
|
+
| `radius` | `ButtonRadius` | `"xl"` | Same steps as `Button` |
|
|
93
|
+
| `style` | view style | — | Outer vertical stack |
|
|
94
|
+
|
|
95
|
+
### Radius
|
|
96
|
+
|
|
97
|
+
Uses the same Tailwind-style steps as `Button` (`buttonRadiusValue`). Default `"xl"` (12px). See the [Button radius table](./button.md#radius).
|
|
98
|
+
|
|
99
|
+
### Disabled rows
|
|
100
|
+
|
|
101
|
+
Set `disabled: true` on an item. The row does not fire `onPress` and uses reduced opacity (same treatment as `Button`).
|
|
102
|
+
|
|
103
|
+
### Accessibility
|
|
104
|
+
|
|
105
|
+
Each row uses `accessibilityRole="button"` and `accessibilityLabel` from the item `label`. The chevron is hidden from accessibility APIs.
|
|
106
|
+
|
|
107
|
+
## Imports
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
import {
|
|
111
|
+
LinkBlocks,
|
|
112
|
+
type LinkBlockGroup,
|
|
113
|
+
type LinkBlockItem,
|
|
114
|
+
type LinkBlocksProps,
|
|
115
|
+
useThemeColor,
|
|
116
|
+
} from "@studio-piot/native-ui";
|
|
117
|
+
|
|
118
|
+
// Or components only:
|
|
119
|
+
import { LinkBlocks } from "@studio-piot/native-ui/components";
|
|
120
|
+
```
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# RadioField — usage
|
|
2
|
+
|
|
3
|
+
Presentational controlled radio group in a LinkBlocks-style rounded card. Each row shows label (and optional description) on the left and a package-drawn radio mark on the right. Colours default from `ThemeProvider`; pass props only to override. Your app owns form state.
|
|
4
|
+
|
|
5
|
+
For the design contract, see [RadioField design](../superpowers/specs/2026-09-25-radio-field-design.md).
|
|
6
|
+
|
|
7
|
+
For `ThemeProvider` and `useThemeColor`, see [theme provider and Button usage](./button.md).
|
|
8
|
+
|
|
9
|
+
## RadioField
|
|
10
|
+
|
|
11
|
+
`RadioField` is controlled only (`value` + `onChange`). When colour props are omitted, colours resolve from fixed theme roles (see below). Explicit colour props always win. `ThemeProvider` is required when at least one colour prop is omitted. Full width of the parent is fixed. An empty `options` array renders nothing.
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
import { RadioField } from "@studio-piot/native-ui";
|
|
15
|
+
import { useState } from "react";
|
|
16
|
+
|
|
17
|
+
type ThemePreference = "automatic" | "light" | "dark";
|
|
18
|
+
|
|
19
|
+
const themeOptions = [
|
|
20
|
+
{ value: "automatic" as const, label: "Automatic" },
|
|
21
|
+
{ value: "light" as const, label: "Light" },
|
|
22
|
+
{ value: "dark" as const, label: "Dark" },
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
export function AppearanceThemePicker() {
|
|
26
|
+
const [theme, setTheme] = useState<ThemePreference>("automatic");
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<RadioField
|
|
30
|
+
label="Appearance"
|
|
31
|
+
value={theme}
|
|
32
|
+
onChange={setTheme}
|
|
33
|
+
options={themeOptions}
|
|
34
|
+
/>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Your `ThemeProvider` colour maps must include the default roles listed below (same keys in light and dark). Apps must add **`fieldRadioSelected`** for the selected radio fill; existing Field `field*` roles are reused.
|
|
40
|
+
|
|
41
|
+
### Colour props (optional overrides)
|
|
42
|
+
|
|
43
|
+
| Prop | Default theme role | Role |
|
|
44
|
+
|---|---|---|
|
|
45
|
+
| `backgroundColor` | `fieldBackground` | Card fill |
|
|
46
|
+
| `labelColor` | `fieldText` | Option label |
|
|
47
|
+
| `descriptionColor` | `fieldDescription` | Option description |
|
|
48
|
+
| `dividerColor` | `fieldBorder` | Row dividers |
|
|
49
|
+
| `radioColor` | `fieldBorder` | Unselected ring |
|
|
50
|
+
| `selectedColor` | `fieldRadioSelected` | Selected radio fill |
|
|
51
|
+
| `titleColor` | `fieldLabel` | Group label above card |
|
|
52
|
+
|
|
53
|
+
### Props
|
|
54
|
+
|
|
55
|
+
| Prop | Type | Default | Notes |
|
|
56
|
+
|---|---|---|---|
|
|
57
|
+
| `value` | `T \| null` | — | Required. Controlled selection; `null` = none selected |
|
|
58
|
+
| `onChange` | `(value: T) => void` | — | Required |
|
|
59
|
+
| `options` | `RadioOption<T>[]` | — | Required. Empty list → renders nothing |
|
|
60
|
+
| `label` | `string` | — | Optional caption above the card |
|
|
61
|
+
| `radius` | `ButtonRadius` | `"xl"` | Same steps as `Button` |
|
|
62
|
+
| `backgroundColor` | `string` | theme `fieldBackground` | Optional override |
|
|
63
|
+
| `labelColor` | `string` | theme `fieldText` | Optional override |
|
|
64
|
+
| `descriptionColor` | `string` | theme `fieldDescription` | Optional override |
|
|
65
|
+
| `dividerColor` | `string` | theme `fieldBorder` | Optional override |
|
|
66
|
+
| `radioColor` | `string` | theme `fieldBorder` | Optional override |
|
|
67
|
+
| `selectedColor` | `string` | theme `fieldRadioSelected` | Optional override |
|
|
68
|
+
| `titleColor` | `string` | theme `fieldLabel` | Optional override |
|
|
69
|
+
| `style` | view style | — | Outer column |
|
|
70
|
+
|
|
71
|
+
### RadioOption
|
|
72
|
+
|
|
73
|
+
| Prop | Type | Notes |
|
|
74
|
+
|---|---|---|
|
|
75
|
+
| `value` | `T` | Stable string id |
|
|
76
|
+
| `label` | `string` | Row title |
|
|
77
|
+
| `description` | `string` | Optional helper under label |
|
|
78
|
+
| `disabled` | `boolean` | Row at 40% opacity; not selectable |
|
|
79
|
+
|
|
80
|
+
### Layout and radius
|
|
81
|
+
|
|
82
|
+
Card uses `buttonRadiusValue` (default `"xl"`, 12px). Rows match LinkBlocks density (`minHeight` 52, horizontal padding 16). See the [Button radius table](./button.md#radius).
|
|
83
|
+
|
|
84
|
+
### Accessibility
|
|
85
|
+
|
|
86
|
+
The card container uses `accessibilityRole="radiogroup"`. Each row is `accessibilityRole="radio"` with `accessibilityState` including `selected`, `checked` (same as selected; required for Android TalkBack radio semantics), and `disabled`. The drawn radio mark is decorative; the row is the hit target.
|
|
87
|
+
|
|
88
|
+
## Imports
|
|
89
|
+
|
|
90
|
+
```ts
|
|
91
|
+
import {
|
|
92
|
+
RadioField,
|
|
93
|
+
RADIO_THEME_ROLES,
|
|
94
|
+
type RadioOption,
|
|
95
|
+
type RadioFieldProps,
|
|
96
|
+
} from "@studio-piot/native-ui";
|
|
97
|
+
|
|
98
|
+
// Or components only:
|
|
99
|
+
import { RadioField } from "@studio-piot/native-ui/components";
|
|
100
|
+
```
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Select — usage
|
|
2
|
+
|
|
3
|
+
Field-like select trigger that opens a native bottom sheet (`@lodev09/react-native-true-sheet`) of options. Selecting an option updates the value and dismisses the sheet. Grabber, dimmed backdrop, and detents are native.
|
|
4
|
+
|
|
5
|
+
For `ThemeProvider`, see [theme provider and Button usage](./button.md). Design: [Select design](../superpowers/specs/2026-09-25-select-design.md).
|
|
6
|
+
|
|
7
|
+
## Peer dependency
|
|
8
|
+
|
|
9
|
+
Install in the app:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npx expo install @lodev09/react-native-true-sheet
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Requires New Architecture (Expo SDK 54+ / TrueSheet 3.7+).
|
|
16
|
+
|
|
17
|
+
## Theme roles
|
|
18
|
+
|
|
19
|
+
Uses Field roles: `fieldBackground`, `fieldText`, `fieldPlaceholder`, `fieldLabel`, `fieldDescription`, `fieldBorder`, `fieldError`, plus `fieldRadioSelected` for the selected check / emphasis.
|
|
20
|
+
|
|
21
|
+
## Basic usage
|
|
22
|
+
|
|
23
|
+
```tsx
|
|
24
|
+
import { Select } from "@studio-piot/native-ui";
|
|
25
|
+
|
|
26
|
+
<Select
|
|
27
|
+
label="Type"
|
|
28
|
+
value={type}
|
|
29
|
+
onChange={setType}
|
|
30
|
+
placeholder="Choose a type"
|
|
31
|
+
options={[
|
|
32
|
+
{ value: "ride", label: "Ride" },
|
|
33
|
+
{ value: "run", label: "Run" },
|
|
34
|
+
{ value: "swim", label: "Swim" },
|
|
35
|
+
]}
|
|
36
|
+
/>
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Glass close (app slot)
|
|
40
|
+
|
|
41
|
+
```tsx
|
|
42
|
+
closeButton={(dismiss) => <FormCloseButton onPress={dismiss} />}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Detents
|
|
46
|
+
|
|
47
|
+
Default `detents={[0.25, 0.5, 0.75]}`, opens at `initialDetent={0.5}`. Override via props.
|
|
48
|
+
|
|
49
|
+
## Imports
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
import {
|
|
53
|
+
Select,
|
|
54
|
+
SelectCloseButton,
|
|
55
|
+
SELECT_THEME_ROLES,
|
|
56
|
+
type SelectProps,
|
|
57
|
+
type SelectOption,
|
|
58
|
+
} from "@studio-piot/native-ui";
|
|
59
|
+
```
|