@twintag/twintag-form 1.0.1 → 1.1.0
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 +18 -0
- package/README.md +61 -3
- package/dev/index.css +2 -0
- package/dev/main.tsx +50 -27
- package/dist/common/accordion.d.ts +7 -0
- package/dist/common/button.d.ts +28 -0
- package/dist/common/calendar.d.ts +9 -0
- package/dist/common/form.d.ts +1 -1
- package/dist/common/icon.d.ts +7 -0
- package/dist/common/popover.d.ts +6 -0
- package/dist/common/scroll-area.d.ts +5 -0
- package/dist/common/select.d.ts +13 -0
- package/dist/components/config.d.ts +4 -0
- package/dist/components/containers/accordion.d.ts +8 -0
- package/dist/components/fields/date-picker.d.ts +3 -0
- package/dist/components/{Input/simple.d.ts → fields/input.d.ts} +1 -1
- package/dist/components/form.d.ts +3 -1
- package/dist/components/index.d.ts +1 -2
- package/dist/index.d.ts +2 -0
- package/dist/twintag-form.react.js +19844 -6458
- package/dist/twintag-form.webcomponent.js +314 -299
- package/dist/types/index.d.ts +6 -5
- package/dist/utils/data.d.ts +2 -0
- package/dist/utils/string.d.ts +3 -0
- package/dist/utils/style.d.ts +2 -0
- package/index.html +3 -1
- package/lib/common/accordion.tsx +77 -0
- package/lib/common/button.tsx +3 -0
- package/lib/common/calendar.tsx +110 -0
- package/lib/common/form.tsx +14 -1
- package/lib/common/icon.tsx +35 -0
- package/lib/common/input.tsx +2 -1
- package/lib/common/popover.tsx +30 -0
- package/lib/common/scroll-area.tsx +46 -0
- package/lib/common/select.tsx +159 -0
- package/lib/common/textarea.tsx +1 -1
- package/lib/components/Render/index.tsx +4 -2
- package/lib/components/config.tsx +44 -0
- package/lib/components/containers/accordion.tsx +105 -0
- package/lib/components/containers/category.tsx +48 -0
- package/lib/components/fields/date-picker.tsx +117 -0
- package/lib/components/{Input → fields}/file.tsx +1 -1
- package/lib/components/{Input/simple.tsx → fields/input.tsx} +5 -5
- package/lib/components/{Input → fields}/textarea.tsx +2 -2
- package/lib/components/form.tsx +19 -3
- package/lib/components/index.tsx +0 -3
- package/lib/index.tsx +7 -8
- package/lib/types/index.ts +13 -11
- package/lib/utils/data.ts +18 -0
- package/lib/utils/string.ts +12 -0
- package/lib/utils/style.ts +8 -0
- package/package.json +13 -5
- package/tailwind.config.ts +19 -1
- package/.turbo/turbo-lint.log +0 -4
- package/dist/components/Input/index.d.ts +0 -8
- package/lib/components/Input/index.tsx +0 -32
- package/lib/components/category.tsx +0 -31
- /package/dist/components/{category.d.ts → containers/category.d.ts} +0 -0
- /package/dist/components/{Input → fields}/checkbox.d.ts +0 -0
- /package/dist/components/{Input → fields}/file.d.ts +0 -0
- /package/dist/components/{Input → fields}/radiogroup.d.ts +0 -0
- /package/dist/components/{Input → fields}/submit.d.ts +0 -0
- /package/dist/components/{Input → fields}/textarea.d.ts +0 -0
- /package/dist/components/{Input → fields}/toggle.d.ts +0 -0
- /package/lib/components/{Input → fields}/checkbox.tsx +0 -0
- /package/lib/components/{Input → fields}/radiogroup.tsx +0 -0
- /package/lib/components/{Input → fields}/submit.tsx +0 -0
- /package/lib/components/{Input → fields}/toggle.tsx +0 -0
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import type { Props } from '@twintag/twintag-form/types';
|
|
2
|
+
import type { WithChildren } from 'twintag-types/dom';
|
|
3
|
+
|
|
4
|
+
import { type FC } from 'react';
|
|
5
|
+
import { tv } from 'tailwind-variants';
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
AccordionContent,
|
|
9
|
+
AccordionItem as AccordionItemPrimitive,
|
|
10
|
+
Accordion as AccordionPrimitive,
|
|
11
|
+
AccordionTrigger,
|
|
12
|
+
} from '@twintag/twintag-form/common/accordion';
|
|
13
|
+
import { Icon } from '@twintag/twintag-form/common/icon';
|
|
14
|
+
import { useTwintagForm } from '@twintag/twintag-form/provider';
|
|
15
|
+
import { camelize } from '@twintag/twintag-form/utils/string';
|
|
16
|
+
|
|
17
|
+
type AccordionRootProps = {
|
|
18
|
+
multiple?: boolean;
|
|
19
|
+
} & WithChildren;
|
|
20
|
+
|
|
21
|
+
const accordionRootVariants = tv({
|
|
22
|
+
base: 'border',
|
|
23
|
+
variants: {
|
|
24
|
+
variant: {
|
|
25
|
+
default: 'rounded-md',
|
|
26
|
+
beta: 'rounded-none',
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
defaultVariants: {
|
|
30
|
+
variant: 'default',
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
const AccordionRoot: FC<AccordionRootProps> = ({
|
|
34
|
+
multiple = true,
|
|
35
|
+
children,
|
|
36
|
+
}) => {
|
|
37
|
+
const { variant } = useTwintagForm();
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<AccordionPrimitive
|
|
41
|
+
type={multiple ? 'multiple' : 'single'}
|
|
42
|
+
collapsible={multiple ? undefined : true}
|
|
43
|
+
>
|
|
44
|
+
<div className={accordionRootVariants({ variant })}>{children}</div>
|
|
45
|
+
</AccordionPrimitive>
|
|
46
|
+
);
|
|
47
|
+
};
|
|
48
|
+
AccordionRoot.displayName = 'AccordionRoot';
|
|
49
|
+
|
|
50
|
+
const accordionItemVariants = tv({
|
|
51
|
+
slots: {
|
|
52
|
+
item: 'px-5 last:border-none',
|
|
53
|
+
trigger: 'flex flex-1 items-center gap-2.5 font-tf-font',
|
|
54
|
+
content: 'space-y-4',
|
|
55
|
+
},
|
|
56
|
+
variants: {
|
|
57
|
+
variant: {
|
|
58
|
+
default: {
|
|
59
|
+
trigger: 'text-base font-medium',
|
|
60
|
+
},
|
|
61
|
+
beta: {
|
|
62
|
+
trigger: 'text-sm',
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
defaultVariants: {
|
|
67
|
+
variant: 'default',
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
type AccordionItemProps = Omit<Props, 'bind'> & WithChildren;
|
|
72
|
+
const AccordionItem: FC<AccordionItemProps> = ({
|
|
73
|
+
label = '',
|
|
74
|
+
icon,
|
|
75
|
+
children,
|
|
76
|
+
}) => {
|
|
77
|
+
const { variant } = useTwintagForm();
|
|
78
|
+
|
|
79
|
+
const { item, trigger, content } = accordionItemVariants({ variant });
|
|
80
|
+
|
|
81
|
+
return (
|
|
82
|
+
<AccordionItemPrimitive value={camelize(label)} className={item()}>
|
|
83
|
+
<AccordionTrigger className='text-category'>
|
|
84
|
+
<span className={trigger()}>
|
|
85
|
+
{icon ? <Icon name={icon} className='shrink-0' /> : <></>}
|
|
86
|
+
{label}
|
|
87
|
+
</span>
|
|
88
|
+
</AccordionTrigger>
|
|
89
|
+
<AccordionContent className={content()}>{children}</AccordionContent>
|
|
90
|
+
</AccordionItemPrimitive>
|
|
91
|
+
);
|
|
92
|
+
};
|
|
93
|
+
AccordionItem.displayName = 'AccordionItem';
|
|
94
|
+
|
|
95
|
+
type AccordionProps = {
|
|
96
|
+
subtype?: 'item' | 'multiple' | 'single';
|
|
97
|
+
label?: string;
|
|
98
|
+
} & WithChildren;
|
|
99
|
+
const Accordion: FC<AccordionProps> = ({ subtype = 'single', ...rest }) => {
|
|
100
|
+
if (subtype === 'item') return <AccordionItem {...rest} />;
|
|
101
|
+
|
|
102
|
+
return <AccordionRoot {...rest} />;
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
export { Accordion };
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { FC } from 'react';
|
|
2
|
+
import type { WithChildren } from 'twintag-types/dom';
|
|
3
|
+
|
|
4
|
+
import { tv } from 'tailwind-variants';
|
|
5
|
+
|
|
6
|
+
import { useTwintagForm } from '@twintag/twintag-form/provider';
|
|
7
|
+
|
|
8
|
+
const categoryStyle = tv({
|
|
9
|
+
slots: {
|
|
10
|
+
container: 'relative w-full space-y-4',
|
|
11
|
+
heading: 'font-tf-font text-category',
|
|
12
|
+
content: 'grid gap-4',
|
|
13
|
+
},
|
|
14
|
+
variants: {
|
|
15
|
+
variant: {
|
|
16
|
+
default: {
|
|
17
|
+
heading: 'text-base font-medium',
|
|
18
|
+
},
|
|
19
|
+
beta: {
|
|
20
|
+
heading: 'text-sm font-normal',
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
defaultVariants: {
|
|
25
|
+
variant: 'default',
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
type Props = {
|
|
30
|
+
label?: string;
|
|
31
|
+
} & WithChildren;
|
|
32
|
+
|
|
33
|
+
const Category: FC<Props> = ({ label, children }) => {
|
|
34
|
+
const { variant } = useTwintagForm();
|
|
35
|
+
|
|
36
|
+
const { container, heading, content } = categoryStyle({ variant });
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<section className={container()}>
|
|
40
|
+
{label ? <h3 className={heading()}>{label}</h3> : <></>}
|
|
41
|
+
|
|
42
|
+
<div className={content()}>{children}</div>
|
|
43
|
+
</section>
|
|
44
|
+
);
|
|
45
|
+
};
|
|
46
|
+
Category.displayName = 'Category';
|
|
47
|
+
|
|
48
|
+
export { Category };
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import type { Props } from '@twintag/twintag-form/types';
|
|
2
|
+
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import { format } from 'date-fns';
|
|
5
|
+
import { AsteriskIcon, Calendar as CalendarIcon } from 'lucide-react';
|
|
6
|
+
import { useFormContext } from 'react-hook-form';
|
|
7
|
+
import { tv } from 'tailwind-variants';
|
|
8
|
+
|
|
9
|
+
import { Button } from '@twintag/twintag-form/common/button';
|
|
10
|
+
import { Calendar } from '@twintag/twintag-form/common/calendar';
|
|
11
|
+
import {
|
|
12
|
+
FormControl,
|
|
13
|
+
FormDescription,
|
|
14
|
+
FormField,
|
|
15
|
+
FormItem,
|
|
16
|
+
FormLabel,
|
|
17
|
+
FormMessage,
|
|
18
|
+
} from '@twintag/twintag-form/common/form';
|
|
19
|
+
import {
|
|
20
|
+
Popover,
|
|
21
|
+
PopoverContent,
|
|
22
|
+
PopoverTrigger,
|
|
23
|
+
} from '@twintag/twintag-form/common/popover';
|
|
24
|
+
import { useTwintagForm } from '@twintag/twintag-form/provider';
|
|
25
|
+
|
|
26
|
+
const triggerVariants = tv({
|
|
27
|
+
base: 'w-full justify-start text-left font-normal',
|
|
28
|
+
variants: {
|
|
29
|
+
variant: {
|
|
30
|
+
default: 'px-3',
|
|
31
|
+
beta: 'rounded-none border-x-transparent border-t-transparent px-1 transition-none hover:bg-white hover:text-muted-foreground',
|
|
32
|
+
},
|
|
33
|
+
withValue: {
|
|
34
|
+
true: 'text-muted-foreground',
|
|
35
|
+
false: '',
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
export const DatePicker: React.FC<Props> = ({ label, bind, options }) => {
|
|
41
|
+
const { variant } = useTwintagForm();
|
|
42
|
+
|
|
43
|
+
const { control } = useFormContext();
|
|
44
|
+
|
|
45
|
+
const isOptional = options?.optionalField;
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<FormField
|
|
49
|
+
control={control}
|
|
50
|
+
name={bind}
|
|
51
|
+
rules={{
|
|
52
|
+
required: isOptional ? false : options?.error ?? true,
|
|
53
|
+
}}
|
|
54
|
+
render={({ field }) => (
|
|
55
|
+
<FormItem className='flex flex-col [&>div[data-radix-popper-content-wrapper]]:!z-50'>
|
|
56
|
+
<div className='space-y-0.5'>
|
|
57
|
+
<FormLabel>
|
|
58
|
+
{label}
|
|
59
|
+
{!isOptional ? (
|
|
60
|
+
<span className='ml-0.5 inline-block'>
|
|
61
|
+
<AsteriskIcon className='h-3 w-3 text-destructive' />
|
|
62
|
+
</span>
|
|
63
|
+
) : (
|
|
64
|
+
<></>
|
|
65
|
+
)}
|
|
66
|
+
</FormLabel>
|
|
67
|
+
{options?.description ? (
|
|
68
|
+
<FormDescription>{options.description}</FormDescription>
|
|
69
|
+
) : (
|
|
70
|
+
<></>
|
|
71
|
+
)}
|
|
72
|
+
</div>
|
|
73
|
+
<Popover>
|
|
74
|
+
<PopoverTrigger asChild>
|
|
75
|
+
<FormControl>
|
|
76
|
+
<Button
|
|
77
|
+
variant='outline'
|
|
78
|
+
className={triggerVariants({
|
|
79
|
+
variant,
|
|
80
|
+
withValue: !field.value,
|
|
81
|
+
className: 'h-10 gap-3',
|
|
82
|
+
})}
|
|
83
|
+
>
|
|
84
|
+
{field.value ? (
|
|
85
|
+
format(field.value, 'PPP')
|
|
86
|
+
) : (
|
|
87
|
+
<span>{options?.placeholder ?? 'Select a date'}</span>
|
|
88
|
+
)}
|
|
89
|
+
<CalendarIcon className='ml-auto h-4 w-4 opacity-50' />
|
|
90
|
+
</Button>
|
|
91
|
+
</FormControl>
|
|
92
|
+
</PopoverTrigger>
|
|
93
|
+
<PopoverContent className='w-auto p-0' align='start'>
|
|
94
|
+
<Calendar
|
|
95
|
+
fromYear={1970}
|
|
96
|
+
toYear={2050}
|
|
97
|
+
captionLayout='dropdown-buttons'
|
|
98
|
+
mode='single'
|
|
99
|
+
selected={
|
|
100
|
+
!isNaN(new Date(field.value as Date).getTime())
|
|
101
|
+
? new Date(field.value as Date)
|
|
102
|
+
: undefined
|
|
103
|
+
}
|
|
104
|
+
onSelect={field.onChange}
|
|
105
|
+
disabled={(date) =>
|
|
106
|
+
date > new Date() || date < new Date('1900-01-01')
|
|
107
|
+
}
|
|
108
|
+
initialFocus
|
|
109
|
+
/>
|
|
110
|
+
</PopoverContent>
|
|
111
|
+
</Popover>
|
|
112
|
+
<FormMessage className='pt-1' />
|
|
113
|
+
</FormItem>
|
|
114
|
+
)}
|
|
115
|
+
/>
|
|
116
|
+
);
|
|
117
|
+
};
|
|
@@ -12,18 +12,18 @@ import {
|
|
|
12
12
|
FormLabel,
|
|
13
13
|
FormMessage,
|
|
14
14
|
} from '@twintag/twintag-form/common/form';
|
|
15
|
-
import { Input } from '@twintag/twintag-form/common/input';
|
|
15
|
+
import { Input as InputPrimitive } from '@twintag/twintag-form/common/input';
|
|
16
16
|
|
|
17
|
-
export const
|
|
17
|
+
export const Input: FC<Props> = ({ label, bind, options }) => {
|
|
18
18
|
const { control } = useFormContext();
|
|
19
19
|
|
|
20
20
|
const isOptional = options?.optionalField;
|
|
21
21
|
|
|
22
22
|
return (
|
|
23
23
|
<FormField
|
|
24
|
+
defaultValue=''
|
|
24
25
|
control={control}
|
|
25
26
|
name={bind}
|
|
26
|
-
defaultValue=''
|
|
27
27
|
rules={{
|
|
28
28
|
required: isOptional ? false : options?.error ?? true,
|
|
29
29
|
}}
|
|
@@ -47,13 +47,13 @@ export const Simple: FC<Props> = ({ label, bind, options }) => {
|
|
|
47
47
|
)}
|
|
48
48
|
</div>
|
|
49
49
|
<FormControl>
|
|
50
|
-
<
|
|
50
|
+
<InputPrimitive
|
|
51
51
|
type='text'
|
|
52
52
|
placeholder={options?.placeholder ?? 'Write here...'}
|
|
53
53
|
{...field}
|
|
54
54
|
/>
|
|
55
55
|
</FormControl>
|
|
56
|
-
<FormMessage />
|
|
56
|
+
<FormMessage className='pt-1' />
|
|
57
57
|
</FormItem>
|
|
58
58
|
)}
|
|
59
59
|
/>
|
|
@@ -21,9 +21,9 @@ export const Textarea: FC<Props> = ({ label, bind, options }) => {
|
|
|
21
21
|
|
|
22
22
|
return (
|
|
23
23
|
<FormField
|
|
24
|
+
defaultValue=''
|
|
24
25
|
control={control}
|
|
25
26
|
name={bind}
|
|
26
|
-
defaultValue=''
|
|
27
27
|
rules={{
|
|
28
28
|
required: isOptional ? false : options?.error ?? true,
|
|
29
29
|
}}
|
|
@@ -52,7 +52,7 @@ export const Textarea: FC<Props> = ({ label, bind, options }) => {
|
|
|
52
52
|
placeholder={options?.placeholder ?? 'Write here...'}
|
|
53
53
|
/>
|
|
54
54
|
</FormControl>
|
|
55
|
-
<FormMessage />
|
|
55
|
+
<FormMessage className='pt-1' />
|
|
56
56
|
</FormItem>
|
|
57
57
|
)}
|
|
58
58
|
/>
|
package/lib/components/form.tsx
CHANGED
|
@@ -7,21 +7,36 @@ import { useForm } from 'react-hook-form';
|
|
|
7
7
|
import { Form } from '@twintag/twintag-form/common/form';
|
|
8
8
|
|
|
9
9
|
type Props = {
|
|
10
|
+
defaultValues?: FieldValues;
|
|
10
11
|
resetOnSubmit?: boolean;
|
|
11
12
|
} & WithChildren;
|
|
12
13
|
|
|
13
|
-
export const TwintagForm = ({
|
|
14
|
+
export const TwintagForm = ({
|
|
15
|
+
defaultValues,
|
|
16
|
+
resetOnSubmit,
|
|
17
|
+
children,
|
|
18
|
+
}: Props) => {
|
|
14
19
|
const ref = useRef<HTMLFormElement>(null);
|
|
15
20
|
|
|
16
|
-
const form = useForm(
|
|
21
|
+
const form = useForm({
|
|
22
|
+
defaultValues,
|
|
23
|
+
});
|
|
17
24
|
|
|
18
25
|
useEffect(() => {
|
|
19
26
|
if (form.formState.isSubmitSuccessful && resetOnSubmit) {
|
|
20
|
-
|
|
27
|
+
if (defaultValues) {
|
|
28
|
+
form.reset(
|
|
29
|
+
Object.keys(defaultValues).reduce(
|
|
30
|
+
(acc, key) => ({ ...acc, [key]: '' }),
|
|
31
|
+
{},
|
|
32
|
+
),
|
|
33
|
+
);
|
|
34
|
+
}
|
|
21
35
|
}
|
|
22
36
|
}, [form.formState]);
|
|
23
37
|
|
|
24
38
|
const onValid = (data: FieldValues) => {
|
|
39
|
+
console.log('form', data);
|
|
25
40
|
const customSubmit = new CustomEvent('twintag-form-valid', {
|
|
26
41
|
detail: data,
|
|
27
42
|
bubbles: true,
|
|
@@ -64,6 +79,7 @@ export const TwintagForm = ({ resetOnSubmit, children }: Props) => {
|
|
|
64
79
|
return (
|
|
65
80
|
<Form {...form}>
|
|
66
81
|
<form
|
|
82
|
+
id='twintag-form'
|
|
67
83
|
ref={ref}
|
|
68
84
|
className='relative space-y-4 font-tf-font'
|
|
69
85
|
onSubmit={form.handleSubmit(onValid, onInvalid)}
|
package/lib/components/index.tsx
CHANGED
|
@@ -3,9 +3,6 @@ import type { RawJsonComponent } from '@twintag/twintag-form/types';
|
|
|
3
3
|
import { Renderer } from '@twintag/twintag-form/components/Render';
|
|
4
4
|
import { useJson } from '@twintag/twintag-form/components/Render/useJson';
|
|
5
5
|
|
|
6
|
-
export { Input } from './Input';
|
|
7
|
-
export { Category } from './category';
|
|
8
|
-
|
|
9
6
|
type Props = {
|
|
10
7
|
config: RawJsonComponent | RawJsonComponent[];
|
|
11
8
|
};
|
package/lib/index.tsx
CHANGED
|
@@ -1,19 +1,15 @@
|
|
|
1
|
-
import type { ComponentProps
|
|
1
|
+
import type { ComponentProps } from 'react';
|
|
2
2
|
import type { RawJsonComponent } from '@twintag/twintag-form/types';
|
|
3
3
|
|
|
4
4
|
import { TwintagJsonRenderer } from '@twintag/twintag-form/components';
|
|
5
|
-
import {
|
|
5
|
+
import { components } from '@twintag/twintag-form/components/config';
|
|
6
6
|
import { TwintagForm } from '@twintag/twintag-form/components/form';
|
|
7
|
-
import { Input } from '@twintag/twintag-form/components/Input';
|
|
8
7
|
import { TwintagFormProvider } from '@twintag/twintag-form/provider';
|
|
9
8
|
|
|
10
|
-
const components = new Map<string, ElementType>()
|
|
11
|
-
.set('input', Input)
|
|
12
|
-
.set('category', Category);
|
|
13
|
-
|
|
14
9
|
type Props = {
|
|
15
10
|
config: RawJsonComponent[];
|
|
16
11
|
options?: Partial<{
|
|
12
|
+
defaultValues?: ComponentProps<typeof TwintagForm>['defaultValues'];
|
|
17
13
|
variant?: ComponentProps<typeof TwintagFormProvider>['variant'];
|
|
18
14
|
resetOnSubmit?: boolean;
|
|
19
15
|
}>;
|
|
@@ -22,7 +18,10 @@ type Props = {
|
|
|
22
18
|
export const Form = ({ config, options }: Props) => {
|
|
23
19
|
return (
|
|
24
20
|
<TwintagFormProvider variant={options?.variant} components={components}>
|
|
25
|
-
<TwintagForm
|
|
21
|
+
<TwintagForm
|
|
22
|
+
defaultValues={options?.defaultValues}
|
|
23
|
+
resetOnSubmit={options?.resetOnSubmit}
|
|
24
|
+
>
|
|
26
25
|
{config.map((item, idx) => (
|
|
27
26
|
<TwintagJsonRenderer key={idx} config={item} />
|
|
28
27
|
))}
|
package/lib/types/index.ts
CHANGED
|
@@ -9,6 +9,7 @@ type RawProps = Partial<{
|
|
|
9
9
|
bind: string;
|
|
10
10
|
subtype: string;
|
|
11
11
|
label: string;
|
|
12
|
+
icon: string;
|
|
12
13
|
options: Partial<{
|
|
13
14
|
description: string;
|
|
14
15
|
placeholder: string;
|
|
@@ -17,17 +18,13 @@ type RawProps = Partial<{
|
|
|
17
18
|
}>;
|
|
18
19
|
}>;
|
|
19
20
|
|
|
20
|
-
export type RawJsonComponent = RawProps &
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
export type Props = Omit<RawProps, 'subtype'> & {
|
|
28
|
-
bind: string;
|
|
29
|
-
items?: ParsedJsonComponent[];
|
|
30
|
-
};
|
|
21
|
+
export type RawJsonComponent = RawProps &
|
|
22
|
+
Record<string, unknown> & {
|
|
23
|
+
type: string;
|
|
24
|
+
children?: RawJsonComponent[];
|
|
25
|
+
} & {
|
|
26
|
+
props?: RawProps;
|
|
27
|
+
};
|
|
31
28
|
|
|
32
29
|
export type ParsedJsonComponent = Props & {
|
|
33
30
|
id: string;
|
|
@@ -35,3 +32,8 @@ export type ParsedJsonComponent = Props & {
|
|
|
35
32
|
subtype?: string;
|
|
36
33
|
comp?: ElementType;
|
|
37
34
|
};
|
|
35
|
+
|
|
36
|
+
export type Props = Omit<RawProps, 'subtype'> & {
|
|
37
|
+
bind: string;
|
|
38
|
+
items?: ParsedJsonComponent[];
|
|
39
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Props } from '@twintag/twintag-form/types';
|
|
2
|
+
|
|
3
|
+
export const getBindSetFromItems = (items: Props[]): Set<string> => {
|
|
4
|
+
const binds = new Set<string>();
|
|
5
|
+
|
|
6
|
+
items.forEach((item) => {
|
|
7
|
+
if (item.bind) {
|
|
8
|
+
binds.add(item.bind);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
if (item.items) {
|
|
12
|
+
const subBinds = getBindSetFromItems(item.items);
|
|
13
|
+
subBinds.forEach((bind) => binds.add(bind));
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
return binds;
|
|
18
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export const camelize = (str: string) => {
|
|
2
|
+
return str
|
|
3
|
+
.replace(/(?:^\w|[A-Z]|\b\w)/g, function (word, index) {
|
|
4
|
+
return index === 0 ? word.toLowerCase() : word.toUpperCase();
|
|
5
|
+
})
|
|
6
|
+
.replace(/\s+/g, '');
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export const camelizeKebab = (s: string) =>
|
|
10
|
+
s.replace(/-./g, (x) => x[1].toUpperCase());
|
|
11
|
+
|
|
12
|
+
export const capitalize = (s: string) => s && s[0].toUpperCase() + s.slice(1);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@twintag/twintag-form",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"author": "Twintag",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -14,12 +14,16 @@
|
|
|
14
14
|
}
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"@heroicons/react": "^2.
|
|
17
|
+
"@heroicons/react": "^2.1.1",
|
|
18
18
|
"@r2wc/react-to-web-component": "^2.0.3",
|
|
19
|
+
"@radix-ui/react-accordion": "^1.1.2",
|
|
19
20
|
"@radix-ui/react-checkbox": "^1.0.4",
|
|
20
21
|
"@radix-ui/react-icons": "^1.3.0",
|
|
21
22
|
"@radix-ui/react-label": "^2.0.2",
|
|
23
|
+
"@radix-ui/react-popover": "^1.0.7",
|
|
22
24
|
"@radix-ui/react-radio-group": "^1.1.3",
|
|
25
|
+
"@radix-ui/react-scroll-area": "^1.0.5",
|
|
26
|
+
"@radix-ui/react-select": "^2.0.0",
|
|
23
27
|
"@radix-ui/react-slot": "^1.0.2",
|
|
24
28
|
"@radix-ui/react-switch": "^1.0.2",
|
|
25
29
|
"@storybook/react": "7.0.2",
|
|
@@ -27,10 +31,14 @@
|
|
|
27
31
|
"@twind/preset-autoprefix": "^1.0.7",
|
|
28
32
|
"@twind/preset-tailwind": "^1.1.4",
|
|
29
33
|
"@twind/with-web-components": "^1.1.3",
|
|
34
|
+
"clsx": "^2.1.0",
|
|
35
|
+
"date-fns": "^3.3.1",
|
|
30
36
|
"lucide-react": "^0.269.0",
|
|
31
37
|
"react": "^18.2.0",
|
|
38
|
+
"react-day-picker": "^8.10.0",
|
|
32
39
|
"react-dom": "^18.2.0",
|
|
33
|
-
"react-hook-form": "^7.
|
|
40
|
+
"react-hook-form": "^7.50.1",
|
|
41
|
+
"tailwind-merge": "^2.2.1",
|
|
34
42
|
"uuid": "^9.0.1"
|
|
35
43
|
},
|
|
36
44
|
"devDependencies": {
|
|
@@ -43,9 +51,9 @@
|
|
|
43
51
|
"tailwindcss": "^3.3.6",
|
|
44
52
|
"vite": "^4.4.9",
|
|
45
53
|
"vite-plugin-dts": "^3.0.3",
|
|
54
|
+
"eslint-config-custom": "1.0.0",
|
|
46
55
|
"tsconfig": "0.0.0",
|
|
47
|
-
"twintag-types": "0.2.0"
|
|
48
|
-
"eslint-config-custom": "1.0.0"
|
|
56
|
+
"twintag-types": "0.2.0"
|
|
49
57
|
},
|
|
50
58
|
"publishConfig": {
|
|
51
59
|
"access": "public"
|
package/tailwind.config.ts
CHANGED
|
@@ -3,13 +3,16 @@ import type { Config } from 'tailwindcss';
|
|
|
3
3
|
import { fontFamily } from 'tailwindcss/defaultTheme';
|
|
4
4
|
|
|
5
5
|
const config = {
|
|
6
|
-
content: ['./
|
|
6
|
+
content: ['./lib/**/*.{ts,tsx}'],
|
|
7
7
|
theme: {
|
|
8
8
|
extend: {
|
|
9
9
|
colors: {
|
|
10
10
|
category: 'hsl(var(--tf-category) / <alpha-value>)',
|
|
11
11
|
label: 'hsl(var(--tf-label) / <alpha-value>)',
|
|
12
|
+
|
|
12
13
|
background: 'hsl(var(--tf-background) / <alpha-value>)',
|
|
14
|
+
foreground: 'hsl(var(--tf-foreground) / <alpha-value>)',
|
|
15
|
+
|
|
13
16
|
primary: {
|
|
14
17
|
DEFAULT: 'hsl(var(--tf-primary) / <alpha-value>)',
|
|
15
18
|
foreground: 'hsl(var(--tf-primary-foreground) / <alpha-value>)',
|
|
@@ -26,6 +29,7 @@ const config = {
|
|
|
26
29
|
DEFAULT: 'hsl(var(--tf-muted) / <alpha-value>)',
|
|
27
30
|
foreground: 'hsl(var(--tf-muted-foreground) / <alpha-value>)',
|
|
28
31
|
},
|
|
32
|
+
|
|
29
33
|
input: 'hsl(var(--tf-input) / <alpha-value>)',
|
|
30
34
|
ring: 'hsl(var(--tf-ring) / <alpha-value>)',
|
|
31
35
|
},
|
|
@@ -37,6 +41,20 @@ const config = {
|
|
|
37
41
|
fontFamily: {
|
|
38
42
|
'tf-font': ['var(--tf-font)', ...fontFamily.sans],
|
|
39
43
|
},
|
|
44
|
+
keyframes: {
|
|
45
|
+
'accordion-down': {
|
|
46
|
+
from: { height: '0' },
|
|
47
|
+
to: { height: 'var(--radix-accordion-content-height)' },
|
|
48
|
+
},
|
|
49
|
+
'accordion-up': {
|
|
50
|
+
from: { height: 'var(--radix-accordion-content-height)' },
|
|
51
|
+
to: { height: '0' },
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
animation: {
|
|
55
|
+
'accordion-down': 'accordion-down 0.2s ease-out',
|
|
56
|
+
'accordion-up': 'accordion-up 0.2s ease-out',
|
|
57
|
+
},
|
|
40
58
|
},
|
|
41
59
|
},
|
|
42
60
|
} satisfies Config;
|
package/.turbo/turbo-lint.log
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import type { ElementType, FC } from 'react';
|
|
2
|
-
|
|
3
|
-
import { Checkbox } from './checkbox';
|
|
4
|
-
import { File } from './file';
|
|
5
|
-
import { RadioGroup } from './radiogroup';
|
|
6
|
-
import { Simple } from './simple';
|
|
7
|
-
import { Submit } from './submit';
|
|
8
|
-
import { Textarea } from './textarea';
|
|
9
|
-
import { Toggle } from './toggle';
|
|
10
|
-
|
|
11
|
-
const Inputs = new Map<string, ElementType>()
|
|
12
|
-
.set('simple', Simple)
|
|
13
|
-
.set('textarea', Textarea)
|
|
14
|
-
.set('toggle', Toggle)
|
|
15
|
-
.set('file', File)
|
|
16
|
-
.set('submit', Submit)
|
|
17
|
-
.set('checkbox', Checkbox)
|
|
18
|
-
.set('radiogroup', RadioGroup);
|
|
19
|
-
|
|
20
|
-
type Props = {
|
|
21
|
-
subtype?: string;
|
|
22
|
-
label?: string;
|
|
23
|
-
bind?: string;
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
export const Input: FC<Props> = ({ subtype = 'simple', ...rest }) => {
|
|
27
|
-
const Comp = Inputs.get(subtype);
|
|
28
|
-
|
|
29
|
-
if (!Comp) return <></>;
|
|
30
|
-
|
|
31
|
-
return <Comp {...rest} />;
|
|
32
|
-
};
|