mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2026-07-12 22:14:54 +10:00
refactor(web): finding 1 - extract TypographyGroupFields component
Extract useTypographyForm helper (captures form creation + sync) and
TypographyForm type. Extract TypographyGroupFields component with
prefix "body" | "heading" to replace 2x4 duplicated form.Field blocks.
Font Weight label ("Font Weights" vs "Font Weight") is preserved per
prefix.
Claude-Session: https://claude.ai/code/session_012Bnvt1MghwHj4qQRxuQUGa
This commit is contained in:
@@ -29,6 +29,21 @@ const formSchema = typographySchema;
|
||||
|
||||
type FormValues = z.infer<typeof formSchema>;
|
||||
type FontWeight = FormValues["body"]["fontWeights"][number];
|
||||
type TypographyPrefix = "body" | "heading";
|
||||
|
||||
function useTypographyForm(typography: FormValues | undefined, persist: (data: FormValues) => void) {
|
||||
const form = useAppForm({
|
||||
defaultValues: typography,
|
||||
validators: { onChange: formSchema },
|
||||
onSubmit: ({ value }) => {
|
||||
persist(value);
|
||||
},
|
||||
});
|
||||
useSyncFormValues(form, typography);
|
||||
return form;
|
||||
}
|
||||
|
||||
type TypographyForm = ReturnType<typeof useTypographyForm>;
|
||||
|
||||
function TypographySectionForm() {
|
||||
const resume = useResume();
|
||||
@@ -42,22 +57,12 @@ function TypographySectionForm() {
|
||||
});
|
||||
};
|
||||
|
||||
const form = useAppForm({
|
||||
defaultValues: typography,
|
||||
validators: { onChange: formSchema },
|
||||
onSubmit: ({ value }) => {
|
||||
persist(value);
|
||||
},
|
||||
});
|
||||
useSyncFormValues(form, typography);
|
||||
const form = useTypographyForm(typography, persist);
|
||||
|
||||
const handleAutoSave = () => {
|
||||
persist(form.state.values);
|
||||
};
|
||||
|
||||
const bodyFontFamily = useStore(form.store, (s) => s.values.body.fontFamily);
|
||||
const headingFontFamily = useStore(form.store, (s) => s.values.heading.fontFamily);
|
||||
|
||||
return (
|
||||
<form
|
||||
className="grid @md:grid-cols-2 grid-cols-1 gap-4"
|
||||
@@ -68,134 +73,25 @@ function TypographySectionForm() {
|
||||
}}
|
||||
>
|
||||
<TypographyFieldGroup label={<Trans context="Body Text (paragraphs, lists, etc.)">Body</Trans>} />
|
||||
|
||||
<form.Field name="body.fontFamily">
|
||||
{(field) => (
|
||||
<FormItem
|
||||
className="col-span-full"
|
||||
hasError={field.state.meta.isTouched && field.state.meta.errors.length > 0}
|
||||
>
|
||||
<FormLabel>
|
||||
<Trans>Font Family</Trans>
|
||||
</FormLabel>
|
||||
<FormControl
|
||||
render={
|
||||
<FontFamilyCombobox
|
||||
value={field.state.value}
|
||||
className="text-base"
|
||||
onValueChange={(value: string | null) => {
|
||||
if (value === null) return;
|
||||
field.handleChange(value);
|
||||
const nextWeights = getNextWeights(value);
|
||||
if (nextWeights) form.setFieldValue("body.fontWeights", nextWeights);
|
||||
handleAutoSave();
|
||||
}}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<FormMessage errors={field.state.meta.errors} />
|
||||
</FormItem>
|
||||
)}
|
||||
</form.Field>
|
||||
|
||||
<form.Field name="body.fontWeights">
|
||||
{(field) => (
|
||||
<FormItem
|
||||
className="col-span-full"
|
||||
hasError={field.state.meta.isTouched && field.state.meta.errors.length > 0}
|
||||
>
|
||||
<FormLabel>
|
||||
<Trans>Font Weights</Trans>
|
||||
</FormLabel>
|
||||
<FormControl
|
||||
render={
|
||||
<FontWeightCombobox
|
||||
value={field.state.value}
|
||||
fontFamily={bodyFontFamily}
|
||||
onValueChange={(value) => {
|
||||
if (value?.length === 0) return;
|
||||
field.handleChange(value as FontWeight[]);
|
||||
handleAutoSave();
|
||||
}}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<FormMessage errors={field.state.meta.errors} />
|
||||
</FormItem>
|
||||
)}
|
||||
</form.Field>
|
||||
|
||||
<form.Field name="body.fontSize">
|
||||
{(field) => (
|
||||
<FormItem hasError={field.state.meta.isTouched && field.state.meta.errors.length > 0}>
|
||||
<FormLabel>
|
||||
<Trans>Font Size</Trans>
|
||||
</FormLabel>
|
||||
<InputGroup>
|
||||
<FormControl
|
||||
render={
|
||||
<InputGroupInput
|
||||
name={field.name}
|
||||
value={field.state.value}
|
||||
min={6}
|
||||
max={24}
|
||||
step={0.1}
|
||||
type="number"
|
||||
onBlur={field.handleBlur}
|
||||
onChange={(e) => {
|
||||
const value = e.target.value;
|
||||
if (value === "") field.handleChange("" as unknown as number);
|
||||
else field.handleChange(Number(value));
|
||||
handleAutoSave();
|
||||
}}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<InputGroupAddon align="inline-end">
|
||||
<InputGroupText>pt</InputGroupText>
|
||||
</InputGroupAddon>
|
||||
</InputGroup>
|
||||
</FormItem>
|
||||
)}
|
||||
</form.Field>
|
||||
|
||||
<form.Field name="body.lineHeight">
|
||||
{(field) => (
|
||||
<FormItem hasError={field.state.meta.isTouched && field.state.meta.errors.length > 0}>
|
||||
<FormLabel>
|
||||
<Trans>Line Height</Trans>
|
||||
</FormLabel>
|
||||
<InputGroup>
|
||||
<FormControl
|
||||
render={
|
||||
<InputGroupInput
|
||||
name={field.name}
|
||||
value={field.state.value}
|
||||
min={0.5}
|
||||
max={4}
|
||||
step={0.05}
|
||||
type="number"
|
||||
onBlur={field.handleBlur}
|
||||
onChange={(e) => {
|
||||
const value = e.target.value;
|
||||
if (value === "") field.handleChange("" as unknown as number);
|
||||
else field.handleChange(Number(value));
|
||||
handleAutoSave();
|
||||
}}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<InputGroupAddon align="inline-end">
|
||||
<InputGroupText>x</InputGroupText>
|
||||
</InputGroupAddon>
|
||||
</InputGroup>
|
||||
</FormItem>
|
||||
)}
|
||||
</form.Field>
|
||||
|
||||
<TypographyGroupFields form={form} prefix="body" handleAutoSave={handleAutoSave} />
|
||||
<TypographyFieldGroup label={<Trans context="Headings or Titles (H1, H2, H3, H4, H5, H6)">Heading</Trans>} />
|
||||
<TypographyGroupFields form={form} prefix="heading" handleAutoSave={handleAutoSave} />
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
<form.Field name="heading.fontFamily">
|
||||
type TypographyGroupFieldsProps = {
|
||||
form: TypographyForm;
|
||||
prefix: TypographyPrefix;
|
||||
handleAutoSave: () => void;
|
||||
};
|
||||
|
||||
function TypographyGroupFields({ form, prefix, handleAutoSave }: TypographyGroupFieldsProps) {
|
||||
const fontFamily = useStore(form.store, (s) => s.values[prefix].fontFamily);
|
||||
|
||||
return (
|
||||
<>
|
||||
<form.Field name={`${prefix}.fontFamily`}>
|
||||
{(field) => (
|
||||
<FormItem
|
||||
className="col-span-full"
|
||||
@@ -213,7 +109,7 @@ function TypographySectionForm() {
|
||||
if (value === null) return;
|
||||
field.handleChange(value);
|
||||
const nextWeights = getNextWeights(value);
|
||||
if (nextWeights) form.setFieldValue("heading.fontWeights", nextWeights);
|
||||
if (nextWeights) form.setFieldValue(`${prefix}.fontWeights`, nextWeights);
|
||||
handleAutoSave();
|
||||
}}
|
||||
/>
|
||||
@@ -224,20 +120,18 @@ function TypographySectionForm() {
|
||||
)}
|
||||
</form.Field>
|
||||
|
||||
<form.Field name="heading.fontWeights">
|
||||
<form.Field name={`${prefix}.fontWeights`}>
|
||||
{(field) => (
|
||||
<FormItem
|
||||
className="col-span-full"
|
||||
hasError={field.state.meta.isTouched && field.state.meta.errors.length > 0}
|
||||
>
|
||||
<FormLabel>
|
||||
<Trans>Font Weight</Trans>
|
||||
</FormLabel>
|
||||
<FormLabel>{prefix === "body" ? <Trans>Font Weights</Trans> : <Trans>Font Weight</Trans>}</FormLabel>
|
||||
<FormControl
|
||||
render={
|
||||
<FontWeightCombobox
|
||||
value={field.state.value}
|
||||
fontFamily={headingFontFamily}
|
||||
fontFamily={fontFamily}
|
||||
onValueChange={(value) => {
|
||||
if (value?.length === 0) return;
|
||||
field.handleChange(value as FontWeight[]);
|
||||
@@ -251,7 +145,7 @@ function TypographySectionForm() {
|
||||
)}
|
||||
</form.Field>
|
||||
|
||||
<form.Field name="heading.fontSize">
|
||||
<form.Field name={`${prefix}.fontSize`}>
|
||||
{(field) => (
|
||||
<FormItem hasError={field.state.meta.isTouched && field.state.meta.errors.length > 0}>
|
||||
<FormLabel>
|
||||
@@ -285,7 +179,7 @@ function TypographySectionForm() {
|
||||
)}
|
||||
</form.Field>
|
||||
|
||||
<form.Field name="heading.lineHeight">
|
||||
<form.Field name={`${prefix}.lineHeight`}>
|
||||
{(field) => (
|
||||
<FormItem hasError={field.state.meta.isTouched && field.state.meta.errors.length > 0}>
|
||||
<FormLabel>
|
||||
@@ -318,7 +212,7 @@ function TypographySectionForm() {
|
||||
</FormItem>
|
||||
)}
|
||||
</form.Field>
|
||||
</form>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user