@js-empire/emperor-ui 1.2.5 → 1.2.7
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/package.json +5 -2
- package/src/components/atoms/color-picker/color-picker.tsx +0 -1
- package/src/components/atoms/color-picker/free-color-picker.tsx +8 -10
- package/src/components/atoms/color-picker/preset-color-picker.tsx +5 -3
- package/src/components/atoms/color-picker/stories/color-picker.stories.tsx +25 -11
- package/src/components/atoms/field/field.tsx +75 -5
- package/src/components/atoms/field/index.ts +2 -0
- package/src/components/atoms/field/units/autocomplete-field.tsx +49 -0
- package/src/components/atoms/field/units/checkbox-field.tsx +35 -0
- package/src/components/atoms/field/units/index.ts +7 -0
- package/src/components/atoms/field/units/input-field.tsx +35 -0
- package/src/components/atoms/field/units/radio-field.tsx +43 -0
- package/src/components/atoms/field/units/select-field.tsx +50 -0
- package/src/components/atoms/field/units/switch-field.tsx +35 -0
- package/src/components/atoms/field/units/textarea-field.tsx +37 -0
- package/src/components/atoms/filter/filter.tsx +2 -1
- package/src/components/atoms/filter/stories/filter.stories.tsx +148 -7
- package/src/components/atoms/filter/units/autocomplete-filter.tsx +38 -11
- package/src/components/atoms/filter/units/checkbox-filter.tsx +16 -6
- package/src/components/atoms/filter/units/checkbox-group-filter.tsx +40 -9
- package/src/components/atoms/filter/units/date-filter.tsx +18 -22
- package/src/components/atoms/filter/units/numeric-filter.tsx +12 -7
- package/src/components/atoms/filter/units/range-filter.tsx +42 -13
- package/src/components/atoms/filter/units/search-filter.tsx +8 -7
- package/src/components/atoms/filter/units/select-filter.tsx +17 -9
- package/src/components/atoms/filter/units/switch-filter.tsx +6 -4
- package/src/components/atoms/uploader/{avatar-label.tsx → components/avatar-label.tsx} +17 -5
- package/src/components/atoms/uploader/components/index.ts +8 -0
- package/src/components/atoms/uploader/components/upload-file-error-box.tsx +40 -0
- package/src/components/atoms/uploader/{upload-file-label.tsx → components/upload-file-label.tsx} +18 -11
- package/src/components/atoms/uploader/index.ts +1 -8
- package/src/components/atoms/uploader/stories/uploader.stories.tsx +17 -10
- package/src/components/molecules/item-card/index.ts +2 -0
- package/src/components/molecules/item-card/item-actions-buttons.tsx +43 -0
- package/src/components/molecules/item-card/item-actions-overlay.tsx +41 -0
- package/src/components/molecules/item-card/item-card-body.tsx +8 -2
- package/src/components/molecules/item-card/item-card-footer.tsx +22 -1
- package/src/components/molecules/item-card/item-card-header.tsx +8 -2
- package/src/components/molecules/item-card/item-card.tsx +39 -1
- package/src/components/molecules/item-card/stories/item-card.stories.tsx +36 -0
- package/src/components/organisms/deletion-confirmor/deletion-confirmor.tsx +114 -0
- package/src/components/organisms/deletion-confirmor/index.ts +3 -0
- package/src/components/organisms/deletion-confirmor/stories/components.tsx +22 -0
- package/src/components/organisms/deletion-confirmor/stories/deletion-confirmor.stories.tsx +78 -0
- package/src/components/organisms/deletion-confirmor/styles/classes.ts +28 -0
- package/src/components/organisms/{filters → deletion-confirmor}/styles/index.ts +1 -0
- package/src/components/organisms/deletion-confirmor/styles/styles.ts +4 -0
- package/src/components/organisms/form-builder/form-builder.stories.tsx +144 -0
- package/src/components/organisms/form-builder/form-builder.tsx +75 -0
- package/src/components/organisms/form-builder/index.ts +1 -0
- package/src/components/organisms/index.ts +2 -1
- package/src/constants/card.tsx +5 -2
- package/src/constants/defaults.ts +40 -0
- package/src/context/form-builder-context.tsx +8 -0
- package/src/context/index.ts +1 -0
- package/src/hooks/index.ts +1 -0
- package/src/hooks/use-filters.ts +2 -2
- package/src/hooks/use-form-builder-context.ts +16 -0
- package/src/hooks/use-uploader.tsx +21 -9
- package/src/i18n/locales/atoms/ar.ts +1 -1
- package/src/i18n/locales/atoms/en.ts +1 -1
- package/src/i18n/locales/organisms/ar.ts +4 -0
- package/src/i18n/locales/organisms/en.ts +4 -0
- package/src/mocks/deletion-confirmor.ts +16 -0
- package/src/mocks/index.ts +2 -0
- package/src/mocks/locales/index.ts +1 -0
- package/src/mocks/locales/uploader.ts +33 -0
- package/src/providers/config-provider.tsx +8 -0
- package/src/providers/form-builder-provider-context.tsx +18 -0
- package/src/providers/index.ts +1 -0
- package/src/styles/globals.css +26 -1
- package/src/styles/hero.ts +1 -0
- package/src/styles/index.css +0 -5
- package/src/types/components/atoms/color-picker/color-picker.ts +10 -1
- package/src/types/components/atoms/field/field.ts +53 -1
- package/src/types/components/atoms/filter/filter.ts +26 -17
- package/src/types/components/atoms/uploader.ts +4 -0
- package/src/types/components/molecules/item-card/item-card.ts +14 -4
- package/src/types/components/organisms/deletion-confirmor/deletion-confirmor.ts +22 -0
- package/src/types/components/organisms/deletion-confirmor/index.ts +1 -0
- package/src/types/components/organisms/form-builder/context.ts +6 -0
- package/src/types/components/organisms/form-builder/form-builder.ts +39 -0
- package/src/types/components/organisms/form-builder/index.ts +2 -0
- package/src/types/components/organisms/index.ts +2 -0
- package/src/types/context/config.ts +2 -0
- package/src/types/context/index.ts +1 -0
- package/src/types/context/theme.ts +33 -0
- package/src/utils/uploader.ts +25 -7
- package/dist/emperor-ui.js +0 -123
- package/dist/emperor-ui.umd.cjs +0 -63
- package/dist/globals.css +0 -1
- package/dist/icons/emperor-ui-logo.ico +0 -0
- package/dist/images/avatar-female.jpg +0 -0
- package/dist/images/avatar-male.jpg +0 -0
- package/dist/images/emperor-ui-logo.png +0 -0
- package/dist/index-BXtdEByK.js +0 -5
- package/dist/index-CDB93OLO.js +0 -55965
- package/dist/index-CYORMghp.js +0 -290
- package/dist/index.d.ts +0 -1090
- package/dist/src-UW24ZMRV-C1Pn8-w8.js +0 -5
- package/src/components/atoms/color-picker/styles/color-picker.css +0 -23
- package/src/components/atoms/field/field.stories.tsx +0 -27
- package/src/components/atoms/uploader/upload-file-error-box.tsx +0 -29
- package/src/components/organisms/filters/filters.stories.tsx +0 -32
- package/src/components/organisms/filters/filters.tsx +0 -36
- package/src/components/organisms/filters/index.ts +0 -1
- package/src/components/organisms/filters/styles/classes.ts +0 -9
- /package/src/components/atoms/uploader/{upload-file-input.tsx → components/upload-file-input.tsx} +0 -0
- /package/src/components/atoms/uploader/{upload-file-listing.tsx → components/upload-file-listing.tsx} +0 -0
- /package/src/components/atoms/uploader/{uploader-title.tsx → components/uploader-title.tsx} +0 -0
- /package/src/components/atoms/uploader/{uploader.tsx → components/uploader.tsx} +0 -0
- /package/src/components/atoms/uploader/{view-image-modal.tsx → components/view-image-modal.tsx} +0 -0
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { Button } from "@heroui/button";
|
|
4
|
+
import {
|
|
5
|
+
Modal,
|
|
6
|
+
ModalBody,
|
|
7
|
+
ModalContent,
|
|
8
|
+
ModalFooter,
|
|
9
|
+
ModalHeader,
|
|
10
|
+
} from "@heroui/modal";
|
|
11
|
+
import { Trash2 } from "lucide-react";
|
|
12
|
+
import { Locale } from "@/i18n";
|
|
13
|
+
import { useEmperorUI } from "@/hooks";
|
|
14
|
+
import { cn } from "@/utils";
|
|
15
|
+
import type { DeletionConfirmorProps } from "@/types";
|
|
16
|
+
import {
|
|
17
|
+
deletionConfirmorBodyClasses,
|
|
18
|
+
deletionConfirmorContentClasses,
|
|
19
|
+
deletionConfirmorFooterClasses,
|
|
20
|
+
deletionConfirmorHeaderClasses,
|
|
21
|
+
} from "./styles";
|
|
22
|
+
|
|
23
|
+
export function DeletionConfirmor({
|
|
24
|
+
isOpen,
|
|
25
|
+
onClose,
|
|
26
|
+
title,
|
|
27
|
+
description,
|
|
28
|
+
className,
|
|
29
|
+
classNames,
|
|
30
|
+
confirmProps,
|
|
31
|
+
declineProps,
|
|
32
|
+
}: DeletionConfirmorProps) {
|
|
33
|
+
const { config } = useEmperorUI();
|
|
34
|
+
|
|
35
|
+
const lang = config?.interLocalization?.lang ?? "en";
|
|
36
|
+
const locale = config?.interLocalization?.locales?.[lang] as
|
|
37
|
+
| Locale
|
|
38
|
+
| undefined;
|
|
39
|
+
const deletionConfirmorLocale = locale?.organisms?.deletionConfirmor;
|
|
40
|
+
|
|
41
|
+
const handleDecline = (e?: unknown) => {
|
|
42
|
+
if (typeof declineProps?.onPress === "function") {
|
|
43
|
+
(declineProps.onPress as (e?: unknown) => void)(e);
|
|
44
|
+
}
|
|
45
|
+
onClose();
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<Modal
|
|
50
|
+
placement="center"
|
|
51
|
+
isOpen={isOpen}
|
|
52
|
+
onClose={onClose}
|
|
53
|
+
onOpenChange={(open) => {
|
|
54
|
+
if (!open) onClose();
|
|
55
|
+
}}
|
|
56
|
+
dir={lang === "ar" ? "rtl" : "ltr"}
|
|
57
|
+
classNames={
|
|
58
|
+
className || classNames?.base
|
|
59
|
+
? { base: cn(className, classNames?.base) }
|
|
60
|
+
: undefined
|
|
61
|
+
}
|
|
62
|
+
>
|
|
63
|
+
<ModalContent
|
|
64
|
+
className={cn(deletionConfirmorContentClasses(), classNames?.content)}
|
|
65
|
+
>
|
|
66
|
+
<ModalHeader
|
|
67
|
+
className={cn(deletionConfirmorHeaderClasses(), classNames?.header)}
|
|
68
|
+
>
|
|
69
|
+
{title}
|
|
70
|
+
</ModalHeader>
|
|
71
|
+
|
|
72
|
+
<ModalBody
|
|
73
|
+
className={cn(deletionConfirmorBodyClasses(), classNames?.body)}
|
|
74
|
+
>
|
|
75
|
+
{description}
|
|
76
|
+
</ModalBody>
|
|
77
|
+
|
|
78
|
+
<ModalFooter
|
|
79
|
+
className={cn(deletionConfirmorFooterClasses(), classNames?.footer)}
|
|
80
|
+
>
|
|
81
|
+
<Button
|
|
82
|
+
variant="flat"
|
|
83
|
+
size="sm"
|
|
84
|
+
{...declineProps}
|
|
85
|
+
onPress={handleDecline}
|
|
86
|
+
className={cn(declineProps?.className, classNames?.declineButton)}
|
|
87
|
+
>
|
|
88
|
+
{declineProps?.children ??
|
|
89
|
+
deletionConfirmorLocale?.decline ??
|
|
90
|
+
"Decline"}
|
|
91
|
+
</Button>
|
|
92
|
+
|
|
93
|
+
<Button
|
|
94
|
+
color="danger"
|
|
95
|
+
size="sm"
|
|
96
|
+
{...confirmProps}
|
|
97
|
+
className={cn(confirmProps?.className, classNames?.confirmButton)}
|
|
98
|
+
startContent={
|
|
99
|
+
confirmProps?.isLoading
|
|
100
|
+
? undefined
|
|
101
|
+
: (confirmProps?.startContent ?? (
|
|
102
|
+
<Trash2 className="size-4" aria-hidden />
|
|
103
|
+
))
|
|
104
|
+
}
|
|
105
|
+
>
|
|
106
|
+
{confirmProps?.children ??
|
|
107
|
+
deletionConfirmorLocale?.confirm ??
|
|
108
|
+
"Confirm"}
|
|
109
|
+
</Button>
|
|
110
|
+
</ModalFooter>
|
|
111
|
+
</ModalContent>
|
|
112
|
+
</Modal>
|
|
113
|
+
);
|
|
114
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Button } from "@heroui/button";
|
|
2
|
+
import { DeletionConfirmorProps } from "@/types";
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import { DeletionConfirmor } from "@/components";
|
|
5
|
+
|
|
6
|
+
export const DeletionConfirmorWithTrigger = (args: DeletionConfirmorProps) => {
|
|
7
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
8
|
+
|
|
9
|
+
return (
|
|
10
|
+
<div className="flex flex-col gap-4">
|
|
11
|
+
<Button color="danger" onPress={() => setIsOpen(true)}>
|
|
12
|
+
Delete item
|
|
13
|
+
</Button>
|
|
14
|
+
|
|
15
|
+
<DeletionConfirmor
|
|
16
|
+
{...args}
|
|
17
|
+
isOpen={isOpen}
|
|
18
|
+
onClose={() => setIsOpen(false)}
|
|
19
|
+
/>
|
|
20
|
+
</div>
|
|
21
|
+
);
|
|
22
|
+
};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
2
|
+
import { DeletionConfirmor, DeletionConfirmorWithTrigger } from "@/components";
|
|
3
|
+
import { getStorybookDecorators } from "@/utils";
|
|
4
|
+
import { DeletionConfirmorProps } from "@/types";
|
|
5
|
+
import { mockAwaitingDelete } from "@/mocks";
|
|
6
|
+
import { Button } from "@heroui/button";
|
|
7
|
+
import { useState } from "react";
|
|
8
|
+
|
|
9
|
+
const meta: Meta<typeof DeletionConfirmor> = {
|
|
10
|
+
title: "Organisms/DeletionConfirmor",
|
|
11
|
+
component: DeletionConfirmor,
|
|
12
|
+
parameters: {
|
|
13
|
+
layout: "centered",
|
|
14
|
+
},
|
|
15
|
+
tags: ["autodocs"],
|
|
16
|
+
decorators: getStorybookDecorators({
|
|
17
|
+
config: {
|
|
18
|
+
layout: {
|
|
19
|
+
withScaffold: false,
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
}),
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export default meta;
|
|
26
|
+
|
|
27
|
+
type Story = StoryObj<typeof meta>;
|
|
28
|
+
|
|
29
|
+
export const Default: Story = {
|
|
30
|
+
args: {
|
|
31
|
+
title: "Delete item?",
|
|
32
|
+
description:
|
|
33
|
+
"This action cannot be undone. This will permanently delete the item.",
|
|
34
|
+
},
|
|
35
|
+
render: (args: DeletionConfirmorProps) => {
|
|
36
|
+
return <DeletionConfirmorWithTrigger {...args} />;
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export const WithConfirmLoading: Story = {
|
|
41
|
+
args: {
|
|
42
|
+
title: "Delete item?",
|
|
43
|
+
description:
|
|
44
|
+
"This action cannot be undone. The confirm button will show a loading state for 2 seconds.",
|
|
45
|
+
},
|
|
46
|
+
render: (args: DeletionConfirmorProps) => {
|
|
47
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
48
|
+
const [isConfirmLoading, setIsConfirmLoading] = useState(false);
|
|
49
|
+
|
|
50
|
+
const handleConfirm = async () => {
|
|
51
|
+
setIsConfirmLoading(true);
|
|
52
|
+
|
|
53
|
+
await mockAwaitingDelete(() => {
|
|
54
|
+
setIsOpen(false);
|
|
55
|
+
setIsConfirmLoading(false);
|
|
56
|
+
})();
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
<div className="flex flex-col gap-4">
|
|
61
|
+
<Button color="danger" onPress={() => setIsOpen(true)}>
|
|
62
|
+
Delete item
|
|
63
|
+
</Button>
|
|
64
|
+
|
|
65
|
+
<DeletionConfirmor
|
|
66
|
+
{...args}
|
|
67
|
+
isOpen={isOpen}
|
|
68
|
+
onClose={() => setIsOpen(false)}
|
|
69
|
+
confirmProps={{
|
|
70
|
+
...args.confirmProps,
|
|
71
|
+
isLoading: isConfirmLoading,
|
|
72
|
+
onPress: handleConfirm,
|
|
73
|
+
}}
|
|
74
|
+
/>
|
|
75
|
+
</div>
|
|
76
|
+
);
|
|
77
|
+
},
|
|
78
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { cva } from "class-variance-authority";
|
|
2
|
+
|
|
3
|
+
export const deletionConfirmorContentClasses = cva(["p-0"], {
|
|
4
|
+
variants: {},
|
|
5
|
+
defaultVariants: {},
|
|
6
|
+
compoundVariants: [],
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
export const deletionConfirmorHeaderClasses = cva([], {
|
|
10
|
+
variants: {},
|
|
11
|
+
defaultVariants: {},
|
|
12
|
+
compoundVariants: [],
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
export const deletionConfirmorBodyClasses = cva([], {
|
|
16
|
+
variants: {},
|
|
17
|
+
defaultVariants: {},
|
|
18
|
+
compoundVariants: [],
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
export const deletionConfirmorFooterClasses = cva(
|
|
22
|
+
["flex gap-2 justify-end flex-wrap"],
|
|
23
|
+
{
|
|
24
|
+
variants: {},
|
|
25
|
+
defaultVariants: {},
|
|
26
|
+
compoundVariants: [],
|
|
27
|
+
},
|
|
28
|
+
);
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
2
|
+
import { Field, FormBuilder } from "@/components";
|
|
3
|
+
import { getStorybookDecorators } from "@/utils";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { zodResolver } from "@hookform/resolvers/zod";
|
|
6
|
+
import { useState } from "react";
|
|
7
|
+
|
|
8
|
+
const meta: Meta<typeof FormBuilder> = {
|
|
9
|
+
title: "Organisms/Form Builder",
|
|
10
|
+
component: FormBuilder,
|
|
11
|
+
parameters: {
|
|
12
|
+
layout: "centered",
|
|
13
|
+
},
|
|
14
|
+
tags: ["autodocs"],
|
|
15
|
+
decorators: getStorybookDecorators({
|
|
16
|
+
config: {
|
|
17
|
+
layout: {
|
|
18
|
+
withScaffold: false,
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
}),
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export default meta;
|
|
25
|
+
|
|
26
|
+
type Story = StoryObj<typeof meta>;
|
|
27
|
+
|
|
28
|
+
const schema = z.object({
|
|
29
|
+
fullName: z.string({ error: "Full name is required" }),
|
|
30
|
+
description: z.string({ error: "Description is required" }),
|
|
31
|
+
status: z.enum(["active", "inactive"], { error: "Status is required" }),
|
|
32
|
+
isActive: z.boolean({ error: "Is active is required" }),
|
|
33
|
+
isAvailable: z.boolean({ error: "Is available is required" }),
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
type SchemaValues = z.infer<typeof schema>;
|
|
37
|
+
|
|
38
|
+
export const Default: Story = {
|
|
39
|
+
render: () => {
|
|
40
|
+
const [formValues, setFormValues] = useState<Partial<SchemaValues>>({});
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<div className="w-full max-w-2xl flex flex-col gap-5">
|
|
44
|
+
<h3 className="font-bold text-lg">Form Builder</h3>
|
|
45
|
+
|
|
46
|
+
<FormBuilder<typeof schema>
|
|
47
|
+
className="grid grid-cols-2 gap-5 p-4 rounded-md border"
|
|
48
|
+
formProps={{
|
|
49
|
+
mode: "all",
|
|
50
|
+
resolver: zodResolver(schema),
|
|
51
|
+
}}
|
|
52
|
+
onValuesChange={setFormValues}
|
|
53
|
+
onSubmit={() => {
|
|
54
|
+
alert("Form submitted");
|
|
55
|
+
}}
|
|
56
|
+
>
|
|
57
|
+
<Field
|
|
58
|
+
type="input"
|
|
59
|
+
name="fullName"
|
|
60
|
+
inputProps={{
|
|
61
|
+
label: "Full Name",
|
|
62
|
+
placeholder: "Type your name",
|
|
63
|
+
}}
|
|
64
|
+
/>
|
|
65
|
+
|
|
66
|
+
<Field
|
|
67
|
+
type="select"
|
|
68
|
+
name="status"
|
|
69
|
+
options={[
|
|
70
|
+
{ key: "active", label: "Active" },
|
|
71
|
+
{ key: "inactive", label: "Inactive" },
|
|
72
|
+
]}
|
|
73
|
+
selectProps={{
|
|
74
|
+
label: "Status",
|
|
75
|
+
}}
|
|
76
|
+
/>
|
|
77
|
+
|
|
78
|
+
<Field
|
|
79
|
+
type="autocomplete"
|
|
80
|
+
name="tags"
|
|
81
|
+
options={[
|
|
82
|
+
{ key: "tag1", label: "Tag 1" },
|
|
83
|
+
{ key: "tag2", label: "Tag 2" },
|
|
84
|
+
{ key: "tag3", label: "Tag 3" },
|
|
85
|
+
]}
|
|
86
|
+
autocompleteProps={{
|
|
87
|
+
label: "Tags",
|
|
88
|
+
placeholder: "Type your tags",
|
|
89
|
+
}}
|
|
90
|
+
/>
|
|
91
|
+
|
|
92
|
+
<Field type="checkbox" name="isActive">
|
|
93
|
+
Is Active?
|
|
94
|
+
</Field>
|
|
95
|
+
|
|
96
|
+
<Field type="switch" name="isAvailable">
|
|
97
|
+
Is Available?
|
|
98
|
+
</Field>
|
|
99
|
+
|
|
100
|
+
<Field
|
|
101
|
+
type="textarea"
|
|
102
|
+
name="description"
|
|
103
|
+
className="col-span-2"
|
|
104
|
+
textareaProps={{
|
|
105
|
+
label: "Description",
|
|
106
|
+
placeholder: "Type your description",
|
|
107
|
+
}}
|
|
108
|
+
/>
|
|
109
|
+
</FormBuilder>
|
|
110
|
+
|
|
111
|
+
<section className="flex flex-col gap-2 rounded-md border p-4">
|
|
112
|
+
<h3 className="font-bold text-lg">Summary: </h3>
|
|
113
|
+
|
|
114
|
+
<p className="flex items-center gap-2">
|
|
115
|
+
<span className="font-medium">Full Name:</span>
|
|
116
|
+
<span className="text-gray-500">{formValues.fullName ?? "—"}</span>
|
|
117
|
+
</p>
|
|
118
|
+
<p className="flex items-center gap-2">
|
|
119
|
+
<span className="font-medium">Description:</span>
|
|
120
|
+
<span className="text-gray-500">
|
|
121
|
+
{formValues.description ?? "—"}
|
|
122
|
+
</span>
|
|
123
|
+
</p>
|
|
124
|
+
<p className="flex items-center gap-2">
|
|
125
|
+
<span className="font-medium">Status:</span>
|
|
126
|
+
<span className="text-gray-500">{formValues.status ?? "—"}</span>
|
|
127
|
+
</p>
|
|
128
|
+
<p className="flex items-center gap-2">
|
|
129
|
+
<span className="font-medium">Is Active:</span>
|
|
130
|
+
<span className="text-gray-500">
|
|
131
|
+
{formValues.isActive ? "Selected" : "Not Selected"}
|
|
132
|
+
</span>
|
|
133
|
+
</p>
|
|
134
|
+
<p className="flex items-center gap-2">
|
|
135
|
+
<span className="font-medium">Is Available:</span>
|
|
136
|
+
<span className="text-gray-500">
|
|
137
|
+
{formValues.isAvailable ? "Selected" : "Not Selected"}
|
|
138
|
+
</span>
|
|
139
|
+
</p>
|
|
140
|
+
</section>
|
|
141
|
+
</div>
|
|
142
|
+
);
|
|
143
|
+
},
|
|
144
|
+
};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { Field } from "@/components";
|
|
4
|
+
import { FormBuilderProvider } from "@/providers";
|
|
5
|
+
import type { FormBuilderProps } from "@/types";
|
|
6
|
+
import { cn } from "@/utils";
|
|
7
|
+
import {
|
|
8
|
+
Children,
|
|
9
|
+
cloneElement,
|
|
10
|
+
isValidElement,
|
|
11
|
+
useMemo,
|
|
12
|
+
type ReactElement,
|
|
13
|
+
} from "react";
|
|
14
|
+
import { useEffect } from "react";
|
|
15
|
+
import { useForm, useWatch } from "react-hook-form";
|
|
16
|
+
import type { ZodType } from "zod";
|
|
17
|
+
import { z } from "zod";
|
|
18
|
+
|
|
19
|
+
export function FormBuilder<TSchema extends ZodType<any, any, any>>({
|
|
20
|
+
children,
|
|
21
|
+
className,
|
|
22
|
+
classNames,
|
|
23
|
+
formProps,
|
|
24
|
+
onSubmit,
|
|
25
|
+
onSuccess,
|
|
26
|
+
onError,
|
|
27
|
+
onValuesChange,
|
|
28
|
+
}: FormBuilderProps<TSchema>) {
|
|
29
|
+
const { control, handleSubmit } = useForm<z.infer<TSchema>>(formProps);
|
|
30
|
+
|
|
31
|
+
const emptyValues = useMemo(() => ({}), []);
|
|
32
|
+
const formValues = useWatch({ control }) ?? emptyValues;
|
|
33
|
+
|
|
34
|
+
const allChildren = Children.toArray(children);
|
|
35
|
+
|
|
36
|
+
allChildren.forEach((child) => {
|
|
37
|
+
if (!isValidElement(child) || child.type !== Field) {
|
|
38
|
+
throw new Error(
|
|
39
|
+
"FormBuilder accepts only 'Field' components as children.",
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const formFields = allChildren.map((child) =>
|
|
45
|
+
cloneElement(child as ReactElement),
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
useEffect(() => {
|
|
49
|
+
onValuesChange?.(formValues);
|
|
50
|
+
}, [formValues, onValuesChange]);
|
|
51
|
+
|
|
52
|
+
const handleValidSubmit = async (values: z.infer<TSchema>) => {
|
|
53
|
+
try {
|
|
54
|
+
await onSubmit(values);
|
|
55
|
+
onSuccess?.(values);
|
|
56
|
+
} catch (error) {
|
|
57
|
+
onError?.(error);
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
return (
|
|
62
|
+
<FormBuilderProvider<z.infer<TSchema>> value={{ control, formValues }}>
|
|
63
|
+
<form
|
|
64
|
+
className={cn(className, classNames?.base)}
|
|
65
|
+
onSubmit={handleSubmit(handleValidSubmit, (errors) =>
|
|
66
|
+
onError?.({
|
|
67
|
+
errors,
|
|
68
|
+
}),
|
|
69
|
+
)}
|
|
70
|
+
>
|
|
71
|
+
{formFields}
|
|
72
|
+
</form>
|
|
73
|
+
</FormBuilderProvider>
|
|
74
|
+
);
|
|
75
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./form-builder";
|
package/src/constants/card.tsx
CHANGED
|
@@ -7,20 +7,23 @@ export const ITEM_CARD_ACTIONS: ItemCardAction[] = [
|
|
|
7
7
|
label: "View details",
|
|
8
8
|
color: "primary",
|
|
9
9
|
variant: "flat",
|
|
10
|
-
|
|
10
|
+
size: "sm",
|
|
11
|
+
startContent: <Eye className="size-5" />,
|
|
11
12
|
},
|
|
12
13
|
{
|
|
13
14
|
key: "edit",
|
|
14
15
|
label: "Edit",
|
|
15
16
|
color: "secondary",
|
|
16
17
|
variant: "flat",
|
|
17
|
-
|
|
18
|
+
size: "sm",
|
|
19
|
+
startContent: <Pencil className="size-5" />,
|
|
18
20
|
},
|
|
19
21
|
{
|
|
20
22
|
key: "delete",
|
|
21
23
|
label: "Delete",
|
|
22
24
|
color: "danger",
|
|
23
25
|
variant: "flat",
|
|
26
|
+
size: "sm",
|
|
24
27
|
startContent: <Trash2 className="size-4" />,
|
|
25
28
|
},
|
|
26
29
|
];
|
|
@@ -16,4 +16,44 @@ export const defaultEmperorUIConfig: EmperorUIConfig = {
|
|
|
16
16
|
ar,
|
|
17
17
|
},
|
|
18
18
|
},
|
|
19
|
+
theme: {
|
|
20
|
+
components: {
|
|
21
|
+
input: {
|
|
22
|
+
variant: "faded",
|
|
23
|
+
labelPlacement: "outside",
|
|
24
|
+
size: "sm",
|
|
25
|
+
},
|
|
26
|
+
textarea: {
|
|
27
|
+
variant: "faded",
|
|
28
|
+
labelPlacement: "outside",
|
|
29
|
+
size: "sm",
|
|
30
|
+
},
|
|
31
|
+
button: {
|
|
32
|
+
variant: "faded",
|
|
33
|
+
size: "sm",
|
|
34
|
+
},
|
|
35
|
+
select: {
|
|
36
|
+
variant: "faded",
|
|
37
|
+
labelPlacement: "outside",
|
|
38
|
+
placeholder: "Select an option",
|
|
39
|
+
size: "sm",
|
|
40
|
+
},
|
|
41
|
+
selectItem: {
|
|
42
|
+
variant: "faded",
|
|
43
|
+
},
|
|
44
|
+
autocomplete: {
|
|
45
|
+
variant: "faded",
|
|
46
|
+
labelPlacement: "outside",
|
|
47
|
+
size: "sm",
|
|
48
|
+
},
|
|
49
|
+
autocompleteItem: {
|
|
50
|
+
variant: "faded",
|
|
51
|
+
},
|
|
52
|
+
datePicker: {
|
|
53
|
+
variant: "faded",
|
|
54
|
+
labelPlacement: "outside",
|
|
55
|
+
size: "sm",
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
},
|
|
19
59
|
};
|
package/src/context/index.ts
CHANGED
package/src/hooks/index.ts
CHANGED
package/src/hooks/use-filters.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { useSearchParamsHandler } from "@/hooks";
|
|
|
6
6
|
export function useFilters<
|
|
7
7
|
FiltersType extends Record<string, string | number | boolean>,
|
|
8
8
|
>() {
|
|
9
|
-
const { allParams } = useSearchParamsHandler();
|
|
9
|
+
const { allParams, clearParams } = useSearchParamsHandler();
|
|
10
10
|
|
|
11
11
|
const filters = useMemo(() => {
|
|
12
12
|
if (!Object.keys(allParams).length) {
|
|
@@ -16,5 +16,5 @@ export function useFilters<
|
|
|
16
16
|
return allParams as unknown as FiltersType;
|
|
17
17
|
}, [allParams]);
|
|
18
18
|
|
|
19
|
-
return { filters };
|
|
19
|
+
return { filters, clearFilters: clearParams };
|
|
20
20
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { FormBuilderContext } from "@/context";
|
|
2
|
+
import { FormBuilderValue } from "@/types";
|
|
3
|
+
import { useContext } from "react";
|
|
4
|
+
import { FieldValues } from "react-hook-form";
|
|
5
|
+
|
|
6
|
+
export function useFormBuilder<TSchema extends FieldValues>() {
|
|
7
|
+
const context = useContext(FormBuilderContext) as FormBuilderValue<TSchema>;
|
|
8
|
+
|
|
9
|
+
if (!context) {
|
|
10
|
+
throw new Error(
|
|
11
|
+
"'useFormBuilder' must be used within a 'FormBuilderProvider', make sure to wrap 'Field' components with 'FormBuilder' as well.",
|
|
12
|
+
);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return context;
|
|
16
|
+
}
|