From 0338bac9d04b2677e283771fce54566f256b9c82 Mon Sep 17 00:00:00 2001 From: Rizky Date: Sat, 23 Nov 2024 21:05:46 +0700 Subject: [PATCH] fix lib --- comps/.DS_Store | Bin 0 -> 6148 bytes comps/custom/Breadcrumb.tsx | 6 +- comps/filter/FilterContent.tsx | 2 +- comps/filter/FilterField.tsx | 317 ++++++++++++++-------------- comps/form/base/BaseField.tsx | 46 ++-- comps/form/base/BaseForm.tsx | 316 ++++++++++----------------- comps/form/base/types.ts | 1 + comps/form/base/utils/DivForm.tsx | 24 +++ comps/form/base/utils/create-fm.tsx | 24 +++ comps/form/field/FieldInput.tsx | 4 +- comps/form/field/Label.tsx | 8 +- comps/form/typings.ts | 56 ++--- comps/form/utils/use-field.tsx | 11 +- 13 files changed, 380 insertions(+), 435 deletions(-) create mode 100755 comps/.DS_Store create mode 100755 comps/form/base/utils/DivForm.tsx create mode 100755 comps/form/base/utils/create-fm.tsx diff --git a/comps/.DS_Store b/comps/.DS_Store new file mode 100755 index 0000000000000000000000000000000000000000..f9485af88a1e7a5f87225326bfe713effb697d28 GIT binary patch literal 6148 zcmeH~F$w}f3`G;&La^D=avBfd4F=H@>;)`D8(l%rdXDZ-CJ2t!BJu;tpJXO1`-+{7 zi0JyZUy1Z0GJ~7S(n4d3ypw}m4M3`}dgXhH(h?7~0-B+w9;*1Wg-e+&OK|2Hj6Nq_|Y zjDU8VVY9|d#ohY$dRE^>)z$?L_2URHKLJSWDqg_du%B!J&7q|#Dlq;CI0gn1_$q-1 D^rsTX literal 0 HcmV?d00001 diff --git a/comps/custom/Breadcrumb.tsx b/comps/custom/Breadcrumb.tsx index e1640a5..e3aa3ab 100755 --- a/comps/custom/Breadcrumb.tsx +++ b/comps/custom/Breadcrumb.tsx @@ -78,16 +78,18 @@ export const Breadcrumb: FC = ({ value, className }) => { {cur?.label} ) : ( -

{ + ev.preventDefault(); if (isEditor) return; if (cur.url) navigate(cur.url || ""); if (cur.onClick) cur.onClick(ev); }} > {cur?.label} -

