moverlay 0.0.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/dist/assets/forms/login.json +17 -0
- package/dist/assets/icons/x.svg +6 -0
- package/dist/components/trigger.d.ts +15 -0
- package/dist/components/trigger.js +12 -0
- package/dist/core/types.d.ts +2 -0
- package/dist/core/types.js +1 -0
- package/dist/hooks/field.d.ts +7 -0
- package/dist/hooks/field.js +28 -0
- package/dist/hooks/form.d.ts +8 -0
- package/dist/hooks/form.js +25 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +7 -0
- package/dist/layout/form.d.ts +31 -0
- package/dist/layout/form.js +7 -0
- package/dist/layout/normal.d.ts +12 -0
- package/dist/layout/normal.js +16 -0
- package/dist/layout/root.d.ts +1 -0
- package/dist/layout/root.js +14 -0
- package/dist/layouts/form.d.ts +19 -0
- package/dist/layouts/form.js +13 -0
- package/dist/layouts/load-json.d.ts +1 -0
- package/dist/layouts/load-json.js +21 -0
- package/dist/layouts/normal.d.ts +12 -0
- package/dist/layouts/normal.js +17 -0
- package/dist/state/context.d.ts +12 -0
- package/dist/state/context.js +10 -0
- package/dist/state/manager.d.ts +5 -0
- package/dist/state/manager.js +30 -0
- package/dist/state/provider.d.ts +30 -0
- package/dist/state/provider.js +41 -0
- package/dist/state/root.d.ts +14 -0
- package/dist/state/root.js +19 -0
- package/dist/templates/login.d.ts +20 -0
- package/dist/templates/login.js +18 -0
- package/dist/templates/register.d.ts +20 -0
- package/dist/templates/register.js +18 -0
- package/package.json +86 -0
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512" width="1em" height="1em">
|
|
2
|
+
<path
|
|
3
|
+
d="M342.6 150.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L192 210.7 86.6 105.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L146.7 256 41.4 361.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L192 301.3 297.4 406.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L237.3 256 342.6 150.6z"
|
|
4
|
+
fill="currentColor"
|
|
5
|
+
/>
|
|
6
|
+
</svg>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
type ButtonProps = {
|
|
3
|
+
variant?: "primary" | "outline" | "ghost";
|
|
4
|
+
size?: "xs" | "sm" | "md" | "lg";
|
|
5
|
+
onClick?: () => void;
|
|
6
|
+
disabled?: boolean;
|
|
7
|
+
style?: string;
|
|
8
|
+
children: ReactNode;
|
|
9
|
+
};
|
|
10
|
+
type Props = Omit<ButtonProps, "onClick"> & {
|
|
11
|
+
modal: string;
|
|
12
|
+
};
|
|
13
|
+
export declare function ModalButton({ modal, ...props }: Props): import("react/jsx-runtime").JSX.Element;
|
|
14
|
+
export declare function ModalLink({ modal, ...props }: Props): import("react/jsx-runtime").JSX.Element;
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import useModal from "../state/context";
|
|
4
|
+
import { Button } from "@bouko/react";
|
|
5
|
+
export function ModalButton({ modal, ...props }) {
|
|
6
|
+
const { openModal } = useModal();
|
|
7
|
+
return (_jsx(Button, { action: () => openModal(modal), ...props }));
|
|
8
|
+
}
|
|
9
|
+
export function ModalLink({ modal, ...props }) {
|
|
10
|
+
const { openModal } = useModal();
|
|
11
|
+
return (_jsx(Button, { variant: "ghost", action: () => openModal(modal), ...props }));
|
|
12
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type Struct } from "superstruct";
|
|
2
|
+
export default function useField<T>(schema: Struct, initial?: T): {
|
|
3
|
+
value: T | undefined;
|
|
4
|
+
isValid: boolean;
|
|
5
|
+
error: string | undefined;
|
|
6
|
+
update: import("react").Dispatch<import("react").SetStateAction<T | undefined>>;
|
|
7
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useState, useEffect } from "react";
|
|
3
|
+
import { assert } from "superstruct";
|
|
4
|
+
export default function useField(schema, initial) {
|
|
5
|
+
const [value, update] = useState(initial);
|
|
6
|
+
const [error, setError] = useState();
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
update(initial);
|
|
9
|
+
}, [initial]);
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
try {
|
|
12
|
+
assert(value, schema);
|
|
13
|
+
setError(undefined);
|
|
14
|
+
}
|
|
15
|
+
catch (err) {
|
|
16
|
+
setError(processIssues(err));
|
|
17
|
+
}
|
|
18
|
+
}, [value]);
|
|
19
|
+
return {
|
|
20
|
+
value,
|
|
21
|
+
isValid: !error,
|
|
22
|
+
error,
|
|
23
|
+
update
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
function processIssues(error) {
|
|
27
|
+
return error.refinement ?? error.message;
|
|
28
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ZodType } from "zod";
|
|
2
|
+
export default function useValidation<T>(schema: ZodType<T>, initialData?: Partial<T>): {
|
|
3
|
+
isValid: boolean;
|
|
4
|
+
formData: Partial<T>;
|
|
5
|
+
error: Record<string, string> | null;
|
|
6
|
+
update: import("react").Dispatch<import("react").SetStateAction<Partial<T>>>;
|
|
7
|
+
updateField: <K extends keyof T>(key: K, value: T[K]) => void;
|
|
8
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useState, useEffect } from "react";
|
|
3
|
+
export default function useValidation(schema, initialData) {
|
|
4
|
+
const [formData, update] = useState(initialData ?? {});
|
|
5
|
+
const [error, setError] = useState(null);
|
|
6
|
+
useEffect(() => {
|
|
7
|
+
update(initialData ?? {});
|
|
8
|
+
}, [initialData]);
|
|
9
|
+
const updateField = (key, value) => {
|
|
10
|
+
update((prev) => ({ ...prev, [key]: value }));
|
|
11
|
+
};
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
try {
|
|
14
|
+
schema.parse(formData);
|
|
15
|
+
setError(null);
|
|
16
|
+
}
|
|
17
|
+
catch (err) {
|
|
18
|
+
setError(err.issues.reduce((obj, x) => {
|
|
19
|
+
obj[x.path.toString()] = x.message;
|
|
20
|
+
return obj;
|
|
21
|
+
}, {}));
|
|
22
|
+
}
|
|
23
|
+
}, [formData, schema]);
|
|
24
|
+
return { isValid: !error, formData, error, update, updateField };
|
|
25
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { default as ModalProvider } from "./state/provider";
|
|
2
|
+
export { default as useModal } from "./state/context";
|
|
3
|
+
export { default as RootModal } from "./state/root";
|
|
4
|
+
export { default as Modal } from "./layouts/normal";
|
|
5
|
+
export { default as FormModal } from "./layouts/form";
|
|
6
|
+
export { default as RegisterModal } from "./templates/register";
|
|
7
|
+
export { default as LoginModal } from "./templates/login";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { default as ModalProvider } from "./state/provider";
|
|
2
|
+
export { default as useModal } from "./state/context";
|
|
3
|
+
export { default as RootModal } from "./state/root";
|
|
4
|
+
export { default as Modal } from "./layouts/normal";
|
|
5
|
+
export { default as FormModal } from "./layouts/form";
|
|
6
|
+
export { default as RegisterModal } from "./templates/register";
|
|
7
|
+
export { default as LoginModal } from "./templates/login";
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
import { type Props as BaseProps } from "./normal";
|
|
3
|
+
import { type Option, type FormSection, SetState } from "@bouko/form";
|
|
4
|
+
import { JsonMap } from "@bouko/ts";
|
|
5
|
+
import { ZodTypeAny } from "zod";
|
|
6
|
+
type Field<T, K = string> = {
|
|
7
|
+
id: string;
|
|
8
|
+
label?: string;
|
|
9
|
+
style?: string;
|
|
10
|
+
value?: K;
|
|
11
|
+
update: SetState<T>;
|
|
12
|
+
required?: boolean;
|
|
13
|
+
disabled?: boolean;
|
|
14
|
+
note?: ReactNode;
|
|
15
|
+
};
|
|
16
|
+
type FormBuilderField<T> = (Omit<Field<T>, "value" | "update"> & {
|
|
17
|
+
element: string;
|
|
18
|
+
rows?: number;
|
|
19
|
+
placeholder?: string;
|
|
20
|
+
options?: Option[];
|
|
21
|
+
})[][];
|
|
22
|
+
type Props<T> = BaseProps & FormSection<T> & {
|
|
23
|
+
fields: FormBuilderField<T>;
|
|
24
|
+
validator: ZodTypeAny;
|
|
25
|
+
styles?: {
|
|
26
|
+
submit?: string;
|
|
27
|
+
};
|
|
28
|
+
submit: (data: T, clear?: (() => void) | undefined) => Promise<void>;
|
|
29
|
+
};
|
|
30
|
+
export declare function FormModal<T extends JsonMap>({ fields, data, validator, update, submit, styles, clear, ...props }: Props<T>): import("react/jsx-runtime").JSX.Element;
|
|
31
|
+
export default FormModal;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import Modal from "./normal";
|
|
3
|
+
import { FormBuilder } from "@bouko/form";
|
|
4
|
+
export function FormModal({ fields, data, validator, update, submit, styles, clear, ...props }) {
|
|
5
|
+
return (_jsx(Modal, { ...props, children: _jsx(FormBuilder, { styles: styles, fields: fields, validator: validator.safeParse, submit: submit }) }));
|
|
6
|
+
}
|
|
7
|
+
export default FormModal;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
export type Props = {
|
|
3
|
+
style?: string;
|
|
4
|
+
title: string;
|
|
5
|
+
subtitle?: ReactNode;
|
|
6
|
+
redirect?: {
|
|
7
|
+
label: string;
|
|
8
|
+
to: string;
|
|
9
|
+
};
|
|
10
|
+
children?: ReactNode;
|
|
11
|
+
};
|
|
12
|
+
export default function ModalLayout({ style, title, subtitle, redirect, children }: Props): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "@bouko/style";
|
|
3
|
+
import useModal from "../state/context";
|
|
4
|
+
import X from "../assets/icons/x.svg";
|
|
5
|
+
export default function ModalLayout({ style, title, subtitle, redirect, children }) {
|
|
6
|
+
const { openModal, closeModal } = useModal();
|
|
7
|
+
return (_jsxs("div", { className: cn(styles.container, style), role: "dialog", "aria-modal": "true", children: [_jsxs("div", { className: styles.header, children: [_jsxs("div", { className: styles.heading, children: [_jsx("span", { className: styles.title, children: title }), _jsxs("span", { className: styles.subtitle, children: [subtitle, redirect && (_jsx("button", { className: "p-0 bg-transparent border-none outline-none text-sm text-accent duration-200 hover:text-accent-light cursor-pointer font-semibold", onClick: () => openModal(redirect.to), children: redirect.label }))] })] }), _jsx(X, { className: styles.close, onClick: closeModal })] }), children] }));
|
|
8
|
+
}
|
|
9
|
+
const styles = {
|
|
10
|
+
container: "relative flex flex-col gap-4 w-lg p-7 pt-6 select-none overflow-hidden",
|
|
11
|
+
header: "relative flex flex-col gap-px mb-1 w-full",
|
|
12
|
+
heading: "flex flex-col gap-px",
|
|
13
|
+
title: "leading-none text-xl font-semibold",
|
|
14
|
+
subtitle: "flex items-center gap-2 text-sm text-slate-400",
|
|
15
|
+
close: "absolute -right-2 -top-1 text-error hover:text-error text-lg duration-200 cursor-pointer"
|
|
16
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default function RootModal(): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import useModal from "../state/context";
|
|
4
|
+
import { AnimatePresence, motion } from "framer-motion";
|
|
5
|
+
export default function RootModal() {
|
|
6
|
+
const { isOpen, templateId, templates } = useModal();
|
|
7
|
+
return (_jsx(AnimatePresence, { mode: "wait", children: isOpen && templateId && (_jsxs(motion.div, { className: styles.container, initial: { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 }, layout: true, children: [_jsx("div", { className: styles.backdrop }), _jsx("div", { className: styles.content, children: _jsx("div", { className: styles.subcontainer, children: templates[templateId] }) })] }, templateId)) }));
|
|
8
|
+
}
|
|
9
|
+
const styles = {
|
|
10
|
+
container: "fixed inset-0 flex justify-center items-center z-[99]",
|
|
11
|
+
backdrop: "absolute inset-0 bg-background-dark/60",
|
|
12
|
+
content: "absolute top-0 left-0 w-[100dvw] h-[100dvh] flex justify-center items-center 2xl:scale-125 select-none",
|
|
13
|
+
subcontainer: "relative flex max-w-[80%] max-h-[85%] bg-background border border-border rounded-lg shadow-soft overflow-hidden"
|
|
14
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { ModalProps } from "./normal";
|
|
2
|
+
import type { FormBuilderProps } from "@bouko/form";
|
|
3
|
+
import type { JsonMap } from "@bouko/ts";
|
|
4
|
+
type Props<T> = Omit<ModalProps, "children"> & Omit<FormBuilderProps<T>, "cancel" | "sound" | "oops">;
|
|
5
|
+
/**
|
|
6
|
+
* Form Modal component.
|
|
7
|
+
*
|
|
8
|
+
* Combines a standard modal layout with a form builder.
|
|
9
|
+
* Wraps @bouko/form `FormBuilder` for form handling.
|
|
10
|
+
**/
|
|
11
|
+
export declare function FormModal<T extends JsonMap>({ formId, style, fields, validator, submit, ...props }: Props<T>): import("react/jsx-runtime").JSX.Element;
|
|
12
|
+
export default FormModal;
|
|
13
|
+
/**
|
|
14
|
+
* Problems:
|
|
15
|
+
*
|
|
16
|
+
* - Perfect `Modal`
|
|
17
|
+
* - Perfect `ModalProps`
|
|
18
|
+
* - Perfect `FormBuilderProps`
|
|
19
|
+
**/
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Modal } from "./normal";
|
|
3
|
+
import { FormBuilder } from "@bouko/form";
|
|
4
|
+
/**
|
|
5
|
+
* Form Modal component.
|
|
6
|
+
*
|
|
7
|
+
* Combines a standard modal layout with a form builder.
|
|
8
|
+
* Wraps @bouko/form `FormBuilder` for form handling.
|
|
9
|
+
**/
|
|
10
|
+
export function FormModal({ formId, style, fields, validator, submit, ...props }) {
|
|
11
|
+
return (_jsx(Modal, { ...props, children: _jsx(FormBuilder, { formId: formId, style: style, fields: fields, validator: validator, submit: submit }) }));
|
|
12
|
+
}
|
|
13
|
+
export default FormModal;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function loadJson<T>(name: string): Promise<T>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { promises as fs } from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { fileURLToPath } from "url";
|
|
4
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
5
|
+
const __dirname = path.dirname(__filename);
|
|
6
|
+
function findSrcDirectory(startDir) {
|
|
7
|
+
let currentDir = startDir;
|
|
8
|
+
while (currentDir !== path.dirname(currentDir)) { // Stop at root
|
|
9
|
+
if (path.basename(currentDir) === 'src') {
|
|
10
|
+
return currentDir;
|
|
11
|
+
}
|
|
12
|
+
currentDir = path.dirname(currentDir);
|
|
13
|
+
}
|
|
14
|
+
throw new Error('Could not find src directory');
|
|
15
|
+
}
|
|
16
|
+
export async function loadJson(name) {
|
|
17
|
+
const srcDir = findSrcDirectory(__dirname);
|
|
18
|
+
const filePath = path.join(srcDir, "assets", `${name}.json`);
|
|
19
|
+
const file = await fs.readFile(filePath, "utf-8");
|
|
20
|
+
return JSON.parse(file);
|
|
21
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import { Component } from "@bouko/react";
|
|
3
|
+
export type ModalProps = Component & {
|
|
4
|
+
title: string;
|
|
5
|
+
subtitle?: ReactNode;
|
|
6
|
+
redirect?: {
|
|
7
|
+
label: string;
|
|
8
|
+
to: string;
|
|
9
|
+
};
|
|
10
|
+
};
|
|
11
|
+
export declare function Modal({ style, title, subtitle, redirect, children }: ModalProps): import("react/jsx-runtime").JSX.Element;
|
|
12
|
+
export default Modal;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "@bouko/style";
|
|
3
|
+
import useModal from "../state/context";
|
|
4
|
+
import X from "../assets/icons/x.svg";
|
|
5
|
+
export function Modal({ style, title, subtitle, redirect, children }) {
|
|
6
|
+
const { openModal, closeModal } = useModal();
|
|
7
|
+
return (_jsxs("div", { className: cn(styles.container, style), role: "dialog", "aria-modal": "true", children: [_jsxs("div", { className: styles.header, children: [_jsxs("div", { className: styles.heading, children: [_jsx("span", { className: styles.title, children: title }), _jsxs("span", { className: styles.subtitle, children: [subtitle, redirect && (_jsx("button", { className: "p-0 bg-transparent border-none outline-none text-sm text-accent duration-200 hover:text-accent-light cursor-pointer font-semibold", onClick: () => openModal(redirect.to), children: redirect.label }))] })] }), _jsx(X, { className: styles.close, onClick: closeModal })] }), children] }));
|
|
8
|
+
}
|
|
9
|
+
const styles = {
|
|
10
|
+
container: "relative flex flex-col gap-4 w-96 p-7 pt-6 select-none overflow-hidden",
|
|
11
|
+
header: "relative flex flex-col gap-px mb-1 w-full",
|
|
12
|
+
heading: "flex flex-col gap-px",
|
|
13
|
+
title: "leading-none text-xl font-semibold",
|
|
14
|
+
subtitle: "flex items-center gap-2 text-sm text-slate-400",
|
|
15
|
+
close: "absolute -right-2 -top-1 text-error hover:text-error text-lg duration-200 cursor-pointer"
|
|
16
|
+
};
|
|
17
|
+
export default Modal;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { JsonMap } from "@bouko/ts";
|
|
2
|
+
import { ReactNode } from "react";
|
|
3
|
+
export type Props = {
|
|
4
|
+
activeId?: string;
|
|
5
|
+
modal: ReactNode;
|
|
6
|
+
parameters: JsonMap;
|
|
7
|
+
openModal: (modal: string, params?: JsonMap) => void;
|
|
8
|
+
closeModal: () => void;
|
|
9
|
+
};
|
|
10
|
+
export declare const Context: import("react").Context<Props>;
|
|
11
|
+
export declare const useModal: () => Props;
|
|
12
|
+
export default useModal;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useContext, createContext, } from "react";
|
|
3
|
+
export const Context = createContext({
|
|
4
|
+
modal: null,
|
|
5
|
+
parameters: {},
|
|
6
|
+
openModal: () => undefined,
|
|
7
|
+
closeModal: () => undefined
|
|
8
|
+
});
|
|
9
|
+
export const useModal = () => useContext(Context);
|
|
10
|
+
export default useModal;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import { Context } from "./context";
|
|
5
|
+
import { parseData } from "@bouko/react";
|
|
6
|
+
export default function ModalManager({ config, children }) {
|
|
7
|
+
const [isOpen, setOpen] = useState(false);
|
|
8
|
+
const [templateId, setTemplate] = useState();
|
|
9
|
+
const [params, setParams] = useState({});
|
|
10
|
+
const openModal = async (template, params) => {
|
|
11
|
+
setTemplate(template);
|
|
12
|
+
setParams(params ?? {});
|
|
13
|
+
setOpen(true);
|
|
14
|
+
};
|
|
15
|
+
const getParams = () => parseData(params ?? {});
|
|
16
|
+
const closeModal = () => {
|
|
17
|
+
setOpen(false);
|
|
18
|
+
setTemplate(undefined);
|
|
19
|
+
setParams({});
|
|
20
|
+
};
|
|
21
|
+
return (_jsx(Context.Provider, { value: {
|
|
22
|
+
templates: config,
|
|
23
|
+
templateId,
|
|
24
|
+
isOpen,
|
|
25
|
+
params,
|
|
26
|
+
getParams,
|
|
27
|
+
openModal,
|
|
28
|
+
closeModal
|
|
29
|
+
}, children: children }));
|
|
30
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import type { Config } from "../core/types";
|
|
3
|
+
/**
|
|
4
|
+
* React Context Provider for Modal component.
|
|
5
|
+
*
|
|
6
|
+
* Supplies modal state and control functions to its children,
|
|
7
|
+
* enabling centralized management of modal configuration, visibility,
|
|
8
|
+
* and parameters throughout the app.
|
|
9
|
+
*
|
|
10
|
+
* Features:
|
|
11
|
+
* - Tracks the currently active modal by its key from the `config` object.
|
|
12
|
+
* - Stores and provides `parameters` for the active modal instance.
|
|
13
|
+
* - Exposes `openModal` and `closeModal` functions for controlling modal state.
|
|
14
|
+
* - Passes modal configuration and state to consumers via context value.
|
|
15
|
+
* - Renders the `RootModal` component for global modal handling.
|
|
16
|
+
*
|
|
17
|
+
* @param {Config} config - A map of modal ID keys to the modal component itself.
|
|
18
|
+
* @param {ReactNode} children - Child components that will have access to modals.
|
|
19
|
+
**/
|
|
20
|
+
export default function ModalProvider({ config, children }: {
|
|
21
|
+
config: Config;
|
|
22
|
+
children: ReactNode;
|
|
23
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
24
|
+
/**
|
|
25
|
+
* Problems:
|
|
26
|
+
*
|
|
27
|
+
* - Perfect `Context`.
|
|
28
|
+
* - Perfect `RootModal`.
|
|
29
|
+
* - Perfect `Config`.
|
|
30
|
+
**/
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import { Context } from "./context";
|
|
5
|
+
import RootModal from "./root";
|
|
6
|
+
/**
|
|
7
|
+
* React Context Provider for Modal component.
|
|
8
|
+
*
|
|
9
|
+
* Supplies modal state and control functions to its children,
|
|
10
|
+
* enabling centralized management of modal configuration, visibility,
|
|
11
|
+
* and parameters throughout the app.
|
|
12
|
+
*
|
|
13
|
+
* Features:
|
|
14
|
+
* - Tracks the currently active modal by its key from the `config` object.
|
|
15
|
+
* - Stores and provides `parameters` for the active modal instance.
|
|
16
|
+
* - Exposes `openModal` and `closeModal` functions for controlling modal state.
|
|
17
|
+
* - Passes modal configuration and state to consumers via context value.
|
|
18
|
+
* - Renders the `RootModal` component for global modal handling.
|
|
19
|
+
*
|
|
20
|
+
* @param {Config} config - A map of modal ID keys to the modal component itself.
|
|
21
|
+
* @param {ReactNode} children - Child components that will have access to modals.
|
|
22
|
+
**/
|
|
23
|
+
export default function ModalProvider({ config, children }) {
|
|
24
|
+
const [activeModal, setActiveModal] = useState();
|
|
25
|
+
const [parameters, setParameters] = useState({});
|
|
26
|
+
const openModal = async (modal, params) => {
|
|
27
|
+
setActiveModal(modal);
|
|
28
|
+
setParameters(params || {});
|
|
29
|
+
};
|
|
30
|
+
const closeModal = () => {
|
|
31
|
+
setActiveModal(undefined);
|
|
32
|
+
setParameters({});
|
|
33
|
+
};
|
|
34
|
+
return (_jsxs(Context.Provider, { value: {
|
|
35
|
+
activeId: activeModal,
|
|
36
|
+
openModal,
|
|
37
|
+
modal: activeModal ? config[activeModal] : null,
|
|
38
|
+
parameters,
|
|
39
|
+
closeModal
|
|
40
|
+
}, children: [children, _jsx(RootModal, {})] }));
|
|
41
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Root Modal component
|
|
3
|
+
*
|
|
4
|
+
* Injected into the root layout in a hidden state,
|
|
5
|
+
* then when a modal is opened this is displayed and wraps
|
|
6
|
+
* the new modal with a backdrop.
|
|
7
|
+
**/
|
|
8
|
+
export default function RootModal(): import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
/**
|
|
10
|
+
* Problems:
|
|
11
|
+
*
|
|
12
|
+
* - Perfect `useModal`
|
|
13
|
+
* - Perfect `Animation`
|
|
14
|
+
**/
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { useModal } from "./context";
|
|
4
|
+
import { AnimatePresence, Animation } from "@bouko/react";
|
|
5
|
+
/**
|
|
6
|
+
* Root Modal component
|
|
7
|
+
*
|
|
8
|
+
* Injected into the root layout in a hidden state,
|
|
9
|
+
* then when a modal is opened this is displayed and wraps
|
|
10
|
+
* the new modal with a backdrop.
|
|
11
|
+
**/
|
|
12
|
+
export default function RootModal() {
|
|
13
|
+
const { activeId, modal } = useModal();
|
|
14
|
+
return (_jsx(AnimatePresence, { mode: "wait", children: modal && (_jsx(Animation, { style: styles.backdrop, children: _jsx("div", { className: styles.content, children: modal }) }, activeId)) }));
|
|
15
|
+
}
|
|
16
|
+
const styles = {
|
|
17
|
+
backdrop: "fixed inset-0 flex justify-center items-center bg-background-light/60 dark:bg-background-dark/60 z-[99]",
|
|
18
|
+
content: "max-w-[80%] max-h-[85%] bg-background border border-border rounded-lg shadow-soft"
|
|
19
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Login Modal component
|
|
3
|
+
*
|
|
4
|
+
* Displays a modal for user login with handling for
|
|
5
|
+
* input validation and submission logic for existing users.
|
|
6
|
+
* Uses `nupabase` which is a Supabase wrapper.
|
|
7
|
+
*
|
|
8
|
+
* Form Fields:
|
|
9
|
+
* `email` - User's email address
|
|
10
|
+
* `password` - User's desired password
|
|
11
|
+
**/
|
|
12
|
+
export declare function LoginModal(): import("react/jsx-runtime").JSX.Element;
|
|
13
|
+
export default LoginModal;
|
|
14
|
+
/**
|
|
15
|
+
* Problems:
|
|
16
|
+
*
|
|
17
|
+
* - Perfect `FormModal`.
|
|
18
|
+
* - Perfect `login`.
|
|
19
|
+
* - Perfect `LoginSchema`.
|
|
20
|
+
**/
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { FormModal } from "../layouts/form";
|
|
3
|
+
import { login, LoginSchema } from "nupabase";
|
|
4
|
+
/**
|
|
5
|
+
* Login Modal component
|
|
6
|
+
*
|
|
7
|
+
* Displays a modal for user login with handling for
|
|
8
|
+
* input validation and submission logic for existing users.
|
|
9
|
+
* Uses `nupabase` which is a Supabase wrapper.
|
|
10
|
+
*
|
|
11
|
+
* Form Fields:
|
|
12
|
+
* `email` - User's email address
|
|
13
|
+
* `password` - User's desired password
|
|
14
|
+
**/
|
|
15
|
+
export function LoginModal() {
|
|
16
|
+
return (_jsx(FormModal, { formId: "login", title: "Login to your Account", subtitle: "We'll send a verification to your email.", validator: LoginSchema.safeParse, submit: login }));
|
|
17
|
+
}
|
|
18
|
+
export default LoginModal;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Register Modal component
|
|
3
|
+
*
|
|
4
|
+
* Displays a modal for user registration with handling
|
|
5
|
+
* for input validation and submission logic for new users.
|
|
6
|
+
* Uses `nupabase` which is a Supabase wrapper.
|
|
7
|
+
*
|
|
8
|
+
* Form Fields:
|
|
9
|
+
* `email` - User's email address
|
|
10
|
+
* `password` - User's desired password
|
|
11
|
+
**/
|
|
12
|
+
export declare function RegisterModal(): import("react/jsx-runtime").JSX.Element;
|
|
13
|
+
export default RegisterModal;
|
|
14
|
+
/**
|
|
15
|
+
* Problems:
|
|
16
|
+
*
|
|
17
|
+
* - Perfect `FormModal`.
|
|
18
|
+
* - Perfect `register`.
|
|
19
|
+
* - Perfect `RegisterSchema`.
|
|
20
|
+
**/
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { FormModal } from "../layouts/form";
|
|
3
|
+
import { register, RegisterSchema } from "nupabase";
|
|
4
|
+
/**
|
|
5
|
+
* Register Modal component
|
|
6
|
+
*
|
|
7
|
+
* Displays a modal for user registration with handling
|
|
8
|
+
* for input validation and submission logic for new users.
|
|
9
|
+
* Uses `nupabase` which is a Supabase wrapper.
|
|
10
|
+
*
|
|
11
|
+
* Form Fields:
|
|
12
|
+
* `email` - User's email address
|
|
13
|
+
* `password` - User's desired password
|
|
14
|
+
**/
|
|
15
|
+
export function RegisterModal() {
|
|
16
|
+
return (_jsx(FormModal, { formId: "register", title: "Create an Account", subtitle: "We'll send a verification to your email.", validator: RegisterSchema.safeParse, submit: register }));
|
|
17
|
+
}
|
|
18
|
+
export default RegisterModal;
|
package/package.json
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
{
|
|
2
|
+
|
|
3
|
+
"name": "moverlay",
|
|
4
|
+
|
|
5
|
+
"version": "0.0.0",
|
|
6
|
+
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
|
|
13
|
+
"files": [
|
|
14
|
+
|
|
15
|
+
"dist"
|
|
16
|
+
|
|
17
|
+
],
|
|
18
|
+
|
|
19
|
+
"publishConfig": {
|
|
20
|
+
|
|
21
|
+
"access": "public"
|
|
22
|
+
|
|
23
|
+
},
|
|
24
|
+
|
|
25
|
+
"author": "",
|
|
26
|
+
|
|
27
|
+
"description": "",
|
|
28
|
+
|
|
29
|
+
"peerDependencies": {
|
|
30
|
+
|
|
31
|
+
"next": ">=15.4.5",
|
|
32
|
+
|
|
33
|
+
"react": ">=18.3.1"
|
|
34
|
+
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
"engines": {},
|
|
38
|
+
|
|
39
|
+
"scripts": {
|
|
40
|
+
|
|
41
|
+
"build": "tsc"
|
|
42
|
+
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
"dependencies": {
|
|
46
|
+
|
|
47
|
+
"@bouko/form": "^0.6.1",
|
|
48
|
+
|
|
49
|
+
"@bouko/react": "^2.4.8",
|
|
50
|
+
|
|
51
|
+
"@bouko/style": "^0.1.6",
|
|
52
|
+
|
|
53
|
+
"@bouko/ts": "^0.2.0",
|
|
54
|
+
|
|
55
|
+
"clsx": "^2.1.1",
|
|
56
|
+
|
|
57
|
+
"framer-motion": "^12.16.0",
|
|
58
|
+
|
|
59
|
+
"superstruct": "^2.0.2",
|
|
60
|
+
|
|
61
|
+
"tailwind-merge": "^3.3.0",
|
|
62
|
+
|
|
63
|
+
"tailwind-variants": "^1.0.0",
|
|
64
|
+
|
|
65
|
+
"zod": "^4.0.17"
|
|
66
|
+
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
"devDependencies": {
|
|
70
|
+
|
|
71
|
+
"@types/react": "^19.1.10",
|
|
72
|
+
|
|
73
|
+
"dependency-cruiser": "^17.0.1",
|
|
74
|
+
|
|
75
|
+
"next": "^15.4.5",
|
|
76
|
+
|
|
77
|
+
"nupabase": "^0.2.0",
|
|
78
|
+
|
|
79
|
+
"react": "^19.1.1",
|
|
80
|
+
|
|
81
|
+
"typescript": "^5.9.2"
|
|
82
|
+
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
}
|
|
86
|
+
|