Skip to content

Commit 91c568f

Browse files
refactor: change useForm on FieldEdition (#4320)
Closes: #4170
1 parent 7baa2fd commit 91c568f

2 files changed

Lines changed: 57 additions & 63 deletions

File tree

Lines changed: 56 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
import type { PopoverNextProps } from '@blueprintjs/core';
22
import { PopoverNext } from '@blueprintjs/core';
33
import styled from '@emotion/styled';
4-
import { yupResolver } from '@hookform/resolvers/yup';
5-
import type { KeyboardEvent, MouseEvent, ReactNode } from 'react';
6-
import { useState } from 'react';
7-
import { useForm } from 'react-hook-form';
8-
import * as Yup from 'yup';
9-
10-
import { Input2Controller } from '../elements/Input2Controller.js';
11-
import { NumberInput2Controller } from '../elements/NumberInput2Controller.js';
4+
import { revalidateLogic } from '@tanstack/react-form';
5+
import type { ReactNode } from 'react';
6+
import { useMemo, useState } from 'react';
7+
import { AppForm, coerceNumberInput, useForm } from 'react-science/ui';
8+
import { match } from 'ts-pattern';
9+
import { z } from 'zod';
1210

1311
const StyledPopover = styled(PopoverNext)`
1412
.field-edition-popover {
@@ -21,33 +19,20 @@ type InputType = 'number' | 'text';
2119
interface FieldProps {
2220
value: number | string;
2321
inputType?: InputType;
24-
onChange: (value: any) => void;
22+
onChange: (value: string) => void;
2523
}
24+
2625
interface FieldEditionsProps extends FieldProps {
2726
children: ReactNode;
2827
PopoverProps?: PopoverNextProps;
2928
}
30-
const InputDimension = { height: 28, width: 100 };
31-
32-
const validationSchema = (inputType: 'number' | 'text') =>
33-
Yup.object({
34-
value: (inputType === 'number' ? Yup.number() : Yup.string()).required(),
35-
});
36-
37-
function stopPropagation(e: MouseEvent) {
38-
e.stopPropagation();
39-
}
4029

4130
export function FieldEdition(props: FieldEditionsProps) {
4231
const { value, inputType = 'text', onChange, children, PopoverProps } = props;
4332
const [isOpen, setIsOpen] = useState(false);
4433

45-
function handleChange({
46-
value: newValue,
47-
}: {
48-
value: FieldEditionsProps['value'];
49-
}) {
50-
onChange(newValue);
34+
function handleChange(newValue: FieldEditionsProps['value']) {
35+
onChange(String(newValue));
5136
setIsOpen(false);
5237
}
5338

@@ -67,49 +52,57 @@ export function FieldEdition(props: FieldEditionsProps) {
6752
);
6853
}
6954

55+
function validationSchema(
56+
inputType: InputType,
57+
): z.ZodType<{ value: string } | { value: number }, { value: string }> {
58+
return match(inputType)
59+
.with('number', () =>
60+
z.object({
61+
value: coerceNumberInput(),
62+
}),
63+
)
64+
.with('text', () =>
65+
z.object({
66+
value: z.string(),
67+
}),
68+
)
69+
.exhaustive();
70+
}
71+
7072
function Field(props: FieldProps) {
7173
const { value, inputType = 'text', onChange } = props;
7274

73-
const { control, handleSubmit } = useForm({
74-
defaultValues: { value },
75-
resolver: yupResolver(validationSchema(inputType)),
76-
});
77-
78-
function handleKeyDown(event: KeyboardEvent<HTMLInputElement>) {
79-
if (event.key === 'Enter') {
80-
void handleSubmit(onChange)();
81-
}
82-
}
75+
const validation = useMemo(() => {
76+
return validationSchema(inputType).transform(({ value }) => {
77+
return String(value);
78+
});
79+
}, [inputType]);
8380

84-
const style = {
85-
height: `${InputDimension.height}px`,
86-
outline: 'none',
87-
};
88-
89-
if (inputType === 'number') {
90-
return (
91-
<NumberInput2Controller
92-
name="value"
93-
control={control}
94-
style={style}
95-
onKeyDown={handleKeyDown}
96-
onClick={stopPropagation}
97-
onMouseDown={stopPropagation}
98-
autoFocus
99-
buttonPosition="none"
100-
/>
101-
);
102-
}
81+
const form = useForm({
82+
validationLogic: revalidateLogic({ mode: 'change' }),
83+
defaultValues: {
84+
value,
85+
},
86+
validators: {
87+
onDynamic: validation,
88+
},
89+
onSubmit: ({ value }) => {
90+
const parsedValue = validation.parse(value);
91+
onChange(parsedValue);
92+
},
93+
});
10394

10495
return (
105-
<Input2Controller
106-
name="value"
107-
control={control}
108-
style={style}
109-
onKeyDown={handleKeyDown}
110-
onClick={stopPropagation}
111-
onMouseDown={stopPropagation}
112-
autoFocus
113-
/>
96+
<AppForm form={form}>
97+
<form.AppField name="value">
98+
{(field) =>
99+
inputType === 'number' ? (
100+
<field.NumericInput autoFocus />
101+
) : (
102+
<field.Input autoFocus />
103+
)
104+
}
105+
</form.AppField>
106+
</AppForm>
114107
);
115108
}

‎src/component/1d/ranges/AssignmentLabel.tsx‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ export function AssignmentLabel(props: AssignmentLabelProps) {
4444
}
4545
const baseYOffset = margin?.top + marginTop;
4646
const yOffset = baseYOffset + stackIndex * 12;
47+
4748
return (
4849
<g transform={`translate(${width / 2} ${yOffset})`}>
4950
<FieldEdition

0 commit comments

Comments
 (0)