@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
package/dist/types/index.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ type RawProps = Partial<{
|
|
|
7
7
|
bind: string;
|
|
8
8
|
subtype: string;
|
|
9
9
|
label: string;
|
|
10
|
+
icon: string;
|
|
10
11
|
options: Partial<{
|
|
11
12
|
description: string;
|
|
12
13
|
placeholder: string;
|
|
@@ -14,20 +15,20 @@ type RawProps = Partial<{
|
|
|
14
15
|
optionalField: boolean;
|
|
15
16
|
}>;
|
|
16
17
|
}>;
|
|
17
|
-
export type RawJsonComponent = RawProps & {
|
|
18
|
+
export type RawJsonComponent = RawProps & Record<string, unknown> & {
|
|
18
19
|
type: string;
|
|
19
20
|
children?: RawJsonComponent[];
|
|
20
21
|
} & {
|
|
21
22
|
props?: RawProps;
|
|
22
23
|
};
|
|
23
|
-
export type Props = Omit<RawProps, 'subtype'> & {
|
|
24
|
-
bind: string;
|
|
25
|
-
items?: ParsedJsonComponent[];
|
|
26
|
-
};
|
|
27
24
|
export type ParsedJsonComponent = Props & {
|
|
28
25
|
id: string;
|
|
29
26
|
type: string;
|
|
30
27
|
subtype?: string;
|
|
31
28
|
comp?: ElementType;
|
|
32
29
|
};
|
|
30
|
+
export type Props = Omit<RawProps, 'subtype'> & {
|
|
31
|
+
bind: string;
|
|
32
|
+
items?: ParsedJsonComponent[];
|
|
33
|
+
};
|
|
33
34
|
export {};
|
package/index.html
CHANGED
|
@@ -6,7 +6,9 @@
|
|
|
6
6
|
<title>Twintag Form</title>
|
|
7
7
|
</head>
|
|
8
8
|
<body>
|
|
9
|
-
<div
|
|
9
|
+
<div style="margin-inline: auto; max-width: 32rem; background: hsl(var(--tf-background)); padding: 2rem">
|
|
10
|
+
<twintag-form></twintag-form>
|
|
11
|
+
</div>
|
|
10
12
|
<script type="module" src="./dev/main.tsx"></script>
|
|
11
13
|
</body>
|
|
12
14
|
</html>
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import * as AccordionPrimitive from '@radix-ui/react-accordion';
|
|
3
|
+
import { ChevronDown } from 'lucide-react';
|
|
4
|
+
import { tv } from 'tailwind-variants';
|
|
5
|
+
|
|
6
|
+
const Accordion = AccordionPrimitive.Root;
|
|
7
|
+
|
|
8
|
+
const accordionVariants = tv({
|
|
9
|
+
base: 'border-b',
|
|
10
|
+
});
|
|
11
|
+
const AccordionItem = React.forwardRef<
|
|
12
|
+
React.ElementRef<typeof AccordionPrimitive.Item>,
|
|
13
|
+
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>
|
|
14
|
+
>(({ className, ...props }, ref) => (
|
|
15
|
+
<AccordionPrimitive.Item
|
|
16
|
+
ref={ref}
|
|
17
|
+
className={accordionVariants({ className })}
|
|
18
|
+
{...props}
|
|
19
|
+
/>
|
|
20
|
+
));
|
|
21
|
+
AccordionItem.displayName = 'AccordionItem';
|
|
22
|
+
|
|
23
|
+
const accordionTriggerVariants = tv({
|
|
24
|
+
slots: {
|
|
25
|
+
header: 'flex',
|
|
26
|
+
trigger:
|
|
27
|
+
'flex flex-1 items-center justify-between py-4 font-medium hover:underline [&[data-state=open]>svg]:rotate-180',
|
|
28
|
+
icon: 'h-4 w-4 shrink-0 text-current transition-transform duration-200',
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
const AccordionTrigger = React.forwardRef<
|
|
32
|
+
React.ElementRef<typeof AccordionPrimitive.Trigger>,
|
|
33
|
+
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>
|
|
34
|
+
>(({ className, children, ...props }, ref) => {
|
|
35
|
+
const { header, trigger, icon } = accordionTriggerVariants();
|
|
36
|
+
return (
|
|
37
|
+
<AccordionPrimitive.Header className={header()} asChild>
|
|
38
|
+
<div>
|
|
39
|
+
<AccordionPrimitive.Trigger
|
|
40
|
+
ref={ref}
|
|
41
|
+
className={trigger({
|
|
42
|
+
className,
|
|
43
|
+
})}
|
|
44
|
+
{...props}
|
|
45
|
+
>
|
|
46
|
+
{children}
|
|
47
|
+
<ChevronDown className={icon()} />
|
|
48
|
+
</AccordionPrimitive.Trigger>
|
|
49
|
+
</div>
|
|
50
|
+
</AccordionPrimitive.Header>
|
|
51
|
+
);
|
|
52
|
+
});
|
|
53
|
+
AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName;
|
|
54
|
+
|
|
55
|
+
const accordionContentVariants = tv({
|
|
56
|
+
slots: {
|
|
57
|
+
content:
|
|
58
|
+
'overflow-hidden text-sm transition-all data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down',
|
|
59
|
+
wrapper: 'pb-4 pt-0',
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
const AccordionContent = React.forwardRef<
|
|
63
|
+
React.ElementRef<typeof AccordionPrimitive.Content>,
|
|
64
|
+
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content>
|
|
65
|
+
>(({ className, children, ...props }, ref) => {
|
|
66
|
+
const { content, wrapper } = accordionContentVariants();
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
<AccordionPrimitive.Content ref={ref} className={content()} {...props}>
|
|
70
|
+
<div className={wrapper({ className })}>{children}</div>
|
|
71
|
+
</AccordionPrimitive.Content>
|
|
72
|
+
);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
AccordionContent.displayName = AccordionPrimitive.Content.displayName;
|
|
76
|
+
|
|
77
|
+
export { Accordion, AccordionItem, AccordionTrigger, AccordionContent };
|
package/lib/common/button.tsx
CHANGED
|
@@ -14,6 +14,9 @@ const buttonVariants = tv({
|
|
|
14
14
|
variant: {
|
|
15
15
|
default:
|
|
16
16
|
'bg-primary-foreground text-primary shadow hover:bg-primary-foreground/90',
|
|
17
|
+
outline:
|
|
18
|
+
'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
|
|
19
|
+
ghost: 'hover:bg-accent hover:text-accent-foreground',
|
|
17
20
|
},
|
|
18
21
|
size: {
|
|
19
22
|
default: 'h-9 px-4 py-2',
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { ChevronLeft, ChevronRight } from 'lucide-react';
|
|
3
|
+
import { DayPicker } from 'react-day-picker';
|
|
4
|
+
import { tv } from 'tailwind-variants';
|
|
5
|
+
|
|
6
|
+
import { buttonVariants } from '@twintag/twintag-form/common/button';
|
|
7
|
+
|
|
8
|
+
export type CalendarProps = React.ComponentProps<typeof DayPicker>;
|
|
9
|
+
|
|
10
|
+
const calendarVariants = tv({
|
|
11
|
+
slots: {
|
|
12
|
+
picker: 'p-3',
|
|
13
|
+
months: 'flex flex-col space-y-4 sm:flex-row sm:space-x-4 sm:space-y-0',
|
|
14
|
+
month: 'space-y-4',
|
|
15
|
+
caption: 'relative flex items-center justify-center pt-1',
|
|
16
|
+
caption_dropdowns: '',
|
|
17
|
+
caption_label: 'text-sm font-semibold',
|
|
18
|
+
nav: 'flex items-center space-x-1',
|
|
19
|
+
nav_button: buttonVariants({
|
|
20
|
+
variant: 'outline',
|
|
21
|
+
className: 'h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100',
|
|
22
|
+
}),
|
|
23
|
+
nav_button_previous: 'absolute left-1',
|
|
24
|
+
nav_button_next: 'absolute right-1',
|
|
25
|
+
table: 'w-full border-collapse space-y-1',
|
|
26
|
+
head_row: 'flex',
|
|
27
|
+
head_cell: 'w-9 rounded-md text-[0.8rem] font-normal text-muted-foreground',
|
|
28
|
+
row: 'mt-2 flex w-full',
|
|
29
|
+
cell: 'relative h-9 w-9 p-0 text-center text-sm focus-within:relative focus-within:z-20 [&:has([aria-selected])]:bg-transparent first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md [&:has([aria-selected].day-outside)]:bg-muted/50 [&:has([aria-selected].day-range-end)]:rounded-r-md',
|
|
30
|
+
day: buttonVariants({
|
|
31
|
+
variant: 'ghost',
|
|
32
|
+
className: 'h-9 w-9 p-0 font-normal aria-selected:opacity-100',
|
|
33
|
+
}),
|
|
34
|
+
day_range_end: 'day-range-end',
|
|
35
|
+
day_selected:
|
|
36
|
+
'bg-accent text-accent-foreground hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground',
|
|
37
|
+
day_today: 'bg-muted text-muted-foreground',
|
|
38
|
+
day_outside:
|
|
39
|
+
'day-outside text-muted-foreground opacity-50 aria-selected:bg-accent/30 aria-selected:text-accent-foreground',
|
|
40
|
+
day_disabled: 'text-muted-foreground opacity-50',
|
|
41
|
+
day_range_middle:
|
|
42
|
+
'aria-selected:bg-accent aria-selected:text-accent-foreground',
|
|
43
|
+
day_hidden: 'invisible',
|
|
44
|
+
},
|
|
45
|
+
variants: {
|
|
46
|
+
variant: {
|
|
47
|
+
dropdown: {
|
|
48
|
+
caption_label: 'hidden',
|
|
49
|
+
caption_dropdowns: [
|
|
50
|
+
'flex w-4/6 justify-center gap-1',
|
|
51
|
+
'[&>*>span]:hidden [&>*]:flex-1 [&>.rdp-vhidden]:hidden ',
|
|
52
|
+
'[&>*>select]:h-7 [&>*>select]:min-w-[3.6rem] [&>*>select]:rounded-md [&>*>select]:border [&>*>select]:border-input [&>*>select]:bg-background [&>*>select]:px-1 [&>*>select]:text-sm',
|
|
53
|
+
'[&>*>select]:ring-offset-background [&>*>select]:focus-visible:outline-none [&>*>select]:focus-visible:ring-1 [&>*>select]:focus-visible:ring-ring',
|
|
54
|
+
],
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
function Calendar({
|
|
61
|
+
className,
|
|
62
|
+
classNames,
|
|
63
|
+
showOutsideDays = true,
|
|
64
|
+
...props
|
|
65
|
+
}: CalendarProps) {
|
|
66
|
+
const slots = calendarVariants({
|
|
67
|
+
variant:
|
|
68
|
+
props.captionLayout === 'dropdown-buttons' ? 'dropdown' : undefined,
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
return (
|
|
72
|
+
<DayPicker
|
|
73
|
+
showOutsideDays={showOutsideDays}
|
|
74
|
+
className={slots.picker({ className })}
|
|
75
|
+
classNames={{
|
|
76
|
+
months: slots.months(),
|
|
77
|
+
month: slots.month(),
|
|
78
|
+
caption: slots.caption(),
|
|
79
|
+
caption_label: slots.caption_label(),
|
|
80
|
+
caption_dropdowns: slots.caption_dropdowns(),
|
|
81
|
+
nav: slots.nav(),
|
|
82
|
+
nav_button: slots.nav_button(),
|
|
83
|
+
nav_button_previous: slots.nav_button_previous(),
|
|
84
|
+
nav_button_next: slots.nav_button_next(),
|
|
85
|
+
table: slots.table(),
|
|
86
|
+
head_row: slots.head_row(),
|
|
87
|
+
head_cell: slots.head_cell(),
|
|
88
|
+
row: slots.row(),
|
|
89
|
+
cell: slots.cell(),
|
|
90
|
+
day: slots.day(),
|
|
91
|
+
day_range_end: slots.day_range_end(),
|
|
92
|
+
day_selected: slots.day_selected(),
|
|
93
|
+
day_today: slots.day_today(),
|
|
94
|
+
day_outside: slots.day_outside(),
|
|
95
|
+
day_disabled: slots.day_disabled(),
|
|
96
|
+
day_range_middle: slots.day_range_middle(),
|
|
97
|
+
day_hidden: slots.day_hidden(),
|
|
98
|
+
...classNames,
|
|
99
|
+
}}
|
|
100
|
+
components={{
|
|
101
|
+
IconLeft: () => <ChevronLeft className='h-4 w-4' />,
|
|
102
|
+
IconRight: () => <ChevronRight className='h-4 w-4' />,
|
|
103
|
+
}}
|
|
104
|
+
{...props}
|
|
105
|
+
/>
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
Calendar.displayName = 'Calendar';
|
|
109
|
+
|
|
110
|
+
export { Calendar };
|
package/lib/common/form.tsx
CHANGED
|
@@ -6,6 +6,7 @@ import { Slot } from '@radix-ui/react-slot';
|
|
|
6
6
|
import { Controller, FormProvider, useFormContext } from 'react-hook-form';
|
|
7
7
|
import { tv } from 'tailwind-variants';
|
|
8
8
|
|
|
9
|
+
import { useTwintagForm } from '../provider';
|
|
9
10
|
import { Label } from './label';
|
|
10
11
|
|
|
11
12
|
const Form = FormProvider;
|
|
@@ -67,6 +68,12 @@ const FormItemContext = React.createContext<FormItemContextValue>(
|
|
|
67
68
|
|
|
68
69
|
const formItemVariants = tv({
|
|
69
70
|
base: 'space-y-2',
|
|
71
|
+
variants: {
|
|
72
|
+
variant: {
|
|
73
|
+
default: '',
|
|
74
|
+
beta: 'space-y-1',
|
|
75
|
+
},
|
|
76
|
+
},
|
|
70
77
|
});
|
|
71
78
|
const FormItem = React.forwardRef<
|
|
72
79
|
HTMLDivElement,
|
|
@@ -74,9 +81,15 @@ const FormItem = React.forwardRef<
|
|
|
74
81
|
>(({ className, ...props }, ref) => {
|
|
75
82
|
const id = React.useId();
|
|
76
83
|
|
|
84
|
+
const { variant } = useTwintagForm();
|
|
85
|
+
|
|
77
86
|
return (
|
|
78
87
|
<FormItemContext.Provider value={{ id }}>
|
|
79
|
-
<div
|
|
88
|
+
<div
|
|
89
|
+
ref={ref}
|
|
90
|
+
className={formItemVariants({ variant, className })}
|
|
91
|
+
{...props}
|
|
92
|
+
/>
|
|
80
93
|
</FormItemContext.Provider>
|
|
81
94
|
);
|
|
82
95
|
});
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { FC } from 'react';
|
|
2
|
+
|
|
3
|
+
import { useMemo } from 'react';
|
|
4
|
+
import * as icons from '@heroicons/react/24/outline';
|
|
5
|
+
import { tv } from 'tailwind-variants';
|
|
6
|
+
|
|
7
|
+
import { camelizeKebab, capitalize } from '../utils/string';
|
|
8
|
+
|
|
9
|
+
type Props = {
|
|
10
|
+
name: string;
|
|
11
|
+
className?: string;
|
|
12
|
+
};
|
|
13
|
+
const iconVariants = tv({
|
|
14
|
+
base: 'h-5 w-5',
|
|
15
|
+
});
|
|
16
|
+
export const Icon: FC<Props> = ({ name, className, ...props }) => {
|
|
17
|
+
const camel = useMemo(
|
|
18
|
+
() => `${capitalize(camelizeKebab(name))}Icon` as keyof typeof icons,
|
|
19
|
+
[name],
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
const Comp = icons[camel];
|
|
23
|
+
|
|
24
|
+
if (!Comp) return <></>;
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<Comp
|
|
28
|
+
aria-hidden='true'
|
|
29
|
+
className={iconVariants({
|
|
30
|
+
className,
|
|
31
|
+
})}
|
|
32
|
+
{...props}
|
|
33
|
+
/>
|
|
34
|
+
);
|
|
35
|
+
};
|
package/lib/common/input.tsx
CHANGED
|
@@ -21,7 +21,7 @@ const inputVariants = tv({
|
|
|
21
21
|
'rounded-md border px-3 ring-offset-background focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',
|
|
22
22
|
},
|
|
23
23
|
beta: {
|
|
24
|
-
root: 'after:absolute after:left-0 after:top-[calc(100%_+
|
|
24
|
+
root: 'after:absolute after:left-0 after:top-[calc(100%_+_1px)] after:w-full after:rounded-full after:content-[""] focus-within:after:h-0.5 focus-within:after:bg-ring',
|
|
25
25
|
input: 'border-b px-1',
|
|
26
26
|
},
|
|
27
27
|
},
|
|
@@ -30,6 +30,7 @@ const inputVariants = tv({
|
|
|
30
30
|
variant: 'default',
|
|
31
31
|
},
|
|
32
32
|
});
|
|
33
|
+
|
|
33
34
|
const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|
34
35
|
({ className, type, ...props }, ref) => {
|
|
35
36
|
const { variant } = useTwintagForm();
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import * as PopoverPrimitive from '@radix-ui/react-popover';
|
|
3
|
+
import { tv } from 'tailwind-variants';
|
|
4
|
+
|
|
5
|
+
const Popover = PopoverPrimitive.Root;
|
|
6
|
+
|
|
7
|
+
const PopoverTrigger = PopoverPrimitive.Trigger;
|
|
8
|
+
|
|
9
|
+
const popoverContentVariants = tv({
|
|
10
|
+
base: 'z-50 w-72 rounded-md border bg-background p-4 text-foreground shadow-md outline-none',
|
|
11
|
+
});
|
|
12
|
+
const PopoverContent = React.forwardRef<
|
|
13
|
+
React.ElementRef<typeof PopoverPrimitive.Content>,
|
|
14
|
+
React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>
|
|
15
|
+
>(({ className, align = 'center', sideOffset = 2, ...props }, ref) => {
|
|
16
|
+
return (
|
|
17
|
+
<PopoverPrimitive.Content
|
|
18
|
+
ref={ref}
|
|
19
|
+
align={align}
|
|
20
|
+
sideOffset={sideOffset}
|
|
21
|
+
className={popoverContentVariants({
|
|
22
|
+
className,
|
|
23
|
+
})}
|
|
24
|
+
{...props}
|
|
25
|
+
/>
|
|
26
|
+
);
|
|
27
|
+
});
|
|
28
|
+
PopoverContent.displayName = PopoverPrimitive.Content.displayName;
|
|
29
|
+
|
|
30
|
+
export { Popover, PopoverTrigger, PopoverContent };
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area';
|
|
3
|
+
|
|
4
|
+
import { cn } from '@twintag/twintag-form/utils/style';
|
|
5
|
+
|
|
6
|
+
const ScrollArea = React.forwardRef<
|
|
7
|
+
React.ElementRef<typeof ScrollAreaPrimitive.Root>,
|
|
8
|
+
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root>
|
|
9
|
+
>(({ className, children, ...props }, ref) => (
|
|
10
|
+
<ScrollAreaPrimitive.Root
|
|
11
|
+
ref={ref}
|
|
12
|
+
className={cn('relative overflow-hidden', className)}
|
|
13
|
+
{...props}
|
|
14
|
+
>
|
|
15
|
+
<ScrollAreaPrimitive.Viewport className='h-full w-full rounded-[inherit]'>
|
|
16
|
+
{children}
|
|
17
|
+
</ScrollAreaPrimitive.Viewport>
|
|
18
|
+
<ScrollBar />
|
|
19
|
+
<ScrollAreaPrimitive.Corner />
|
|
20
|
+
</ScrollAreaPrimitive.Root>
|
|
21
|
+
));
|
|
22
|
+
ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName;
|
|
23
|
+
|
|
24
|
+
const ScrollBar = React.forwardRef<
|
|
25
|
+
React.ElementRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>,
|
|
26
|
+
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>
|
|
27
|
+
>(({ className, orientation = 'vertical', ...props }, ref) => (
|
|
28
|
+
<ScrollAreaPrimitive.ScrollAreaScrollbar
|
|
29
|
+
ref={ref}
|
|
30
|
+
orientation={orientation}
|
|
31
|
+
className={cn(
|
|
32
|
+
'flex touch-none select-none transition-colors',
|
|
33
|
+
orientation === 'vertical' &&
|
|
34
|
+
'h-full w-2.5 border-l border-l-transparent p-[1px]',
|
|
35
|
+
orientation === 'horizontal' &&
|
|
36
|
+
'h-2.5 flex-col border-t border-t-transparent p-[1px]',
|
|
37
|
+
className,
|
|
38
|
+
)}
|
|
39
|
+
{...props}
|
|
40
|
+
>
|
|
41
|
+
<ScrollAreaPrimitive.ScrollAreaThumb className='relative flex-1 rounded-full bg-input' />
|
|
42
|
+
</ScrollAreaPrimitive.ScrollAreaScrollbar>
|
|
43
|
+
));
|
|
44
|
+
ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName;
|
|
45
|
+
|
|
46
|
+
export { ScrollArea, ScrollBar };
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { CaretSortIcon } from '@radix-ui/react-icons';
|
|
3
|
+
import * as SelectPrimitive from '@radix-ui/react-select';
|
|
4
|
+
import { Check, ChevronDown, ChevronUp } from 'lucide-react';
|
|
5
|
+
|
|
6
|
+
import { cn } from '../utils/style';
|
|
7
|
+
|
|
8
|
+
const Select = SelectPrimitive.Root;
|
|
9
|
+
|
|
10
|
+
const SelectGroup = SelectPrimitive.Group;
|
|
11
|
+
|
|
12
|
+
const SelectValue = SelectPrimitive.Value;
|
|
13
|
+
|
|
14
|
+
const SelectTrigger = React.forwardRef<
|
|
15
|
+
React.ElementRef<typeof SelectPrimitive.Trigger>,
|
|
16
|
+
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>
|
|
17
|
+
>(({ className, children, ...props }, ref) => (
|
|
18
|
+
<SelectPrimitive.Trigger
|
|
19
|
+
ref={ref}
|
|
20
|
+
className={cn(
|
|
21
|
+
'flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1',
|
|
22
|
+
className,
|
|
23
|
+
)}
|
|
24
|
+
{...props}
|
|
25
|
+
>
|
|
26
|
+
{children}
|
|
27
|
+
<SelectPrimitive.Icon asChild>
|
|
28
|
+
<CaretSortIcon className='h-4 w-4 opacity-50' />
|
|
29
|
+
</SelectPrimitive.Icon>
|
|
30
|
+
</SelectPrimitive.Trigger>
|
|
31
|
+
));
|
|
32
|
+
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
|
|
33
|
+
|
|
34
|
+
const SelectScrollUpButton = React.forwardRef<
|
|
35
|
+
React.ElementRef<typeof SelectPrimitive.ScrollUpButton>,
|
|
36
|
+
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton>
|
|
37
|
+
>(({ className, ...props }, ref) => (
|
|
38
|
+
<SelectPrimitive.ScrollUpButton
|
|
39
|
+
ref={ref}
|
|
40
|
+
className={cn(
|
|
41
|
+
'flex cursor-default items-center justify-center py-1',
|
|
42
|
+
className,
|
|
43
|
+
)}
|
|
44
|
+
{...props}
|
|
45
|
+
>
|
|
46
|
+
<ChevronUp className='h-4 w-4' />
|
|
47
|
+
</SelectPrimitive.ScrollUpButton>
|
|
48
|
+
));
|
|
49
|
+
SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
|
|
50
|
+
|
|
51
|
+
const SelectScrollDownButton = React.forwardRef<
|
|
52
|
+
React.ElementRef<typeof SelectPrimitive.ScrollDownButton>,
|
|
53
|
+
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton>
|
|
54
|
+
>(({ className, ...props }, ref) => (
|
|
55
|
+
<SelectPrimitive.ScrollDownButton
|
|
56
|
+
ref={ref}
|
|
57
|
+
className={cn(
|
|
58
|
+
'flex cursor-default items-center justify-center py-1',
|
|
59
|
+
className,
|
|
60
|
+
)}
|
|
61
|
+
{...props}
|
|
62
|
+
>
|
|
63
|
+
<ChevronDown className='h-4 w-4' />
|
|
64
|
+
</SelectPrimitive.ScrollDownButton>
|
|
65
|
+
));
|
|
66
|
+
SelectScrollDownButton.displayName =
|
|
67
|
+
SelectPrimitive.ScrollDownButton.displayName;
|
|
68
|
+
|
|
69
|
+
const SelectContent = React.forwardRef<
|
|
70
|
+
React.ElementRef<typeof SelectPrimitive.Content>,
|
|
71
|
+
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
|
|
72
|
+
>(({ className, children, position = 'popper', ...props }, ref) => {
|
|
73
|
+
return (
|
|
74
|
+
<SelectPrimitive.Content
|
|
75
|
+
ref={ref}
|
|
76
|
+
className={cn(
|
|
77
|
+
'relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-background text-foreground shadow-md',
|
|
78
|
+
position === 'popper' &&
|
|
79
|
+
'data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1',
|
|
80
|
+
className,
|
|
81
|
+
)}
|
|
82
|
+
position={position}
|
|
83
|
+
{...props}
|
|
84
|
+
>
|
|
85
|
+
<SelectScrollUpButton />
|
|
86
|
+
<SelectPrimitive.Viewport
|
|
87
|
+
className={cn(
|
|
88
|
+
'p-1',
|
|
89
|
+
position === 'popper' &&
|
|
90
|
+
'h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]',
|
|
91
|
+
)}
|
|
92
|
+
>
|
|
93
|
+
{children}
|
|
94
|
+
</SelectPrimitive.Viewport>
|
|
95
|
+
<SelectScrollDownButton />
|
|
96
|
+
</SelectPrimitive.Content>
|
|
97
|
+
);
|
|
98
|
+
});
|
|
99
|
+
SelectContent.displayName = SelectPrimitive.Content.displayName;
|
|
100
|
+
|
|
101
|
+
const SelectLabel = React.forwardRef<
|
|
102
|
+
React.ElementRef<typeof SelectPrimitive.Label>,
|
|
103
|
+
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
|
|
104
|
+
>(({ className, ...props }, ref) => (
|
|
105
|
+
<SelectPrimitive.Label
|
|
106
|
+
ref={ref}
|
|
107
|
+
className={cn('py-1.5 pl-8 pr-2 text-sm font-semibold', className)}
|
|
108
|
+
{...props}
|
|
109
|
+
/>
|
|
110
|
+
));
|
|
111
|
+
SelectLabel.displayName = SelectPrimitive.Label.displayName;
|
|
112
|
+
|
|
113
|
+
const SelectItem = React.forwardRef<
|
|
114
|
+
React.ElementRef<typeof SelectPrimitive.Item>,
|
|
115
|
+
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
|
|
116
|
+
>(({ className, children, ...props }, ref) => (
|
|
117
|
+
<SelectPrimitive.Item
|
|
118
|
+
ref={ref}
|
|
119
|
+
className={cn(
|
|
120
|
+
'relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
|
121
|
+
className,
|
|
122
|
+
)}
|
|
123
|
+
{...props}
|
|
124
|
+
>
|
|
125
|
+
<span className='absolute left-2 flex h-3.5 w-3.5 items-center justify-center'>
|
|
126
|
+
<SelectPrimitive.ItemIndicator>
|
|
127
|
+
<Check className='h-4 w-4' />
|
|
128
|
+
</SelectPrimitive.ItemIndicator>
|
|
129
|
+
</span>
|
|
130
|
+
|
|
131
|
+
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
|
|
132
|
+
</SelectPrimitive.Item>
|
|
133
|
+
));
|
|
134
|
+
SelectItem.displayName = SelectPrimitive.Item.displayName;
|
|
135
|
+
|
|
136
|
+
const SelectSeparator = React.forwardRef<
|
|
137
|
+
React.ElementRef<typeof SelectPrimitive.Separator>,
|
|
138
|
+
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>
|
|
139
|
+
>(({ className, ...props }, ref) => (
|
|
140
|
+
<SelectPrimitive.Separator
|
|
141
|
+
ref={ref}
|
|
142
|
+
className={cn('-mx-1 my-1 h-px bg-muted', className)}
|
|
143
|
+
{...props}
|
|
144
|
+
/>
|
|
145
|
+
));
|
|
146
|
+
SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
|
|
147
|
+
|
|
148
|
+
export {
|
|
149
|
+
Select,
|
|
150
|
+
SelectGroup,
|
|
151
|
+
SelectValue,
|
|
152
|
+
SelectTrigger,
|
|
153
|
+
SelectContent,
|
|
154
|
+
SelectLabel,
|
|
155
|
+
SelectItem,
|
|
156
|
+
SelectSeparator,
|
|
157
|
+
SelectScrollUpButton,
|
|
158
|
+
SelectScrollDownButton,
|
|
159
|
+
};
|
package/lib/common/textarea.tsx
CHANGED
|
@@ -20,7 +20,7 @@ const textareaVariants = tv({
|
|
|
20
20
|
'rounded-md border px-3 ring-offset-background focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',
|
|
21
21
|
},
|
|
22
22
|
beta: {
|
|
23
|
-
root: 'after:absolute after:left-0 after:top-[calc(100%_+
|
|
23
|
+
root: 'after:absolute after:left-0 after:top-[calc(100%_+_1px)] after:w-full after:rounded-full after:content-[""] focus-within:after:h-0.5 focus-within:after:bg-ring',
|
|
24
24
|
textarea: 'border-b px-1',
|
|
25
25
|
},
|
|
26
26
|
},
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { FC } from 'react';
|
|
2
2
|
import type { ParsedJsonComponent } from '@twintag/twintag-form/types';
|
|
3
3
|
|
|
4
|
+
import { Containers } from '@twintag/twintag-form/components/config';
|
|
5
|
+
|
|
4
6
|
type Props = {
|
|
5
7
|
config: ParsedJsonComponent;
|
|
6
8
|
};
|
|
@@ -13,9 +15,9 @@ export const Renderer: FC<Props> = ({ config }) => {
|
|
|
13
15
|
const Component = comp;
|
|
14
16
|
|
|
15
17
|
if (items && Array.isArray(items) && items.length > 0) {
|
|
16
|
-
if (type
|
|
18
|
+
if (Containers.has(type)) {
|
|
17
19
|
return (
|
|
18
|
-
<Component key={id} {...rest}>
|
|
20
|
+
<Component key={id} items={items} {...rest}>
|
|
19
21
|
{items.map((item) => (
|
|
20
22
|
<Renderer key={item.id} config={{ ...item }} />
|
|
21
23
|
))}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { ElementType, FC } from 'react';
|
|
2
|
+
|
|
3
|
+
import { Accordion } from './containers/accordion';
|
|
4
|
+
import { Category } from './containers/category';
|
|
5
|
+
import { Checkbox } from './fields/checkbox';
|
|
6
|
+
import { DatePicker } from './fields/date-picker';
|
|
7
|
+
import { File } from './fields/file';
|
|
8
|
+
import { Input as InputField } from './fields/input';
|
|
9
|
+
import { RadioGroup } from './fields/radiogroup';
|
|
10
|
+
import { Submit } from './fields/submit';
|
|
11
|
+
import { Textarea } from './fields/textarea';
|
|
12
|
+
import { Toggle } from './fields/toggle';
|
|
13
|
+
|
|
14
|
+
export const Inputs = new Map<string, ElementType>()
|
|
15
|
+
.set('simple', InputField)
|
|
16
|
+
.set('textarea', Textarea)
|
|
17
|
+
.set('toggle', Toggle)
|
|
18
|
+
.set('file', File)
|
|
19
|
+
.set('submit', Submit)
|
|
20
|
+
.set('checkbox', Checkbox)
|
|
21
|
+
.set('radiogroup', RadioGroup)
|
|
22
|
+
.set('datepicker', DatePicker);
|
|
23
|
+
|
|
24
|
+
type InputProps = {
|
|
25
|
+
subtype?: string;
|
|
26
|
+
label?: string;
|
|
27
|
+
bind?: string;
|
|
28
|
+
};
|
|
29
|
+
const Input: FC<InputProps> = ({ subtype = 'simple', ...rest }) => {
|
|
30
|
+
const Comp = Inputs.get(subtype);
|
|
31
|
+
|
|
32
|
+
if (!Comp) return <></>;
|
|
33
|
+
|
|
34
|
+
return <Comp {...rest} />;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export const Containers = new Map<string, ElementType>()
|
|
38
|
+
.set('category', Category)
|
|
39
|
+
.set('accordion', Accordion);
|
|
40
|
+
|
|
41
|
+
export const components = new Map<string, ElementType>(Containers).set(
|
|
42
|
+
'input',
|
|
43
|
+
Input,
|
|
44
|
+
);
|