modal-system 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +5 -3
- package/dist/cli/templates/ark/ConfirmModal.tsx.tpl +74 -0
- package/dist/cli/templates/ark/index.ts.tpl +19 -0
- package/dist/cli/templates/ark/modals.ts.tpl +37 -0
- package/dist/cli/templates/chakra/ConfirmModal.tsx.tpl +75 -0
- package/dist/cli/templates/chakra/index.ts.tpl +19 -0
- package/dist/cli/templates/chakra/modals.ts.tpl +37 -0
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -688,7 +688,9 @@ async function main() {
|
|
|
688
688
|
message: "Which UI library are you using?",
|
|
689
689
|
options: [
|
|
690
690
|
{ value: "shadcn", label: "shadcn/ui" },
|
|
691
|
-
{ value: "daisyui", label: "DaisyUI" }
|
|
691
|
+
{ value: "daisyui", label: "DaisyUI" },
|
|
692
|
+
{ value: "chakra", label: "Chakra UI" },
|
|
693
|
+
{ value: "ark", label: "Ark UI" }
|
|
692
694
|
]
|
|
693
695
|
});
|
|
694
696
|
if (lD(uiLibrary)) {
|
|
@@ -710,7 +712,7 @@ async function main() {
|
|
|
710
712
|
}
|
|
711
713
|
variant = `shadcn-${primitive}`;
|
|
712
714
|
} else {
|
|
713
|
-
variant =
|
|
715
|
+
variant = uiLibrary;
|
|
714
716
|
}
|
|
715
717
|
const modalsDir = await te({
|
|
716
718
|
message: "Where should the modals folder be created?",
|
|
@@ -756,7 +758,7 @@ async function main() {
|
|
|
756
758
|
le(
|
|
757
759
|
[
|
|
758
760
|
`${import_picocolors3.default.bold("1.")} Install dependencies:`,
|
|
759
|
-
uiLibrary === "daisyui" ? import_picocolors3.default.cyan(" bun add zustand daisyui") : variant === "shadcn-radix" ? import_picocolors3.default.cyan(" bun add zustand @radix-ui/react-dialog") : import_picocolors3.default.cyan(" bun add zustand @base-ui-components/react"),
|
|
761
|
+
uiLibrary === "daisyui" ? import_picocolors3.default.cyan(" bun add zustand daisyui") : uiLibrary === "chakra" ? import_picocolors3.default.cyan(" bun add zustand @chakra-ui/react @emotion/react") : uiLibrary === "ark" ? import_picocolors3.default.cyan(" bun add zustand @ark-ui/react") : variant === "shadcn-radix" ? import_picocolors3.default.cyan(" bun add zustand @radix-ui/react-dialog") : import_picocolors3.default.cyan(" bun add zustand @base-ui-components/react"),
|
|
760
762
|
"",
|
|
761
763
|
`${import_picocolors3.default.bold("2.")} Wrap your app in the provider:`,
|
|
762
764
|
import_picocolors3.default.cyan(` import { ModalProvider } from "modal-system"`),
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ConfirmModal.tsx (Ark UI)
|
|
3
|
+
*
|
|
4
|
+
* Ark UI is headless — the markup below is styled with Tailwind classes.
|
|
5
|
+
* bun add @ark-ui/react
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { Dialog } from "@ark-ui/react/dialog";
|
|
9
|
+
import { Portal } from "@ark-ui/react/portal";
|
|
10
|
+
import type { BaseModalProps } from "modal-system";
|
|
11
|
+
import type { ConfirmModalData } from "./modals";
|
|
12
|
+
|
|
13
|
+
export function ConfirmModal({
|
|
14
|
+
isOpen,
|
|
15
|
+
onClose,
|
|
16
|
+
data,
|
|
17
|
+
}: BaseModalProps<ConfirmModalData>) {
|
|
18
|
+
const handleClose = () => {
|
|
19
|
+
data?.onCancel?.();
|
|
20
|
+
onClose();
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const handleConfirm = () => {
|
|
24
|
+
data?.onConfirm?.();
|
|
25
|
+
onClose();
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<Dialog.Root
|
|
30
|
+
open={isOpen}
|
|
31
|
+
onOpenChange={(e) => !e.open && handleClose()}
|
|
32
|
+
>
|
|
33
|
+
<Portal>
|
|
34
|
+
<Dialog.Backdrop className="fixed inset-0 z-50 bg-black/50 transition-opacity data-[state=closed]:opacity-0 data-[state=open]:opacity-100" />
|
|
35
|
+
|
|
36
|
+
<Dialog.Positioner className="fixed inset-0 z-50 flex items-center justify-center p-4">
|
|
37
|
+
<Dialog.Content className="relative w-full max-w-md min-h-40 rounded-lg bg-white p-6 shadow-lg transition-all data-[state=closed]:opacity-0 data-[state=closed]:scale-95 data-[state=open]:opacity-100 dark:bg-zinc-900">
|
|
38
|
+
<div className="flex flex-col gap-1">
|
|
39
|
+
<Dialog.Title className="text-lg font-semibold">
|
|
40
|
+
{data?.title ?? "Are you sure?"}
|
|
41
|
+
</Dialog.Title>
|
|
42
|
+
{data?.description && (
|
|
43
|
+
<Dialog.Description className="text-xs text-zinc-500 dark:text-zinc-400">
|
|
44
|
+
{data.description}
|
|
45
|
+
</Dialog.Description>
|
|
46
|
+
)}
|
|
47
|
+
</div>
|
|
48
|
+
|
|
49
|
+
<div className="absolute bottom-3 right-3 flex gap-1">
|
|
50
|
+
<Dialog.CloseTrigger
|
|
51
|
+
className="inline-flex h-9 items-center justify-center rounded-md bg-zinc-100 px-4 text-sm font-medium text-zinc-900 shadow-sm transition-colors hover:bg-zinc-200 dark:bg-zinc-800 dark:text-zinc-100 dark:hover:bg-zinc-700"
|
|
52
|
+
onClick={handleClose}
|
|
53
|
+
>
|
|
54
|
+
{data?.closeText ?? "Cancel"}
|
|
55
|
+
</Dialog.CloseTrigger>
|
|
56
|
+
|
|
57
|
+
<button
|
|
58
|
+
type="button"
|
|
59
|
+
onClick={handleConfirm}
|
|
60
|
+
className={`inline-flex h-9 items-center justify-center rounded-md px-4 text-sm font-medium text-white shadow transition-colors ${
|
|
61
|
+
data?.isActionButtonDestructive
|
|
62
|
+
? "bg-red-600 hover:bg-red-700"
|
|
63
|
+
: "bg-blue-600 hover:bg-blue-700"
|
|
64
|
+
}`}
|
|
65
|
+
>
|
|
66
|
+
{data?.actionText ?? "Confirm"}
|
|
67
|
+
</button>
|
|
68
|
+
</div>
|
|
69
|
+
</Dialog.Content>
|
|
70
|
+
</Dialog.Positioner>
|
|
71
|
+
</Portal>
|
|
72
|
+
</Dialog.Root>
|
|
73
|
+
);
|
|
74
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* modals/index.ts
|
|
3
|
+
*
|
|
4
|
+
* Register your modal components here.
|
|
5
|
+
* The key you use (e.g. "confirm") becomes the name you pass to openModal().
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { defineModals, createModalHook } from "modal-system";
|
|
9
|
+
|
|
10
|
+
import { ConfirmModal } from "./ConfirmModal";
|
|
11
|
+
// import { DeleteUserModal } from "./DeleteUserModal";
|
|
12
|
+
|
|
13
|
+
export const MODALS = defineModals({
|
|
14
|
+
confirm: ConfirmModal,
|
|
15
|
+
// deleteUser: DeleteUserModal,
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
// Import `useModal` from THIS file in your components — it is typed to your registry.
|
|
19
|
+
export const useModal = createModalHook(MODALS);
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* modals.ts
|
|
3
|
+
*
|
|
4
|
+
* Define the data interface for each modal here.
|
|
5
|
+
* The key must match the name you register in index.ts.
|
|
6
|
+
*
|
|
7
|
+
* Your component receives these fields via the `data` prop:
|
|
8
|
+
* function ConfirmModal({ data }: BaseModalProps<ConfirmModalData>) {
|
|
9
|
+
* data?.title
|
|
10
|
+
* }
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import type { ReactNode } from "react";
|
|
14
|
+
|
|
15
|
+
// ── Add your own modals below ────────────────────────────────────────────────
|
|
16
|
+
|
|
17
|
+
export interface ConfirmModalData {
|
|
18
|
+
title?: string;
|
|
19
|
+
description?: string | ReactNode;
|
|
20
|
+
/** Confirm button label. Defaults to "Confirm" */
|
|
21
|
+
actionText?: string;
|
|
22
|
+
/** Cancel button label. Defaults to "Cancel" */
|
|
23
|
+
closeText?: string;
|
|
24
|
+
/** Called when the user clicks the confirm button */
|
|
25
|
+
onConfirm?: () => void;
|
|
26
|
+
/** Called when the user clicks the cancel / X button */
|
|
27
|
+
onCancel?: () => void;
|
|
28
|
+
/** Renders the confirm button with destructive (red) variant */
|
|
29
|
+
isActionButtonDestructive?: boolean;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Example of another modal:
|
|
33
|
+
// export interface DeleteUserModalData {
|
|
34
|
+
// userId: string;
|
|
35
|
+
// userName: string;
|
|
36
|
+
// onDeleted?: () => void;
|
|
37
|
+
// }
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ConfirmModal.tsx (Chakra UI v3)
|
|
3
|
+
*
|
|
4
|
+
* Uses the Chakra UI v3 Dialog component:
|
|
5
|
+
* bun add @chakra-ui/react @emotion/react
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
Dialog,
|
|
10
|
+
Portal,
|
|
11
|
+
Button,
|
|
12
|
+
CloseButton,
|
|
13
|
+
Text,
|
|
14
|
+
} from "@chakra-ui/react";
|
|
15
|
+
import type { BaseModalProps } from "modal-system";
|
|
16
|
+
import type { ConfirmModalData } from "./modals";
|
|
17
|
+
|
|
18
|
+
export function ConfirmModal({
|
|
19
|
+
isOpen,
|
|
20
|
+
onClose,
|
|
21
|
+
data,
|
|
22
|
+
}: BaseModalProps<ConfirmModalData>) {
|
|
23
|
+
const handleClose = () => {
|
|
24
|
+
data?.onCancel?.();
|
|
25
|
+
onClose();
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const handleConfirm = () => {
|
|
29
|
+
data?.onConfirm?.();
|
|
30
|
+
onClose();
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<Dialog.Root
|
|
35
|
+
open={isOpen}
|
|
36
|
+
onOpenChange={(e) => !e.open && handleClose()}
|
|
37
|
+
placement="center"
|
|
38
|
+
>
|
|
39
|
+
<Portal>
|
|
40
|
+
<Dialog.Backdrop />
|
|
41
|
+
<Dialog.Positioner>
|
|
42
|
+
<Dialog.Content>
|
|
43
|
+
<Dialog.Header>
|
|
44
|
+
<Dialog.Title>{data?.title ?? "Are you sure?"}</Dialog.Title>
|
|
45
|
+
</Dialog.Header>
|
|
46
|
+
|
|
47
|
+
{data?.description && (
|
|
48
|
+
<Dialog.Body>
|
|
49
|
+
<Text fontSize="sm" color="fg.muted">
|
|
50
|
+
{data.description}
|
|
51
|
+
</Text>
|
|
52
|
+
</Dialog.Body>
|
|
53
|
+
)}
|
|
54
|
+
|
|
55
|
+
<Dialog.Footer>
|
|
56
|
+
<Button variant="outline" onClick={handleClose}>
|
|
57
|
+
{data?.closeText ?? "Cancel"}
|
|
58
|
+
</Button>
|
|
59
|
+
<Button
|
|
60
|
+
colorPalette={data?.isActionButtonDestructive ? "red" : "blue"}
|
|
61
|
+
onClick={handleConfirm}
|
|
62
|
+
>
|
|
63
|
+
{data?.actionText ?? "Confirm"}
|
|
64
|
+
</Button>
|
|
65
|
+
</Dialog.Footer>
|
|
66
|
+
|
|
67
|
+
<Dialog.CloseTrigger asChild>
|
|
68
|
+
<CloseButton size="sm" onClick={handleClose} />
|
|
69
|
+
</Dialog.CloseTrigger>
|
|
70
|
+
</Dialog.Content>
|
|
71
|
+
</Dialog.Positioner>
|
|
72
|
+
</Portal>
|
|
73
|
+
</Dialog.Root>
|
|
74
|
+
);
|
|
75
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* modals/index.ts
|
|
3
|
+
*
|
|
4
|
+
* Register your modal components here.
|
|
5
|
+
* The key you use (e.g. "confirm") becomes the name you pass to openModal().
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { defineModals, createModalHook } from "modal-system";
|
|
9
|
+
|
|
10
|
+
import { ConfirmModal } from "./ConfirmModal";
|
|
11
|
+
// import { DeleteUserModal } from "./DeleteUserModal";
|
|
12
|
+
|
|
13
|
+
export const MODALS = defineModals({
|
|
14
|
+
confirm: ConfirmModal,
|
|
15
|
+
// deleteUser: DeleteUserModal,
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
// Import `useModal` from THIS file in your components — it is typed to your registry.
|
|
19
|
+
export const useModal = createModalHook(MODALS);
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* modals.ts
|
|
3
|
+
*
|
|
4
|
+
* Define the data interface for each modal here.
|
|
5
|
+
* The key must match the name you register in index.ts.
|
|
6
|
+
*
|
|
7
|
+
* Your component receives these fields via the `data` prop:
|
|
8
|
+
* function ConfirmModal({ data }: BaseModalProps<ConfirmModalData>) {
|
|
9
|
+
* data?.title
|
|
10
|
+
* }
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import type { ReactNode } from "react";
|
|
14
|
+
|
|
15
|
+
// ── Add your own modals below ────────────────────────────────────────────────
|
|
16
|
+
|
|
17
|
+
export interface ConfirmModalData {
|
|
18
|
+
title?: string;
|
|
19
|
+
description?: string | ReactNode;
|
|
20
|
+
/** Confirm button label. Defaults to "Confirm" */
|
|
21
|
+
actionText?: string;
|
|
22
|
+
/** Cancel button label. Defaults to "Cancel" */
|
|
23
|
+
closeText?: string;
|
|
24
|
+
/** Called when the user clicks the confirm button */
|
|
25
|
+
onConfirm?: () => void;
|
|
26
|
+
/** Called when the user clicks the cancel / X button */
|
|
27
|
+
onCancel?: () => void;
|
|
28
|
+
/** Renders the confirm button with destructive (red) color palette */
|
|
29
|
+
isActionButtonDestructive?: boolean;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Example of another modal:
|
|
33
|
+
// export interface DeleteUserModalData {
|
|
34
|
+
// userId: string;
|
|
35
|
+
// userName: string;
|
|
36
|
+
// onDeleted?: () => void;
|
|
37
|
+
// }
|