@carlonicora/nextjs-jsonapi 1.24.1 → 1.24.3
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/dist/{BlockNoteEditor-UCHRVVVZ.js → BlockNoteEditor-OFSTXGZX.js} +6 -6
- package/dist/{BlockNoteEditor-UCHRVVVZ.js.map → BlockNoteEditor-OFSTXGZX.js.map} +1 -1
- package/dist/{BlockNoteEditor-ZYZZ6B45.mjs → BlockNoteEditor-TJNLCNIP.mjs} +2 -2
- package/dist/billing/index.js +300 -303
- package/dist/billing/index.js.map +1 -1
- package/dist/billing/index.mjs +5 -8
- package/dist/billing/index.mjs.map +1 -1
- package/dist/{chunk-ILKUML3Z.js → chunk-EJALOG7L.js} +4002 -3923
- package/dist/chunk-EJALOG7L.js.map +1 -0
- package/dist/{chunk-CU4RXSNY.mjs → chunk-H5JZ5E7M.mjs} +4336 -4257
- package/dist/chunk-H5JZ5E7M.mjs.map +1 -0
- package/dist/client/index.js +2 -2
- package/dist/client/index.mjs +1 -1
- package/dist/components/index.d.mts +69 -34
- package/dist/components/index.d.ts +69 -34
- package/dist/components/index.js +20 -2
- package/dist/components/index.js.map +1 -1
- package/dist/components/index.mjs +35 -17
- package/dist/contexts/index.js +2 -2
- package/dist/contexts/index.mjs +1 -1
- package/dist/scripts/generate-web-module/templates/components/editor.template.js +11 -13
- package/dist/scripts/generate-web-module/templates/components/editor.template.js.map +1 -1
- package/dist/scripts/generate-web-module/templates/components/multi-selector.template.d.ts.map +1 -1
- package/dist/scripts/generate-web-module/templates/components/multi-selector.template.js +13 -26
- package/dist/scripts/generate-web-module/templates/components/multi-selector.template.js.map +1 -1
- package/dist/scripts/generate-web-module/templates/components/selector.template.d.ts.map +1 -1
- package/dist/scripts/generate-web-module/templates/components/selector.template.js +59 -76
- package/dist/scripts/generate-web-module/templates/components/selector.template.js.map +1 -1
- package/dist/scripts/generate-web-module/transformers/field-mapper.d.ts.map +1 -1
- package/dist/scripts/generate-web-module/transformers/field-mapper.js +10 -12
- package/dist/scripts/generate-web-module/transformers/field-mapper.js.map +1 -1
- package/package.json +2 -2
- package/scripts/generate-web-module/templates/components/editor.template.ts +11 -13
- package/scripts/generate-web-module/templates/components/multi-selector.template.ts +13 -26
- package/scripts/generate-web-module/templates/components/selector.template.ts +59 -76
- package/scripts/generate-web-module/transformers/field-mapper.ts +10 -12
- package/src/components/forms/FormCheckbox.tsx +18 -24
- package/src/components/forms/FormDate.tsx +103 -116
- package/src/components/forms/FormDateTime.tsx +122 -130
- package/src/components/forms/FormFieldWrapper.tsx +54 -0
- package/src/components/forms/FormInput.tsx +58 -46
- package/src/components/forms/FormPassword.tsx +17 -24
- package/src/components/forms/FormPlaceAutocomplete.tsx +50 -75
- package/src/components/forms/FormSelect.tsx +29 -35
- package/src/components/forms/FormSlider.tsx +23 -27
- package/src/components/forms/FormSwitch.tsx +12 -14
- package/src/components/forms/FormTextarea.tsx +12 -19
- package/src/components/forms/__tests__/FormInput.test.tsx +4 -2
- package/src/components/forms/index.ts +1 -1
- package/src/components/pages/PageContentContainer.tsx +2 -1
- package/src/features/billing/stripe-price/components/forms/PriceEditor.tsx +9 -13
- package/src/features/company/components/forms/CompanyConfigurationSecurityForm.tsx +19 -33
- package/src/features/feature/components/forms/FormFeatures.tsx +3 -4
- package/src/features/role/components/forms/FormRoles.tsx +40 -51
- package/src/features/user/components/forms/UserMultiSelect.tsx +12 -29
- package/src/features/user/components/forms/UserSelector.tsx +79 -91
- package/src/hooks/__tests__/useDataListRetriever.test.ts +10 -1
- package/src/shadcnui/index.ts +2 -0
- package/src/shadcnui/ui/field.tsx +3 -3
- package/src/shadcnui/ui/form.tsx +17 -134
- package/src/shadcnui/ui/input-group.tsx +4 -4
- package/src/shadcnui/ui/resizable.tsx +8 -8
- package/dist/chunk-CU4RXSNY.mjs.map +0 -1
- package/dist/chunk-ILKUML3Z.js.map +0 -1
- package/src/components/forms/FormContainerGeneric.tsx +0 -39
- /package/dist/{BlockNoteEditor-ZYZZ6B45.mjs.map → BlockNoteEditor-TJNLCNIP.mjs.map} +0 -0
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import { useTranslations } from "next-intl";
|
|
4
|
+
import { FormFieldWrapper } from "../../../../components/forms";
|
|
4
5
|
import {
|
|
5
6
|
Checkbox,
|
|
6
|
-
|
|
7
|
-
FormField,
|
|
8
|
-
FormItem,
|
|
9
|
-
FormLabel,
|
|
10
|
-
FormMessage,
|
|
7
|
+
FieldLabel,
|
|
11
8
|
Tooltip,
|
|
12
9
|
TooltipContent,
|
|
13
10
|
TooltipTrigger,
|
|
@@ -29,54 +26,46 @@ export function FormRoles({ form, id, name, roles }: FormRolesProps) {
|
|
|
29
26
|
|
|
30
27
|
return (
|
|
31
28
|
<div className="flex w-full flex-col">
|
|
32
|
-
<
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
{roles
|
|
41
|
-
.filter((role: RoleInterface) => role.isSelectable)
|
|
42
|
-
.sort((a: RoleInterface, b: RoleInterface) => a.name.localeCompare(b.name))
|
|
43
|
-
.map((role: RoleInterface) => {
|
|
44
|
-
if (role.requiredFeature && !hasAccesToFeature(role.requiredFeature.id)) return null;
|
|
29
|
+
<FormFieldWrapper form={form} name={id} label={name}>
|
|
30
|
+
{(field) => (
|
|
31
|
+
<div>
|
|
32
|
+
{roles
|
|
33
|
+
.filter((role: RoleInterface) => role.isSelectable)
|
|
34
|
+
.sort((a: RoleInterface, b: RoleInterface) => a.name.localeCompare(b.name))
|
|
35
|
+
.map((role: RoleInterface) => {
|
|
36
|
+
if (role.requiredFeature && !hasAccesToFeature(role.requiredFeature.id)) return null;
|
|
45
37
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
</FormControl>
|
|
76
|
-
<FormMessage />
|
|
77
|
-
</FormItem>
|
|
38
|
+
return (
|
|
39
|
+
<div key={role.id}>
|
|
40
|
+
<Checkbox
|
|
41
|
+
defaultChecked={(field.value as string[]).some((roleId: string) => roleId === role.id)}
|
|
42
|
+
onCheckedChange={(checked) => {
|
|
43
|
+
if (checked) {
|
|
44
|
+
form.setValue(id, [...(field.value as string[]), role.id]);
|
|
45
|
+
} else {
|
|
46
|
+
form.setValue(
|
|
47
|
+
id,
|
|
48
|
+
(field.value as string[]).filter((roleId: string) => roleId !== role.id),
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
}}
|
|
52
|
+
/>
|
|
53
|
+
<Tooltip>
|
|
54
|
+
<TooltipTrigger>
|
|
55
|
+
<FieldLabel className="ml-3 font-normal">
|
|
56
|
+
{t(`foundations.role.roles`, { role: role.id.replaceAll(`-`, ``) })}
|
|
57
|
+
</FieldLabel>
|
|
58
|
+
</TooltipTrigger>
|
|
59
|
+
<TooltipContent>
|
|
60
|
+
{t(`foundations.role.roles_descriptions`, { role: role.id.replaceAll(`-`, ``) })}
|
|
61
|
+
</TooltipContent>
|
|
62
|
+
</Tooltip>
|
|
63
|
+
</div>
|
|
64
|
+
);
|
|
65
|
+
})}
|
|
66
|
+
</div>
|
|
78
67
|
)}
|
|
79
|
-
|
|
68
|
+
</FormFieldWrapper>
|
|
80
69
|
</div>
|
|
81
70
|
);
|
|
82
71
|
}
|
|
@@ -2,17 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
4
4
|
import { useWatch } from "react-hook-form";
|
|
5
|
+
import { FormFieldWrapper } from "../../../../components/forms";
|
|
5
6
|
import { Modules } from "../../../../core";
|
|
6
7
|
import { DataListRetriever, useDataListRetriever, useDebounce } from "../../../../hooks";
|
|
7
8
|
import {
|
|
8
9
|
Avatar,
|
|
9
10
|
AvatarFallback,
|
|
10
11
|
AvatarImage,
|
|
11
|
-
FormControl,
|
|
12
|
-
FormField,
|
|
13
|
-
FormItem,
|
|
14
|
-
FormLabel,
|
|
15
|
-
FormMessage,
|
|
16
12
|
MultiSelect,
|
|
17
13
|
} from "../../../../shadcnui";
|
|
18
14
|
import { useCurrentUserContext } from "../../contexts";
|
|
@@ -189,31 +185,18 @@ export function UserMultiSelect({
|
|
|
189
185
|
|
|
190
186
|
return (
|
|
191
187
|
<div className="flex w-full flex-col">
|
|
192
|
-
<
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
{
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
)}
|
|
203
|
-
<FormControl>
|
|
204
|
-
<MultiSelect
|
|
205
|
-
options={userOptions}
|
|
206
|
-
onValueChange={handleValueChange}
|
|
207
|
-
defaultValue={selectedUserIds}
|
|
208
|
-
placeholder={placeholder}
|
|
209
|
-
maxCount={maxCount}
|
|
210
|
-
animation={0}
|
|
211
|
-
/>
|
|
212
|
-
</FormControl>
|
|
213
|
-
<FormMessage />
|
|
214
|
-
</FormItem>
|
|
188
|
+
<FormFieldWrapper form={form} name={id} label={label} isRequired={isRequired}>
|
|
189
|
+
{() => (
|
|
190
|
+
<MultiSelect
|
|
191
|
+
options={userOptions}
|
|
192
|
+
onValueChange={handleValueChange}
|
|
193
|
+
defaultValue={selectedUserIds}
|
|
194
|
+
placeholder={placeholder}
|
|
195
|
+
maxCount={maxCount}
|
|
196
|
+
animation={0}
|
|
197
|
+
/>
|
|
215
198
|
)}
|
|
216
|
-
|
|
199
|
+
</FormFieldWrapper>
|
|
217
200
|
</div>
|
|
218
201
|
);
|
|
219
202
|
}
|
|
@@ -5,6 +5,7 @@ import { useTranslations } from "next-intl";
|
|
|
5
5
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
6
6
|
import { Modules } from "../../../../core";
|
|
7
7
|
import { DataListRetriever, useDataListRetriever, useDebounce } from "../../../../hooks";
|
|
8
|
+
import { FormFieldWrapper } from "../../../../components/forms";
|
|
8
9
|
import {
|
|
9
10
|
Avatar,
|
|
10
11
|
AvatarFallback,
|
|
@@ -12,11 +13,6 @@ import {
|
|
|
12
13
|
Command,
|
|
13
14
|
CommandItem,
|
|
14
15
|
CommandList,
|
|
15
|
-
FormControl,
|
|
16
|
-
FormField,
|
|
17
|
-
FormItem,
|
|
18
|
-
FormLabel,
|
|
19
|
-
FormMessage,
|
|
20
16
|
Input,
|
|
21
17
|
Popover,
|
|
22
18
|
PopoverContent,
|
|
@@ -92,99 +88,91 @@ export function UserSelector({ id, form, label, placeholder, onChange, isRequire
|
|
|
92
88
|
|
|
93
89
|
return (
|
|
94
90
|
<div className="flex w-full flex-col">
|
|
95
|
-
<
|
|
96
|
-
|
|
91
|
+
<FormFieldWrapper
|
|
92
|
+
form={form}
|
|
97
93
|
name={id}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
{field.value?.name
|
|
119
|
-
? field.value?.name.split(" ").map((name: string) => name.charAt(0).toUpperCase())
|
|
120
|
-
: "X"}
|
|
121
|
-
</AvatarFallback>
|
|
122
|
-
</Avatar>
|
|
123
|
-
</div>
|
|
124
|
-
<span className="">{field.value?.name ?? ""}</span>
|
|
125
|
-
</div>
|
|
126
|
-
</>
|
|
127
|
-
) : (
|
|
128
|
-
<div className="text-muted-foreground mr-7 flex h-10 w-full flex-row items-center justify-start rounded-md border p-2 text-sm">
|
|
129
|
-
{placeholder ?? t(`generic.search.placeholder`, { type: t(`types.users`, { count: 1 }) })}
|
|
94
|
+
label={label}
|
|
95
|
+
isRequired={isRequired}
|
|
96
|
+
>
|
|
97
|
+
{(field) => (
|
|
98
|
+
<Popover open={open} onOpenChange={setOpen} modal={true}>
|
|
99
|
+
<div className="flex w-full flex-row items-center justify-between">
|
|
100
|
+
<PopoverTrigger className="w-full">
|
|
101
|
+
<div className="flex w-full flex-row items-center justify-start rounded-md">
|
|
102
|
+
{field.value ? (
|
|
103
|
+
<>
|
|
104
|
+
<div className="flex w-full flex-row items-center justify-start rounded-md border p-2">
|
|
105
|
+
<div className="*:ring-border *:ring-1">
|
|
106
|
+
<Avatar className={`mr-2 h-6 w-6`}>
|
|
107
|
+
<AvatarImage src={field.value?.avatar} />
|
|
108
|
+
<AvatarFallback>
|
|
109
|
+
{field.value?.name
|
|
110
|
+
? field.value?.name.split(" ").map((name: string) => name.charAt(0).toUpperCase())
|
|
111
|
+
: "X"}
|
|
112
|
+
</AvatarFallback>
|
|
113
|
+
</Avatar>
|
|
130
114
|
</div>
|
|
131
|
-
|
|
115
|
+
<span className="">{field.value?.name ?? ""}</span>
|
|
116
|
+
</div>
|
|
117
|
+
</>
|
|
118
|
+
) : (
|
|
119
|
+
<div className="text-muted-foreground mr-7 flex h-10 w-full flex-row items-center justify-start rounded-md border p-2 text-sm">
|
|
120
|
+
{placeholder ?? t(`generic.search.placeholder`, { type: t(`types.users`, { count: 1 }) })}
|
|
132
121
|
</div>
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
122
|
+
)}
|
|
123
|
+
</div>
|
|
124
|
+
</PopoverTrigger>
|
|
125
|
+
{field.value && (
|
|
126
|
+
<CircleX
|
|
127
|
+
className="text-muted hover:text-destructive ml-2 h-6 w-6 cursor-pointer"
|
|
128
|
+
onClick={() => setUser()}
|
|
129
|
+
/>
|
|
130
|
+
)}
|
|
131
|
+
</div>
|
|
132
|
+
<PopoverContent>
|
|
133
|
+
<Command shouldFilter={false}>
|
|
134
|
+
<div className="relative mb-2 w-full">
|
|
135
|
+
<SearchIcon className="text-muted-foreground absolute top-2.5 left-2.5 h-4 w-4" />
|
|
136
|
+
<Input
|
|
137
|
+
placeholder={t(`generic.search.placeholder`, { type: t(`types.users`, { count: 1 }) })}
|
|
138
|
+
type="text"
|
|
139
|
+
className="w-full pr-8 pl-8"
|
|
140
|
+
onChange={(e) => setSearchTerm(e.target.value)}
|
|
141
|
+
value={searchTerm}
|
|
142
|
+
/>
|
|
143
|
+
{isSearching ? (
|
|
144
|
+
<RefreshCwIcon className="text-muted-foreground absolute top-2.5 right-2.5 h-4 w-4 animate-spin" />
|
|
145
|
+
) : searchTermRef.current ? (
|
|
146
|
+
<XIcon
|
|
147
|
+
className={`absolute top-2.5 right-2.5 h-4 w-4 ${searchTermRef.current ? "cursor-pointer" : "text-muted-foreground"}`}
|
|
148
|
+
onClick={() => {
|
|
149
|
+
setSearchTerm("");
|
|
150
|
+
search("");
|
|
151
|
+
}}
|
|
138
152
|
/>
|
|
153
|
+
) : (
|
|
154
|
+
<></>
|
|
139
155
|
)}
|
|
140
156
|
</div>
|
|
141
|
-
<
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
<
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
setSearchTerm("");
|
|
159
|
-
search("");
|
|
160
|
-
}}
|
|
161
|
-
/>
|
|
162
|
-
) : (
|
|
163
|
-
<></>
|
|
164
|
-
)}
|
|
165
|
-
</div>
|
|
166
|
-
<CommandList>
|
|
167
|
-
{data.data &&
|
|
168
|
-
data.data.length > 0 &&
|
|
169
|
-
(data.data as UserInterface[]).map((user: UserInterface) => (
|
|
170
|
-
<CommandItem
|
|
171
|
-
className="cursor-pointer hover:bg-muted data-selected:hover:bg-muted bg-transparent data-selected:bg-transparent"
|
|
172
|
-
key={user.id}
|
|
173
|
-
onSelect={() => setUser(user)}
|
|
174
|
-
>
|
|
175
|
-
<UserAvatar user={user} className={`mr-2 h-4 w-4`} />
|
|
176
|
-
<span className="">{user.name}</span>
|
|
177
|
-
</CommandItem>
|
|
178
|
-
))}
|
|
179
|
-
</CommandList>
|
|
180
|
-
</Command>
|
|
181
|
-
</PopoverContent>
|
|
182
|
-
</Popover>
|
|
183
|
-
</FormControl>
|
|
184
|
-
<FormMessage />
|
|
185
|
-
</FormItem>
|
|
157
|
+
<CommandList>
|
|
158
|
+
{data.data &&
|
|
159
|
+
data.data.length > 0 &&
|
|
160
|
+
(data.data as UserInterface[]).map((user: UserInterface) => (
|
|
161
|
+
<CommandItem
|
|
162
|
+
className="cursor-pointer hover:bg-muted data-selected:hover:bg-muted bg-transparent data-selected:bg-transparent"
|
|
163
|
+
key={user.id}
|
|
164
|
+
onSelect={() => setUser(user)}
|
|
165
|
+
>
|
|
166
|
+
<UserAvatar user={user} className={`mr-2 h-4 w-4`} />
|
|
167
|
+
<span className="">{user.name}</span>
|
|
168
|
+
</CommandItem>
|
|
169
|
+
))}
|
|
170
|
+
</CommandList>
|
|
171
|
+
</Command>
|
|
172
|
+
</PopoverContent>
|
|
173
|
+
</Popover>
|
|
186
174
|
)}
|
|
187
|
-
|
|
175
|
+
</FormFieldWrapper>
|
|
188
176
|
</div>
|
|
189
177
|
);
|
|
190
178
|
}
|
|
@@ -25,7 +25,11 @@ describe("useDataListRetriever", () => {
|
|
|
25
25
|
})
|
|
26
26
|
);
|
|
27
27
|
|
|
28
|
-
//
|
|
28
|
+
// Wait for async operations to settle
|
|
29
|
+
await waitFor(() => {
|
|
30
|
+
expect(result.current.isLoaded).toBe(true);
|
|
31
|
+
});
|
|
32
|
+
|
|
29
33
|
expect(result.current.ready).toBe(true);
|
|
30
34
|
expect(result.current.isSearch).toBe(false);
|
|
31
35
|
});
|
|
@@ -194,6 +198,11 @@ describe("useDataListRetriever", () => {
|
|
|
194
198
|
result.current.setReady(true);
|
|
195
199
|
});
|
|
196
200
|
|
|
201
|
+
// Wait for async operations triggered by setReady(true) to settle
|
|
202
|
+
await waitFor(() => {
|
|
203
|
+
expect(result.current.isLoaded).toBe(true);
|
|
204
|
+
});
|
|
205
|
+
|
|
197
206
|
expect(result.current.ready).toBe(true);
|
|
198
207
|
});
|
|
199
208
|
});
|
package/src/shadcnui/index.ts
CHANGED
|
@@ -19,9 +19,11 @@ export * from "./ui/context-menu";
|
|
|
19
19
|
export * from "./ui/dialog";
|
|
20
20
|
export * from "./ui/drawer";
|
|
21
21
|
export * from "./ui/dropdown-menu";
|
|
22
|
+
export * from "./ui/field";
|
|
22
23
|
export * from "./ui/form";
|
|
23
24
|
export * from "./ui/hover-card";
|
|
24
25
|
export * from "./ui/input";
|
|
26
|
+
export * from "./ui/input-group";
|
|
25
27
|
export * from "./ui/input-otp";
|
|
26
28
|
export * from "./ui/label";
|
|
27
29
|
export * from "./ui/navigation-menu";
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
import { useMemo } from "react"
|
|
4
4
|
import { cva, type VariantProps } from "class-variance-authority"
|
|
5
5
|
|
|
6
|
-
import { cn } from "
|
|
7
|
-
import { Label } from "
|
|
8
|
-
import { Separator } from "
|
|
6
|
+
import { cn } from "../../utils/cn"
|
|
7
|
+
import { Label } from "./label"
|
|
8
|
+
import { Separator } from "./separator"
|
|
9
9
|
|
|
10
10
|
function FieldSet({ className, ...props }: React.ComponentProps<"fieldset">) {
|
|
11
11
|
return (
|
package/src/shadcnui/ui/form.tsx
CHANGED
|
@@ -1,138 +1,21 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
3
|
+
import { FormProvider } from "react-hook-form";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Form component is a wrapper around react-hook-form's FormProvider.
|
|
7
|
+
* Use this to wrap your form and spread the form object into it.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* const form = useForm<FormValues>();
|
|
11
|
+
* return (
|
|
12
|
+
* <Form {...form}>
|
|
13
|
+
* <form onSubmit={form.handleSubmit(onSubmit)}>
|
|
14
|
+
* <FormInput form={form} id="email" name="Email" />
|
|
15
|
+
* </form>
|
|
16
|
+
* </Form>
|
|
17
|
+
* );
|
|
18
|
+
*/
|
|
11
19
|
const Form = FormProvider;
|
|
12
20
|
|
|
13
|
-
|
|
14
|
-
TFieldValues extends FieldValues = FieldValues,
|
|
15
|
-
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
|
|
16
|
-
> = {
|
|
17
|
-
name: TName;
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
const FormFieldContext = React.createContext<FormFieldContextValue>({} as FormFieldContextValue);
|
|
21
|
-
|
|
22
|
-
const FormField = <
|
|
23
|
-
TFieldValues extends FieldValues = FieldValues,
|
|
24
|
-
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
|
|
25
|
-
>({
|
|
26
|
-
...props
|
|
27
|
-
}: ControllerProps<TFieldValues, TName>) => {
|
|
28
|
-
return (
|
|
29
|
-
<FormFieldContext.Provider value={{ name: props.name }}>
|
|
30
|
-
<Controller {...props} />
|
|
31
|
-
</FormFieldContext.Provider>
|
|
32
|
-
);
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
const useFormField = () => {
|
|
36
|
-
const fieldContext = React.useContext(FormFieldContext);
|
|
37
|
-
const itemContext = React.useContext(FormItemContext);
|
|
38
|
-
const { getFieldState, formState } = useFormContext();
|
|
39
|
-
|
|
40
|
-
const fieldState = getFieldState(fieldContext.name, formState);
|
|
41
|
-
|
|
42
|
-
if (!fieldContext) {
|
|
43
|
-
throw new Error("useFormField should be used within <FormField>");
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const { id } = itemContext;
|
|
47
|
-
|
|
48
|
-
return {
|
|
49
|
-
id,
|
|
50
|
-
name: fieldContext.name,
|
|
51
|
-
formItemId: `${id}-form-item`,
|
|
52
|
-
formDescriptionId: `${id}-form-item-description`,
|
|
53
|
-
formMessageId: `${id}-form-item-message`,
|
|
54
|
-
...fieldState,
|
|
55
|
-
};
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
type FormItemContextValue = {
|
|
59
|
-
id: string;
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
const FormItemContext = React.createContext<FormItemContextValue>({} as FormItemContextValue);
|
|
63
|
-
|
|
64
|
-
const FormItem = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
|
65
|
-
({ className, ...props }, ref) => {
|
|
66
|
-
const id = React.useId();
|
|
67
|
-
|
|
68
|
-
return (
|
|
69
|
-
<FormItemContext.Provider value={{ id }}>
|
|
70
|
-
<div ref={ref} className={cn("space-y-2", className)} {...props} />
|
|
71
|
-
</FormItemContext.Provider>
|
|
72
|
-
);
|
|
73
|
-
},
|
|
74
|
-
);
|
|
75
|
-
FormItem.displayName = "FormItem";
|
|
76
|
-
|
|
77
|
-
const FormLabel = React.forwardRef<
|
|
78
|
-
React.ElementRef<typeof LabelPrimitive.Root>,
|
|
79
|
-
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root>
|
|
80
|
-
>(({ className, ...props }, ref) => {
|
|
81
|
-
const { error, formItemId } = useFormField();
|
|
82
|
-
|
|
83
|
-
return <Label ref={ref} className={cn(error && "text-destructive", className)} htmlFor={formItemId} {...props} />;
|
|
84
|
-
});
|
|
85
|
-
FormLabel.displayName = "FormLabel";
|
|
86
|
-
|
|
87
|
-
const FormControl = React.forwardRef<React.ElementRef<typeof Slot>, React.ComponentPropsWithoutRef<typeof Slot>>(
|
|
88
|
-
({ ...props }, ref) => {
|
|
89
|
-
const { error, formItemId, formDescriptionId, formMessageId } = useFormField();
|
|
90
|
-
|
|
91
|
-
return (
|
|
92
|
-
<Slot
|
|
93
|
-
ref={ref}
|
|
94
|
-
id={formItemId}
|
|
95
|
-
aria-describedby={!error ? `${formDescriptionId}` : `${formDescriptionId} ${formMessageId}`}
|
|
96
|
-
aria-invalid={!!error}
|
|
97
|
-
{...props}
|
|
98
|
-
/>
|
|
99
|
-
);
|
|
100
|
-
},
|
|
101
|
-
);
|
|
102
|
-
FormControl.displayName = "FormControl";
|
|
103
|
-
|
|
104
|
-
const FormDescription = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLParagraphElement>>(
|
|
105
|
-
({ className, ...props }, ref) => {
|
|
106
|
-
const { formDescriptionId } = useFormField();
|
|
107
|
-
|
|
108
|
-
return (
|
|
109
|
-
<p ref={ref} id={formDescriptionId} className={cn("text-muted-foreground text-[0.8rem]", className)} {...props} />
|
|
110
|
-
);
|
|
111
|
-
},
|
|
112
|
-
);
|
|
113
|
-
FormDescription.displayName = "FormDescription";
|
|
114
|
-
|
|
115
|
-
const FormMessage = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLParagraphElement>>(
|
|
116
|
-
({ className, children, ...props }, ref) => {
|
|
117
|
-
const { error, formMessageId } = useFormField();
|
|
118
|
-
const body = error ? String(error?.message) : children;
|
|
119
|
-
|
|
120
|
-
if (!body) {
|
|
121
|
-
return null;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
return (
|
|
125
|
-
<p
|
|
126
|
-
ref={ref}
|
|
127
|
-
id={formMessageId}
|
|
128
|
-
className={cn("text-destructive text-[0.8rem] font-medium", className)}
|
|
129
|
-
{...props}
|
|
130
|
-
>
|
|
131
|
-
{body}
|
|
132
|
-
</p>
|
|
133
|
-
);
|
|
134
|
-
},
|
|
135
|
-
);
|
|
136
|
-
FormMessage.displayName = "FormMessage";
|
|
137
|
-
|
|
138
|
-
export { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, useFormField };
|
|
21
|
+
export { Form };
|
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
import * as React from "react"
|
|
4
4
|
import { cva, type VariantProps } from "class-variance-authority"
|
|
5
5
|
|
|
6
|
-
import { cn } from "
|
|
7
|
-
import { Button } from "
|
|
8
|
-
import { Input } from "
|
|
9
|
-
import { Textarea } from "
|
|
6
|
+
import { cn } from "../../utils/cn"
|
|
7
|
+
import { Button } from "./button"
|
|
8
|
+
import { Input } from "./input"
|
|
9
|
+
import { Textarea } from "./textarea"
|
|
10
10
|
|
|
11
11
|
function InputGroup({ className, ...props }: React.ComponentProps<"div">) {
|
|
12
12
|
return (
|
|
@@ -8,12 +8,12 @@ import { cn } from "@/lib/utils"
|
|
|
8
8
|
function ResizablePanelGroup({
|
|
9
9
|
className,
|
|
10
10
|
...props
|
|
11
|
-
}: React.ComponentProps<typeof ResizablePrimitive.
|
|
11
|
+
}: React.ComponentProps<typeof ResizablePrimitive.PanelGroup>) {
|
|
12
12
|
return (
|
|
13
|
-
<ResizablePrimitive.
|
|
13
|
+
<ResizablePrimitive.PanelGroup
|
|
14
14
|
data-slot="resizable-panel-group"
|
|
15
15
|
className={cn(
|
|
16
|
-
"flex h-full w-full
|
|
16
|
+
"flex h-full w-full data-[panel-group-direction=vertical]:flex-col",
|
|
17
17
|
className
|
|
18
18
|
)}
|
|
19
19
|
{...props}
|
|
@@ -31,22 +31,22 @@ function ResizableHandle({
|
|
|
31
31
|
withHandle,
|
|
32
32
|
className,
|
|
33
33
|
...props
|
|
34
|
-
}: React.ComponentProps<typeof ResizablePrimitive.
|
|
34
|
+
}: React.ComponentProps<typeof ResizablePrimitive.PanelResizeHandle> & {
|
|
35
35
|
withHandle?: boolean
|
|
36
36
|
}) {
|
|
37
37
|
return (
|
|
38
|
-
<ResizablePrimitive.
|
|
38
|
+
<ResizablePrimitive.PanelResizeHandle
|
|
39
39
|
data-slot="resizable-handle"
|
|
40
40
|
className={cn(
|
|
41
|
-
"bg-border focus-visible:ring-ring relative flex w-px items-center justify-center after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2 focus-visible:ring-1 focus-visible:ring-offset-1 focus-visible:outline-hidden
|
|
41
|
+
"bg-border focus-visible:ring-ring relative flex w-px items-center justify-center after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2 focus-visible:ring-1 focus-visible:ring-offset-1 focus-visible:outline-hidden data-[panel-group-direction=vertical]:h-px data-[panel-group-direction=vertical]:w-full data-[panel-group-direction=vertical]:after:left-0 data-[panel-group-direction=vertical]:after:h-1 data-[panel-group-direction=vertical]:after:w-full data-[panel-group-direction=vertical]:after:translate-x-0 data-[panel-group-direction=vertical]:after:-translate-y-1/2 [&[data-panel-group-direction=vertical]>div]:rotate-90",
|
|
42
42
|
className
|
|
43
43
|
)}
|
|
44
44
|
{...props}
|
|
45
45
|
>
|
|
46
46
|
{withHandle && (
|
|
47
|
-
<div className="bg-border h-6 w-1 rounded-lg
|
|
47
|
+
<div className="bg-border z-10 flex h-6 w-1 shrink-0 rounded-lg" />
|
|
48
48
|
)}
|
|
49
|
-
</ResizablePrimitive.
|
|
49
|
+
</ResizablePrimitive.PanelResizeHandle>
|
|
50
50
|
)
|
|
51
51
|
}
|
|
52
52
|
|