@twintag/twintag-form 1.3.0 → 1.4.0-beta.1
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/.turbo/turbo-build.log +6 -6
- package/CHANGELOG.md +12 -0
- package/README.md +121 -62
- package/dev/main.tsx +233 -210
- package/dist/common/button.d.ts +17 -30
- package/dist/common/calendar.d.ts +212 -4
- package/dist/common/form.d.ts +151 -9
- package/dist/common/icon.d.ts +2 -1
- package/dist/common/input.d.ts +276 -2
- package/dist/common/label.d.ts +39 -1
- package/dist/common/slider.d.ts +4 -0
- package/dist/common/textarea.d.ts +139 -2
- package/dist/components/config.d.ts +4 -2
- package/dist/components/containers/accordion.d.ts +3 -2
- package/dist/components/containers/category.d.ts +3 -2
- package/dist/components/fields/checkbox.d.ts +4 -2
- package/dist/components/fields/date-picker.d.ts +3 -1
- package/dist/components/fields/file.d.ts +4 -2
- package/dist/components/fields/input.d.ts +4 -2
- package/dist/components/fields/radiogroup.d.ts +4 -2
- package/dist/components/fields/submit.d.ts +3 -2
- package/dist/components/fields/textarea.d.ts +4 -2
- package/dist/components/fields/toggle.d.ts +4 -2
- package/dist/components/fields/tri-toggle.d.ts +5 -0
- package/dist/components/form.d.ts +2 -2
- package/dist/components/provider.d.ts +21 -0
- package/dist/components/renderer.d.ts +1 -0
- package/dist/index.d.ts +5 -3
- package/dist/twintag-form.react.js +12868 -12156
- package/dist/types/index.d.ts +25 -16
- package/dist/utils/data.d.ts +2 -1
- package/dist/utils/style.d.ts +2 -1
- package/index.html +5 -3
- package/lib/common/button.tsx +4 -5
- package/lib/common/calendar.tsx +25 -5
- package/lib/common/form.tsx +40 -19
- package/lib/common/input.tsx +20 -10
- package/lib/common/label.tsx +16 -3
- package/lib/common/slider.tsx +38 -0
- package/lib/common/textarea.tsx +17 -9
- package/lib/components/config.tsx +46 -17
- package/lib/components/containers/accordion.tsx +24 -7
- package/lib/components/containers/category.tsx +16 -7
- package/lib/components/fields/checkbox.tsx +12 -6
- package/lib/components/fields/date-picker.tsx +54 -18
- package/lib/components/fields/file.tsx +83 -76
- package/lib/components/fields/input.tsx +15 -6
- package/lib/components/fields/radiogroup.tsx +13 -7
- package/lib/components/fields/submit.tsx +5 -2
- package/lib/components/fields/textarea.tsx +14 -6
- package/lib/components/fields/toggle.tsx +12 -6
- package/lib/components/fields/tri-toggle.tsx +141 -0
- package/lib/components/form.tsx +5 -7
- package/lib/components/provider.tsx +159 -0
- package/lib/components/{Render/index.tsx → renderer.tsx} +19 -2
- package/lib/index.tsx +41 -36
- package/lib/types/index.ts +49 -39
- package/package.json +5 -7
- package/dist/components/Render/index.d.ts +0 -7
- package/dist/components/Render/useJson.d.ts +0 -2
- package/dist/components/index.d.ts +0 -7
- package/dist/provider.d.ts +0 -8
- package/lib/components/Render/useJson.ts +0 -76
- package/lib/components/index.tsx +0 -24
- package/lib/provider.tsx +0 -36
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import type { FC } from 'react';
|
|
2
|
+
import type { Props } from '@twintag/twintag-form/types';
|
|
3
|
+
import type { VariantProps } from 'tailwind-variants';
|
|
4
|
+
|
|
5
|
+
import * as React from 'react';
|
|
6
|
+
import * as SliderPrimitive from '@radix-ui/react-slider';
|
|
7
|
+
import { useFormContext } from 'react-hook-form';
|
|
8
|
+
import { tv } from 'tailwind-variants';
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
FormControl,
|
|
12
|
+
FormDescription,
|
|
13
|
+
FormField,
|
|
14
|
+
FormItem,
|
|
15
|
+
FormLabel,
|
|
16
|
+
FormMessage,
|
|
17
|
+
} from '@twintag/twintag-form/common/form';
|
|
18
|
+
import { useTwintagForm } from '@twintag/twintag-form/components/provider';
|
|
19
|
+
|
|
20
|
+
const sliderVariants = tv({
|
|
21
|
+
slots: {
|
|
22
|
+
root: 'relative flex w-11 touch-none select-none items-center',
|
|
23
|
+
track: 'relative h-5 w-full grow overflow-hidden rounded-full bg-muted',
|
|
24
|
+
range: 'absolute h-full bg-accent',
|
|
25
|
+
thumb:
|
|
26
|
+
'relative block h-4 w-4 rounded-full border-2 border-transparent bg-background shadow-lg focus-visible:outline-none focus-visible:ring-0 disabled:pointer-events-none disabled:opacity-50',
|
|
27
|
+
},
|
|
28
|
+
variants: {
|
|
29
|
+
state: {
|
|
30
|
+
// false
|
|
31
|
+
0: {
|
|
32
|
+
thumb: 'left-0.5',
|
|
33
|
+
},
|
|
34
|
+
// null
|
|
35
|
+
1: {
|
|
36
|
+
thumb: 'h-5 w-5 border-accent',
|
|
37
|
+
},
|
|
38
|
+
// true
|
|
39
|
+
2: {
|
|
40
|
+
thumb: 'right-0.5',
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
defaultVariants: {
|
|
45
|
+
state: 1,
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
const Slider = React.forwardRef<
|
|
50
|
+
React.ElementRef<typeof SliderPrimitive.Root>,
|
|
51
|
+
React.ComponentPropsWithoutRef<typeof SliderPrimitive.Root> & {
|
|
52
|
+
classNames?: Record<keyof (typeof sliderVariants)['slots'], string>;
|
|
53
|
+
}
|
|
54
|
+
>(({ className, ...props }, ref) => {
|
|
55
|
+
const val = props.value?.[0] as VariantProps<typeof sliderVariants>['state'];
|
|
56
|
+
|
|
57
|
+
const { root, track, range, thumb } = sliderVariants({
|
|
58
|
+
state: val,
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
return (
|
|
62
|
+
<SliderPrimitive.Root
|
|
63
|
+
disabled
|
|
64
|
+
ref={ref}
|
|
65
|
+
className={root({
|
|
66
|
+
className,
|
|
67
|
+
})}
|
|
68
|
+
onClick={(e) => {
|
|
69
|
+
e.preventDefault();
|
|
70
|
+
|
|
71
|
+
if (val !== undefined) {
|
|
72
|
+
const states = Object.keys(sliderVariants.variants.state);
|
|
73
|
+
const value = states.indexOf(val.toString());
|
|
74
|
+
const nextValue = value === states.length - 1 ? 0 : value + 1;
|
|
75
|
+
props.onValueChange?.([nextValue]);
|
|
76
|
+
}
|
|
77
|
+
}}
|
|
78
|
+
{...props}
|
|
79
|
+
>
|
|
80
|
+
<SliderPrimitive.Track className={track()}>
|
|
81
|
+
<SliderPrimitive.Range className={range()} />
|
|
82
|
+
</SliderPrimitive.Track>
|
|
83
|
+
<SliderPrimitive.Thumb className={thumb()} />
|
|
84
|
+
</SliderPrimitive.Root>
|
|
85
|
+
);
|
|
86
|
+
});
|
|
87
|
+
Slider.displayName = SliderPrimitive.Root.displayName;
|
|
88
|
+
|
|
89
|
+
export const DEFAULT_TRI_TOGGLE_VALUE = null;
|
|
90
|
+
|
|
91
|
+
export const TriToggle: FC<Props> = ({ label, bind, options }) => {
|
|
92
|
+
const { control } = useFormContext();
|
|
93
|
+
const { variant, size } = useTwintagForm();
|
|
94
|
+
|
|
95
|
+
return (
|
|
96
|
+
<FormField
|
|
97
|
+
defaultValue={DEFAULT_TRI_TOGGLE_VALUE}
|
|
98
|
+
control={control}
|
|
99
|
+
name={bind}
|
|
100
|
+
rules={{
|
|
101
|
+
required: false,
|
|
102
|
+
}}
|
|
103
|
+
render={({ field }) => (
|
|
104
|
+
<FormItem variant={variant}>
|
|
105
|
+
<div className='flex flex-row items-center justify-between'>
|
|
106
|
+
<div className='space-y-1'>
|
|
107
|
+
<FormLabel htmlFor={bind} size={size}>
|
|
108
|
+
{label}
|
|
109
|
+
</FormLabel>
|
|
110
|
+
{options?.description ? (
|
|
111
|
+
<FormDescription size={size}>
|
|
112
|
+
{options.description}
|
|
113
|
+
</FormDescription>
|
|
114
|
+
) : (
|
|
115
|
+
<></>
|
|
116
|
+
)}
|
|
117
|
+
</div>
|
|
118
|
+
<FormControl>
|
|
119
|
+
<Slider
|
|
120
|
+
id={bind}
|
|
121
|
+
ref={field.ref}
|
|
122
|
+
min={0}
|
|
123
|
+
max={2}
|
|
124
|
+
step={1}
|
|
125
|
+
value={[
|
|
126
|
+
field.value === true ? 2 : field.value === null ? 1 : 0,
|
|
127
|
+
]}
|
|
128
|
+
onValueChange={(value) => {
|
|
129
|
+
field.onChange(
|
|
130
|
+
value[0] === 2 ? true : value[0] === 1 ? null : false,
|
|
131
|
+
);
|
|
132
|
+
}}
|
|
133
|
+
/>
|
|
134
|
+
</FormControl>
|
|
135
|
+
</div>
|
|
136
|
+
<FormMessage size={size} />
|
|
137
|
+
</FormItem>
|
|
138
|
+
)}
|
|
139
|
+
/>
|
|
140
|
+
);
|
|
141
|
+
};
|
package/lib/components/form.tsx
CHANGED
|
@@ -5,6 +5,7 @@ import { forwardRef, useEffect, useImperativeHandle, useRef } from 'react';
|
|
|
5
5
|
import { useForm } from 'react-hook-form';
|
|
6
6
|
|
|
7
7
|
import { Form } from '@twintag/twintag-form/common/form';
|
|
8
|
+
import { useTwintagForm } from '@twintag/twintag-form/components/provider';
|
|
8
9
|
|
|
9
10
|
type Props = {
|
|
10
11
|
defaultValues?: FieldValues;
|
|
@@ -13,6 +14,8 @@ type Props = {
|
|
|
13
14
|
|
|
14
15
|
export const TwintagForm = forwardRef<HTMLFormElement, Props>(
|
|
15
16
|
({ defaultValues, resetOnSubmit, children }, fowardedRef) => {
|
|
17
|
+
const { resetValues } = useTwintagForm();
|
|
18
|
+
|
|
16
19
|
const ref = useRef<HTMLFormElement>(null);
|
|
17
20
|
|
|
18
21
|
const form = useForm({
|
|
@@ -24,15 +27,10 @@ export const TwintagForm = forwardRef<HTMLFormElement, Props>(
|
|
|
24
27
|
useEffect(() => {
|
|
25
28
|
if (form.formState.isSubmitSuccessful && resetOnSubmit) {
|
|
26
29
|
if (defaultValues) {
|
|
27
|
-
form.reset(
|
|
28
|
-
Object.keys(defaultValues).reduce(
|
|
29
|
-
(acc, key) => ({ ...acc, [key]: '' }),
|
|
30
|
-
{},
|
|
31
|
-
),
|
|
32
|
-
);
|
|
30
|
+
form.reset(resetValues || defaultValues);
|
|
33
31
|
}
|
|
34
32
|
}
|
|
35
|
-
}, [form.formState]);
|
|
33
|
+
}, [form.formState, defaultValues]);
|
|
36
34
|
|
|
37
35
|
const onValid = (data: FieldValues) => {
|
|
38
36
|
const eventWithValidData = new CustomEvent('twintag-form-valid', {
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ParsedJsonComponent,
|
|
3
|
+
RawJsonComponent,
|
|
4
|
+
TwintagFormContext,
|
|
5
|
+
} from '@twintag/twintag-form/types';
|
|
6
|
+
import type { WithChildren } from 'twintag-types/dom';
|
|
7
|
+
|
|
8
|
+
import { createContext, useContext, useMemo } from 'react';
|
|
9
|
+
import { LabelToString } from 'twintag-types/dom';
|
|
10
|
+
import { v4 as uuid } from 'uuid';
|
|
11
|
+
|
|
12
|
+
export const twintagFormContext = createContext<TwintagFormContext>({
|
|
13
|
+
variant: 'default',
|
|
14
|
+
size: 'base',
|
|
15
|
+
inputs: new Map(),
|
|
16
|
+
components: new Map(),
|
|
17
|
+
config: [],
|
|
18
|
+
resetValues: {},
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
export const useTwintagForm = () => {
|
|
22
|
+
const context = useContext(twintagFormContext);
|
|
23
|
+
|
|
24
|
+
if (!context) {
|
|
25
|
+
throw new Error('useTwintagForm must be used within a TwintagFormProvider');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return context;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
type Components = TwintagFormContext['components'];
|
|
32
|
+
type Inputs = TwintagFormContext['inputs'];
|
|
33
|
+
|
|
34
|
+
const format = (component: RawJsonComponent, components: Components) => {
|
|
35
|
+
const copy = { ...component };
|
|
36
|
+
let formattedConfig: ParsedJsonComponent;
|
|
37
|
+
|
|
38
|
+
if (copy.props) {
|
|
39
|
+
formattedConfig = { ...copy.props } as ParsedJsonComponent;
|
|
40
|
+
} else {
|
|
41
|
+
formattedConfig = { ...copy } as unknown as ParsedJsonComponent;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (!copy.type) return Object.create(null) as ParsedJsonComponent;
|
|
45
|
+
|
|
46
|
+
const [type, subtype = undefined] = copy.type.split(':');
|
|
47
|
+
formattedConfig.id = `${type}-${uuid()}`;
|
|
48
|
+
|
|
49
|
+
const comp = components.get(type);
|
|
50
|
+
|
|
51
|
+
if (comp) {
|
|
52
|
+
formattedConfig.comp = comp;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
formattedConfig.type = type;
|
|
56
|
+
|
|
57
|
+
if (subtype) {
|
|
58
|
+
formattedConfig.subtype = subtype;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (copy.props) {
|
|
62
|
+
if (copy.props.subtype) {
|
|
63
|
+
formattedConfig.subtype = copy.props.subtype;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (copy.props.label) {
|
|
67
|
+
const labelParsed = LabelToString.safeParse(copy.props.label);
|
|
68
|
+
formattedConfig.label = labelParsed.success
|
|
69
|
+
? labelParsed.data
|
|
70
|
+
: undefined;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (copy.children && Array.isArray(copy.children)) {
|
|
75
|
+
formattedConfig.items = copy.children.map((item) =>
|
|
76
|
+
format(item, components),
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return formattedConfig;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
export const getResetValues = (
|
|
84
|
+
config: RawJsonComponent | RawJsonComponent[],
|
|
85
|
+
inputs: Inputs,
|
|
86
|
+
) => {
|
|
87
|
+
const stack = Array.isArray(config) ? [...config] : [config];
|
|
88
|
+
|
|
89
|
+
const resetValues = new Map<string, unknown>();
|
|
90
|
+
|
|
91
|
+
while (stack.length > 0) {
|
|
92
|
+
const currentObj = stack.pop();
|
|
93
|
+
|
|
94
|
+
if (!currentObj) continue;
|
|
95
|
+
|
|
96
|
+
for (const [key, value] of Object.entries(currentObj)) {
|
|
97
|
+
if (Array.isArray(value)) {
|
|
98
|
+
for (const item of value) {
|
|
99
|
+
stack.push(item as unknown as RawJsonComponent);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (typeof value === 'object' && value !== null) {
|
|
104
|
+
stack.push(value as unknown as RawJsonComponent);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (key === 'subtype' && currentObj.bind) {
|
|
108
|
+
resetValues.set(
|
|
109
|
+
currentObj.bind,
|
|
110
|
+
inputs.get(value as string)?.defaultValue,
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return Object.fromEntries(resetValues);
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
export const TwintagFormProvider = ({
|
|
120
|
+
variant,
|
|
121
|
+
size,
|
|
122
|
+
inputs,
|
|
123
|
+
components,
|
|
124
|
+
config,
|
|
125
|
+
children,
|
|
126
|
+
}: {
|
|
127
|
+
config: RawJsonComponent | RawJsonComponent[];
|
|
128
|
+
variant: TwintagFormContext['variant'];
|
|
129
|
+
size: TwintagFormContext['size'];
|
|
130
|
+
inputs: Inputs;
|
|
131
|
+
components: Components;
|
|
132
|
+
} & WithChildren) => {
|
|
133
|
+
const __config = useMemo(() => {
|
|
134
|
+
if (Array.isArray(config)) {
|
|
135
|
+
return config.map((item) => format(item, components));
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return format(config, components);
|
|
139
|
+
}, [config, components]);
|
|
140
|
+
|
|
141
|
+
const resetValues = useMemo(() => {
|
|
142
|
+
return getResetValues(__config, inputs);
|
|
143
|
+
}, [__config, inputs]);
|
|
144
|
+
|
|
145
|
+
return (
|
|
146
|
+
<twintagFormContext.Provider
|
|
147
|
+
value={{
|
|
148
|
+
config: __config,
|
|
149
|
+
variant,
|
|
150
|
+
size,
|
|
151
|
+
inputs,
|
|
152
|
+
components,
|
|
153
|
+
resetValues,
|
|
154
|
+
}}
|
|
155
|
+
>
|
|
156
|
+
{children}
|
|
157
|
+
</twintagFormContext.Provider>
|
|
158
|
+
);
|
|
159
|
+
};
|
|
@@ -2,12 +2,13 @@ import type { FC } from 'react';
|
|
|
2
2
|
import type { ParsedJsonComponent } from '@twintag/twintag-form/types';
|
|
3
3
|
|
|
4
4
|
import { Containers } from '@twintag/twintag-form/components/config';
|
|
5
|
+
import { useTwintagForm } from '@twintag/twintag-form/components/provider';
|
|
5
6
|
|
|
6
7
|
type Props = {
|
|
7
8
|
config: ParsedJsonComponent;
|
|
8
9
|
};
|
|
9
10
|
|
|
10
|
-
|
|
11
|
+
const RecursiveRenderer: FC<Props> = ({ config }) => {
|
|
11
12
|
const { id, type, items, comp, ...rest } = config;
|
|
12
13
|
|
|
13
14
|
if (!type || !comp) return <></>;
|
|
@@ -19,7 +20,7 @@ export const Renderer: FC<Props> = ({ config }) => {
|
|
|
19
20
|
return (
|
|
20
21
|
<Component key={id} items={items} {...rest}>
|
|
21
22
|
{items.map((item) => (
|
|
22
|
-
<
|
|
23
|
+
<RecursiveRenderer key={item.id} config={{ ...item }} />
|
|
23
24
|
))}
|
|
24
25
|
</Component>
|
|
25
26
|
);
|
|
@@ -30,3 +31,19 @@ export const Renderer: FC<Props> = ({ config }) => {
|
|
|
30
31
|
|
|
31
32
|
return <Component key={id} {...rest} />;
|
|
32
33
|
};
|
|
34
|
+
|
|
35
|
+
export const JsonRenderer = () => {
|
|
36
|
+
const { config } = useTwintagForm();
|
|
37
|
+
|
|
38
|
+
if (Array.isArray(config)) {
|
|
39
|
+
return (
|
|
40
|
+
<>
|
|
41
|
+
{config.map((item) => (
|
|
42
|
+
<RecursiveRenderer key={item.id} config={{ ...item }} />
|
|
43
|
+
))}
|
|
44
|
+
</>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return <RecursiveRenderer config={config} />;
|
|
49
|
+
};
|
package/lib/index.tsx
CHANGED
|
@@ -1,36 +1,41 @@
|
|
|
1
|
-
import type { ComponentProps } from 'react';
|
|
2
|
-
import type { RawJsonComponent } from '@twintag/twintag-form/types';
|
|
3
|
-
|
|
4
|
-
import { forwardRef } from 'react';
|
|
5
|
-
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
type Props = {
|
|
12
|
-
config: RawJsonComponent[];
|
|
13
|
-
options?: Partial<{
|
|
14
|
-
defaultValues?: ComponentProps<typeof TwintagForm>['defaultValues'];
|
|
15
|
-
variant?: ComponentProps<typeof TwintagFormProvider>['variant'];
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
1
|
+
import type { ComponentProps } from 'react';
|
|
2
|
+
import type { RawJsonComponent } from '@twintag/twintag-form/types';
|
|
3
|
+
|
|
4
|
+
import { forwardRef } from 'react';
|
|
5
|
+
|
|
6
|
+
import { components, Inputs } from '@twintag/twintag-form/components/config';
|
|
7
|
+
import { TwintagForm } from '@twintag/twintag-form/components/form';
|
|
8
|
+
import { TwintagFormProvider } from '@twintag/twintag-form/components/provider';
|
|
9
|
+
import { JsonRenderer } from '@twintag/twintag-form/components/renderer';
|
|
10
|
+
|
|
11
|
+
type Props = {
|
|
12
|
+
config: RawJsonComponent[];
|
|
13
|
+
options?: Partial<{
|
|
14
|
+
defaultValues?: ComponentProps<typeof TwintagForm>['defaultValues'];
|
|
15
|
+
variant?: ComponentProps<typeof TwintagFormProvider>['variant'];
|
|
16
|
+
size?: ComponentProps<typeof TwintagFormProvider>['size'];
|
|
17
|
+
resetOnSubmit?: boolean;
|
|
18
|
+
}>;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const Form = forwardRef<HTMLFormElement, Props>(
|
|
22
|
+
({ config, options }, ref) => {
|
|
23
|
+
return (
|
|
24
|
+
<TwintagFormProvider
|
|
25
|
+
inputs={Inputs}
|
|
26
|
+
components={components}
|
|
27
|
+
variant={options?.variant}
|
|
28
|
+
size={options?.size}
|
|
29
|
+
config={config}
|
|
30
|
+
>
|
|
31
|
+
<TwintagForm
|
|
32
|
+
ref={ref}
|
|
33
|
+
defaultValues={options?.defaultValues}
|
|
34
|
+
resetOnSubmit={options?.resetOnSubmit}
|
|
35
|
+
>
|
|
36
|
+
<JsonRenderer />
|
|
37
|
+
</TwintagForm>
|
|
38
|
+
</TwintagFormProvider>
|
|
39
|
+
);
|
|
40
|
+
},
|
|
41
|
+
);
|
package/lib/types/index.ts
CHANGED
|
@@ -1,39 +1,49 @@
|
|
|
1
|
-
import type { ElementType } from 'react';
|
|
2
|
-
|
|
3
|
-
export type
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
};
|
|
7
|
-
|
|
8
|
-
type
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export type Props = Omit<RawProps, 'subtype'> & {
|
|
37
|
-
bind: string;
|
|
38
|
-
items?: ParsedJsonComponent[];
|
|
39
|
-
};
|
|
1
|
+
import type { ElementType } from 'react';
|
|
2
|
+
|
|
3
|
+
export type ComponentConfig = {
|
|
4
|
+
defaultValue?: unknown;
|
|
5
|
+
component: ElementType;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export type ParsedJsonComponent = Props & {
|
|
9
|
+
id: string;
|
|
10
|
+
type: string;
|
|
11
|
+
subtype?: string;
|
|
12
|
+
comp?: ElementType;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export type RawJsonComponent = RawProps &
|
|
16
|
+
Record<string, unknown> & {
|
|
17
|
+
type: string;
|
|
18
|
+
children?: RawJsonComponent[];
|
|
19
|
+
} & {
|
|
20
|
+
props?: RawProps;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
type RawProps = Partial<{
|
|
24
|
+
bind: string;
|
|
25
|
+
subtype: string;
|
|
26
|
+
label: string;
|
|
27
|
+
icon: string;
|
|
28
|
+
options: Partial<{
|
|
29
|
+
description: string;
|
|
30
|
+
placeholder: string;
|
|
31
|
+
error: string;
|
|
32
|
+
optionalField: boolean;
|
|
33
|
+
}>;
|
|
34
|
+
}>;
|
|
35
|
+
|
|
36
|
+
export type Props = Omit<RawProps, 'subtype'> & {
|
|
37
|
+
bind: string;
|
|
38
|
+
items?: ParsedJsonComponent[];
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export type TwintagFormContext = {
|
|
42
|
+
variant?: 'default' | 'beta';
|
|
43
|
+
size?: 'base' | 'sm';
|
|
44
|
+
inputs: Map<string, ComponentConfig>;
|
|
45
|
+
components: Map<string, ElementType>;
|
|
46
|
+
config: ParsedJsonComponent[] | ParsedJsonComponent;
|
|
47
|
+
resetValues: Record<string, unknown>;
|
|
48
|
+
};
|
|
49
|
+
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@twintag/twintag-form",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0-beta.1",
|
|
4
4
|
"author": "Twintag",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
"@radix-ui/react-radio-group": "^1.1.3",
|
|
25
25
|
"@radix-ui/react-scroll-area": "^1.0.5",
|
|
26
26
|
"@radix-ui/react-select": "^2.0.0",
|
|
27
|
+
"@radix-ui/react-slider": "^1.1.2",
|
|
27
28
|
"@radix-ui/react-slot": "^1.0.2",
|
|
28
29
|
"@radix-ui/react-switch": "^1.0.2",
|
|
29
30
|
"@storybook/react": "7.0.2",
|
|
@@ -42,15 +43,12 @@
|
|
|
42
43
|
"uuid": "^9.0.1"
|
|
43
44
|
},
|
|
44
45
|
"devDependencies": {
|
|
45
|
-
"@rollup/plugin-typescript": "^11.1.1",
|
|
46
|
-
"@storybook/addon-actions": "^7.0.20",
|
|
47
46
|
"@twind/with-react": "^1.1.3",
|
|
48
47
|
"@types/uuid": "^9.0.7",
|
|
49
|
-
"@vitejs/plugin-react-swc": "^3.
|
|
50
|
-
"rollup": "^3.18.0",
|
|
48
|
+
"@vitejs/plugin-react-swc": "^3.6.0",
|
|
51
49
|
"tailwindcss": "^3.3.6",
|
|
52
|
-
"vite": "^
|
|
53
|
-
"vite-plugin-dts": "^3.
|
|
50
|
+
"vite": "^5.2.11",
|
|
51
|
+
"vite-plugin-dts": "^3.9.1",
|
|
54
52
|
"eslint-config-custom": "1.0.0",
|
|
55
53
|
"tsconfig": "0.0.0",
|
|
56
54
|
"twintag-types": "0.2.0"
|
package/dist/provider.d.ts
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { JSX } from 'react/jsx-runtime';
|
|
2
|
-
import { Context } from 'react';
|
|
3
|
-
/// <reference types="react" />
|
|
4
|
-
import type { TwintagFormContext } from './types';
|
|
5
|
-
import type { WithChildren } from 'twintag-types/dom';
|
|
6
|
-
export declare const twintagFormContext: Context<TwintagFormContext>;
|
|
7
|
-
export declare const useTwintagForm: () => TwintagFormContext;
|
|
8
|
-
export declare const TwintagFormProvider: ({ children, variant, components, }: TwintagFormContext & WithChildren) => JSX.Element;
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
ParsedJsonComponent,
|
|
3
|
-
RawJsonComponent,
|
|
4
|
-
TwintagFormContext,
|
|
5
|
-
} from '@twintag/twintag-form/types';
|
|
6
|
-
|
|
7
|
-
import { useMemo } from 'react';
|
|
8
|
-
import { LabelToString } from 'twintag-types/dom';
|
|
9
|
-
import { v4 as uuid } from 'uuid';
|
|
10
|
-
|
|
11
|
-
import { useTwintagForm } from '@twintag/twintag-form/provider';
|
|
12
|
-
|
|
13
|
-
type Components = TwintagFormContext['components'];
|
|
14
|
-
|
|
15
|
-
const format = (component: RawJsonComponent, components: Components) => {
|
|
16
|
-
const copy = { ...component };
|
|
17
|
-
let formattedConfig: ParsedJsonComponent;
|
|
18
|
-
|
|
19
|
-
if (copy.props) {
|
|
20
|
-
formattedConfig = { ...copy.props } as ParsedJsonComponent;
|
|
21
|
-
} else {
|
|
22
|
-
formattedConfig = { ...copy } as unknown as ParsedJsonComponent;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
if (!copy.type) return Object.create(null) as ParsedJsonComponent;
|
|
26
|
-
|
|
27
|
-
const [type, subtype = undefined] = copy.type.split(':');
|
|
28
|
-
formattedConfig.id = `${type}-${uuid()}`;
|
|
29
|
-
|
|
30
|
-
const comp = components.get(type);
|
|
31
|
-
|
|
32
|
-
if (comp) {
|
|
33
|
-
formattedConfig.comp = comp;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
formattedConfig.type = type;
|
|
37
|
-
|
|
38
|
-
if (subtype) {
|
|
39
|
-
formattedConfig.subtype = subtype;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
if (copy.props) {
|
|
43
|
-
if (copy.props.subtype) {
|
|
44
|
-
formattedConfig.subtype = copy.props.subtype;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
if (copy.props.label) {
|
|
48
|
-
const labelParsed = LabelToString.safeParse(copy.props.label);
|
|
49
|
-
formattedConfig.label = labelParsed.success
|
|
50
|
-
? labelParsed.data
|
|
51
|
-
: undefined;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
if (copy.children && Array.isArray(copy.children)) {
|
|
56
|
-
formattedConfig.items = copy.children.map((item) =>
|
|
57
|
-
format(item, components),
|
|
58
|
-
);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
return formattedConfig;
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
export const useJson = (config: RawJsonComponent | RawJsonComponent[]) => {
|
|
65
|
-
const { components } = useTwintagForm();
|
|
66
|
-
|
|
67
|
-
const __config = useMemo(() => {
|
|
68
|
-
if (Array.isArray(config)) {
|
|
69
|
-
return config.map((item) => format(item, components));
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
return format(config, components);
|
|
73
|
-
}, [config, components]);
|
|
74
|
-
|
|
75
|
-
return __config;
|
|
76
|
-
};
|