Skip to content

Commit 1d59c3f

Browse files
committed
Use FastField for widget rendering and other improvements
1 parent 1f51e13 commit 1d59c3f

File tree

6 files changed

+157
-107
lines changed

6 files changed

+157
-107
lines changed

packages/data-widgets/lib/widgets/array-input.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,13 @@ export function WidgetArrayInput(props: WidgetProps) {
5555
onDeleteClick={() => {
5656
remove(index);
5757
}}
58+
transformValue={(v) => {
59+
if (field.items.type === 'number') {
60+
const n = Number(v);
61+
return isNaN(n) ? null : n;
62+
}
63+
return v;
64+
}}
5865
isDeleteDisabled={fields.length <= minItems}
5966
/>
6067
</ListItem>

packages/data-widgets/lib/widgets/checkbox.tsx

Lines changed: 36 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@ import {
44
CheckboxGroup,
55
Flex,
66
FormControl,
7+
FormErrorMessage,
78
FormLabel
89
} from '@chakra-ui/react';
9-
import { useField } from 'formik';
10+
import { FastField, FastFieldProps } from 'formik';
1011
import {
1112
SchemaFieldArray,
1213
SchemaFieldString,
@@ -19,34 +20,45 @@ export function WidgetCheckbox(props: WidgetProps) {
1920
const { pointer, isRequired } = props;
2021
const field = props.field as SchemaFieldArray<SchemaFieldString>;
2122

22-
const [{ value }, , { setValue, setTouched }] = useField(pointer);
23+
const options = field.items.enum;
2324

24-
if (!field.items.enum?.length) {
25+
if (!options?.length) {
2526
throw new Error('WidgetCheckbox: items.enum is required');
2627
}
2728

2829
return (
29-
<FormControl isRequired={isRequired}>
30-
{field.label && (
31-
<FormLabel>
32-
<FieldLabel size='xs'>{field.label}</FieldLabel>
33-
</FormLabel>
30+
<FastField name={pointer}>
31+
{({
32+
field: { value },
33+
meta,
34+
form: { setFieldValue, setFieldTouched }
35+
}: FastFieldProps) => (
36+
<FormControl isRequired={isRequired}>
37+
{field.label && (
38+
<FormLabel>
39+
<FieldLabel size='xs'>{field.label}</FieldLabel>
40+
</FormLabel>
41+
)}
42+
<Flex gap={4}>
43+
<CheckboxGroup
44+
value={value}
45+
onChange={(v) => {
46+
setFieldValue(pointer, v);
47+
setFieldTouched(pointer, true);
48+
}}
49+
>
50+
{options.map(([checkboxVal, label]) => (
51+
<Checkbox key={checkboxVal} size='sm' value={checkboxVal}>
52+
{label}
53+
</Checkbox>
54+
))}
55+
</CheckboxGroup>
56+
</Flex>
57+
{meta.touched && meta.error && (
58+
<FormErrorMessage>{meta.error}</FormErrorMessage>
59+
)}
60+
</FormControl>
3461
)}
35-
<Flex gap={4}>
36-
<CheckboxGroup
37-
value={value}
38-
onChange={(v) => {
39-
setValue(v);
40-
setTouched(true);
41-
}}
42-
>
43-
{field.items.enum.map(([label, checkboxVal]) => (
44-
<Checkbox key={label} size='sm' value={checkboxVal}>
45-
{label}
46-
</Checkbox>
47-
))}
48-
</CheckboxGroup>
49-
</Flex>
50-
</FormControl>
62+
</FastField>
5163
);
5264
}

packages/data-widgets/lib/widgets/input.tsx

Lines changed: 46 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {
66
FormLabel,
77
Input
88
} from '@chakra-ui/react';
9-
import { useField } from 'formik';
9+
import { FastField, FastFieldProps } from 'formik';
1010
import { SchemaFieldString, WidgetProps } from '@stac-manager/data-core';
1111
import { CollecticonTrashBin } from '@devseed-ui/collecticons-chakra';
1212

@@ -21,6 +21,8 @@ interface WidgetInputProps extends WidgetProps {
2121
isDeleteDisabled?: boolean;
2222
}
2323

24+
const identity = (v: any) => v;
25+
2426
export function WidgetInput(props: WidgetInputProps) {
2527
const {
2628
label,
@@ -30,47 +32,56 @@ export function WidgetInput(props: WidgetInputProps) {
3032
pointer,
3133
isRequired,
3234
type,
33-
transformValue = (v) => v
35+
transformValue = identity
3436
} = props;
3537
const field = props.field as SchemaFieldString;
3638

37-
const [{ onBlur }, meta, { setValue }] = useField<number>(props.pointer);
38-
3939
const fieldLabel = label || field.label;
4040

4141
return (
42-
<FormControl isRequired={isRequired}>
43-
<Flex gap={4}>
44-
{fieldLabel && (
45-
<FormLabel>
46-
<FieldLabel size='xs'>{fieldLabel}</FieldLabel>
47-
</FormLabel>
48-
)}
49-
<Flex ml='auto' gap={2}>
50-
{isDeletable && (
51-
<FieldIconBtn
52-
aria-label='Remove item'
53-
onClick={onDeleteClick}
54-
icon={<CollecticonTrashBin size={3} />}
55-
isDisabled={isDeleteDisabled}
56-
/>
42+
<FastField name={pointer}>
43+
{({
44+
field: { value, onBlur },
45+
meta,
46+
form: { setFieldValue }
47+
}: FastFieldProps) => (
48+
<FormControl isRequired={isRequired}>
49+
<Flex gap={4}>
50+
{fieldLabel && (
51+
<FormLabel>
52+
<FieldLabel size='xs'>{fieldLabel}</FieldLabel>
53+
</FormLabel>
54+
)}
55+
<Flex ml='auto' gap={2}>
56+
{isDeletable && (
57+
<FieldIconBtn
58+
aria-label='Remove item'
59+
onClick={onDeleteClick}
60+
icon={<CollecticonTrashBin size={3} />}
61+
isDisabled={isDeleteDisabled}
62+
/>
63+
)}
64+
</Flex>
65+
</Flex>
66+
<Input
67+
type={type}
68+
size='sm'
69+
name={pointer}
70+
bg='surface.500'
71+
borderColor='base.200'
72+
borderRadius='md'
73+
value={value === null ? '' : value}
74+
onBlur={onBlur}
75+
onChange={(e) => {
76+
setFieldValue(pointer, transformValue(e.target.value));
77+
}}
78+
isInvalid={meta.touched && meta.error ? true : false}
79+
/>
80+
{meta.touched && meta.error && (
81+
<FormErrorMessage>{meta.error}</FormErrorMessage>
5782
)}
58-
</Flex>
59-
</Flex>
60-
<Input
61-
type={type}
62-
size='sm'
63-
name={pointer}
64-
bg='surface.500'
65-
borderColor='base.200'
66-
borderRadius='md'
67-
onBlur={onBlur}
68-
onChange={(e) => setValue(transformValue(e.target.value))}
69-
isInvalid={meta.touched && meta.error ? true : false}
70-
/>
71-
{meta.touched && meta.error && (
72-
<FormErrorMessage>{meta.error}</FormErrorMessage>
83+
</FormControl>
7384
)}
74-
</FormControl>
85+
</FastField>
7586
);
7687
}

packages/data-widgets/lib/widgets/json.tsx

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {
66
FormControl,
77
FormLabel
88
} from '@chakra-ui/react';
9-
import { useField } from 'formik';
9+
import { FastField, FastFieldProps } from 'formik';
1010
import {
1111
CollecticonArrowSemiSpinCcw,
1212
CollecticonArrowSemiSpinCw,
@@ -20,6 +20,7 @@ import { CollecticonIndent } from '../components/icons/indent';
2020

2121
const JsonEditor = React.lazy(() => import('../components/json-jsoneditor'));
2222

23+
// Extend to have access to internal methods provided by the textmode.
2324
interface JSONEditorCodeMode extends JSONEditor {
2425
compact: () => void;
2526
format: () => void;
@@ -33,28 +34,30 @@ export function WidgetJSON(props: WidgetProps) {
3334
const editorRef = useRef<JSONEditorCodeMode>(null);
3435
const [isLoaded, setIsLoaded] = useState(false);
3536

36-
const [{ value }, , { setValue }] = useField<any>(props.pointer);
37-
3837
return (
39-
<FormControl>
40-
<Flex gap={4}>
41-
{field.label && (
42-
<FormLabel>
43-
<FieldLabel size='xs'>{field.label}</FieldLabel>
44-
</FormLabel>
45-
)}
46-
{isLoaded && <ControlBar editor={editorRef.current!} />}
47-
</Flex>
38+
<FastField name={props.pointer}>
39+
{({ field: { value }, form: { setFieldValue } }: FastFieldProps) => (
40+
<FormControl>
41+
<Flex gap={4}>
42+
{field.label && (
43+
<FormLabel>
44+
<FieldLabel size='xs'>{field.label}</FieldLabel>
45+
</FormLabel>
46+
)}
47+
{isLoaded && <ControlBar editor={editorRef.current!} />}
48+
</Flex>
4849

49-
<Suspense fallback={<Loading />}>
50-
<JsonEditor
51-
value={value}
52-
onChange={setValue}
53-
editorRef={editorRef}
54-
onLoad={() => setIsLoaded(true)}
55-
/>
56-
</Suspense>
57-
</FormControl>
50+
<Suspense fallback={<Loading />}>
51+
<JsonEditor
52+
value={value}
53+
onChange={(v) => setFieldValue(props.pointer, v)}
54+
editorRef={editorRef}
55+
onLoad={() => setIsLoaded(true)}
56+
/>
57+
</Suspense>
58+
</FormControl>
59+
)}
60+
</FastField>
5861
);
5962
}
6063

Lines changed: 43 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,60 @@
11
import React from 'react';
2-
import { FormControl, FormLabel, Radio, RadioGroup } from '@chakra-ui/react';
2+
import {
3+
FormControl,
4+
FormErrorMessage,
5+
FormLabel,
6+
Radio,
7+
RadioGroup
8+
} from '@chakra-ui/react';
39
import { SchemaFieldString, WidgetProps } from '@stac-manager/data-core';
4-
import { useField } from 'formik';
10+
import { FastField, FastFieldProps } from 'formik';
511

612
import { FieldLabel } from '../components/elements';
713

814
export function WidgetRadio(props: WidgetProps) {
915
const { pointer, isRequired } = props;
1016
const field = props.field as SchemaFieldString;
1117

12-
const [{ value }, , { setValue, setTouched }] = useField<string>(pointer);
18+
const options = field.enum;
1319

14-
if (!field.enum?.length) {
20+
if (!options?.length) {
1521
throw new Error('WidgetRadio: enum is required');
1622
}
1723

1824
return (
19-
<FormControl isRequired={isRequired}>
20-
{field.label && (
21-
<FormLabel>
22-
<FieldLabel size='xs'>{field.label}</FieldLabel>
23-
</FormLabel>
25+
<FastField name={pointer}>
26+
{({
27+
field: { value },
28+
meta,
29+
form: { setFieldValue, setFieldTouched }
30+
}: FastFieldProps) => (
31+
<FormControl isRequired={isRequired}>
32+
{field.label && (
33+
<FormLabel>
34+
<FieldLabel size='xs'>{field.label}</FieldLabel>
35+
</FormLabel>
36+
)}
37+
<RadioGroup
38+
size='sm'
39+
gap={4}
40+
display='flex'
41+
value={value}
42+
onChange={(v) => {
43+
setFieldValue(pointer, v);
44+
setFieldTouched(pointer, true);
45+
}}
46+
>
47+
{options.map(([radioValue, label]) => (
48+
<Radio key={radioValue} size='sm' value={radioValue}>
49+
{label}
50+
</Radio>
51+
))}
52+
</RadioGroup>
53+
{meta.touched && meta.error && (
54+
<FormErrorMessage>{meta.error}</FormErrorMessage>
55+
)}
56+
</FormControl>
2457
)}
25-
<RadioGroup
26-
size='sm'
27-
gap={4}
28-
display='flex'
29-
value={value}
30-
onChange={(v) => {
31-
setValue(v);
32-
setTouched(true);
33-
}}
34-
>
35-
{field.enum.map(([label, radioValue]) => (
36-
<Radio key={label} size='sm' value={radioValue}>
37-
{label}
38-
</Radio>
39-
))}
40-
</RadioGroup>
41-
</FormControl>
58+
</FastField>
4259
);
4360
}

packages/data-widgets/lib/widgets/select.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export function WidgetSelect(props: WidgetProps) {
2727
placeholder='Select option'
2828
size='sm'
2929
>
30-
{field.enum.map(([label, value]) => (
30+
{field.enum.map(([value, label]) => (
3131
<option key={value} value={value}>
3232
{label}
3333
</option>

0 commit comments

Comments
 (0)