@a-type/ui 6.0.22 → 6.0.23
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 +10 -11
- package/dist/components/forms/EmojiField.js.map +1 -1
- package/dist/components/forms/Field.d.ts +16 -0
- package/dist/components/forms/Field.js +37 -0
- package/dist/components/forms/Field.js.map +1 -0
- package/dist/components/forms/Field.module.css +51 -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 +10 -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 +23 -15
- package/src/components/forms/EmojiField.tsx +50 -33
- package/src/components/forms/Field.module.css +51 -0
- package/src/components/forms/Field.tsx +43 -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 +24 -17
- package/src/components/forms/SwitchField.tsx +23 -15
- package/src/components/forms/TextField.tsx +49 -25
- package/src/components/forms/ToggleGroupField.tsx +29 -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,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,31 @@ 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
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
28
|
+
<Field horizontal className={className}>
|
|
29
|
+
<Field.Control>
|
|
30
|
+
<Checkbox
|
|
31
|
+
{...props}
|
|
32
|
+
checked={props.checked}
|
|
33
|
+
onCheckedChange={(v) => {
|
|
34
|
+
tools.setValue(!!v);
|
|
35
|
+
}}
|
|
36
|
+
id={id}
|
|
37
|
+
aria-describedby={description ? `${id}-description` : undefined}
|
|
38
|
+
{...rest}
|
|
39
|
+
/>
|
|
40
|
+
</Field.Control>
|
|
41
|
+
{label && <Field.Label htmlFor={id}>{label}</Field.Label>}
|
|
42
|
+
{description && (
|
|
43
|
+
<Field.Description id={`${id}-description`}>
|
|
44
|
+
{description}
|
|
45
|
+
</Field.Description>
|
|
38
46
|
)}
|
|
39
|
-
</
|
|
47
|
+
</Field>
|
|
40
48
|
);
|
|
41
49
|
}
|
|
@@ -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,44 +22,61 @@ 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
|
-
<
|
|
33
|
-
<Popover
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
{props.value || <Icon name="smile" />}
|
|
38
|
-
</Button.Icon>
|
|
39
|
-
</Button>
|
|
40
|
-
}
|
|
41
|
-
/>
|
|
42
|
-
<Popover.Content>
|
|
43
|
-
<EmojiPicker
|
|
44
|
-
onValueChange={(v) => {
|
|
45
|
-
tools.setValue(v);
|
|
46
|
-
setOpen(false);
|
|
47
|
-
}}
|
|
48
|
-
onClear={
|
|
49
|
-
required
|
|
50
|
-
? undefined
|
|
51
|
-
: () => {
|
|
52
|
-
tools.setValue('');
|
|
53
|
-
setOpen(false);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
32
|
+
<Field horizontal className={className}>
|
|
33
|
+
<Field.Control>
|
|
34
|
+
<Popover open={open} onOpenChange={setOpen}>
|
|
35
|
+
<input
|
|
36
|
+
type="hidden"
|
|
37
|
+
{...props}
|
|
56
38
|
id={id}
|
|
39
|
+
aria-describedby={description ? `${id}-description` : undefined}
|
|
40
|
+
/>
|
|
41
|
+
<Popover.Trigger
|
|
42
|
+
render={
|
|
43
|
+
<Button
|
|
44
|
+
id={`${id}-trigger`}
|
|
45
|
+
aria-label="Select emoji"
|
|
46
|
+
size="wrapper"
|
|
47
|
+
{...rest}
|
|
48
|
+
>
|
|
49
|
+
<Button.Icon className={cls.triggerIcon}>
|
|
50
|
+
{props.value || <Icon name="smile" />}
|
|
51
|
+
</Button.Icon>
|
|
52
|
+
</Button>
|
|
53
|
+
}
|
|
57
54
|
/>
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
55
|
+
<Popover.Content>
|
|
56
|
+
<EmojiPicker
|
|
57
|
+
onValueChange={(v) => {
|
|
58
|
+
tools.setValue(v);
|
|
59
|
+
setOpen(false);
|
|
60
|
+
}}
|
|
61
|
+
onClear={
|
|
62
|
+
required
|
|
63
|
+
? undefined
|
|
64
|
+
: () => {
|
|
65
|
+
tools.setValue('');
|
|
66
|
+
setOpen(false);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
id={id}
|
|
70
|
+
/>
|
|
71
|
+
</Popover.Content>
|
|
72
|
+
</Popover>
|
|
73
|
+
</Field.Control>
|
|
74
|
+
{label && <Field.Label htmlFor={id}>{label}</Field.Label>}
|
|
75
|
+
{description && (
|
|
76
|
+
<Field.Description id={`${id}-description`}>
|
|
77
|
+
{description}
|
|
78
|
+
</Field.Description>
|
|
62
79
|
)}
|
|
63
|
-
</
|
|
80
|
+
</Field>
|
|
64
81
|
);
|
|
65
82
|
}
|
|
@@ -0,0 +1,51 @@
|
|
|
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
|
+
}
|
|
12
|
+
|
|
13
|
+
&[data-stretch] {
|
|
14
|
+
align-self: stretch;
|
|
15
|
+
justify-content: stretch;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.control {
|
|
20
|
+
grid-area: control;
|
|
21
|
+
}
|
|
22
|
+
:where(.control) {
|
|
23
|
+
display: flex;
|
|
24
|
+
flex-direction: column;
|
|
25
|
+
align-items: center;
|
|
26
|
+
gap: var(--m-space-sm);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.label {
|
|
30
|
+
grid-area: label;
|
|
31
|
+
@apply --mx-prose-ambient;
|
|
32
|
+
@apply --mx-fg(var(--m-color-neutral-heavy));
|
|
33
|
+
font-weight: var(--m-text-weight-bold);
|
|
34
|
+
margin-bottom: var(--m-space-xs);
|
|
35
|
+
margin-left: calc(var(--m-global-shape-roundness) * var(--m-space-md));
|
|
36
|
+
|
|
37
|
+
[data-horizontal] & {
|
|
38
|
+
@apply --mx-fg(var(--m-color-neutral-ink));
|
|
39
|
+
font-weight: var(--m-text-weight-normal);
|
|
40
|
+
margin-block-start: --fn-literal(5px);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.description {
|
|
45
|
+
grid-area: description;
|
|
46
|
+
@apply --mx-prose-ambient;
|
|
47
|
+
@apply --mx-fg(var(--m-color-neutral-heavy));
|
|
48
|
+
font-style: italic;
|
|
49
|
+
margin-top: var(--m-space-sm);
|
|
50
|
+
margin-left: calc(var(--m-global-shape-roundness) * var(--m-space-md));
|
|
51
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import clsx from 'clsx';
|
|
2
|
+
import { ComponentProps } from 'react';
|
|
3
|
+
import { SlotDiv } from '../utility/SlotDiv.js';
|
|
4
|
+
import cls from './Field.module.css';
|
|
5
|
+
|
|
6
|
+
export interface FieldRootProps extends ComponentProps<'div'> {
|
|
7
|
+
horizontal?: boolean;
|
|
8
|
+
stretch?: boolean;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function FieldRoot({ className, horizontal, ...props }: FieldRootProps) {
|
|
12
|
+
return (
|
|
13
|
+
<SlotDiv
|
|
14
|
+
className={clsx(cls.root, className)}
|
|
15
|
+
data-horizontal={horizontal ? '' : undefined}
|
|
16
|
+
data-stretch={props.stretch ? '' : undefined}
|
|
17
|
+
{...props}
|
|
18
|
+
/>
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function FieldControl({ className, ...props }: ComponentProps<typeof SlotDiv>) {
|
|
23
|
+
return <SlotDiv className={clsx(cls.control, className)} {...props} />;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function FieldLabel({ className, ...props }: ComponentProps<'label'>) {
|
|
27
|
+
return <label className={clsx(cls.label, className)} {...props} />;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function FieldDescription({ className, ...props }: ComponentProps<'div'>) {
|
|
31
|
+
return (
|
|
32
|
+
<div
|
|
33
|
+
className={clsx('@mode-dense', cls.description, className)}
|
|
34
|
+
{...props}
|
|
35
|
+
/>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export const Field = Object.assign(FieldRoot, {
|
|
40
|
+
Label: FieldLabel,
|
|
41
|
+
Description: FieldDescription,
|
|
42
|
+
Control: FieldControl,
|
|
43
|
+
});
|
|
@@ -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,31 @@ 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
|
-
|
|
41
|
-
|
|
42
|
-
|
|
32
|
+
<Field className={className} horizontal>
|
|
33
|
+
{label && <Field.Label htmlFor={id}>{label}</Field.Label>}
|
|
34
|
+
<Field.Control>
|
|
35
|
+
<NumberStepper
|
|
36
|
+
value={field.value}
|
|
37
|
+
onChange={(v) => {
|
|
38
|
+
tools.setValue(v);
|
|
39
|
+
onChange?.(v);
|
|
40
|
+
}}
|
|
41
|
+
id={id}
|
|
42
|
+
aria-describedby={description ? `${id}-description` : undefined}
|
|
43
|
+
{...rest}
|
|
44
|
+
/>
|
|
45
|
+
</Field.Control>
|
|
46
|
+
{description && (
|
|
47
|
+
<Field.Description id={`${id}-description`}>
|
|
48
|
+
{description}
|
|
49
|
+
</Field.Description>
|
|
50
|
+
)}
|
|
51
|
+
</Field>
|
|
43
52
|
);
|
|
44
53
|
}
|
|
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,31 @@ 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
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
28
|
+
<Field horizontal className={className}>
|
|
29
|
+
<Field.Control>
|
|
30
|
+
<Switch
|
|
31
|
+
{...props}
|
|
32
|
+
checked={props.checked}
|
|
33
|
+
onCheckedChange={(v) => {
|
|
34
|
+
tools.setValue(!!v);
|
|
35
|
+
}}
|
|
36
|
+
id={id}
|
|
37
|
+
aria-describedby={description ? `${id}-description` : undefined}
|
|
38
|
+
{...rest}
|
|
39
|
+
/>
|
|
40
|
+
</Field.Control>
|
|
41
|
+
{label && <Field.Label htmlFor={id}>{label}</Field.Label>}
|
|
42
|
+
{description && (
|
|
43
|
+
<Field.Description id={`${id}-description`}>
|
|
44
|
+
{description}
|
|
45
|
+
</Field.Description>
|
|
38
46
|
)}
|
|
39
|
-
</
|
|
47
|
+
</Field>
|
|
40
48
|
);
|
|
41
49
|
}
|
|
@@ -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,27 @@ 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}>
|
|
67
|
+
{label && <Field.Label htmlFor={id}>{label}</Field.Label>}
|
|
68
|
+
<Field.Control
|
|
69
|
+
render={
|
|
70
|
+
<Input
|
|
71
|
+
{...props}
|
|
72
|
+
{...rest}
|
|
73
|
+
id={id}
|
|
74
|
+
autoFocus={autoFocus}
|
|
75
|
+
className={clsx(cls.input, inputClassName)}
|
|
76
|
+
ref={useMergedRef(innerInputRef, inputRef || emptyRef)}
|
|
77
|
+
aria-describedby={description ? `${id}-description` : undefined}
|
|
78
|
+
/>
|
|
79
|
+
}
|
|
73
80
|
/>
|
|
74
|
-
|
|
81
|
+
{description && (
|
|
82
|
+
<Field.Description id={`${id}-description`}>
|
|
83
|
+
{description}
|
|
84
|
+
</Field.Description>
|
|
85
|
+
)}
|
|
86
|
+
</Field>
|
|
75
87
|
);
|
|
76
88
|
};
|
|
77
89
|
|
|
@@ -85,7 +97,9 @@ export type TextAreaFieldProps = {
|
|
|
85
97
|
inputRef?: Ref<HTMLTextAreaElement>;
|
|
86
98
|
submitOnEnter?: boolean;
|
|
87
99
|
textAreaClassName?: string;
|
|
88
|
-
|
|
100
|
+
description?: ReactNode;
|
|
101
|
+
ref?: Ref<HTMLDivElement>;
|
|
102
|
+
} & Omit<TextAreaProps, 'ref'>;
|
|
89
103
|
|
|
90
104
|
export function TextAreaField({
|
|
91
105
|
name,
|
|
@@ -96,6 +110,8 @@ export function TextAreaField({
|
|
|
96
110
|
submitOnEnter,
|
|
97
111
|
id: providedId,
|
|
98
112
|
textAreaClassName,
|
|
113
|
+
description,
|
|
114
|
+
ref,
|
|
99
115
|
...rest
|
|
100
116
|
}: TextAreaFieldProps) {
|
|
101
117
|
const [props] = useField(name);
|
|
@@ -113,18 +129,26 @@ export function TextAreaField({
|
|
|
113
129
|
const id = useIdOrGenerated(providedId);
|
|
114
130
|
|
|
115
131
|
return (
|
|
116
|
-
<
|
|
117
|
-
{label && <
|
|
118
|
-
<
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
132
|
+
<Field stretch className={className} ref={ref}>
|
|
133
|
+
{label && <Field.Label htmlFor={id}>{label}</Field.Label>}
|
|
134
|
+
<Field.Control
|
|
135
|
+
render={
|
|
136
|
+
<TextArea
|
|
137
|
+
ref={inputRef as any}
|
|
138
|
+
{...props}
|
|
139
|
+
{...rest}
|
|
140
|
+
className={clsx(cls.input, textAreaClassName)}
|
|
141
|
+
id={id}
|
|
142
|
+
onKeyDown={onKeyDownInner}
|
|
143
|
+
aria-describedby={description ? `${id}-description` : undefined}
|
|
144
|
+
/>
|
|
145
|
+
}
|
|
125
146
|
/>
|
|
126
|
-
|
|
147
|
+
{description && (
|
|
148
|
+
<Field.Description id={`${id}-description`}>
|
|
149
|
+
{description}
|
|
150
|
+
</Field.Description>
|
|
151
|
+
)}
|
|
152
|
+
</Field>
|
|
127
153
|
);
|
|
128
154
|
}
|
|
129
|
-
|
|
130
|
-
export const FieldRoot = withClassName('div', cls.root);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useField } from 'formik';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
2
3
|
import { ToggleGroup, ToggleGroupProps } from '../toggleGroup/toggleGroup.js';
|
|
3
|
-
import {
|
|
4
|
-
import { FieldRoot } from './TextField.js';
|
|
4
|
+
import { Field } from './Field.js';
|
|
5
5
|
|
|
6
6
|
export type ToggleGroupFieldProps<Value extends string> =
|
|
7
7
|
ToggleGroupProps<Value> & {
|
|
@@ -11,6 +11,7 @@ export type ToggleGroupFieldProps<Value extends string> =
|
|
|
11
11
|
className?: string;
|
|
12
12
|
id?: string;
|
|
13
13
|
type?: 'single' | 'multiple';
|
|
14
|
+
description?: ReactNode;
|
|
14
15
|
};
|
|
15
16
|
|
|
16
17
|
function ToggleGroupFieldDefault({
|
|
@@ -20,29 +21,38 @@ function ToggleGroupFieldDefault({
|
|
|
20
21
|
className,
|
|
21
22
|
type = 'single',
|
|
22
23
|
multiple,
|
|
24
|
+
description,
|
|
23
25
|
id,
|
|
24
26
|
...props
|
|
25
27
|
}: ToggleGroupFieldProps<string>) {
|
|
26
28
|
const [fieldProps, _, tools] = useField({ name, required, ...props });
|
|
27
29
|
|
|
28
30
|
return (
|
|
29
|
-
<
|
|
30
|
-
{label && <
|
|
31
|
-
<
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
31
|
+
<Field>
|
|
32
|
+
{label && <Field.Label htmlFor={id}>{label}</Field.Label>}
|
|
33
|
+
<Field.Control>
|
|
34
|
+
<ToggleGroup
|
|
35
|
+
{...fieldProps}
|
|
36
|
+
onValueChange={(v) => {
|
|
37
|
+
if (multiple || type === 'multiple') {
|
|
38
|
+
tools.setValue(v);
|
|
39
|
+
} else {
|
|
40
|
+
tools.setValue(v[0]);
|
|
41
|
+
}
|
|
42
|
+
}}
|
|
43
|
+
multiple={multiple || type === 'multiple'}
|
|
44
|
+
{...props}
|
|
45
|
+
id={id}
|
|
46
|
+
className={className}
|
|
47
|
+
aria-describedby={description ? `${id}-description` : undefined}
|
|
48
|
+
/>
|
|
49
|
+
</Field.Control>
|
|
50
|
+
{description && (
|
|
51
|
+
<Field.Description id={`${id}-description`}>
|
|
52
|
+
{description}
|
|
53
|
+
</Field.Description>
|
|
54
|
+
)}
|
|
55
|
+
</Field>
|
|
46
56
|
);
|
|
47
57
|
}
|
|
48
58
|
|
|
@@ -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] {
|