+ )} {index !== lastIndex && ( diff --git a/comps/filter/FilterContent.tsx b/comps/filter/FilterContent.tsx index 95b3405..663e48d 100755 --- a/comps/filter/FilterContent.tsx +++ b/comps/filter/FilterContent.tsx @@ -105,7 +105,7 @@ export const FilterContent: FC<{ > { + onSubmit={async (form) => { const fm = form.fm; try { if (typeof form.fm?.data === "object") { diff --git a/comps/filter/FilterField.tsx b/comps/filter/FilterField.tsx index 88868f2..8cc623b 100755 --- a/comps/filter/FilterField.tsx +++ b/comps/filter/FilterField.tsx @@ -35,163 +35,164 @@ export const FilterField: FC<{ }, [filter.form]); let show_modifier = filter.mode !== "inline"; - return ( - ( - { - filter.modifiers[name] = modifier; - filter.render(); - filter_window.prasiContext.render(); - }} - modifier={filter.modifiers[name]} - type={type} - /> - ) - : undefined, - onLoad() { - return [{ label: "halo", value: "asda" }]; - }, - subType: singleOptions.includes(filter.modifiers[name]) - ? "dropdown" - : "typeahead", - })} - > - {(field) => { - if (type === "search-all") { - return ( -
-
- - - - -
- { - // 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" - onChange={(e) => { - field.fm.data[name] = e.currentTarget.value; - field.fm.render(); - clearTimeout(internal.search_timeout); - internal.search_timeout = setTimeout(() => { - filter.form?.submit(); - }, 1500); - }} - /> -
- ); - } + return null; + // return ( + // ( + // { + // filter.modifiers[name] = modifier; + // filter.render(); + // filter_window.prasiContext.render(); + // }} + // modifier={filter.modifiers[name]} + // type={type} + // /> + // ) + // : undefined, + // onLoad() { + // return [{ label: "halo", value: "asda" }]; + // }, + // subType: singleOptions.includes(filter.modifiers[name]) + // ? "dropdown" + // : "typeahead", + // })} + // > + // {(field) => { + // if (type === "search-all") { + // return ( + //
+ //
+ // + // + // + // + //
+ // { + // // 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" + // onChange={(e) => { + // field.fm.data[name] = e.currentTarget.value; + // field.fm.render(); + // clearTimeout(internal.search_timeout); + // internal.search_timeout = setTimeout(() => { + // filter.form?.submit(); + // }, 1500); + // }} + // /> + //
+ // ); + // } - return ( - <> - {type === "text" && ( - - )} - {type === "number" && ( - <> - - {filter.modifiers[name] === "between" && ( - - )} - - )} - {type === "date" && ( - <> - - {filter.modifiers[name] === "between" && ( - - )} - - )} - {type === "boolean" && ( - - )} - {type === "options" && ( - <> - {singleOptions.includes(filter.modifiers[name]) && ( - - )} - {multiOptions.includes(filter.modifiers[name]) && ( - - )} - - )} - - ); - }} -
- ); + // return ( + // <> + // {type === "text" && ( + // + // )} + // {type === "number" && ( + // <> + // + // {filter.modifiers[name] === "between" && ( + // + // )} + // + // )} + // {type === "date" && ( + // <> + // + // {filter.modifiers[name] === "between" && ( + // + // )} + // + // )} + // {type === "boolean" && ( + // + // )} + // {type === "options" && ( + // <> + // {singleOptions.includes(filter.modifiers[name]) && ( + // + // )} + // {multiOptions.includes(filter.modifiers[name]) && ( + // + // )} + // + // )} + // + // ); + // }} + //
+ // ); }; diff --git a/comps/form/base/BaseField.tsx b/comps/form/base/BaseField.tsx index 58623ed..76a57ef 100755 --- a/comps/form/base/BaseField.tsx +++ b/comps/form/base/BaseField.tsx @@ -1,20 +1,19 @@ -import { ReactNode } from "react"; -import { FMLocal, FieldLocal, FieldProp } from "../typings"; +import { FieldLoading } from "lib/comps/ui/field-loading"; 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: { - field: FieldLocal; - fm: FMLocal; - arg: FieldProp; - children: (arg: { - field: FieldLocal; - fm: FMLocal; - arg: FieldProp; - }) => ReactNode; -}) => { - const { field, fm, arg } = prop; - const w = field.width; +export const BaseField = ( + arg: FieldProp & { + children?: (arg: { fm: FMLocal; field: FieldLocal }) => ReactNode; + } +) => { + const field = useField(arg); + const fm = arg.fm; + arg.fm.fields[arg.name] = field; + + const w = field.width || "auto"; const prefix = typeof field.prefix === "function" ? field.prefix() @@ -29,9 +28,7 @@ export const BaseField = (prop: { : null; const name = field.name; const errors = fm.error.get(name); - const showlabel = arg.show_label || "y"; - const disabled = typeof field.disabled === "function" ? field.disabled() : field.disabled; const show = @@ -54,8 +51,8 @@ export const BaseField = (prop: { css` padding: 5px 0px 0px 10px; `, - w === "auto" && fm.size.field === "full" && "c-w-full", - w === "auto" && fm.size.field === "half" && "c-w-1/2", + w === "auto" && fm.size?.field === "full" && "c-w-full", + w === "auto" && fm.size?.field === "half" && "c-w-1/2", w === "full" && "c-w-full", w === "¾" && "c-w-3/4", w === "½" && "c-w-1/2", @@ -65,7 +62,6 @@ export const BaseField = (prop: { css` .field-outer { border: 1px solid ${disabled ? "#ececeb" : "#cecece"}; - &.focused { border: 1px solid #1c4ed8; outline: 1px solid #1c4ed8; @@ -95,7 +91,9 @@ export const BaseField = (prop: { >
- {prop.children(prop)} + {typeof arg.children === "function" && + arg.children({ fm, field })}
)} {suffix && suffix !== "" ? ( @@ -153,14 +152,11 @@ export const BaseField = (prop: { <> )} - - {/* {JSON.stringify(errors)} */} {errors.length > 0 && (
= { + name: string; data: T; className?: string; - on_submit?: (form: BaseFormLocal) => Promise | any; - children: ReactNode | ((form: BaseFormLocal) => ReactNode); - render?: () => void; - on_change?: (fm: FMLocal, name: string, new_value: any) => any; - is_form?: boolean; - name: string; + onField?: () => void; + onSubmit?: (arg: { fm: FMLocal }) => Promise | boolean; + onChange?: (fm: FMLocal, name: string, new_value: any) => any; + children: ReactNode | ((arg: { fm: FMLocal }) => ReactNode); + tag?: "form" | "div"; }; -export const BaseForm = >( - props: BaseFormProps -) => { - const { data, children, className, on_submit, render, on_change } = props; - const form = useLocal({ - ...default_base_form_local, - }) as BaseFormLocal; - if (render) { - form.render = render; - } - - form.submit = useCallback(async () => { - if (form.status === "ready") { - form.status = "submitting"; - form.render(); - const result = await on_submit?.(form); - - setTimeout(() => { - 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({ +export const BaseForm = >({ + tag, + data, + children, + name, + onSubmit, + className, +}: BaseFormProps) => { + const render = useState({})[1]; + const local = useRef({ + fm: null as null | FMLocal, el: null as null | HTMLFormElement, - timer: null as any, rob: new ResizeObserver(async ([e]) => { - let fm = form.fm; - if (!fm) { - if (ref.current.timer) { - return; - } - ref.current.timer = await new Promise((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(); - } + measureSize(name, fm, e.target as HTMLFormElement); }), - }); + }).current; + if (!local.fm) { + local.fm = createFm({ + data, + onSubmit, + render() { + render({}); + }, + }); + } useEffect(() => { - if (form.internal.width === 0) { - setTimeout(() => { - form.render(); - }, 1000); + if (local.fm && local.fm.data !== data) { + for (const k of Object.keys(local.fm.data)) { + delete local.fm.data[k]; + } + for (const [k, v] of Object.entries(data)) { + local.fm.data[k] = v; + } + console.log(local.fm.data, data) + local.fm.render(); } }, [data]); - if (form.status === "init") { - form.status = "ready"; - } + const fm = local.fm; - if (!form.fm) { - form.fm = form.createFm(); - } - const fm = form.fm; - - if (typeof props.is_form === "boolean") { - if (!props.is_form) { - if (!form.fm.data) return ; - return <>{typeof children === "function" ? children(form) : children}; + let child = null; + if (children) { + if (typeof children === "function") { + child = children({ fm: local.fm }); + } else { + child = children; } } - if (form.internal.width === 0) { - if (form.internal.init_render > 30) { - return <>Failed to render BaseForm; - } - setTimeout(() => { - form.internal.init_render++; - form.render(); - }, 50); + if (document.getElementsByClassName("prasi-toaster").length === 0) { + const elemDiv = document.createElement("div"); + elemDiv.className = "prasi-toaster"; + document.body.appendChild(elemDiv); } + const toaster_el = document.getElementsByClassName("prasi-toaster")[0]; + return ( -
{ e.preventDefault(); - e.stopPropagation(); - form.submit(); }} ref={(el) => { if (el) { - if (!ref.current.el && fm && fm?.status !== "resizing") { - ref.current.el = el; - ref.current.rob.observe(el); - if (fm.status === "ready") { - fm.status = "resizing"; - fm.render(); - } + if (!local.el && fm) { + local.el = el; + measureSize(name, fm, el); + local.rob.observe(el); } } }} @@ -218,28 +94,50 @@ export const BaseForm = >( className )} > - {fm?.status === "ready" && ( - <> -
{ - if (el?.offsetWidth) { - form.internal.width = el?.offsetWidth; - form.createFm(); - } - }} - > - {form.internal.width > 0 && ( - <>{typeof children === "function" ? children(form) : children} - )} -
- - )} - + {toaster_el && createPortal(, toaster_el)} + +
+ {fm.size && fm.size.width > 0 && <>{child}} +
+ ); }; + +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(); + } +}; diff --git a/comps/form/base/types.ts b/comps/form/base/types.ts index f212e87..2d24cf9 100755 --- a/comps/form/base/types.ts +++ b/comps/form/base/types.ts @@ -34,4 +34,5 @@ export type BaseFormLocal = Omit & { }; submit: () => Promise | any; render: () => void; + init: boolean; }; diff --git a/comps/form/base/utils/DivForm.tsx b/comps/form/base/utils/DivForm.tsx new file mode 100755 index 0000000..a96c06a --- /dev/null +++ b/comps/form/base/utils/DivForm.tsx @@ -0,0 +1,24 @@ +import { forwardRef } from "react"; + +export const DivForm = forwardRef< + HTMLFormElement, + React.DetailedHTMLProps< + React.HTMLAttributes, + HTMLFormElement + > & { tag: "form" | "div" } +>((arg, ref) => { + if (arg.tag === "div") { + const props = { ...arg } as any; + if (props.onSubmit) delete props.onSubmit; + return ( +
+ {arg.children} +
+ ); + } + return ( +
+ {arg?.children} +
+ ); +}); diff --git a/comps/form/base/utils/create-fm.tsx b/comps/form/base/utils/create-fm.tsx new file mode 100755 index 0000000..50e3ca4 --- /dev/null +++ b/comps/form/base/utils/create-fm.tsx @@ -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; + 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; +}; diff --git a/comps/form/field/FieldInput.tsx b/comps/form/field/FieldInput.tsx index c55e8af..3184a41 100755 --- a/comps/form/field/FieldInput.tsx +++ b/comps/form/field/FieldInput.tsx @@ -134,7 +134,7 @@ export const FieldInput: FC<{ return (
diff --git a/comps/form/field/Label.tsx b/comps/form/field/Label.tsx index 69048c2..42f17cd 100755 --- a/comps/form/field/Label.tsx +++ b/comps/form/field/Label.tsx @@ -17,8 +17,8 @@ export const Label: FC<{ field: FieldLocal; fm: FMLocal; arg: FieldProp }> = ({ typeof arg.required === "string" ? arg.required === "y" : typeof arg.required === "function" - ? arg.required() - : false; + ? arg.required() + : false; if (typeof required === "boolean" && field.required !== required) { field.required = required; field.render(); @@ -28,9 +28,9 @@ export const Label: FC<{ field: FieldLocal; fm: FMLocal; arg: FieldProp }> = ({ className={cx( "label c-text-sm c-flex c-items-center", "c-mt-3 c-w-full c-justify-between" - )} + )} > -
+
0 && `c-text-red-600`)}> {field.label} diff --git a/comps/form/typings.ts b/comps/form/typings.ts index db4087d..379a11f 100755 --- a/comps/form/typings.ts +++ b/comps/form/typings.ts @@ -46,7 +46,7 @@ type FieldType = export type FieldProp = { name: string; label: string; - label_action: ReactNode; + label_action?: ReactNode; desc?: string; props?: any; mask?: string; @@ -57,7 +57,7 @@ export type FieldProp = { cover_text: string; cover_field: string; }; - link: { + link?: { text: | string | ((arg: { @@ -68,60 +68,60 @@ export type FieldProp = { params: (field: FieldLocal) => { where: any } | Promise<{ where: any }>; }; fm: FMLocal; - type: FieldType | (() => FieldType); - required: ("y" | "n") | (() => "y" | "n"); + type?: FieldType | (() => FieldType); + required?: ("y" | "n") | (() => "y" | "n"); field_ref?: (ref: any) => void; - required_msg: (name: string) => string | ReactElement; - on_change: (arg: { + required_msg?: (name: string) => string | ReactElement; + on_change?: (arg: { value: any; name: string; fm: any; }) => void | Promise; - PassProp: any; - disabled: ("y" | "n") | (() => true | false); - child: any; - selection: "single" | "multi"; - prefix: any; - suffix: any; - width: "auto" | "full" | "¾" | "½" | "⅓" | "¼"; - _item: PrasiItem; - __props: any; + PassProp?: any; + disabled?: ("y" | "n") | (() => true | false); + child?: any; + selection?: "single" | "multi"; + prefix?: any; + suffix?: any; + width?: "auto" | "full" | "¾" | "½" | "⅓" | "¼"; + _item?: PrasiItem; + __props?: any; custom?: () => CustomField; - on_load: ( + on_load?: ( arg?: any ) => | { value: string; label: string }[] | Promise<{ value: string; label: string }[]>; - opt_get_label: ( + opt_get_label?: ( row: any, mode: "list" | "label", opt?: { next?: any; prev?: any } ) => string; - opt_get_value: (arg: { + opt_get_value?: (arg: { options: { label: string; value: string; item?: string }[]; fm: FMLocal; name: string; type: string; }) => any; tbl_show_header?: "y" | "n"; - opt_set_value: (arg: { + opt_set_value?: (arg: { selected: string[]; options: { label: string; value: string; item?: string }[]; fm: FMLocal; name: string; type: string; }) => any; - opt_selected: (arg: { + opt_selected?: (arg: { item: { value: string; label: string; item?: any }; current: any; options: { value: string; label: string; item?: any }[]; }) => boolean; - on_init: (arg: { field: any; name: string }) => void; - pk: string; - sub_type: string; - placeholder: string; - show_label: boolean | "y" | "n"; - msg_error: string; + on_init?: (arg: { field: any; name: string }) => void; + pk?: string; + sub_type?: string; + placeholder?: string; + show_label?: boolean | "y" | "n"; + msg_error?: string; gen_table?: string; gen_fields?: string; model_upload?: "upload" | "import"; @@ -154,8 +154,8 @@ export type FMInternal = { internal: { original_render?: () => void; }; - props: Exclude; - size: { + props?: Exclude; + size?: { width: number; height: number; field: "full" | "half"; diff --git a/comps/form/utils/use-field.tsx b/comps/form/utils/use-field.tsx index 2997f86..3ba511d 100755 --- a/comps/form/utils/use-field.tsx +++ b/comps/form/utils/use-field.tsx @@ -21,9 +21,11 @@ export const useField = ( const name = typeof arg.name === "string" ? arg.name : arg.name(); const label = typeof arg.label === "string" ? arg.label : arg.label(); - + const required = - typeof arg.required === "string" ? arg.required === "y" : arg.required(); + (typeof arg.required === "string" + ? arg.required === "y" + : arg.required?.()) || "n"; const update_field = { name: name.replace(/\s*/gi, ""), label: label, @@ -43,9 +45,8 @@ export const useField = ( min_date: arg.min_date, table_fields: [], disabled_search: arg.disabled_search, - hidden: arg.show + hidden: arg.show, }; - if (field.status === "init" || isEditor) { for (const [k, v] of Object.entries(update_field)) { @@ -67,7 +68,5 @@ export const useField = ( field.prop = arg as any; - if(field.name === "complete_description") console.log(update_field); - if(field.name === "complete_description") console.log(field); return field; };