Skip to content

Commit

Permalink
feat: added DynamicZoneBlock
Browse files Browse the repository at this point in the history
  • Loading branch information
Quentin Le Caignec committed Aug 20, 2024
1 parent 8bfefc0 commit f5777e1
Show file tree
Hide file tree
Showing 6 changed files with 119 additions and 24 deletions.
Original file line number Diff line number Diff line change
@@ -1,28 +1,31 @@
import type { IBaseBlock } from '@smile/haring-react/src/Form/DynamicZone/DynamicZone';
import type { ReactElement } from 'react';

import { Flex } from '@mantine/core';
import { DynamicZone } from '@smile/haring-react';
import { useFieldArray, useFormContext, useWatch } from 'react-hook-form';

export interface IFormDynamicZoneProps {
dynamicZoneName: string;
interface IFormBlock extends IBaseBlock {
fieldName: string;
value?: string;
}

interface IBaseField extends Record<string, unknown> {
readonly id: string;
export interface IFormDynamicZoneProps {
dynamicZoneName: string;
}

export function FormDynamicZone(props: IFormDynamicZoneProps): ReactElement {
const { dynamicZoneName } = props;

const { control, register, handleSubmit } = useFormContext();
const { control, register, handleSubmit } =
useFormContext<Record<typeof dynamicZoneName, Omit<IFormBlock, 'id'>[]>>();
const { fields, append, remove, swap } = useFieldArray({
control,
name: dynamicZoneName,
});
const watched = useWatch({ control, name: dynamicZoneName });

function newBlock(field: IBaseField, index: number): ReactElement {
function newBlock(field: IFormBlock, index: number): ReactElement {
return (
<input
key={field.id}
Expand All @@ -34,7 +37,7 @@ export function FormDynamicZone(props: IFormDynamicZoneProps): ReactElement {
function onAppend(): void {
append({
fieldName: `appended-${fields.length}`,
value: null,
value: '',
});
}

Expand All @@ -61,7 +64,10 @@ export function FormDynamicZone(props: IFormDynamicZoneProps): ReactElement {
{/* eslint-disable-next-line @typescript-eslint/no-misused-promises */}
<form onSubmit={handleSubmit(onSubmit)}>
form container, {dynamicZoneName}: {JSON.stringify(watched)}
<DynamicZone<IBaseField> fields={fields} onCreatingField={newBlock} />
<DynamicZone<IFormBlock>
blocks={fields as IFormBlock[]}
onCreatingBlock={newBlock}
/>
<input type="submit" />
</form>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
.container {
display: flex;
gap: 10px;
margin-top: 20px;
flex-wrap: wrap;
/* WIP */
border: 1px dashed black;
}

.blocksContainer {
}

.buttonsContainer {
}
23 changes: 23 additions & 0 deletions packages/haring-react/src/Form/DynamicZone/DynamicZone.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import type { Meta, StoryObj } from '@storybook/react';

import { DynamicZone as Cmp } from './DynamicZone';

const meta = {
component: Cmp,
tags: ['autodocs'],
title: '3-custom/Form/DynamicZone',
} satisfies Meta<typeof Cmp>;

export default meta;
type IStory = StoryObj<typeof meta>;

export const DynamicZone: IStory = {
args: {
blocks: [
{ id: '1', value: 'initial' },
{ id: '2', value: 'initial' },
{ id: '3', value: 'initial' },
],
onCreatingBlock: (_b, index) => <input key={index} />,
},
};
56 changes: 44 additions & 12 deletions packages/haring-react/src/Form/DynamicZone/DynamicZone.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,56 @@
import type { ContainerProps, GroupProps, StackProps } from '@mantine/core';
import type { ReactElement } from 'react';

import { Stack } from '@mantine/core';
import { Container, Group, Stack } from '@mantine/core';

import classes from './DynamicZone.module.css';
import { DynamicZoneBlock } from './DynamicZoneBlock/DynamicZoneBlock';

export interface IDynamicZoneProps<Field> {
fields: Field[];
onCreatingField: (field: Field, index: number) => ReactElement;
export interface IBaseBlock extends Record<string, unknown> {
readonly id: string;
}

export function DynamicZone<Field>(
props: IDynamicZoneProps<Field>,
export interface IDynamicZoneProps<Block extends IBaseBlock> {
blocks: Block[];
blocksStackProps?: StackProps;
buttonsGroupProps?: GroupProps;
onCreatingBlock: (block: Block, index: number) => ReactElement;
rootContainerProps?: ContainerProps;
}

export function DynamicZone<Block extends IBaseBlock>(
props: IDynamicZoneProps<Block>,
): ReactElement {
const { fields, onCreatingField } = props;
const {
blocks,
blocksStackProps,
buttonsGroupProps,
onCreatingBlock,
rootContainerProps,
} = props;
return (
<div className={classes.container}>
wip
<Stack>
{fields.map((field, index) => onCreatingField(field, index))}
<Container
className={classes.container}
fluid
p={10}
{...rootContainerProps}
>
<Stack className={classes.blocksContainer} {...blocksStackProps}>
{blocks.map((block, index) => (
<DynamicZoneBlock key={block.id}>
{onCreatingBlock(block, index)}
</DynamicZoneBlock>
))}
</Stack>
</div>
<Group
className={classes.buttonsContainer}
mt={20}
{...buttonsGroupProps}
>
<button type="button">A</button>
<button type="button">B</button>
<button type="button">C</button>
</Group>
</Container>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.container {
/* WIP */
border: 1px red dashed;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import type { ContainerProps } from '@mantine/core';
import type { ReactElement, ReactNode } from 'react';

import { Container } from '@mantine/core';

import classes from './DynamicZoneBlock.module.css';

export interface IDynamicZoneBlockProps {
children: ReactNode;
containerProps?: ContainerProps;
}

export function DynamicZoneBlock(props: IDynamicZoneBlockProps): ReactElement {
const { children, containerProps } = props;
return (
<Container
className={classes.container}
fluid
m={0}
p={10}
{...containerProps}
>
{children}
</Container>
);
}

0 comments on commit f5777e1

Please sign in to comment.