@a-type/ui 6.0.22 → 6.0.24
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/dist/components/forms/CheckboxField.d.ts +3 -1
- package/dist/components/forms/CheckboxField.js +5 -6
- package/dist/components/forms/CheckboxField.js.map +1 -1
- package/dist/components/forms/EmojiField.d.ts +3 -1
- package/dist/components/forms/EmojiField.js +4 -5
- package/dist/components/forms/EmojiField.js.map +1 -1
- package/dist/components/forms/Field.d.ts +21 -0
- package/dist/components/forms/Field.js +71 -0
- package/dist/components/forms/Field.js.map +1 -0
- package/dist/components/forms/Field.module.css +62 -0
- package/dist/components/forms/Form.module.css +1 -1
- package/dist/components/forms/FormikForm.d.ts +2 -2
- package/dist/components/forms/FormikForm.stories.d.ts +2 -2
- package/dist/components/forms/FormikForm.stories.js +1 -1
- package/dist/components/forms/FormikForm.stories.js.map +1 -1
- package/dist/components/forms/NumberStepperField.d.ts +3 -1
- package/dist/components/forms/NumberStepperField.js +6 -9
- package/dist/components/forms/NumberStepperField.js.map +1 -1
- package/dist/components/forms/SwitchField.d.ts +3 -1
- package/dist/components/forms/SwitchField.js +5 -6
- package/dist/components/forms/SwitchField.js.map +1 -1
- package/dist/components/forms/TextField.d.ts +7 -5
- package/dist/components/forms/TextField.js +5 -7
- package/dist/components/forms/TextField.js.map +1 -1
- package/dist/components/forms/ToggleGroupField.d.ts +3 -1
- package/dist/components/forms/ToggleGroupField.js +12 -11
- package/dist/components/forms/ToggleGroupField.js.map +1 -1
- package/dist/components/forms/index.d.ts +1 -1
- package/dist/components/forms/index.js +1 -1
- package/dist/components/forms/index.js.map +1 -1
- package/dist/components/switch/Switch.module.css +1 -1
- package/package.json +1 -1
- package/src/components/forms/CheckboxField.tsx +19 -15
- package/src/components/forms/EmojiField.tsx +15 -9
- package/src/components/forms/Field.module.css +62 -0
- package/src/components/forms/Field.tsx +125 -0
- package/src/components/forms/Form.module.css +1 -1
- package/src/components/forms/FormikForm.stories.tsx +25 -5
- package/src/components/forms/NumberStepperField.tsx +19 -16
- package/src/components/forms/SwitchField.tsx +19 -15
- package/src/components/forms/TextField.tsx +37 -25
- package/src/components/forms/ToggleGroupField.tsx +27 -19
- package/src/components/forms/index.tsx +1 -1
- package/src/components/switch/Switch.module.css +1 -1
- package/dist/components/forms/FieldLabel.d.ts +0 -2
- package/dist/components/forms/FieldLabel.js +0 -5
- package/dist/components/forms/FieldLabel.js.map +0 -1
- package/dist/components/forms/FieldLabel.module.css +0 -20
- package/src/components/forms/FieldLabel.module.css +0 -20
- package/src/components/forms/FieldLabel.tsx +0 -9
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/components/forms/index.tsx"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC;AACnC,cAAc,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/components/forms/index.tsx"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC;AACnC,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,iBAAiB,CAAC;AAChC,cAAc,YAAY,CAAC;AAC3B,cAAc,yBAAyB,CAAC;AACxC,cAAc,mBAAmB,CAAC;AAClC,cAAc,gBAAgB,CAAC"}
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
--mx-shadow-value: inset var(--m-shadow-sm);
|
|
16
16
|
|
|
17
17
|
@apply --mx-bg(var(--m-control-bg));
|
|
18
|
-
@apply --mx-borderColor(var(--m-control-
|
|
18
|
+
@apply --mx-borderColor(var(--m-control-borderColor));
|
|
19
19
|
border-radius: var(--m-radius-lg);
|
|
20
20
|
|
|
21
21
|
&[data-checked] {
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { useField } from 'formik';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
2
3
|
import { useIdOrGenerated } from '../../hooks/useIdOrGenerated.js';
|
|
3
|
-
import { Box } from '../box/Box.js';
|
|
4
4
|
import { Checkbox } from '../checkbox/index.js';
|
|
5
|
-
import {
|
|
5
|
+
import { Field } from './Field.js';
|
|
6
6
|
|
|
7
7
|
export interface CheckboxFieldProps {
|
|
8
8
|
name: string;
|
|
@@ -10,6 +10,7 @@ export interface CheckboxFieldProps {
|
|
|
10
10
|
required?: boolean;
|
|
11
11
|
className?: string;
|
|
12
12
|
id?: string;
|
|
13
|
+
description?: ReactNode;
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
export function CheckboxField({
|
|
@@ -18,24 +19,27 @@ export function CheckboxField({
|
|
|
18
19
|
className,
|
|
19
20
|
required,
|
|
20
21
|
id: providedId,
|
|
22
|
+
description,
|
|
21
23
|
...rest
|
|
22
24
|
}: CheckboxFieldProps) {
|
|
23
25
|
const [props, _, tools] = useField({ name, required, type: 'checkbox' });
|
|
24
26
|
const id = useIdOrGenerated(providedId);
|
|
25
27
|
return (
|
|
26
|
-
<
|
|
27
|
-
<
|
|
28
|
-
{
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
28
|
+
<Field horizontal className={className} id={id}>
|
|
29
|
+
<Field.Control
|
|
30
|
+
render={
|
|
31
|
+
<Checkbox
|
|
32
|
+
{...props}
|
|
33
|
+
checked={props.checked}
|
|
34
|
+
onCheckedChange={(v) => {
|
|
35
|
+
tools.setValue(!!v);
|
|
36
|
+
}}
|
|
37
|
+
{...rest}
|
|
38
|
+
/>
|
|
39
|
+
}
|
|
35
40
|
/>
|
|
36
|
-
{label &&
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
</Box>
|
|
41
|
+
{label && <Field.Label>{label}</Field.Label>}
|
|
42
|
+
{description && <Field.Description>{description}</Field.Description>}
|
|
43
|
+
</Field>
|
|
40
44
|
);
|
|
41
45
|
}
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import { useField } from 'formik';
|
|
2
|
-
import { useState } from 'react';
|
|
2
|
+
import { ReactNode, useState } from 'react';
|
|
3
3
|
import { useIdOrGenerated } from '../../hooks/useIdOrGenerated.js';
|
|
4
|
-
import { Box } from '../box/Box.js';
|
|
5
4
|
import { Button, ButtonProps } from '../button/Button.js';
|
|
6
5
|
import { EmojiPicker } from '../emojiPicker/EmojiPicker.js';
|
|
7
6
|
import { Icon } from '../icon/Icon.js';
|
|
8
7
|
import { Popover } from '../popover/Popover.js';
|
|
9
8
|
import cls from './EmojiField.module.css';
|
|
10
|
-
import {
|
|
9
|
+
import { Field } from './Field.js';
|
|
11
10
|
|
|
12
11
|
export type EmojiFieldProps = Omit<ButtonProps, 'className'> & {
|
|
13
12
|
name: string;
|
|
14
13
|
label?: string;
|
|
15
14
|
required?: string;
|
|
16
15
|
className?: string;
|
|
16
|
+
description?: ReactNode;
|
|
17
17
|
};
|
|
18
18
|
|
|
19
19
|
export function EmojiField({
|
|
@@ -22,17 +22,24 @@ export function EmojiField({
|
|
|
22
22
|
className,
|
|
23
23
|
required,
|
|
24
24
|
id: providedId,
|
|
25
|
+
description,
|
|
25
26
|
...rest
|
|
26
27
|
}: EmojiFieldProps) {
|
|
27
28
|
const [props, _, tools] = useField({ name });
|
|
28
29
|
const id = useIdOrGenerated(providedId);
|
|
29
30
|
const [open, setOpen] = useState(false);
|
|
30
31
|
return (
|
|
31
|
-
<
|
|
32
|
+
<Field horizontal className={className} id={id}>
|
|
32
33
|
<Popover open={open} onOpenChange={setOpen}>
|
|
34
|
+
<Field.Control render={<input type="hidden" {...props} />} />
|
|
33
35
|
<Popover.Trigger
|
|
34
36
|
render={
|
|
35
|
-
<Button
|
|
37
|
+
<Button
|
|
38
|
+
id={`${id}-trigger`}
|
|
39
|
+
aria-label="Select emoji"
|
|
40
|
+
size="wrapper"
|
|
41
|
+
{...rest}
|
|
42
|
+
>
|
|
36
43
|
<Button.Icon className={cls.triggerIcon}>
|
|
37
44
|
{props.value || <Icon name="smile" />}
|
|
38
45
|
</Button.Icon>
|
|
@@ -57,9 +64,8 @@ export function EmojiField({
|
|
|
57
64
|
/>
|
|
58
65
|
</Popover.Content>
|
|
59
66
|
</Popover>
|
|
60
|
-
{label &&
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
</Box>
|
|
67
|
+
{label && <Field.Label>{label}</Field.Label>}
|
|
68
|
+
{description && <Field.Description>{description}</Field.Description>}
|
|
69
|
+
</Field>
|
|
64
70
|
);
|
|
65
71
|
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
.root {
|
|
2
|
+
display: grid;
|
|
3
|
+
grid-template-areas: 'label' 'control' 'description';
|
|
4
|
+
justify-content: start;
|
|
5
|
+
|
|
6
|
+
&[data-horizontal] {
|
|
7
|
+
flex-direction: row;
|
|
8
|
+
align-items: center;
|
|
9
|
+
grid-template-areas: 'control label' 'description description';
|
|
10
|
+
grid-template-columns: auto 1fr;
|
|
11
|
+
&[data-reverse] {
|
|
12
|
+
grid-template-areas: 'label control' 'description description';
|
|
13
|
+
grid-template-columns: auto 1fr;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
&[data-stretch] {
|
|
18
|
+
align-self: stretch;
|
|
19
|
+
justify-content: stretch;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.control {
|
|
24
|
+
grid-area: control;
|
|
25
|
+
|
|
26
|
+
[data-stretch] & {
|
|
27
|
+
align-self: stretch;
|
|
28
|
+
justify-content: stretch;
|
|
29
|
+
width: 100%;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.label {
|
|
34
|
+
grid-area: label;
|
|
35
|
+
@apply --mx-prose-ambient;
|
|
36
|
+
@apply --mx-fg(var(--m-color-neutral-heavy));
|
|
37
|
+
font-weight: var(--m-text-weight-bold);
|
|
38
|
+
margin-bottom: var(--m-space-xs);
|
|
39
|
+
margin-left: calc(var(--m-global-shape-roundness) * var(--m-space-md));
|
|
40
|
+
|
|
41
|
+
[data-horizontal] & {
|
|
42
|
+
@apply --mx-fg(var(--m-color-neutral-ink));
|
|
43
|
+
font-weight: var(--m-text-weight-normal);
|
|
44
|
+
margin-block-start: --fn-literal(5px);
|
|
45
|
+
|
|
46
|
+
[data-reverse] & {
|
|
47
|
+
margin-inline-start: 0;
|
|
48
|
+
margin-inline-end: calc(
|
|
49
|
+
var(--m-global-shape-roundness) * var(--m-space-md)
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.description {
|
|
56
|
+
grid-area: description;
|
|
57
|
+
@apply --mx-prose-ambient;
|
|
58
|
+
@apply --mx-fg(var(--m-color-neutral-heavy));
|
|
59
|
+
font-style: italic;
|
|
60
|
+
margin-top: var(--m-space-sm);
|
|
61
|
+
margin-left: calc(var(--m-global-shape-roundness) * var(--m-space-md));
|
|
62
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import clsx from 'clsx';
|
|
2
|
+
import {
|
|
3
|
+
ComponentProps,
|
|
4
|
+
createContext,
|
|
5
|
+
useContext,
|
|
6
|
+
useEffect,
|
|
7
|
+
useRef,
|
|
8
|
+
useState,
|
|
9
|
+
} from 'react';
|
|
10
|
+
import useMergedRef from '../../hooks/useMergedRef.js';
|
|
11
|
+
import { SlotDiv } from '../utility/SlotDiv.js';
|
|
12
|
+
import cls from './Field.module.css';
|
|
13
|
+
|
|
14
|
+
export interface FieldRootProps extends ComponentProps<'div'> {
|
|
15
|
+
horizontal?: boolean;
|
|
16
|
+
stretch?: boolean;
|
|
17
|
+
reverse?: boolean;
|
|
18
|
+
/**
|
|
19
|
+
* This ID will be passed to Control, and Label will be associated.
|
|
20
|
+
*/
|
|
21
|
+
id?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const FieldContext = createContext<{ id?: string; events: EventTarget }>({
|
|
25
|
+
events: new EventTarget(),
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
function FieldRoot({
|
|
29
|
+
className,
|
|
30
|
+
horizontal,
|
|
31
|
+
stretch,
|
|
32
|
+
reverse,
|
|
33
|
+
id,
|
|
34
|
+
...props
|
|
35
|
+
}: FieldRootProps) {
|
|
36
|
+
const [events] = useState(() => new EventTarget());
|
|
37
|
+
return (
|
|
38
|
+
<FieldContext.Provider value={{ id, events }}>
|
|
39
|
+
<SlotDiv
|
|
40
|
+
className={clsx(cls.root, className)}
|
|
41
|
+
data-horizontal={horizontal ? '' : undefined}
|
|
42
|
+
data-stretch={stretch ? '' : undefined}
|
|
43
|
+
data-reverse={reverse ? '' : undefined}
|
|
44
|
+
{...props}
|
|
45
|
+
/>
|
|
46
|
+
</FieldContext.Provider>
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function FieldControl({
|
|
51
|
+
className,
|
|
52
|
+
ref,
|
|
53
|
+
...props
|
|
54
|
+
}: ComponentProps<typeof SlotDiv>) {
|
|
55
|
+
const innerRef = useRef<HTMLElement>(null);
|
|
56
|
+
const finalRef = useMergedRef(ref, innerRef);
|
|
57
|
+
const { id, events } = useContext(FieldContext);
|
|
58
|
+
|
|
59
|
+
useEffect(() => {
|
|
60
|
+
if (!id) return;
|
|
61
|
+
|
|
62
|
+
function onDescriptionVisible(event: CustomEvent) {
|
|
63
|
+
if (event.detail === true) {
|
|
64
|
+
innerRef.current?.setAttribute('aria-describedby', `${id}-description`);
|
|
65
|
+
} else {
|
|
66
|
+
innerRef.current?.removeAttribute('aria-describedby');
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
events.addEventListener(
|
|
71
|
+
'description-visible',
|
|
72
|
+
onDescriptionVisible as EventListener,
|
|
73
|
+
);
|
|
74
|
+
return () => {
|
|
75
|
+
events.removeEventListener(
|
|
76
|
+
'description-visible',
|
|
77
|
+
onDescriptionVisible as EventListener,
|
|
78
|
+
);
|
|
79
|
+
};
|
|
80
|
+
}, [events, id]);
|
|
81
|
+
|
|
82
|
+
return (
|
|
83
|
+
<SlotDiv
|
|
84
|
+
className={clsx(cls.control, className)}
|
|
85
|
+
id={id}
|
|
86
|
+
ref={finalRef}
|
|
87
|
+
{...props}
|
|
88
|
+
/>
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function FieldLabel({ className, ...props }: ComponentProps<'label'>) {
|
|
93
|
+
const { id } = useContext(FieldContext);
|
|
94
|
+
return (
|
|
95
|
+
<label className={clsx(cls.label, className)} htmlFor={id} {...props} />
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function FieldDescription({ className, ...props }: ComponentProps<'div'>) {
|
|
100
|
+
const { id, events } = useContext(FieldContext);
|
|
101
|
+
|
|
102
|
+
useEffect(() => {
|
|
103
|
+
events.dispatchEvent(
|
|
104
|
+
new CustomEvent('description-visible', { detail: true }),
|
|
105
|
+
);
|
|
106
|
+
return () => {
|
|
107
|
+
events.dispatchEvent(
|
|
108
|
+
new CustomEvent('description-visible', { detail: false }),
|
|
109
|
+
);
|
|
110
|
+
};
|
|
111
|
+
}, [events]);
|
|
112
|
+
return (
|
|
113
|
+
<div
|
|
114
|
+
className={clsx('@mode-dense', cls.description, className)}
|
|
115
|
+
id={id ? `${id}-description` : undefined}
|
|
116
|
+
{...props}
|
|
117
|
+
/>
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export const Field = Object.assign(FieldRoot, {
|
|
122
|
+
Label: FieldLabel,
|
|
123
|
+
Description: FieldDescription,
|
|
124
|
+
Control: FieldControl,
|
|
125
|
+
});
|
|
@@ -47,19 +47,39 @@ export const Default: Story = {
|
|
|
47
47
|
label="Email"
|
|
48
48
|
placeholder="you@example.com"
|
|
49
49
|
/>
|
|
50
|
-
<TextField
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
50
|
+
<TextField
|
|
51
|
+
name="password"
|
|
52
|
+
type="password"
|
|
53
|
+
label="Password"
|
|
54
|
+
description="Make it a good one"
|
|
55
|
+
/>
|
|
56
|
+
<NumberStepperField
|
|
57
|
+
name="age"
|
|
58
|
+
label="Age"
|
|
59
|
+
min={13}
|
|
60
|
+
max={100}
|
|
61
|
+
description="You can mail your government ID to us, don't worry, we're the good guys"
|
|
62
|
+
/>
|
|
63
|
+
<CheckboxField
|
|
64
|
+
name="tos"
|
|
65
|
+
label="I agree"
|
|
66
|
+
description="To what? Not telling."
|
|
67
|
+
/>
|
|
68
|
+
<SwitchField
|
|
69
|
+
name="newsletter"
|
|
70
|
+
label="Subscribe to newsletter"
|
|
71
|
+
description="Don't worry, we don't bother you much"
|
|
72
|
+
/>
|
|
54
73
|
<ToggleGroupField
|
|
55
74
|
type="single"
|
|
56
75
|
name="plan"
|
|
57
76
|
label="Select your plan"
|
|
58
77
|
required
|
|
78
|
+
description="Please do the expensive one!!!"
|
|
59
79
|
>
|
|
60
80
|
<ToggleGroupField.Item value="basic">Basic</ToggleGroupField.Item>
|
|
61
81
|
<ToggleGroupField.Item value="pro">Pro</ToggleGroupField.Item>
|
|
62
|
-
<ToggleGroupField.Item value="enterprise">
|
|
82
|
+
<ToggleGroupField.Item value="enterprise" className="@mode-accent">
|
|
63
83
|
Enterprise
|
|
64
84
|
</ToggleGroupField.Item>
|
|
65
85
|
</ToggleGroupField>
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { useField } from 'formik';
|
|
2
|
-
import {
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
3
|
import { useIdOrGenerated } from '../../hooks/useIdOrGenerated.js';
|
|
4
4
|
import {
|
|
5
5
|
NumberStepper,
|
|
6
6
|
NumberStepperProps,
|
|
7
7
|
} from '../numberStepper/NumberStepper.js';
|
|
8
|
-
import {
|
|
9
|
-
import cls from './NumberStepperField.module.css';
|
|
8
|
+
import { Field } from './Field.js';
|
|
10
9
|
|
|
11
10
|
export interface NumberStepperFieldProps
|
|
12
11
|
extends Omit<NumberStepperProps, 'value' | 'onChange'> {
|
|
@@ -15,6 +14,7 @@ export interface NumberStepperFieldProps
|
|
|
15
14
|
className?: string;
|
|
16
15
|
id?: string;
|
|
17
16
|
onChange?: (value: number) => void;
|
|
17
|
+
description?: ReactNode;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
export function NumberStepperField({
|
|
@@ -23,24 +23,27 @@ export function NumberStepperField({
|
|
|
23
23
|
className,
|
|
24
24
|
id: providedId,
|
|
25
25
|
onChange,
|
|
26
|
+
description,
|
|
26
27
|
...rest
|
|
27
28
|
}: NumberStepperFieldProps) {
|
|
28
29
|
const [_, field, tools] = useField({ name });
|
|
29
30
|
const id = useIdOrGenerated(providedId);
|
|
30
31
|
return (
|
|
31
|
-
<
|
|
32
|
-
{label && <
|
|
33
|
-
<
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
32
|
+
<Field className={className} id={id}>
|
|
33
|
+
{label && <Field.Label>{label}</Field.Label>}
|
|
34
|
+
<Field.Control
|
|
35
|
+
render={
|
|
36
|
+
<NumberStepper
|
|
37
|
+
value={field.value}
|
|
38
|
+
onChange={(v) => {
|
|
39
|
+
tools.setValue(v);
|
|
40
|
+
onChange?.(v);
|
|
41
|
+
}}
|
|
42
|
+
{...rest}
|
|
43
|
+
/>
|
|
44
|
+
}
|
|
41
45
|
/>
|
|
42
|
-
|
|
46
|
+
{description && <Field.Description>{description}</Field.Description>}
|
|
47
|
+
</Field>
|
|
43
48
|
);
|
|
44
49
|
}
|
|
45
|
-
|
|
46
|
-
const FieldRoot = withClassName('div', cls.root);
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { useField } from 'formik';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
2
3
|
import { useIdOrGenerated } from '../../hooks/useIdOrGenerated.js';
|
|
3
|
-
import { Box } from '../box/Box.js';
|
|
4
4
|
import { Switch } from '../switch/Switch.js';
|
|
5
|
-
import {
|
|
5
|
+
import { Field } from './Field.js';
|
|
6
6
|
|
|
7
7
|
export interface SwitchFieldProps {
|
|
8
8
|
name: string;
|
|
@@ -10,6 +10,7 @@ export interface SwitchFieldProps {
|
|
|
10
10
|
required?: boolean;
|
|
11
11
|
className?: string;
|
|
12
12
|
id?: string;
|
|
13
|
+
description?: ReactNode;
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
export function SwitchField({
|
|
@@ -18,24 +19,27 @@ export function SwitchField({
|
|
|
18
19
|
className,
|
|
19
20
|
required,
|
|
20
21
|
id: providedId,
|
|
22
|
+
description,
|
|
21
23
|
...rest
|
|
22
24
|
}: SwitchFieldProps) {
|
|
23
25
|
const [props, _, tools] = useField({ name, required, type: 'checkbox' });
|
|
24
26
|
const id = useIdOrGenerated(providedId);
|
|
25
27
|
return (
|
|
26
|
-
<
|
|
27
|
-
<
|
|
28
|
-
{
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
28
|
+
<Field horizontal className={className} id={id}>
|
|
29
|
+
<Field.Control
|
|
30
|
+
render={
|
|
31
|
+
<Switch
|
|
32
|
+
{...props}
|
|
33
|
+
checked={props.checked}
|
|
34
|
+
onCheckedChange={(v) => {
|
|
35
|
+
tools.setValue(!!v);
|
|
36
|
+
}}
|
|
37
|
+
{...rest}
|
|
38
|
+
/>
|
|
39
|
+
}
|
|
35
40
|
/>
|
|
36
|
-
{label &&
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
</Box>
|
|
41
|
+
{label && <Field.Label>{label}</Field.Label>}
|
|
42
|
+
{description && <Field.Description>{description}</Field.Description>}
|
|
43
|
+
</Field>
|
|
40
44
|
);
|
|
41
45
|
}
|
|
@@ -3,17 +3,17 @@ import { useField, useFormikContext } from 'formik';
|
|
|
3
3
|
import {
|
|
4
4
|
InputHTMLAttributes,
|
|
5
5
|
KeyboardEvent,
|
|
6
|
+
ReactNode,
|
|
6
7
|
Ref,
|
|
7
8
|
useCallback,
|
|
8
9
|
useEffect,
|
|
9
10
|
useRef,
|
|
10
11
|
} from 'react';
|
|
11
|
-
import { withClassName } from '../../hooks.js';
|
|
12
12
|
import { useIdOrGenerated } from '../../hooks/useIdOrGenerated.js';
|
|
13
13
|
import useMergedRef from '../../hooks/useMergedRef.js';
|
|
14
14
|
import { Input, InputProps } from '../input/Input.js';
|
|
15
15
|
import { TextArea, TextAreaProps } from '../textArea/TextArea.js';
|
|
16
|
-
import {
|
|
16
|
+
import { Field } from './Field.js';
|
|
17
17
|
import cls from './TextField.module.css';
|
|
18
18
|
|
|
19
19
|
export type TextFieldProps = {
|
|
@@ -24,6 +24,7 @@ export type TextFieldProps = {
|
|
|
24
24
|
inputRef?: Ref<HTMLInputElement>;
|
|
25
25
|
inputClassName?: string;
|
|
26
26
|
ref?: Ref<HTMLDivElement>;
|
|
27
|
+
description?: ReactNode;
|
|
27
28
|
} & Omit<InputProps, 'ref'>;
|
|
28
29
|
|
|
29
30
|
const emptyRef = (() => {}) as any;
|
|
@@ -41,6 +42,7 @@ export const TextField = function TextField({
|
|
|
41
42
|
onBlur,
|
|
42
43
|
id: providedId,
|
|
43
44
|
inputClassName,
|
|
45
|
+
description,
|
|
44
46
|
...rest
|
|
45
47
|
}: TextFieldProps) {
|
|
46
48
|
const [props] = useField({
|
|
@@ -61,17 +63,21 @@ export const TextField = function TextField({
|
|
|
61
63
|
}, [autoFocusDelay]);
|
|
62
64
|
|
|
63
65
|
return (
|
|
64
|
-
<
|
|
65
|
-
{label && <
|
|
66
|
-
<
|
|
67
|
-
{
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
66
|
+
<Field className={className} stretch ref={ref} id={id}>
|
|
67
|
+
{label && <Field.Label>{label}</Field.Label>}
|
|
68
|
+
<Field.Control
|
|
69
|
+
render={
|
|
70
|
+
<Input
|
|
71
|
+
{...props}
|
|
72
|
+
{...rest}
|
|
73
|
+
autoFocus={autoFocus}
|
|
74
|
+
className={clsx(cls.input, inputClassName)}
|
|
75
|
+
ref={useMergedRef(innerInputRef, inputRef || emptyRef)}
|
|
76
|
+
/>
|
|
77
|
+
}
|
|
73
78
|
/>
|
|
74
|
-
|
|
79
|
+
{description && <Field.Description>{description}</Field.Description>}
|
|
80
|
+
</Field>
|
|
75
81
|
);
|
|
76
82
|
};
|
|
77
83
|
|
|
@@ -85,7 +91,9 @@ export type TextAreaFieldProps = {
|
|
|
85
91
|
inputRef?: Ref<HTMLTextAreaElement>;
|
|
86
92
|
submitOnEnter?: boolean;
|
|
87
93
|
textAreaClassName?: string;
|
|
88
|
-
|
|
94
|
+
description?: ReactNode;
|
|
95
|
+
ref?: Ref<HTMLDivElement>;
|
|
96
|
+
} & Omit<TextAreaProps, 'ref'>;
|
|
89
97
|
|
|
90
98
|
export function TextAreaField({
|
|
91
99
|
name,
|
|
@@ -96,6 +104,8 @@ export function TextAreaField({
|
|
|
96
104
|
submitOnEnter,
|
|
97
105
|
id: providedId,
|
|
98
106
|
textAreaClassName,
|
|
107
|
+
description,
|
|
108
|
+
ref,
|
|
99
109
|
...rest
|
|
100
110
|
}: TextAreaFieldProps) {
|
|
101
111
|
const [props] = useField(name);
|
|
@@ -113,18 +123,20 @@ export function TextAreaField({
|
|
|
113
123
|
const id = useIdOrGenerated(providedId);
|
|
114
124
|
|
|
115
125
|
return (
|
|
116
|
-
<
|
|
117
|
-
{label && <
|
|
118
|
-
<
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
126
|
+
<Field stretch className={className} ref={ref} id={id}>
|
|
127
|
+
{label && <Field.Label>{label}</Field.Label>}
|
|
128
|
+
<Field.Control
|
|
129
|
+
render={
|
|
130
|
+
<TextArea
|
|
131
|
+
ref={inputRef as any}
|
|
132
|
+
{...props}
|
|
133
|
+
{...rest}
|
|
134
|
+
className={clsx(cls.input, textAreaClassName)}
|
|
135
|
+
onKeyDown={onKeyDownInner}
|
|
136
|
+
/>
|
|
137
|
+
}
|
|
125
138
|
/>
|
|
126
|
-
|
|
139
|
+
{description && <Field.Description>{description}</Field.Description>}
|
|
140
|
+
</Field>
|
|
127
141
|
);
|
|
128
142
|
}
|
|
129
|
-
|
|
130
|
-
export const FieldRoot = withClassName('div', cls.root);
|