This commit is contained in:
Rizky 2024-11-23 21:05:46 +07:00
parent 8425b4ea95
commit 0338bac9d0
13 changed files with 380 additions and 435 deletions

BIN
comps/.DS_Store vendored Executable file

Binary file not shown.

View File

@ -78,16 +78,18 @@ export const Breadcrumb: FC<BreadcrumbProps> = ({ value, className }) => {
{cur?.label} {cur?.label}
</h1> </h1>
) : ( ) : (
<h1 <a
href={baseurl(cur.url || "")}
className="c-font-normal c-text-xs md:c-text-base hover:c-cursor-pointer hover:c-underline" className="c-font-normal c-text-xs md:c-text-base hover:c-cursor-pointer hover:c-underline"
onClick={(ev) => { onClick={(ev) => {
ev.preventDefault();
if (isEditor) return; if (isEditor) return;
if (cur.url) navigate(cur.url || ""); if (cur.url) navigate(cur.url || "");
if (cur.onClick) cur.onClick(ev); if (cur.onClick) cur.onClick(ev);
}} }}
> >
{cur?.label} {cur?.label}
</h1> </a>
)} )}
{index !== lastIndex && ( {index !== lastIndex && (

View File

@ -105,7 +105,7 @@ export const FilterContent: FC<{
> >
<BaseForm <BaseForm
data={filter.data} data={filter.data}
on_submit={async (form) => { onSubmit={async (form) => {
const fm = form.fm; const fm = form.fm;
try { try {
if (typeof form.fm?.data === "object") { if (typeof form.fm?.data === "object") {

View File

@ -35,163 +35,164 @@ export const FilterField: FC<{
}, [filter.form]); }, [filter.form]);
let show_modifier = filter.mode !== "inline"; let show_modifier = filter.mode !== "inline";
return ( return null;
<BaseField // return (
{...filter.form.fieldProps({ // <BaseField
name: name || "", // {...filter.form.fieldProps({
label: label || name || "", // name: name || "",
render: internal.render, // label: label || name || "",
prefix: show_modifier // render: internal.render,
? () => ( // prefix: show_modifier
<FieldModifier // ? () => (
onChange={(modifier) => { // <FieldModifier
filter.modifiers[name] = modifier; // onChange={(modifier) => {
filter.render(); // filter.modifiers[name] = modifier;
filter_window.prasiContext.render(); // filter.render();
}} // filter_window.prasiContext.render();
modifier={filter.modifiers[name]} // }}
type={type} // modifier={filter.modifiers[name]}
/> // type={type}
) // />
: undefined, // )
onLoad() { // : undefined,
return [{ label: "halo", value: "asda" }]; // onLoad() {
}, // return [{ label: "halo", value: "asda" }];
subType: singleOptions.includes(filter.modifiers[name]) // },
? "dropdown" // subType: singleOptions.includes(filter.modifiers[name])
: "typeahead", // ? "dropdown"
})} // : "typeahead",
> // })}
{(field) => { // >
if (type === "search-all") { // {(field) => {
return ( // if (type === "search-all") {
<div className={cx("search-all c-flex items-center")}> // return (
<div className="c-pl-2"> // <div className={cx("search-all c-flex items-center")}>
<svg // <div className="c-pl-2">
xmlns="http://www.w3.org/2000/svg" // <svg
width="14" // xmlns="http://www.w3.org/2000/svg"
height="14" // width="14"
viewBox="0 0 24 24" // height="14"
fill="none" // viewBox="0 0 24 24"
stroke="currentColor" // fill="none"
strokeWidth="2" // stroke="currentColor"
strokeLinecap="round" // strokeWidth="2"
strokeLinejoin="round" // strokeLinecap="round"
> // strokeLinejoin="round"
<circle cx="11" cy="11" r="8" /> // >
<path d="m21 21-4.3-4.3" /> // <circle cx="11" cy="11" r="8" />
</svg> // <path d="m21 21-4.3-4.3" />
</div> // </svg>
<input // </div>
type="search" // <input
value={field.fm?.data?.[name]} // type="search"
placeholder={field.field.label} // value={field.fm?.data?.[name]}
onBlur={() => { // placeholder={field.field.label}
// clearTimeout(internal.search_timeout); // onBlur={() => {
// filter.form?.submit(); // // clearTimeout(internal.search_timeout);
}} // // filter.form?.submit();
spellCheck={false} // }}
className="c-flex-1 c-transition-all c-bg-transparent c-outline-none c-px-2 c-text-sm c-w-full" // spellCheck={false}
onChange={(e) => { // className="c-flex-1 c-transition-all c-bg-transparent c-outline-none c-px-2 c-text-sm c-w-full"
field.fm.data[name] = e.currentTarget.value; // onChange={(e) => {
field.fm.render(); // field.fm.data[name] = e.currentTarget.value;
clearTimeout(internal.search_timeout); // field.fm.render();
internal.search_timeout = setTimeout(() => { // clearTimeout(internal.search_timeout);
filter.form?.submit(); // internal.search_timeout = setTimeout(() => {
}, 1500); // filter.form?.submit();
}} // }, 1500);
/> // }}
</div> // />
); // </div>
} // );
// }
return ( // return (
<> // <>
{type === "text" && ( // {type === "text" && (
<FieldTypeInput // <FieldTypeInput
{...field} // {...field}
prop={{ // prop={{
type: "input", // type: "input",
sub_type: "text", // sub_type: "text",
}} // }}
/> // />
)} // )}
{type === "number" && ( // {type === "number" && (
<> // <>
<FieldTypeInput // <FieldTypeInput
{...field} // {...field}
field={{ // field={{
...field.field, // ...field.field,
name: // name:
filter.modifiers[name] === "between" // filter.modifiers[name] === "between"
? name // ? name
: `${name}_from`, // : `${name}_from`,
}} // }}
prop={{ // prop={{
type: "input", // type: "input",
sub_type: "number", // sub_type: "number",
}} // }}
/> // />
{filter.modifiers[name] === "between" && ( // {filter.modifiers[name] === "between" && (
<FieldTypeInput // <FieldTypeInput
{...field} // {...field}
field={{ ...field.field, name: `${name}_to` }} // field={{ ...field.field, name: `${name}_to` }}
prop={{ // prop={{
type: "input", // type: "input",
sub_type: "number", // sub_type: "number",
}} // }}
/> // />
)} // )}
</> // </>
)} // )}
{type === "date" && ( // {type === "date" && (
<> // <>
<FieldTypeInput // <FieldTypeInput
{...field} // {...field}
field={{ // field={{
...field.field, // ...field.field,
name: // name:
filter.modifiers[name] === "between" // filter.modifiers[name] === "between"
? name // ? name
: `${name}_from`, // : `${name}_from`,
}} // }}
prop={{ // prop={{
type: "input", // type: "input",
sub_type: "date", // sub_type: "date",
}} // }}
/> // />
{filter.modifiers[name] === "between" && ( // {filter.modifiers[name] === "between" && (
<FieldTypeInput // <FieldTypeInput
{...field} // {...field}
field={{ ...field.field, name: `${name}_to` }} // field={{ ...field.field, name: `${name}_to` }}
prop={{ // prop={{
type: "input", // type: "input",
sub_type: "date", // sub_type: "date",
}} // }}
/> // />
)} // )}
</> // </>
)} // )}
{type === "boolean" && ( // {type === "boolean" && (
<FieldCheckbox // <FieldCheckbox
arg={field.arg} // arg={field.arg}
field={field.field} // field={field.field}
fm={field.fm} // fm={field.fm}
/> // />
)} // )}
{type === "options" && ( // {type === "options" && (
<> // <>
{singleOptions.includes(filter.modifiers[name]) && ( // {singleOptions.includes(filter.modifiers[name]) && (
<SingleOption {...field} /> // <SingleOption {...field} />
)} // )}
{multiOptions.includes(filter.modifiers[name]) && ( // {multiOptions.includes(filter.modifiers[name]) && (
<MultiOption {...field} /> // <MultiOption {...field} />
)} // )}
</> // </>
)} // )}
</> // </>
); // );
}} // }}
</BaseField> // </BaseField>
); // );
}; };

View File

@ -1,20 +1,19 @@
import { ReactNode } from "react"; import { FieldLoading } from "lib/comps/ui/field-loading";
import { FMLocal, FieldLocal, FieldProp } from "../typings";
import { Label } from "../field/Label"; import { Label } from "../field/Label";
import { FieldLoading } from "../../ui/field-loading"; import { FMLocal, FieldLocal, FieldProp } from "../typings";
import { useField } from "../utils/use-field";
import { ReactNode } from "react";
export const BaseField = (prop: { export const BaseField = (
field: FieldLocal; arg: FieldProp & {
fm: FMLocal; children?: (arg: { fm: FMLocal; field: FieldLocal }) => ReactNode;
arg: FieldProp; }
children: (arg: { ) => {
field: FieldLocal; const field = useField(arg);
fm: FMLocal; const fm = arg.fm;
arg: FieldProp; arg.fm.fields[arg.name] = field;
}) => ReactNode;
}) => { const w = field.width || "auto";
const { field, fm, arg } = prop;
const w = field.width;
const prefix = const prefix =
typeof field.prefix === "function" typeof field.prefix === "function"
? field.prefix() ? field.prefix()
@ -29,9 +28,7 @@ export const BaseField = (prop: {
: null; : null;
const name = field.name; const name = field.name;
const errors = fm.error.get(name); const errors = fm.error.get(name);
const showlabel = arg.show_label || "y"; const showlabel = arg.show_label || "y";
const disabled = const disabled =
typeof field.disabled === "function" ? field.disabled() : field.disabled; typeof field.disabled === "function" ? field.disabled() : field.disabled;
const show = const show =
@ -54,8 +51,8 @@ export const BaseField = (prop: {
css` css`
padding: 5px 0px 0px 10px; padding: 5px 0px 0px 10px;
`, `,
w === "auto" && fm.size.field === "full" && "c-w-full", w === "auto" && fm.size?.field === "full" && "c-w-full",
w === "auto" && fm.size.field === "half" && "c-w-1/2", w === "auto" && fm.size?.field === "half" && "c-w-1/2",
w === "full" && "c-w-full", w === "full" && "c-w-full",
w === "¾" && "c-w-3/4", w === "¾" && "c-w-3/4",
w === "½" && "c-w-1/2", w === "½" && "c-w-1/2",
@ -65,7 +62,6 @@ export const BaseField = (prop: {
css` css`
.field-outer { .field-outer {
border: 1px solid ${disabled ? "#ececeb" : "#cecece"}; border: 1px solid ${disabled ? "#ececeb" : "#cecece"};
&.focused { &.focused {
border: 1px solid #1c4ed8; border: 1px solid #1c4ed8;
outline: 1px solid #1c4ed8; outline: 1px solid #1c4ed8;
@ -95,7 +91,9 @@ export const BaseField = (prop: {
> >
<div <div
className={cx( className={cx(
!["toggle", "button", "radio", "checkbox"].includes(arg.sub_type) !["toggle", "button", "radio", "checkbox"].includes(
arg.sub_type || ""
)
? cx( ? cx(
"field-outer c-overflow-hidden c-flex-1 c-flex c-flex-row c-text-sm c-bg-white", "field-outer c-overflow-hidden c-flex-1 c-flex c-flex-row c-text-sm c-bg-white",
"c-rounded " "c-rounded "
@ -139,7 +137,8 @@ export const BaseField = (prop: {
field.disabled && "c-pointer-events-none" field.disabled && "c-pointer-events-none"
)} )}
> >
{prop.children(prop)} {typeof arg.children === "function" &&
arg.children({ fm, field })}
</div> </div>
)} )}
{suffix && suffix !== "" ? ( {suffix && suffix !== "" ? (
@ -153,14 +152,11 @@ export const BaseField = (prop: {
<></> <></>
)} )}
</div> </div>
{/* {JSON.stringify(errors)} */}
{errors.length > 0 && ( {errors.length > 0 && (
<div <div
className={cx( className={cx(
"field-error c-p-2 c-pl-0 c-text-xs c-text-red-600", "field-error c-p-2 c-pl-0 c-text-xs c-text-red-600",
field.desc && "c-pt-0", field.desc && "c-pt-0",
css` css`
padding-left: 0px !important; padding-left: 0px !important;
` `

View File

@ -1,215 +1,91 @@
import { useLocal } from "lib/utils/use-local"; import { ReactNode, useEffect, useRef, useState } from "react";
import { ReactNode, useCallback, useEffect, useRef } from "react"; import { createPortal } from "react-dom";
import { FieldLocal, FieldProp, FMLocal } from "../typings"; import { Toaster } from "sonner";
import { BaseFormLocal, default_base_form_local } from "./types";
import { FieldLoading } from "lib/comps/ui/field-loading";
import { editorFormWidth } from "../Form"; import { editorFormWidth } from "../Form";
import { ConsoleLogWriter } from "drizzle-orm"; import { FMLocal } from "../typings";
import { createFm } from "./utils/create-fm";
import { DivForm } from "./utils/DivForm";
export type BaseFormProps<T> = { export type BaseFormProps<T> = {
name: string;
data: T; data: T;
className?: string; className?: string;
on_submit?: (form: BaseFormLocal<T>) => Promise<any> | any; onField?: () => void;
children: ReactNode | ((form: BaseFormLocal<T>) => ReactNode); onSubmit?: (arg: { fm: FMLocal }) => Promise<boolean> | boolean;
render?: () => void; onChange?: (fm: FMLocal, name: string, new_value: any) => any;
on_change?: (fm: FMLocal, name: string, new_value: any) => any; children: ReactNode | ((arg: { fm: FMLocal }) => ReactNode);
is_form?: boolean; tag?: "form" | "div";
name: string;
}; };
export const BaseForm = <T extends Record<string, any>>(
props: BaseFormProps<T>
) => {
const { data, children, className, on_submit, render, on_change } = props;
const form = useLocal({
...default_base_form_local,
}) as BaseFormLocal<T>;
if (render) { export const BaseForm = <T extends Record<string, any>>({
form.render = render; tag,
} data,
children,
form.submit = useCallback(async () => { name,
if (form.status === "ready") { onSubmit,
form.status = "submitting"; className,
form.render(); }: BaseFormProps<T>) => {
const result = await on_submit?.(form); const render = useState({})[1];
const local = useRef({
setTimeout(() => { fm: null as null | FMLocal,
form.status = "ready";
form.render();
}, 1000);
return result;
}
}, [on_submit]);
form.createArg = (arg) => {
const prop: FieldProp = {
name: arg.name,
on_load: arg.onLoad,
sub_type: arg.subType,
} as any;
if (arg.onChange) prop.on_change = arg.onChange;
return prop;
};
form.createField = (arg) => {
if (form.fields[arg.name]) return form.fields[arg.name];
if (form.fm) form.fm.fields = form.fields;
const prop: FieldLocal = {
name: arg.name,
label: typeof arg.label !== "undefined" ? arg.label : arg.name,
render: arg.render || form.render,
width: "auto",
prefix: arg.prefix,
} as any;
form.fields[arg.name] = prop;
return prop;
};
form.createFm = useCallback(() => {
if (form.fm) {
form.fm.data = data;
return form.fm;
}
let size = "full";
if (form.internal.width > 650) {
size = "half";
}
form.fm = {
data: data,
status: "ready",
deps: {},
props: { label_mode: "vertical" },
error: {
get: () => {
return [];
},
},
events: {
on_change: (n: any, v: any) => {
if (on_change && form.fm) {
on_change(form.fm, n, v);
}
},
},
submit: () => on_submit?.(form),
size: { field: size },
render: form.render,
} as any;
return form.fm as any;
}, [data]);
form.fieldProps = (arg) => {
return {
fm: form.createFm(),
arg: form.createArg(arg),
field: form.createField(arg),
};
};
const ref = useRef({
el: null as null | HTMLFormElement, el: null as null | HTMLFormElement,
timer: null as any,
rob: new ResizeObserver(async ([e]) => { rob: new ResizeObserver(async ([e]) => {
let fm = form.fm; measureSize(name, fm, e.target as HTMLFormElement);
if (!fm) {
if (ref.current.timer) {
return;
}
ref.current.timer = await new Promise<void>((done) => {
const ival = setInterval(() => {
if (form.fm) {
ref.current.timer = null;
clearInterval(ival);
done();
}
}, 100);
});
fm = form.fm;
}
if (e.contentRect.width > 0 && fm) {
fm.size.height = e.contentRect.height;
fm.size.width = e.contentRect.width;
// if (fm.status === "ready" && !isEditor) fm.status = "resizing";
if (fm.props.layout === "auto" || !fm.props.layout) {
if (fm.size.width > 650) {
fm.size.field = "half";
} else {
fm.size.field = "full";
}
} else {
if (fm.props.layout === "1-col") fm.size.field = "full";
if (fm.props.layout === "2-col") fm.size.field = "half";
}
if (isEditor) {
editorFormWidth[props.name] = {
w: fm.size.width,
f: fm.size.field,
};
}
fm.status = "ready";
fm.render();
}
}), }),
}); }).current;
if (!local.fm) {
local.fm = createFm({
data,
onSubmit,
render() {
render({});
},
});
}
useEffect(() => { useEffect(() => {
if (form.internal.width === 0) { if (local.fm && local.fm.data !== data) {
setTimeout(() => { for (const k of Object.keys(local.fm.data)) {
form.render(); delete local.fm.data[k];
}, 1000); }
for (const [k, v] of Object.entries(data)) {
local.fm.data[k] = v;
}
console.log(local.fm.data, data)
local.fm.render();
} }
}, [data]); }, [data]);
if (form.status === "init") { const fm = local.fm;
form.status = "ready";
}
if (!form.fm) { let child = null;
form.fm = form.createFm(); if (children) {
} if (typeof children === "function") {
const fm = form.fm; child = children({ fm: local.fm });
} else {
if (typeof props.is_form === "boolean") { child = children;
if (!props.is_form) {
if (!form.fm.data) return <FieldLoading />;
return <>{typeof children === "function" ? children(form) : children}</>;
} }
} }
if (form.internal.width === 0) { if (document.getElementsByClassName("prasi-toaster").length === 0) {
if (form.internal.init_render > 30) { const elemDiv = document.createElement("div");
return <>Failed to render BaseForm</>; elemDiv.className = "prasi-toaster";
} document.body.appendChild(elemDiv);
setTimeout(() => {
form.internal.init_render++;
form.render();
}, 50);
} }
const toaster_el = document.getElementsByClassName("prasi-toaster")[0];
return ( return (
<form <DivForm
tag={tag || "form"}
onSubmit={(e) => { onSubmit={(e) => {
e.preventDefault(); e.preventDefault();
e.stopPropagation();
form.submit();
}} }}
ref={(el) => { ref={(el) => {
if (el) { if (el) {
if (!ref.current.el && fm && fm?.status !== "resizing") { if (!local.el && fm) {
ref.current.el = el; local.el = el;
ref.current.rob.observe(el); measureSize(name, fm, el);
if (fm.status === "ready") { local.rob.observe(el);
fm.status = "resizing";
fm.render();
}
} }
} }
}} }}
@ -218,28 +94,50 @@ export const BaseForm = <T extends Record<string, any>>(
className className
)} )}
> >
{fm?.status === "ready" && ( {toaster_el && createPortal(<Toaster cn={cx} />, toaster_el)}
<>
<div <div
className={cx( className={cx(
"form-inner c-flex-1 c-flex c-flex-row c-flex-wrap c-items-start c-content-start c-absolute c-inset-0", "form-inner c-flex-1 c-flex c-flex-row c-flex-wrap c-items-start c-content-start c-absolute c-inset-0",
css` css`
padding-right: 10px; padding-right: 10px;
` `
)} )}
ref={(el) => { >
if (el?.offsetWidth) { {fm.size && fm.size.width > 0 && <>{child}</>}
form.internal.width = el?.offsetWidth; </div>
form.createFm(); </DivForm>
}
}}
>
{form.internal.width > 0 && (
<>{typeof children === "function" ? children(form) : children}</>
)}
</div>
</>
)}
</form>
); );
}; };
const measureSize = (name: string, fm: FMLocal, e: HTMLFormElement) => {
if (!fm.size) {
fm.size = { field: "full", width: 0, height: 0 };
}
if (e.clientWidth > 0 && fm && fm.size) {
fm.size.height = e.clientHeight;
fm.size.width = e.clientWidth;
if (fm.props?.layout === "auto" || !fm.props?.layout) {
if (fm.size.width > 650) {
fm.size.field = "half";
} else {
fm.size.field = "full";
}
} else {
if (fm.props.layout === "1-col") fm.size.field = "full";
if (fm.props.layout === "2-col") fm.size.field = "half";
}
if (isEditor) {
editorFormWidth[name] = {
w: fm.size.width,
f: fm.size.field,
};
}
fm.status = "ready";
fm.render();
}
};

View File

@ -34,4 +34,5 @@ export type BaseFormLocal<T> = Omit<typeof default_base_form_local, "data"> & {
}; };
submit: () => Promise<any> | any; submit: () => Promise<any> | any;
render: () => void; render: () => void;
init: boolean;
}; };

View File

@ -0,0 +1,24 @@
import { forwardRef } from "react";
export const DivForm = forwardRef<
HTMLFormElement,
React.DetailedHTMLProps<
React.HTMLAttributes<HTMLFormElement>,
HTMLFormElement
> & { tag: "form" | "div" }
>((arg, ref) => {
if (arg.tag === "div") {
const props = { ...arg } as any;
if (props.onSubmit) delete props.onSubmit;
return (
<div {...props} ref={ref}>
{arg.children}
</div>
);
}
return (
<form {...arg} ref={ref}>
{arg?.children}
</form>
);
});

View File

@ -0,0 +1,24 @@
import { FMLocal } from "lib/exports";
import { formError } from "../../utils/error";
export const createFm = (arg: {
data: any;
onSubmit?: (arg: { fm: FMLocal }) => Promise<boolean> | boolean;
render: () => void;
}) => {
const fm = {
data: arg.data,
fields: {},
render: arg.render,
} as FMLocal;
fm.submit = async () => {
if (arg.onSubmit) {
await arg.onSubmit({ fm });
}
return true;
};
fm.error = formError(fm);
return fm;
};

View File

@ -134,7 +134,7 @@ export const FieldInput: FC<{
return ( return (
<div <div
className={cx( className={cx(
!["toggle", "button", "radio", "checkbox"].includes(arg.sub_type) !["toggle", "button", "radio", "checkbox"].includes(arg.sub_type || "")
? cx( ? cx(
"field-outer c-overflow-hidden c-flex-1 c-flex c-flex-row c-text-sm c-bg-white", "field-outer c-overflow-hidden c-flex-1 c-flex c-flex-row c-text-sm c-bg-white",
"c-rounded " "c-rounded "
@ -182,7 +182,7 @@ export const FieldInput: FC<{
field.focused && "focused", field.focused && "focused",
arg.sub_type !== "upload" && disabled && "c-pointer-events-none", arg.sub_type !== "upload" && disabled && "c-pointer-events-none",
disabled && disabled &&
!["checkbox"].includes(arg.sub_type) && !["checkbox"].includes(arg.sub_type || '') &&
" c-bg-gray-50" " c-bg-gray-50"
)} )}
> >

View File

@ -17,8 +17,8 @@ export const Label: FC<{ field: FieldLocal; fm: FMLocal; arg: FieldProp }> = ({
typeof arg.required === "string" typeof arg.required === "string"
? arg.required === "y" ? arg.required === "y"
: typeof arg.required === "function" : typeof arg.required === "function"
? arg.required() ? arg.required()
: false; : false;
if (typeof required === "boolean" && field.required !== required) { if (typeof required === "boolean" && field.required !== required) {
field.required = required; field.required = required;
field.render(); field.render();
@ -28,9 +28,9 @@ export const Label: FC<{ field: FieldLocal; fm: FMLocal; arg: FieldProp }> = ({
className={cx( className={cx(
"label c-text-sm c-flex c-items-center", "label c-text-sm c-flex c-items-center",
"c-mt-3 c-w-full c-justify-between" "c-mt-3 c-w-full c-justify-between"
)} )}
> >
<div> <div className="c-flex c-items-center">
<span className={cx(errors.length > 0 && `c-text-red-600`)}> <span className={cx(errors.length > 0 && `c-text-red-600`)}>
{field.label} {field.label}
</span> </span>

View File

@ -46,7 +46,7 @@ type FieldType =
export type FieldProp = { export type FieldProp = {
name: string; name: string;
label: string; label: string;
label_action: ReactNode; label_action?: ReactNode;
desc?: string; desc?: string;
props?: any; props?: any;
mask?: string; mask?: string;
@ -57,7 +57,7 @@ export type FieldProp = {
cover_text: string; cover_text: string;
cover_field: string; cover_field: string;
}; };
link: { link?: {
text: text:
| string | string
| ((arg: { | ((arg: {
@ -68,60 +68,60 @@ export type FieldProp = {
params: (field: FieldLocal) => { where: any } | Promise<{ where: any }>; params: (field: FieldLocal) => { where: any } | Promise<{ where: any }>;
}; };
fm: FMLocal; fm: FMLocal;
type: FieldType | (() => FieldType); type?: FieldType | (() => FieldType);
required: ("y" | "n") | (() => "y" | "n"); required?: ("y" | "n") | (() => "y" | "n");
field_ref?: (ref: any) => void; field_ref?: (ref: any) => void;
required_msg: (name: string) => string | ReactElement; required_msg?: (name: string) => string | ReactElement;
on_change: (arg: { on_change?: (arg: {
value: any; value: any;
name: string; name: string;
fm: any; fm: any;
}) => void | Promise<void>; }) => void | Promise<void>;
PassProp: any; PassProp?: any;
disabled: ("y" | "n") | (() => true | false); disabled?: ("y" | "n") | (() => true | false);
child: any; child?: any;
selection: "single" | "multi"; selection?: "single" | "multi";
prefix: any; prefix?: any;
suffix: any; suffix?: any;
width: "auto" | "full" | "¾" | "½" | "⅓" | "¼"; width?: "auto" | "full" | "¾" | "½" | "⅓" | "¼";
_item: PrasiItem; _item?: PrasiItem;
__props: any; __props?: any;
custom?: () => CustomField; custom?: () => CustomField;
on_load: ( on_load?: (
arg?: any arg?: any
) => ) =>
| { value: string; label: string }[] | { value: string; label: string }[]
| Promise<{ value: string; label: string }[]>; | Promise<{ value: string; label: string }[]>;
opt_get_label: ( opt_get_label?: (
row: any, row: any,
mode: "list" | "label", mode: "list" | "label",
opt?: { next?: any; prev?: any } opt?: { next?: any; prev?: any }
) => string; ) => string;
opt_get_value: (arg: { opt_get_value?: (arg: {
options: { label: string; value: string; item?: string }[]; options: { label: string; value: string; item?: string }[];
fm: FMLocal; fm: FMLocal;
name: string; name: string;
type: string; type: string;
}) => any; }) => any;
tbl_show_header?: "y" | "n"; tbl_show_header?: "y" | "n";
opt_set_value: (arg: { opt_set_value?: (arg: {
selected: string[]; selected: string[];
options: { label: string; value: string; item?: string }[]; options: { label: string; value: string; item?: string }[];
fm: FMLocal; fm: FMLocal;
name: string; name: string;
type: string; type: string;
}) => any; }) => any;
opt_selected: (arg: { opt_selected?: (arg: {
item: { value: string; label: string; item?: any }; item: { value: string; label: string; item?: any };
current: any; current: any;
options: { value: string; label: string; item?: any }[]; options: { value: string; label: string; item?: any }[];
}) => boolean; }) => boolean;
on_init: (arg: { field: any; name: string }) => void; on_init?: (arg: { field: any; name: string }) => void;
pk: string; pk?: string;
sub_type: string; sub_type?: string;
placeholder: string; placeholder?: string;
show_label: boolean | "y" | "n"; show_label?: boolean | "y" | "n";
msg_error: string; msg_error?: string;
gen_table?: string; gen_table?: string;
gen_fields?: string; gen_fields?: string;
model_upload?: "upload" | "import"; model_upload?: "upload" | "import";
@ -154,8 +154,8 @@ export type FMInternal = {
internal: { internal: {
original_render?: () => void; original_render?: () => void;
}; };
props: Exclude<FMProps, "body" | "PassProp">; props?: Exclude<FMProps, "body" | "PassProp">;
size: { size?: {
width: number; width: number;
height: number; height: number;
field: "full" | "half"; field: "full" | "half";

View File

@ -21,9 +21,11 @@ export const useField = (
const name = typeof arg.name === "string" ? arg.name : arg.name(); const name = typeof arg.name === "string" ? arg.name : arg.name();
const label = typeof arg.label === "string" ? arg.label : arg.label(); const label = typeof arg.label === "string" ? arg.label : arg.label();
const required = const required =
typeof arg.required === "string" ? arg.required === "y" : arg.required(); (typeof arg.required === "string"
? arg.required === "y"
: arg.required?.()) || "n";
const update_field = { const update_field = {
name: name.replace(/\s*/gi, ""), name: name.replace(/\s*/gi, ""),
label: label, label: label,
@ -43,9 +45,8 @@ export const useField = (
min_date: arg.min_date, min_date: arg.min_date,
table_fields: [], table_fields: [],
disabled_search: arg.disabled_search, disabled_search: arg.disabled_search,
hidden: arg.show hidden: arg.show,
}; };
if (field.status === "init" || isEditor) { if (field.status === "init" || isEditor) {
for (const [k, v] of Object.entries(update_field)) { for (const [k, v] of Object.entries(update_field)) {
@ -67,7 +68,5 @@ export const useField = (
field.prop = arg as any; field.prop = arg as any;
if(field.name === "complete_description") console.log(update_field);
if(field.name === "complete_description") console.log(field);
return field; return field;
}; };