formeact 0.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.
@@ -0,0 +1,16 @@
1
+ import type { ReactNode } from "react";
2
+ type Props<T> = Omit<FieldProps<T>, "update"> & {
3
+ children: ReactNode;
4
+ };
5
+ export type FieldProps<T> = {
6
+ id?: string;
7
+ label?: string;
8
+ style?: string;
9
+ value?: T;
10
+ update: (x: T) => void;
11
+ required?: boolean;
12
+ disabled?: boolean;
13
+ note?: ReactNode;
14
+ };
15
+ export declare function Field<T>({ style, label, required, note, children }: Props<T>): import("react/jsx-runtime").JSX.Element;
16
+ export {};
@@ -0,0 +1,10 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { cn } from "@bouko/style";
3
+ export function Field({ style, label, required = true, note, children }) {
4
+ return (_jsxs("div", { className: cn(fieldStyles.container, style), children: [label && _jsxs("span", { className: fieldStyles.label, children: [label, " ", !required ? _jsx("span", { className: "italic text-slate-400", children: "(optional)" }) : ""] }), children, note && _jsx("span", { className: fieldStyles.note, children: note })] }));
5
+ }
6
+ const fieldStyles = {
7
+ container: "flex flex-col shrink-0 gap-1 w-full overflow-hidden",
8
+ label: "text-xs text-primary-dark",
9
+ note: "mt-1 text-xs text-slate-500 whitespace-pre-line"
10
+ };
@@ -0,0 +1,36 @@
1
+ import type { Field, Option } from "../../core/types";
2
+ import { JsonMap } from "@bouko/ts";
3
+ type FormBuilderField<T = unknown> = (Omit<Field<string>, "value" | "update"> & {
4
+ element: string;
5
+ rows?: number;
6
+ placeholder?: string;
7
+ options?: Option[];
8
+ })[][];
9
+ type JsonMapWithFiles = {
10
+ [key: string]: JsonValueWithFile | JsonValueWithFile[];
11
+ };
12
+ type JsonValueWithFile = string | number | boolean | null | undefined | File | JsonMap | JsonValueWithFile[];
13
+ export type FormBuilderProps<T> = {
14
+ formId?: string;
15
+ style?: Styles;
16
+ fields: FormBuilderField<T>;
17
+ oops?: (reason: string) => void;
18
+ validator?: (x: Partial<T>) => {
19
+ success: boolean;
20
+ };
21
+ sound?: string;
22
+ cancel?: boolean;
23
+ submit: (data: T) => void;
24
+ };
25
+ type Styles = {
26
+ container?: string;
27
+ submit?: string;
28
+ cancel?: string;
29
+ };
30
+ type Item = {
31
+ id?: string;
32
+ initial?: string;
33
+ };
34
+ export declare function mapIdToInitial<T>(arrays: Item[][]): Partial<T>;
35
+ export default function Fields<T extends JsonMapWithFiles>({ fields, submit, cancel, sound }: FormBuilderProps<T>): import("react/jsx-runtime").JSX.Element;
36
+ export {};
@@ -0,0 +1,37 @@
1
+ "use client";
2
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useForm } from "../../core/hooks";
4
+ import { Footer } from "./footer";
5
+ import { RowBox, Select, TextArea, MultipleChoice, Attachment } from "@bouko/react";
6
+ import Input from "../elements/input";
7
+ import { ImageUploader } from "./uploader";
8
+ export function mapIdToInitial(arrays) {
9
+ const result = {};
10
+ for (const inner of arrays) {
11
+ for (const obj of inner) {
12
+ if ("initial" in obj && obj["initial"]) {
13
+ result[obj.id] = obj.initial;
14
+ }
15
+ }
16
+ }
17
+ return result;
18
+ }
19
+ export default function Fields({ fields, submit, cancel = false, sound }) {
20
+ const { data, update, clear } = useForm(mapIdToInitial(fields));
21
+ return (_jsxs(_Fragment, { children: [fields.map((row, i) => (_jsx(RowBox, { style: "w-full gap-5 overflow-hidden", children: row.map(({ element, style, id, rows, label, placeholder, disabled, options, required, note }) => {
22
+ if (element === "input")
23
+ return (_jsx(Input, { label: label, placeholder: placeholder, value: data[id] || "", update: x => update(id, x), disabled: disabled, note: note, required: required }, id));
24
+ else if (element === "image-uploader")
25
+ return (_jsx(ImageUploader, { style: style, placeholder: placeholder, value: data[id], update: x => update(id, x) }, id));
26
+ else if (element === "select")
27
+ return (_jsx(Select, { id: id, label: label, placeholder: placeholder, options: options || [], value: data[id], update: x => console.log(x), note: note, required: required }, id));
28
+ else if (element === "textarea")
29
+ return (_jsx(TextArea, { id: id, label: label, placeholder: placeholder, rows: rows, value: data[id], update: x => console.log(x), note: note, required: required }, id));
30
+ else if (element === "multiple-choice")
31
+ return (_jsx(MultipleChoice, { id: id, label: label, options: options || [], value: data[id], update: x => console.log(x), note: note, required: required }, id));
32
+ else if (element === "attachment")
33
+ return (_jsx(Attachment, { id: id, label: label, value: data[id], update: x => console.log(x), note: note, required: required }, id));
34
+ }) }, i))), _jsx(Footer, { data: data,
35
+ // validate={validator}
36
+ submit: submit, cancel: cancel, clear: clear, sound: sound })] }));
37
+ }
@@ -0,0 +1,40 @@
1
+ import { ReactNode } from "react";
2
+ type Props<T> = {
3
+ style?: string;
4
+ data: T;
5
+ icon?: ReactNode;
6
+ button?: string;
7
+ validate?: (x: T) => {
8
+ success: boolean;
9
+ };
10
+ submit: (x: T) => void;
11
+ sound?: string;
12
+ clear?: () => void;
13
+ cancel?: boolean;
14
+ };
15
+ /**
16
+ * Form Builder footer section.
17
+ *
18
+ * Displayed at the bottom of a form, contains a 'Submit' and optional 'Cancel' button.
19
+ * The submit button is disabled if form validation fails, and triggers the submit callback on click.
20
+ * If a sound is provided, it plays on successful submission. Errors are shown as toast notifications.
21
+ * The cancel button appears if a clear callback is provided, allowing users to reset or exit the form.
22
+ *
23
+ * @param {string} style - Additional class names for styling the submit button. (optional)
24
+ * @param {T} data - The form data to be validated and submitted.
25
+ * @param {ReactNode} icon - Icon for the submit button (default: `CheckCircle`).
26
+ * @param {string} button - Label for the submit button (default: "Submit").
27
+ * @param {Function} validate - Validation function for the form data. (optional)
28
+ * @param {Function} submit - Callback to handle form submission.
29
+ * @param {string} sound - Sound to play on successful submission. (optional)
30
+ * @param {Function} clear - Callback to handle form cancellation/reset. (optional)
31
+ * @param {boolean} cancel - Show or hide the cancel button. (optional)
32
+ **/
33
+ export declare function Footer<T>({ style, data, icon, button, validate, submit, sound, clear, cancel }: Props<T>): import("react/jsx-runtime").JSX.Element;
34
+ export {};
35
+ /**
36
+ * Problems:
37
+ *
38
+ * - Perfect `Button`
39
+ * - Perfect `useSound`
40
+ **/
@@ -0,0 +1,45 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { RowBox, Button } from "@bouko/react";
4
+ import CheckCircle from "../../assets/icons/check-circle.svg";
5
+ import XCircle from "../../assets/icons/x-circle.svg";
6
+ import { useSound } from "@bouko/react";
7
+ import { toast } from "@bouko/notify";
8
+ import { cn } from "@bouko/style";
9
+ /**
10
+ * Form Builder footer section.
11
+ *
12
+ * Displayed at the bottom of a form, contains a 'Submit' and optional 'Cancel' button.
13
+ * The submit button is disabled if form validation fails, and triggers the submit callback on click.
14
+ * If a sound is provided, it plays on successful submission. Errors are shown as toast notifications.
15
+ * The cancel button appears if a clear callback is provided, allowing users to reset or exit the form.
16
+ *
17
+ * @param {string} style - Additional class names for styling the submit button. (optional)
18
+ * @param {T} data - The form data to be validated and submitted.
19
+ * @param {ReactNode} icon - Icon for the submit button (default: `CheckCircle`).
20
+ * @param {string} button - Label for the submit button (default: "Submit").
21
+ * @param {Function} validate - Validation function for the form data. (optional)
22
+ * @param {Function} submit - Callback to handle form submission.
23
+ * @param {string} sound - Sound to play on successful submission. (optional)
24
+ * @param {Function} clear - Callback to handle form cancellation/reset. (optional)
25
+ * @param {boolean} cancel - Show or hide the cancel button. (optional)
26
+ **/
27
+ export function Footer({ style, data, icon = _jsx(CheckCircle, {}), button = "Submit", validate, submit, sound, clear, cancel = false }) {
28
+ const isValid = validate ? validate(data).success : true;
29
+ const playSound = useSound(sound);
30
+ const submitForm = async () => {
31
+ try {
32
+ await submit(data);
33
+ playSound?.();
34
+ }
35
+ catch (err) {
36
+ toast.error(err.message);
37
+ }
38
+ };
39
+ return (_jsxs(RowBox, { style: styles.container, children: [_jsx(Button, { style: cn(styles.submit, style), size: "sm", icon: icon, action: submitForm, disabled: !isValid, children: button }), clear && cancel && (_jsx(Button, { style: styles.cancel, size: "sm", variant: "secondary", icon: _jsx(XCircle, {}), action: clear, children: "Cancel" }))] }));
40
+ }
41
+ const styles = {
42
+ container: "items-center gap-2 mt-2 w-full",
43
+ submit: "grow",
44
+ cancel: "text-error hover:text-error-light"
45
+ };
@@ -0,0 +1,38 @@
1
+ import type { Field, Option } from "../../core/types";
2
+ import { JsonMap } from "@bouko/ts";
3
+ type JsonMapWithFiles = {
4
+ [key: string]: JsonValueWithFile | JsonValueWithFile[];
5
+ };
6
+ type JsonValueWithFile = string | number | boolean | null | undefined | File | JsonMap | JsonValueWithFile[];
7
+ type FormBuilderField<T = unknown> = (Omit<Field<string>, "value" | "update"> & {
8
+ element: string;
9
+ rows?: number;
10
+ initial?: string;
11
+ placeholder?: string;
12
+ options?: Option[];
13
+ })[][];
14
+ export type FormBuilderProps<T> = {
15
+ formId?: string;
16
+ style?: Styles;
17
+ initial?: Partial<T>;
18
+ fields?: FormBuilderField<T>;
19
+ oops?: (reason: string) => void;
20
+ validator?: (x: Partial<T>) => {
21
+ success: boolean;
22
+ };
23
+ sound?: string;
24
+ cancel?: boolean;
25
+ submit: (data: T) => void;
26
+ };
27
+ type Styles = {
28
+ container?: string;
29
+ submit?: string;
30
+ cancel?: string;
31
+ };
32
+ type Item<T = string> = {
33
+ id?: string;
34
+ initial?: T;
35
+ };
36
+ export declare function mapIdToInitial<T>(arrays: Item[][]): Partial<T>;
37
+ export declare function FormBuilder<T extends JsonMapWithFiles>({ formId, fields, validator, style, initial, submit, sound, cancel }: FormBuilderProps<T>): import("react/jsx-runtime").JSX.Element;
38
+ export {};
@@ -0,0 +1,38 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { cn } from "@bouko/style";
4
+ import { ColumnBox, } from "@bouko/react";
5
+ import Fields from "./fields";
6
+ import { useState } from "react";
7
+ export function mapIdToInitial(arrays) {
8
+ const result = {};
9
+ for (const inner of arrays) {
10
+ for (const obj of inner) {
11
+ if ("initial" in obj && obj["initial"]) {
12
+ result[obj.id] = obj.initial;
13
+ }
14
+ }
15
+ }
16
+ return result;
17
+ }
18
+ export function FormBuilder({ formId, fields, validator, style, initial = {}, submit, sound, cancel = false }) {
19
+ const [loadedFields, setLoadedFields] = useState(fields || []);
20
+ /* useEffect(() => {
21
+
22
+ const loadFields = async () => {
23
+ const test = await loadForm<T>(formId);
24
+ for (let row = 0; row < test.length; row++) {
25
+ for (let field = 0; field < test[row].length; field++) {
26
+ let initialValue = initial[test[row][field].id!] as string;
27
+ if (initialValue && initialValue !== null)
28
+ test[row][field].initial = initialValue;
29
+ }
30
+ }
31
+ setLoadedFields(test);
32
+ };
33
+
34
+ loadFields();
35
+
36
+ }, []); */
37
+ return (_jsx(ColumnBox, { style: cn("w-full gap-4", style?.container), children: _jsx(Fields, { fields: loadedFields, submit: submit, cancel: cancel, sound: sound }) }));
38
+ }
@@ -0,0 +1,10 @@
1
+ import { StaticImageData } from "next/image";
2
+ type Props = {
3
+ style?: string;
4
+ placeholder?: StaticImageData | string;
5
+ value?: File;
6
+ update?: (x: File) => void;
7
+ disabled?: boolean;
8
+ };
9
+ export declare function ImageUploader({ style, placeholder, value, update, disabled }: Props): import("react/jsx-runtime").JSX.Element;
10
+ export default ImageUploader;
@@ -0,0 +1,31 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useState, useRef, useEffect } from "react";
4
+ import { cn } from "@bouko/style";
5
+ import Image from "next/image";
6
+ import Placeholder from "../../assets/icons/cover.png";
7
+ import Camera from "../../assets/icons/camera.svg";
8
+ export function ImageUploader({ style, placeholder, value, update, disabled = false }) {
9
+ const [preview, setPreview] = useState();
10
+ useEffect(() => {
11
+ if (value) {
12
+ console.log(value);
13
+ setPreview(URL.createObjectURL(value));
14
+ }
15
+ }, [value]);
16
+ const ref = useRef(null);
17
+ const uploadFile = () => ref.current?.click();
18
+ if (!update)
19
+ disabled = true;
20
+ const handleFileChange = (e) => {
21
+ const file = e.target.files?.[0];
22
+ if (!file)
23
+ return;
24
+ update?.(file);
25
+ };
26
+ return (_jsxs("div", { className: cn(styles.container, style), onClick: uploadFile, children: [_jsx("input", { type: "file", className: "hidden", onChange: handleFileChange, ref: ref }), _jsx(Image, { src: preview || placeholder || Placeholder, fill: true, alt: "Image Uploader" }), _jsx("div", { className: "flex justify-center items-center w-full h-full z-10 opacity-0 hover:opacity-50 bg-background-light/80 duration-200 cursor-pointer", children: _jsx(Camera, { className: "text-xl text-primary-light" }) })] }));
27
+ }
28
+ const styles = {
29
+ container: "relative shrink-0 border border-border rounded-md overflow-hidden",
30
+ };
31
+ export default ImageUploader;
@@ -0,0 +1,27 @@
1
+ import { FieldProps } from "../builder/field";
2
+ type Variant = "normal" | "ghost";
3
+ type Props = Omit<FieldProps<string>, "id"> & {
4
+ variant?: Variant;
5
+ style?: string;
6
+ placeholder?: string;
7
+ onEnter?: (x: string) => void;
8
+ };
9
+ /**
10
+ * Text field component.
11
+ *
12
+ * Wraps the native `input` element with custom styling and validation.
13
+ * It displays an optional label and note, and can be marked as required.
14
+ * Optional callback for when the user presses `Enter` key.
15
+ * The value is controlled externally via props, usually `useState`.
16
+ *
17
+ * Variants:
18
+ * - `normal`: Standard input with primary color scheme.
19
+ * - `ghost`: Transparent for injecting input inside other component.
20
+ *
21
+ * @param {Variant} variant - Visual style variant. (optional, default = normal)
22
+ * @param {string} style - Additional class names for styling. (optional)
23
+ * @param {string} placeholder - Placeholder text for the input. (optional)
24
+ * @param {Function} onEnter - Callback when `Enter` key is pressed. (optional)
25
+ **/
26
+ export default function Input({ variant, style, value, update, onEnter, disabled, placeholder, ...props }: Props): import("react/jsx-runtime").JSX.Element;
27
+ export {};
@@ -0,0 +1,33 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Field } from "../builder/field";
3
+ import { cn, tv } from "@bouko/style";
4
+ /**
5
+ * Text field component.
6
+ *
7
+ * Wraps the native `input` element with custom styling and validation.
8
+ * It displays an optional label and note, and can be marked as required.
9
+ * Optional callback for when the user presses `Enter` key.
10
+ * The value is controlled externally via props, usually `useState`.
11
+ *
12
+ * Variants:
13
+ * - `normal`: Standard input with primary color scheme.
14
+ * - `ghost`: Transparent for injecting input inside other component.
15
+ *
16
+ * @param {Variant} variant - Visual style variant. (optional, default = normal)
17
+ * @param {string} style - Additional class names for styling. (optional)
18
+ * @param {string} placeholder - Placeholder text for the input. (optional)
19
+ * @param {Function} onEnter - Callback when `Enter` key is pressed. (optional)
20
+ **/
21
+ export default function Input({ variant, style, value, update, onEnter, disabled, placeholder, ...props }) {
22
+ return (_jsx(Field, { ...props, children: _jsx("input", { className: cn(styles({ variant }), style), placeholder: placeholder, value: value, onChange: ({ target: { value } }) => update?.(value), onKeyUp: ({ key }) => key === "Enter" && value ? onEnter?.(value) : null, disabled: disabled }) }));
23
+ }
24
+ const styles = tv({
25
+ base: "px-3 py-2 duration-200 rounded text-sm placeholder:text-primary-darker text-primary disabled:brightness-90 disabled:cursor-not-allowed",
26
+ defaultVariants: { variant: "normal" },
27
+ variants: {
28
+ variant: {
29
+ normal: "bg-input border border-outline focus:border-outline-light outline-none",
30
+ ghost: "p-0 bg-transparent border-none outline-none"
31
+ }
32
+ }
33
+ });
@@ -0,0 +1,2 @@
1
+ export * from "./builder";
2
+ export { default as Input } from "./elements/input";
@@ -0,0 +1,2 @@
1
+ export * from "./builder";
2
+ export { default as Input } from "./elements/input";
@@ -0,0 +1,3 @@
1
+ import type { SetState } from "./types";
2
+ export declare const setField: <T>(update: SetState<T>, id: string, value: unknown) => void;
3
+ export declare const parseData: <T extends Record<string, unknown>>(data: T) => T;
@@ -0,0 +1,33 @@
1
+ "use client";
2
+ export const setField = (update, id, value) => update(prev => ({
3
+ ...prev,
4
+ [id]: value
5
+ }));
6
+ export const parseData = (data) => {
7
+ const copy = JSON.parse(JSON.stringify(data));
8
+ for (const [key, value] of Object.entries(data)) {
9
+ if (key === "created_at")
10
+ copy[key] = new Date(value);
11
+ else if (key === "timestamp")
12
+ copy[key] = new Date(value.replaceAll("'", ""));
13
+ else if (parseAllowedDate(value))
14
+ copy[key] = new Date(value);
15
+ }
16
+ return copy;
17
+ };
18
+ function parseAllowedDate(input) {
19
+ // ISO 8601 strict (YYYY-MM-DD or YYYY-MM-DDTHH:mm:ss+hh:mm)
20
+ const isoStrictRegex = /^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}(:\d{2}(\.\d{1,3})?)?(Z|[+-]\d{2}:\d{2}))?$/;
21
+ // MySQL/Postgres style with space + offset without colon (YYYY-MM-DD HH:mm:ss+hhmm)
22
+ const spaceOffsetRegex = /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}[+-]\d{4}$/;
23
+ let normalized = input;
24
+ if (spaceOffsetRegex.test(input)) {
25
+ // Convert to ISO by replacing space with T and adding colon in offset
26
+ normalized = input.replace(" ", "T").replace(/([+-]\d{2})(\d{2})$/, "$1:$2");
27
+ }
28
+ else if (!isoStrictRegex.test(input)) {
29
+ return null; // Not allowed format
30
+ }
31
+ const date = new Date(normalized);
32
+ return !isNaN(date.getTime()) ? date : null;
33
+ }
@@ -0,0 +1,27 @@
1
+ import type { FormControls } from "@bouko/form";
2
+ import type { JsonMap } from "@bouko/ts";
3
+ /**
4
+ * React hook for managing form state with type-safety
5
+ * and controlled updates.
6
+ *
7
+ * Features:
8
+ * - Uses deep comparison for initial value updates.
9
+ * - Does not handle validation, errors, or async updates.
10
+ *
11
+ * @template T - The shape of the form data.
12
+ * @template K - The keys of the form data.
13
+ *
14
+ * @param {Partial<T>} init - The initial form data.
15
+ * @returns {FormControls} Utils to control the form.
16
+ **/
17
+ type JsonMapWithFiles = {
18
+ [key: string]: JsonValueWithFile | JsonValueWithFile[];
19
+ };
20
+ type JsonValueWithFile = string | number | boolean | null | undefined | File | JsonMap | JsonValueWithFile[];
21
+ export declare function useForm<T extends JsonMapWithFiles, K extends keyof T = keyof T>(init: Partial<T>): FormControls<T, K>;
22
+ export {};
23
+ /**
24
+ * Problems:
25
+ *
26
+ * - Perfect `FormControls`.
27
+ **/
@@ -0,0 +1,15 @@
1
+ "use client";
2
+ import { useState, useEffect } from "react";
3
+ import { useDeepCompareMemo } from "use-deep-compare";
4
+ export function useForm(init) {
5
+ const initial = useDeepCompareMemo(() => init, [init]);
6
+ const [data, setData] = useState(initial);
7
+ useEffect(() => {
8
+ setData(initial);
9
+ }, [initial]);
10
+ return {
11
+ data,
12
+ update: (id, x) => setData(prev => ({ ...prev, [id]: x })),
13
+ clear: () => setData(initial)
14
+ };
15
+ }
@@ -0,0 +1,45 @@
1
+ import type { ReactNode, Dispatch, SetStateAction } from "react";
2
+ export type SetState<T> = Dispatch<SetStateAction<T>>;
3
+ export type FormSection<T = string> = {
4
+ value: T;
5
+ update: SetState<T>;
6
+ setField: (x: string, value: T) => void;
7
+ clear: () => void;
8
+ };
9
+ export type FormBuilderField<T = unknown> = (Omit<Field<string>, "value" | "update"> & {
10
+ id: string;
11
+ element: string;
12
+ rows?: number;
13
+ initial?: string;
14
+ placeholder?: string;
15
+ options?: Option[];
16
+ })[][];
17
+ export type Field<T = string> = {
18
+ id?: string;
19
+ label?: string;
20
+ style?: string;
21
+ value?: T;
22
+ initial?: T;
23
+ update: (x: T) => void;
24
+ required?: boolean;
25
+ disabled?: boolean;
26
+ note?: ReactNode;
27
+ };
28
+ export type Option = {
29
+ id: string;
30
+ label?: string;
31
+ active?: boolean;
32
+ select?: () => void;
33
+ };
34
+ export type OptionField<T> = Field<T> & {
35
+ options: Option[];
36
+ };
37
+ export type Form<T> = {
38
+ data: T;
39
+ };
40
+ export type FormSubmit<T> = (data: T, clear?: () => void) => Promise<void>;
41
+ export type FormControls<T, K extends keyof T> = {
42
+ data: Partial<T>;
43
+ update: (id: K, x: T[K]) => void;
44
+ clear: () => void;
45
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,4 @@
1
+ export * from "./core/functions";
2
+ export * from "./core/types";
3
+ export * from "./core/hooks";
4
+ export * from "./components";
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export * from "./core/functions";
2
+ export * from "./core/types";
3
+ export * from "./core/hooks";
4
+ export * from "./components";
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+
3
+ "name": "formeact",
4
+ "version": "0.0.1",
5
+ "main": "./dist/index.js",
6
+ "types": "./dist/index.d.ts",
7
+ "license": "MIT",
8
+ "files": [
9
+ "dist"
10
+ ],
11
+ "publishConfig": {
12
+ "access": "public"
13
+ },
14
+ "author": "",
15
+ "description": "",
16
+ "engines": {},
17
+
18
+ "scripts": {
19
+ "build": "tsc",
20
+ "release": "npm run build && npm publish"
21
+ },
22
+
23
+ "dependencies": {
24
+ "@bouko/next": "^1.6.6",
25
+ "@bouko/notify": "^0.1.8",
26
+ "@bouko/react": "^2.6.6",
27
+ "@bouko/style": "^0.1.7",
28
+ "@bouko/ts": "^0.2.0",
29
+ "use-deep-compare": "^1.3.0"
30
+ },
31
+
32
+ "devDependencies": {
33
+ "@types/node": "^24.3.0",
34
+ "@types/react": "^19.1.10",
35
+ "dependency-cruiser": "^17.0.1",
36
+ "react": "^19.1.1",
37
+ "typescript": "^5.9.2"
38
+ }
39
+
40
+ }