@cdx-ui/components 0.2.3 → 0.2.4
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/AGENTS.md +24 -0
- package/README.md +2 -0
- package/coverage.svg +1 -1
- package/docs/components/accordion/content.md +538 -0
- package/docs/components/alert/content.md +203 -0
- package/docs/components/alert-dialog/content.md +319 -0
- package/docs/components/avatar/content.md +368 -0
- package/docs/components/avatar/design.md +202 -0
- package/docs/components/badge/content.md +235 -0
- package/docs/components/bottom-sheet/content.md +351 -0
- package/docs/components/bottom-sheet/design.md +249 -0
- package/docs/components/breadcrumbs/content.md +169 -0
- package/docs/components/button/content.md +398 -0
- package/docs/components/button/design.md +258 -0
- package/docs/components/calendar/content.md +137 -0
- package/docs/components/card/content.md +283 -0
- package/docs/components/carousel/content.md +416 -0
- package/docs/components/checkbox/content.md +236 -0
- package/docs/components/checkbox/design.md +214 -0
- package/docs/components/chip/content.md +439 -0
- package/docs/components/chip/design.md +418 -0
- package/docs/components/color-area/content.md +86 -0
- package/docs/components/color-field/content.md +134 -0
- package/docs/components/color-picker/content.md +208 -0
- package/docs/components/color-slider/content.md +120 -0
- package/docs/components/color-swatch/content.md +64 -0
- package/docs/components/color-swatch-picker/content.md +126 -0
- package/docs/components/content.md +97 -0
- package/docs/components/currency-input/content.md +205 -0
- package/docs/components/data-grid/content.md +522 -0
- package/docs/components/date-field/content.md +147 -0
- package/docs/components/date-picker/content.md +360 -0
- package/docs/components/date-range-picker/content.md +301 -0
- package/docs/components/date-time-field/content.md +157 -0
- package/docs/components/date-time-list/content.md +105 -0
- package/docs/components/date-time-picker/content.md +272 -0
- package/docs/components/dialog/content.md +436 -0
- package/docs/components/dialog/design.md +192 -0
- package/docs/components/divider/content.md +112 -0
- package/docs/components/drawer/content.md +436 -0
- package/docs/components/field/content.md +277 -0
- package/docs/components/form/content.md +62 -0
- package/docs/components/full-screen-dialog/content.md +881 -0
- package/docs/components/heading/content.md +55 -0
- package/docs/components/icon/content.md +190 -0
- package/docs/components/icon-button/content.md +200 -0
- package/docs/components/icon-button/design.md +165 -0
- package/docs/components/image/content.md +104 -0
- package/docs/components/input/content.md +281 -0
- package/docs/components/input/design.md +389 -0
- package/docs/components/insurance-disclosure/content.md +103 -0
- package/docs/components/link/content.md +180 -0
- package/docs/components/link/design.md +160 -0
- package/docs/components/list-item/content.md +695 -0
- package/docs/components/menu/content.md +1247 -0
- package/docs/components/month-calendar/content.md +84 -0
- package/docs/components/month-picker/content.md +273 -0
- package/docs/components/otp-input/content.md +327 -0
- package/docs/components/progress-bar/content.md +87 -0
- package/docs/components/progress-circle/content.md +176 -0
- package/docs/components/progress-segmented/content.md +119 -0
- package/docs/components/quick-action-button/content.md +154 -0
- package/docs/components/quick-action-button/design.md +170 -0
- package/docs/components/radio/content.md +224 -0
- package/docs/components/range-calendar/content.md +123 -0
- package/docs/components/reorderable-list/content.md +309 -0
- package/docs/components/scoped-theme/content.md +47 -0
- package/docs/components/scroll-shadow/content.md +686 -0
- package/docs/components/select/content.md +400 -0
- package/docs/components/select/design.md +301 -0
- package/docs/components/skeleton/content.md +103 -0
- package/docs/components/slider/content.md +423 -0
- package/docs/components/stack/content.md +161 -0
- package/docs/components/switch/content.md +150 -0
- package/docs/components/switch/design.md +208 -0
- package/docs/components/table/content.md +548 -0
- package/docs/components/table-pagination/content.md +177 -0
- package/docs/components/tabs/content.md +292 -0
- package/docs/components/text/content.md +142 -0
- package/docs/components/textarea/content.md +222 -0
- package/docs/components/tile/content.md +703 -0
- package/docs/components/toast/content.md +456 -0
- package/docs/components/toggle-button/content.md +505 -0
- package/docs/components/tooltip/content.md +350 -0
- package/docs/components/virtualized-list/content.md +250 -0
- package/docs/foundations/content.md +9 -0
- package/docs/foundations/expressions/icons/content.md +19 -0
- package/docs/foundations/styles/tokens/all-tokens/content.md +192 -0
- package/docs/foundations/styles/tokens/what-are-tokens/content.md +33 -0
- package/docs/foundations/styles/tokens/what-are-tokens/design.md +33 -0
- package/docs/getting-started/content.md +4 -0
- package/docs/getting-started/designers/content.md +13 -0
- package/docs/getting-started/designers/design.md +13 -0
- package/docs/getting-started/developers/accessibility/content.md +31 -0
- package/docs/getting-started/developers/content.md +612 -0
- package/docs/getting-started/developers/styling/content.md +193 -0
- package/docs/llms.txt +193 -0
- package/docs/patterns/account-management/content.md +75 -0
- package/docs/patterns/authentication/content.md +52 -0
- package/docs/patterns/content.md +10 -0
- package/docs/patterns/dashboard/content.md +39 -0
- package/docs/patterns/empty-states/content.md +31 -0
- package/docs/patterns/forms/content.md +109 -0
- package/docs/patterns/onboarding/content.md +69 -0
- package/docs/patterns/payments/content.md +35 -0
- package/docs/patterns/settings/content.md +72 -0
- package/docs/resources/content.md +9 -0
- package/docs/resources/figma-libraries/content.md +42 -0
- package/docs/resources/release-notes/0.0.1-beta.118/content.md +27 -0
- package/docs/resources/release-notes/0.0.1-beta.122/content.md +21 -0
- package/docs/resources/release-notes/0.0.1-beta.124/content.md +16 -0
- package/docs/resources/release-notes/0.0.1-beta.126/content.md +10 -0
- package/docs/resources/release-notes/0.0.1-beta.127/content.md +14 -0
- package/docs/resources/release-notes/0.0.1-beta.128/content.md +9 -0
- package/docs/resources/release-notes/0.0.1-beta.129/content.md +18 -0
- package/docs/resources/release-notes/0.0.1-beta.130/content.md +18 -0
- package/docs/resources/release-notes/0.0.1-beta.133/content.md +7 -0
- package/docs/resources/release-notes/0.0.1-beta.134/content.md +17 -0
- package/docs/resources/release-notes/0.0.1-beta.136/content.md +7 -0
- package/docs/resources/release-notes/0.0.1-beta.137/content.md +10 -0
- package/docs/resources/release-notes/0.0.1-beta.138/content.md +7 -0
- package/docs/resources/release-notes/0.0.1-beta.139/content.md +13 -0
- package/docs/resources/release-notes/0.0.1-beta.140/content.md +26 -0
- package/docs/resources/release-notes/0.0.1-beta.141/content.md +63 -0
- package/docs/resources/release-notes/0.0.1-beta.142/content.md +7 -0
- package/docs/resources/release-notes/0.0.1-beta.143/content.md +12 -0
- package/docs/resources/release-notes/0.0.1-beta.144/content.md +7 -0
- package/docs/resources/release-notes/0.0.1-beta.145/content.md +7 -0
- package/docs/resources/release-notes/0.0.1-beta.147/content.md +10 -0
- package/docs/resources/release-notes/0.0.1-beta.149/content.md +10 -0
- package/docs/resources/release-notes/0.0.1-beta.150/content.md +13 -0
- package/docs/resources/release-notes/0.0.1-beta.152/content.md +7 -0
- package/docs/resources/release-notes/0.0.1-beta.154/content.md +15 -0
- package/docs/resources/release-notes/0.0.1-beta.156/content.md +7 -0
- package/docs/resources/release-notes/0.0.1-beta.158/content.md +9 -0
- package/docs/resources/release-notes/0.0.1-beta.159/content.md +11 -0
- package/docs/resources/release-notes/0.0.1-beta.160/content.md +7 -0
- package/docs/resources/release-notes/0.0.1-beta.161/content.md +7 -0
- package/docs/resources/release-notes/0.0.1-beta.163/content.md +7 -0
- package/docs/resources/release-notes/0.0.1-beta.164/content.md +9 -0
- package/docs/resources/release-notes/0.0.1-beta.166/content.md +17 -0
- package/docs/resources/release-notes/0.0.1-beta.167/content.md +7 -0
- package/docs/resources/release-notes/0.0.1-beta.168/content.md +7 -0
- package/docs/resources/release-notes/0.0.1-beta.169/content.md +7 -0
- package/docs/resources/release-notes/0.0.1-beta.171/content.md +16 -0
- package/docs/resources/release-notes/0.0.1-beta.172/content.md +24 -0
- package/docs/resources/release-notes/0.0.1-beta.173/content.md +15 -0
- package/docs/resources/release-notes/0.0.1-beta.174/content.md +7 -0
- package/docs/resources/release-notes/0.0.1-beta.176/content.md +32 -0
- package/docs/resources/release-notes/0.0.1-beta.179/content.md +11 -0
- package/docs/resources/release-notes/0.0.1-beta.182/content.md +13 -0
- package/docs/resources/release-notes/0.0.1-beta.185/content.md +7 -0
- package/docs/resources/release-notes/0.0.1-beta.187/content.md +27 -0
- package/docs/resources/release-notes/0.1.1/content.md +23 -0
- package/docs/resources/release-notes/0.1.2/content.md +7 -0
- package/docs/resources/release-notes/0.1.4/content.md +19 -0
- package/docs/resources/release-notes/0.1.5/content.md +7 -0
- package/docs/resources/release-notes/0.1.6/content.md +7 -0
- package/docs/resources/release-notes/0.2.0/content.md +31 -0
- package/docs/resources/release-notes/0.2.1/content.md +17 -0
- package/docs/resources/release-notes/0.2.2/content.md +11 -0
- package/docs/resources/release-notes/0.2.3/content.md +15 -0
- package/docs/resources/release-notes/content.md +64 -0
- package/lib/commonjs/components/DateTimeList/index.js +11 -26
- package/lib/commonjs/components/DateTimeList/index.js.map +1 -1
- package/lib/commonjs/components/DateTimeList/styles.js +2 -12
- package/lib/commonjs/components/DateTimeList/styles.js.map +1 -1
- package/lib/commonjs/components/ListItem/index.js.map +1 -1
- package/lib/commonjs/components/ListItem/styles.js +2 -1
- package/lib/commonjs/components/ListItem/styles.js.map +1 -1
- package/lib/commonjs/components/Menu/index.js +11 -5
- package/lib/commonjs/components/Menu/index.js.map +1 -1
- package/lib/commonjs/components/ScrollShadow/gradient.js +29 -0
- package/lib/commonjs/components/ScrollShadow/gradient.js.map +1 -0
- package/lib/commonjs/components/ScrollShadow/index.js +171 -44
- package/lib/commonjs/components/ScrollShadow/index.js.map +1 -1
- package/lib/commonjs/components/ScrollShadow/styles.js +58 -14
- package/lib/commonjs/components/ScrollShadow/styles.js.map +1 -1
- package/lib/commonjs/components/ScrollShadow/useComposedScrollHandler.js +63 -0
- package/lib/commonjs/components/ScrollShadow/useComposedScrollHandler.js.map +1 -0
- package/lib/commonjs/components/ScrollShadow/useScrollShadow.js +48 -8
- package/lib/commonjs/components/ScrollShadow/useScrollShadow.js.map +1 -1
- package/lib/commonjs/components/Tabs/index.js +16 -23
- package/lib/commonjs/components/Tabs/index.js.map +1 -1
- package/lib/commonjs/components/Tabs/styles.js +1 -10
- package/lib/commonjs/components/Tabs/styles.js.map +1 -1
- package/lib/commonjs/figma/ListItem.SectionHeader.figma.js +20 -5
- package/lib/commonjs/figma/ListItem.SectionHeader.figma.js.map +1 -1
- package/lib/module/components/DateTimeList/index.js +12 -27
- package/lib/module/components/DateTimeList/index.js.map +1 -1
- package/lib/module/components/DateTimeList/styles.js +1 -11
- package/lib/module/components/DateTimeList/styles.js.map +1 -1
- package/lib/module/components/ListItem/index.js.map +1 -1
- package/lib/module/components/ListItem/styles.js +2 -1
- package/lib/module/components/ListItem/styles.js.map +1 -1
- package/lib/module/components/Menu/index.js +11 -5
- package/lib/module/components/Menu/index.js.map +1 -1
- package/lib/module/components/ScrollShadow/gradient.js +24 -0
- package/lib/module/components/ScrollShadow/gradient.js.map +1 -0
- package/lib/module/components/ScrollShadow/index.js +170 -48
- package/lib/module/components/ScrollShadow/index.js.map +1 -1
- package/lib/module/components/ScrollShadow/styles.js +57 -13
- package/lib/module/components/ScrollShadow/styles.js.map +1 -1
- package/lib/module/components/ScrollShadow/useComposedScrollHandler.js +57 -0
- package/lib/module/components/ScrollShadow/useComposedScrollHandler.js.map +1 -0
- package/lib/module/components/ScrollShadow/useScrollShadow.js +46 -8
- package/lib/module/components/ScrollShadow/useScrollShadow.js.map +1 -1
- package/lib/module/components/Tabs/index.js +17 -24
- package/lib/module/components/Tabs/index.js.map +1 -1
- package/lib/module/components/Tabs/styles.js +0 -9
- package/lib/module/components/Tabs/styles.js.map +1 -1
- package/lib/module/figma/ListItem.SectionHeader.figma.js +20 -5
- package/lib/module/figma/ListItem.SectionHeader.figma.js.map +1 -1
- package/lib/typescript/components/DateTimeList/index.d.ts.map +1 -1
- package/lib/typescript/components/DateTimeList/styles.d.ts +1 -4
- package/lib/typescript/components/DateTimeList/styles.d.ts.map +1 -1
- package/lib/typescript/components/ListItem/index.d.ts +5 -4
- package/lib/typescript/components/ListItem/index.d.ts.map +1 -1
- package/lib/typescript/components/ListItem/styles.d.ts +1 -1
- package/lib/typescript/components/ListItem/styles.d.ts.map +1 -1
- package/lib/typescript/components/Menu/index.d.ts.map +1 -1
- package/lib/typescript/components/ScrollShadow/gradient.d.ts +7 -0
- package/lib/typescript/components/ScrollShadow/gradient.d.ts.map +1 -0
- package/lib/typescript/components/ScrollShadow/index.d.ts +57 -15
- package/lib/typescript/components/ScrollShadow/index.d.ts.map +1 -1
- package/lib/typescript/components/ScrollShadow/styles.d.ts +8 -2
- package/lib/typescript/components/ScrollShadow/styles.d.ts.map +1 -1
- package/lib/typescript/components/ScrollShadow/useComposedScrollHandler.d.ts +13 -0
- package/lib/typescript/components/ScrollShadow/useComposedScrollHandler.d.ts.map +1 -0
- package/lib/typescript/components/ScrollShadow/useScrollShadow.d.ts +15 -2
- package/lib/typescript/components/ScrollShadow/useScrollShadow.d.ts.map +1 -1
- package/lib/typescript/components/Tabs/index.d.ts.map +1 -1
- package/lib/typescript/components/Tabs/styles.d.ts +0 -7
- package/lib/typescript/components/Tabs/styles.d.ts.map +1 -1
- package/lib/typescript/figma/ListItem.SectionHeader.figma.d.ts.map +1 -1
- package/package.json +8 -6
- package/src/components/DateTimeList/index.tsx +13 -26
- package/src/components/DateTimeList/styles.ts +1 -10
- package/src/components/ListItem/index.tsx +5 -4
- package/src/components/ListItem/styles.ts +1 -0
- package/src/components/Menu/index.tsx +11 -8
- package/src/components/ScrollShadow/gradient.ts +48 -0
- package/src/components/ScrollShadow/index.tsx +300 -69
- package/src/components/ScrollShadow/styles.ts +45 -28
- package/src/components/ScrollShadow/useComposedScrollHandler.ts +58 -0
- package/src/components/ScrollShadow/useScrollShadow.ts +72 -10
- package/src/components/Tabs/index.tsx +25 -35
- package/src/components/Tabs/styles.ts +0 -9
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
# Field (https://design.candescent.com/docs/components/field)
|
|
2
|
+
|
|
3
|
+
Resources: [Storybook](https://victorious-wave-0daadf00f.6.azurestaticapps.net/?path=/story/inputs-field--default)
|
|
4
|
+
|
|
5
|
+
A compound component that supplies accessible labels, descriptions, validation messages, and state to a nested form control.
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { Field } from '@cdx-ui/components';
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Wrap related fields in `Form`, then place the interactive control between `Field.Label` and the supporting or validation text. Compatible controls such as `Input` consume the field state and accessibility context automatically.
|
|
12
|
+
|
|
13
|
+
## Basic
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
import { Field, Form, Input } from '@cdx-ui/components';
|
|
17
|
+
|
|
18
|
+
export default function FieldBasicDemo() {
|
|
19
|
+
return (
|
|
20
|
+
<Form className="w-full max-w-sm">
|
|
21
|
+
<Field name="email" isRequired>
|
|
22
|
+
<Field.Label>Email address</Field.Label>
|
|
23
|
+
<Input>
|
|
24
|
+
<Input.Field
|
|
25
|
+
autoCapitalize="none"
|
|
26
|
+
autoComplete="email"
|
|
27
|
+
keyboardType="email-address"
|
|
28
|
+
placeholder="you@example.com"
|
|
29
|
+
/>
|
|
30
|
+
</Input>
|
|
31
|
+
<Field.Helper>We will only use this for account notifications.</Field.Helper>
|
|
32
|
+
</Field>
|
|
33
|
+
</Form>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## States
|
|
39
|
+
|
|
40
|
+
Set `isRequired`, `isInvalid`, `isDisabled`, or `isReadOnly` on `Field`. Nested Forge UI controls inherit those states through context. `Field.Helper` is hidden while the field is invalid, and `Field.Error` is only rendered when the field is invalid and has a message.
|
|
41
|
+
|
|
42
|
+
```tsx
|
|
43
|
+
import { Field, Form, Input, Text, VStack } from '@cdx-ui/components';
|
|
44
|
+
|
|
45
|
+
export default function FieldStatesDemo() {
|
|
46
|
+
return (
|
|
47
|
+
<Form className="w-full max-w-sm">
|
|
48
|
+
<VStack space="lg">
|
|
49
|
+
<Field name="required-example" isRequired>
|
|
50
|
+
<Field.Label>Required</Field.Label>
|
|
51
|
+
<Input>
|
|
52
|
+
<Input.Field placeholder="Required value" />
|
|
53
|
+
</Input>
|
|
54
|
+
<Field.Helper>The required indicator is announced with the label.</Field.Helper>
|
|
55
|
+
</Field>
|
|
56
|
+
|
|
57
|
+
<Field name="disabled-example" isDisabled>
|
|
58
|
+
<Field.Label>Disabled</Field.Label>
|
|
59
|
+
<Input>
|
|
60
|
+
<Input.Field defaultValue="Unavailable value" />
|
|
61
|
+
</Input>
|
|
62
|
+
<Field.Helper>This control cannot be edited.</Field.Helper>
|
|
63
|
+
</Field>
|
|
64
|
+
|
|
65
|
+
<Field name="read-only-example" isReadOnly>
|
|
66
|
+
<Field.Label>Read-only</Field.Label>
|
|
67
|
+
<Input>
|
|
68
|
+
<Input.Field defaultValue="Account ending in 1234" />
|
|
69
|
+
</Input>
|
|
70
|
+
<Field.Helper>This value can be selected but not changed.</Field.Helper>
|
|
71
|
+
</Field>
|
|
72
|
+
|
|
73
|
+
<Field name="invalid-example" isInvalid isRequired>
|
|
74
|
+
<Field.Label>Invalid</Field.Label>
|
|
75
|
+
<Input>
|
|
76
|
+
<Input.Field defaultValue="not-an-email" keyboardType="email-address" />
|
|
77
|
+
</Input>
|
|
78
|
+
<Field.Helper>This helper is hidden while the field is invalid.</Field.Helper>
|
|
79
|
+
<Field.Error>Enter a valid email address.</Field.Error>
|
|
80
|
+
</Field>
|
|
81
|
+
|
|
82
|
+
<Text size="sm" color="secondary">
|
|
83
|
+
Field state is shared with each nested control through context.
|
|
84
|
+
</Text>
|
|
85
|
+
</VStack>
|
|
86
|
+
</Form>
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## With other form controls
|
|
92
|
+
|
|
93
|
+
Explore how other Forge UI form controls compose with `Field` for labels, supporting text, and validation state.
|
|
94
|
+
|
|
95
|
+
## React Hook Form
|
|
96
|
+
|
|
97
|
+
Use `Controller` to connect controlled Forge UI inputs to React Hook Form. Pass `fieldState.invalid` to `Field`, render the validation message with `Field.Error`, and forward the controller handlers and ref to the nested input.
|
|
98
|
+
|
|
99
|
+
```tsx
|
|
100
|
+
import { useState } from 'react';
|
|
101
|
+
import { Controller, useForm } from 'react-hook-form';
|
|
102
|
+
import { Button, Field, Form, Input, Text, VStack } from '@cdx-ui/components';
|
|
103
|
+
|
|
104
|
+
interface SignInValues {
|
|
105
|
+
email: string;
|
|
106
|
+
password: string;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export default function FieldReactHookFormDemo() {
|
|
110
|
+
const [submitted, setSubmitted] = useState<SignInValues | null>(null);
|
|
111
|
+
const {
|
|
112
|
+
control,
|
|
113
|
+
handleSubmit,
|
|
114
|
+
formState: { errors },
|
|
115
|
+
} = useForm<SignInValues>({
|
|
116
|
+
defaultValues: {
|
|
117
|
+
email: '',
|
|
118
|
+
password: '',
|
|
119
|
+
},
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
const submit = handleSubmit(
|
|
123
|
+
(values) => setSubmitted(values),
|
|
124
|
+
() => setSubmitted(null),
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
return (
|
|
128
|
+
<Form
|
|
129
|
+
className="w-full max-w-sm"
|
|
130
|
+
onSubmit={(event) => {
|
|
131
|
+
event.preventDefault();
|
|
132
|
+
void submit();
|
|
133
|
+
}}
|
|
134
|
+
>
|
|
135
|
+
<VStack space="lg">
|
|
136
|
+
<Controller
|
|
137
|
+
control={control}
|
|
138
|
+
name="email"
|
|
139
|
+
rules={{
|
|
140
|
+
required: 'Email address is required.',
|
|
141
|
+
pattern: {
|
|
142
|
+
value: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
|
|
143
|
+
message: 'Enter a valid email address.',
|
|
144
|
+
},
|
|
145
|
+
}}
|
|
146
|
+
render={({ field: { onBlur, onChange, ref, value }, fieldState: { invalid } }) => (
|
|
147
|
+
<Field name="email" isInvalid={invalid} isRequired>
|
|
148
|
+
<Field.Label>Email address</Field.Label>
|
|
149
|
+
<Input>
|
|
150
|
+
<Input.Field
|
|
151
|
+
ref={ref}
|
|
152
|
+
autoCapitalize="none"
|
|
153
|
+
autoComplete="email"
|
|
154
|
+
keyboardType="email-address"
|
|
155
|
+
placeholder="you@example.com"
|
|
156
|
+
value={value}
|
|
157
|
+
onBlur={onBlur}
|
|
158
|
+
onChangeText={onChange}
|
|
159
|
+
/>
|
|
160
|
+
</Input>
|
|
161
|
+
<Field.Helper>Use the email associated with your account.</Field.Helper>
|
|
162
|
+
<Field.Error>{errors.email?.message}</Field.Error>
|
|
163
|
+
</Field>
|
|
164
|
+
)}
|
|
165
|
+
/>
|
|
166
|
+
|
|
167
|
+
<Controller
|
|
168
|
+
control={control}
|
|
169
|
+
name="password"
|
|
170
|
+
rules={{
|
|
171
|
+
required: 'Password is required.',
|
|
172
|
+
minLength: {
|
|
173
|
+
value: 8,
|
|
174
|
+
message: 'Password must contain at least 8 characters.',
|
|
175
|
+
},
|
|
176
|
+
}}
|
|
177
|
+
render={({ field: { onBlur, onChange, ref, value }, fieldState: { invalid } }) => (
|
|
178
|
+
<Field name="password" isInvalid={invalid} isRequired>
|
|
179
|
+
<Field.Label>Password</Field.Label>
|
|
180
|
+
<Input>
|
|
181
|
+
<Input.Field
|
|
182
|
+
ref={ref}
|
|
183
|
+
autoComplete="current-password"
|
|
184
|
+
placeholder="At least 8 characters"
|
|
185
|
+
secureTextEntry
|
|
186
|
+
value={value}
|
|
187
|
+
onBlur={onBlur}
|
|
188
|
+
onChangeText={onChange}
|
|
189
|
+
/>
|
|
190
|
+
</Input>
|
|
191
|
+
<Field.Error>{errors.password?.message}</Field.Error>
|
|
192
|
+
</Field>
|
|
193
|
+
)}
|
|
194
|
+
/>
|
|
195
|
+
|
|
196
|
+
<Button fullWidth onPress={() => void submit()}>
|
|
197
|
+
<Button.Label>Sign in</Button.Label>
|
|
198
|
+
</Button>
|
|
199
|
+
|
|
200
|
+
{submitted ? (
|
|
201
|
+
<Text accessibilityRole="alert" color="success">
|
|
202
|
+
Form submitted for {submitted.email}.
|
|
203
|
+
</Text>
|
|
204
|
+
) : null}
|
|
205
|
+
</VStack>
|
|
206
|
+
</Form>
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
## API
|
|
212
|
+
|
|
213
|
+
### Field
|
|
214
|
+
|
|
215
|
+
Provides field-level state and generated IDs to its label, control, helper text, and error message. The `name` is passed through context to compatible controls for form submission.
|
|
216
|
+
|
|
217
|
+
| Prop | Type | Default | Description |
|
|
218
|
+
| --- | --- | --- | --- |
|
|
219
|
+
| `className?` | `string` | — | — |
|
|
220
|
+
| `children?` | `ReactNode` | — | — |
|
|
221
|
+
| `asChild?` | `boolean` | — | — |
|
|
222
|
+
| `name?` | `string` | — | Field name for form submission. Passed through context so child inputs automatically receive it as their `name` attribute for `FormData` support. |
|
|
223
|
+
| `isInvalid?` | `boolean` | — | If true, the field is marked as invalid. Controls error visibility. |
|
|
224
|
+
| `isRequired?` | `boolean` | — | If true, the field is marked as required. Shows asterisk on label. |
|
|
225
|
+
| `isDisabled?` | `boolean` | — | If true, the field is disabled. Nested controls that read field context are disabled as well. |
|
|
226
|
+
| `isReadOnly?` | `boolean` | — | If true, the field is read-only. |
|
|
227
|
+
|
|
228
|
+
Inherits all [`ViewProps`](https://reactnative.dev/docs/view#props). `Field` can be used on its own for a single control or inside `Form` for form-level semantics.
|
|
229
|
+
|
|
230
|
+
## Sub-components
|
|
231
|
+
|
|
232
|
+
### Field.Label
|
|
233
|
+
|
|
234
|
+
Labels the nested control. Required fields receive a visible required indicator. On web, the label is associated through `htmlFor`; on native, pressing the label focuses the control registered with the field.
|
|
235
|
+
|
|
236
|
+
| Prop | Type | Default | Description |
|
|
237
|
+
| --- | --- | --- | --- |
|
|
238
|
+
| `className?` | `string` | — | — |
|
|
239
|
+
| `children?` | `ReactNode` | — | — |
|
|
240
|
+
| `asChild?` | `boolean` | — | — |
|
|
241
|
+
| `htmlFor?` | `string` | — | Associates the label with a control `id` on web (`<label htmlFor>`). If omitted, defaults to the same id `Input.Field` gets from form context: `fieldId + "-input"`. |
|
|
242
|
+
| `requiredIndicator?` | `ReactNode` | — | Element to render after the label text when the field is required. Rendered by the primitive (which owns the form-control context) so the styled layer never needs to read `FormControlContext` across packages. |
|
|
243
|
+
|
|
244
|
+
Inherits all [`TextProps`](https://reactnative.dev/docs/text#props).
|
|
245
|
+
|
|
246
|
+
### Field.Helper
|
|
247
|
+
|
|
248
|
+
Displays supporting text and connects it to the nested control's accessible description. It does not render while `Field` is invalid, allowing the validation message to replace it.
|
|
249
|
+
|
|
250
|
+
| Prop | Type | Default | Description |
|
|
251
|
+
| --- | --- | --- | --- |
|
|
252
|
+
| `className?` | `string` | — | — |
|
|
253
|
+
| `children?` | `ReactNode` | — | — |
|
|
254
|
+
| `asChild?` | `boolean` | — | — |
|
|
255
|
+
|
|
256
|
+
Inherits all [`ViewProps`](https://reactnative.dev/docs/view#props).
|
|
257
|
+
|
|
258
|
+
### Field.Error
|
|
259
|
+
|
|
260
|
+
Displays a validation icon and message when `Field` is invalid. The error is exposed as an assertive live-region alert and connected to the nested control's accessible description.
|
|
261
|
+
|
|
262
|
+
Controls that validate themselves, such as `DatePicker` with `minValue`, `maxValue`, or `isDateUnavailable`, mark the whole `Field` invalid and supply a message; a `Field.Error` with no children renders that message.
|
|
263
|
+
|
|
264
|
+
| Prop | Type | Default | Description |
|
|
265
|
+
| --- | --- | --- | --- |
|
|
266
|
+
| `className?` | `string` | — | — |
|
|
267
|
+
| `children?` | `ReactNode` | — | Error message. When omitted (or `null`), falls back to the message the field's control reports — for example `DatePicker`'s localized `minValue` / `maxValue` / `isDateUnavailable` message. |
|
|
268
|
+
| `asChild?` | `boolean` | — | — |
|
|
269
|
+
|
|
270
|
+
Inherits all [`ViewProps`](https://reactnative.dev/docs/view#props).
|
|
271
|
+
|
|
272
|
+
## Accessibility
|
|
273
|
+
|
|
274
|
+
- Use one `Field.Label` for each field so the control has an accessible name.
|
|
275
|
+
- Put instructions in `Field.Helper` and validation feedback in `Field.Error`; Field manages the generated IDs and description relationships.
|
|
276
|
+
- Set `isRequired` and `isInvalid` from application or form-library state. Field communicates those states but does not run validation itself.
|
|
277
|
+
- Put `autoFocus` on the nested control, not on `Field`.
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Form (https://design.candescent.com/docs/components/form)
|
|
2
|
+
|
|
3
|
+
`Form` groups fields with consistent vertical spacing. Labels, helper text, and validation belong on [`Field`](https://design.candescent.com/docs/components/field/content.md).
|
|
4
|
+
|
|
5
|
+
```tsx
|
|
6
|
+
import { Form } from '@cdx-ui/components';
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## Basic
|
|
10
|
+
|
|
11
|
+
Place fields and a submit button inside `Form`.
|
|
12
|
+
|
|
13
|
+
On web, `Form` renders a `<form>`. Handle submission with `onSubmit`, or call `requestSubmit()` on a ref to the form from a button press. On iOS and Android, `onSubmit`, `action`, and `method` are ignored; validate and submit from the button's press handler.
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
import { Button, Field, Form, Input } from '@cdx-ui/components';
|
|
17
|
+
|
|
18
|
+
export default function FormBasicDemo() {
|
|
19
|
+
return (
|
|
20
|
+
<Form className="w-full max-w-sm">
|
|
21
|
+
<Field name="email" isRequired>
|
|
22
|
+
<Field.Label>Email</Field.Label>
|
|
23
|
+
<Input>
|
|
24
|
+
<Input.Field
|
|
25
|
+
autoCapitalize="none"
|
|
26
|
+
autoComplete="email"
|
|
27
|
+
keyboardType="email-address"
|
|
28
|
+
placeholder="you@example.com"
|
|
29
|
+
/>
|
|
30
|
+
</Input>
|
|
31
|
+
<Field.Helper>We will never share your email.</Field.Helper>
|
|
32
|
+
</Field>
|
|
33
|
+
<Button>
|
|
34
|
+
<Button.Label>Submit</Button.Label>
|
|
35
|
+
</Button>
|
|
36
|
+
</Form>
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## API
|
|
42
|
+
|
|
43
|
+
### Form
|
|
44
|
+
|
|
45
|
+
| Prop | Type | Default | Description |
|
|
46
|
+
| --- | --- | --- | --- |
|
|
47
|
+
| `className?` | `string` | — | — |
|
|
48
|
+
| `children?` | `React.ReactNode` | — | — |
|
|
49
|
+
| `asChild?` | `boolean` | — | — |
|
|
50
|
+
| `onSubmit?` | `((e: React.SyntheticEvent) => void)` | — | Called when the form is submitted. Web only; ignored on native. |
|
|
51
|
+
| `action?` | `string \| ((formData: FormData) => void \| Promise<void>)` | — | HTML `action` URL, or a function that receives the submitted `FormData`. Web only; ignored on native. |
|
|
52
|
+
| `method?` | `string` | — | HTTP method. Web only; ignored on native. |
|
|
53
|
+
|
|
54
|
+
Inherits all [`ViewProps`](https://reactnative.dev/docs/view#props). On web the root is a `<form>` element. On native it is a `View`, and `onSubmit`, `action`, and `method` are ignored.
|
|
55
|
+
|
|
56
|
+
| Attribute | Values | Description |
|
|
57
|
+
| ----------- | ------ | ------------ |
|
|
58
|
+
| `data-slot` | `form` | Root element |
|
|
59
|
+
|
|
60
|
+
## Accessibility
|
|
61
|
+
|
|
62
|
+
On web, the `<form>` element is exposed as a form landmark. On iOS and Android, `Form` renders a `View` with no role. Accessible names, descriptions, and validation messages come from `Field`.
|