diff --git a/comps/filter/FilterField.tsx b/comps/filter/FilterField.tsx index d0d0e2a..0d2d2a6 100755 --- a/comps/filter/FilterField.tsx +++ b/comps/filter/FilterField.tsx @@ -13,8 +13,8 @@ export const FilterField: FC<{ name?: string; label?: string; type: FilterFieldType; - modifiers?: any[] -}> = ({ filter, name, label, type,modifiers }) => { + modifiers?: any[]; +}> = ({ filter, name, label, type, modifiers }) => { const internal = useLocal({ render_timeout: null as any }); if (!name) return <>No Name; if (!filter.form) return
Loading...
; @@ -85,7 +85,7 @@ export const FilterField: FC<{ prop={{ type: "input", sub_type: "search", - placeholder: "Search...", + placeholder: field.field.label, onBlur(e) { filter.form?.submit(); }, diff --git a/comps/form/field/Field.tsx b/comps/form/field/Field.tsx index b169d84..2f04ed4 100755 --- a/comps/form/field/Field.tsx +++ b/comps/form/field/Field.tsx @@ -71,6 +71,8 @@ export const Field: FC = (arg) => { {...props} className={cx( "field", + field.type, + sub_type, "c-flex c-relative", editorClassName, field.type === "single-option" && sub_type === "checkbox" diff --git a/comps/form/field/type/FilePreview.tsx b/comps/form/field/type/FilePreview.tsx index 1411c69..af168d8 100755 --- a/comps/form/field/type/FilePreview.tsx +++ b/comps/form/field/type/FilePreview.tsx @@ -290,3 +290,47 @@ const getFileName = (url: string) => { const extension = fileName.substring(dotIndex + 1); return { name, extension, fullname }; }; + +export const ImgThumb = ({ + className, + url, + w, + h, +}: { + className?: string; + url: string; + w: number; + h: number; +}) => { + const local = useLocal({ error: false }); + return ( +
+ {!local.error && ( + { + local.error = true; + local.render(); + }} + src={siteurl( + `/_img/${url.substring("_file/".length)}?w=${w}&h=${h}&fit=cover` + )} + /> + )} +
+ ); +}; diff --git a/comps/form/field/type/TypeUploadMulti.tsx b/comps/form/field/type/TypeUploadMulti.tsx index 1d10a5b..cd08eef 100755 --- a/comps/form/field/type/TypeUploadMulti.tsx +++ b/comps/form/field/type/TypeUploadMulti.tsx @@ -262,7 +262,7 @@ export const FieldUploadMulti: FC<{
= ({ collapsed: new Set(), cached_row: new WeakMap(), filtering: "" as ReactNode | string | true, + reload: (arg?: { toast: boolean }) => { + return new Promise((done) => { + let should_toast = true; + if (arg?.toast === false) should_toast = false; + local.should_toast = should_toast; + + local.filtering = ""; + if (typeof on_load === "function") { + local.status = "loading"; + local.render(); + + const orderBy = local.sort.orderBy || undefined; + const where = filterWhere(filter_name, __props); + + if (where?.OR?.length > 0) { + const key = Object.keys(where.OR[0])[0]; + if (key && where.OR[0][key]) { + let filtering = where.OR[0][key].contains; + if (typeof local.filtering === "string") { + filtering = filtering.slice(1, -1); + } else { + filtering = ""; + } + + if (filtering) { + local.filtering = ( +
+ Searching for:
"{filtering.trim()}"
+
+ ); + } + } + } + + if (md) { + const last = md.params.links[md.params.links.length - 1]; + + if (last && last.where) { + for (const [k, v] of Object.entries(last.where)) { + where[k] = v; + } + } + } + + call_prasi_events("tablelist", "where", [__props?.gen__table, where]); + + const load_args: any = { + async reload() {}, + orderBy, + where, + paging: { + take: local.paging.take > 0 ? local.paging.take : undefined, + skip: local.paging.skip, + }, + }; + if (id_parent) { + load_args.paging = {}; + } + const result = on_load({ ...load_args, mode: "query" }); + const callback = (data: any[]) => { + if (local.paging.skip === 0) { + local.data = data; + } else { + local.data = [...local.data, ...data]; + } + + local.status = "ready"; + local.render(); + done(); + }; + + if (result instanceof Promise) { + (async () => { + try { + callback(await result); + } catch (e) { + console.error(e); + local.status = "error"; + toast.dismiss(); + toast.error( +
+ + Failed to load data +
, + { + dismissible: true, + className: css` + background: #ffecec; + border: 2px solid red; + `, + } + ); + } + })(); + } else callback(result); + } + }); + }, sort: { columns: (ls_sort?.columns || []) as SortColumn[], on_change: (cols: SortColumn[]) => { @@ -255,104 +353,7 @@ export const TableList: FC = ({ }, }); - const reload = useCallback( - (arg?: { toast: boolean }) => { - let should_toast = true; - if (arg?.toast === false) should_toast = false; - local.should_toast = should_toast; - - local.filtering = ""; - if (typeof on_load === "function") { - local.status = "loading"; - local.render(); - - const orderBy = local.sort.orderBy || undefined; - const where = filterWhere(filter_name, __props); - - if (where?.OR?.length > 0) { - const key = Object.keys(where.OR[0])[0]; - if (key && where.OR[0][key]) { - let filtering = where.OR[0][key].contains; - if (typeof local.filtering === "string") { - filtering = filtering.slice(1, -1); - } else { - filtering = ""; - } - - if (filtering) { - local.filtering = ( -
- Searching for:
"{filtering.trim()}"
-
- ); - } - } - } - - if (md) { - const last = md.params.links[md.params.links.length - 1]; - if (last && last.where) { - for (const [k, v] of Object.entries(last.where)) { - where[k] = v; - } - } - } - - call_prasi_events("tablelist", "where", [__props?.gen__table, where]); - - const load_args: any = { - async reload() {}, - orderBy, - where, - paging: { - take: local.paging.take > 0 ? local.paging.take : undefined, - skip: local.paging.skip, - }, - }; - if (id_parent) { - load_args.paging = {}; - } - const result = on_load({ ...load_args, mode: "query" }); - const callback = (data: any[]) => { - if (local.paging.skip === 0) { - local.data = data; - } else { - local.data = [...local.data, ...data]; - } - - local.status = "ready"; - local.render(); - }; - - if (result instanceof Promise) { - (async () => { - try { - callback(await result); - } catch (e) { - console.error(e); - local.status = "error"; - toast.dismiss(); - toast.error( -
- - Failed to load data -
, - { - dismissible: true, - className: css` - background: #ffecec; - border: 2px solid red; - `, - } - ); - } - })(); - } else callback(result); - } - }, - [on_load, local.sort.orderBy, local.paging.take, local.paging.skip] - ); - + const reload = local.reload; if (md) { md.master.reload = reload; } @@ -366,6 +367,7 @@ export const TableList: FC = ({ // code ini digunakan untuk mengambil nama dari pk yang akan digunakan sebagai key untuk id const pk = local.pk?.name || "id"; + useEffect(() => { if (isEditor || value) { on_init(local); @@ -373,7 +375,10 @@ export const TableList: FC = ({ } (async () => { on_init(local); - if (local.status === "reload" && typeof on_load === "function") { + if ( + (local.status === "init" || local.status === "reload") && + typeof on_load === "function" + ) { reload(); } })(); @@ -703,35 +708,24 @@ export const TableList: FC = ({ if (columns.length > 1) columns = columns.slice(0, 0 + 1); } + if (local.status === "loading") { + toast.dismiss(); + toast.loading( + <> + + Loading Data ... + + ); + } else { + toast.dismiss(); + } + if (local.status === "resizing" && !isEditor) { local.status = "ready"; local.render(); return null; } - if (!isEditor) { - if (local.status === "loading") { - if (local.should_toast) { - toast.dismiss(); - toast.loading( - <> - - Loading {local.paging.skip === 0 ? "Data" : "more rows"} ... - , - { - dismissible: true, - } - ); - } else { - local.should_toast = true; - } - } else { - if (local.status !== "error") { - toast.dismiss(); - } - } - } - if (document.getElementsByClassName("prasi-toaster").length === 0) { const elemDiv = document.createElement("div"); elemDiv.className = "prasi-toaster"; @@ -908,32 +902,16 @@ export const TableList: FC = ({ } }} > -
- {toaster_el && createPortal(, toaster_el)} - {local.status === "init" ? ( - { - local.status = "reload"; - local.paging.take = local.paging.take * 5; - local.render(); - }, 100); - return <>; - }, - }, - ]} - rows={genRows(200)} - /> - ) : ( + {toaster_el && createPortal(, toaster_el)} + + {local.status !== "ready" ? ( +
+ + + +
+ ) : ( +
<> {Array.isArray(data) && data.length > 0 ? (
= ({ ) : (
-
+
No Data
{local.filtering && ( @@ -983,8 +961,8 @@ export const TableList: FC = ({
)} - )} -
+
+ )}
); } else { diff --git a/comps/md/parts/MDHeader.tsx b/comps/md/parts/MDHeader.tsx index 7926fd1..7c46d31 100755 --- a/comps/md/parts/MDHeader.tsx +++ b/comps/md/parts/MDHeader.tsx @@ -1,20 +1,51 @@ -import { FC, useState } from "react"; +import { FC, ReactNode, useEffect, useState } from "react"; import { breadcrumbPrefix } from "../utils/md-hash"; import { MDLocal, MDRef } from "../utils/typings"; +import { BreadItem } from "lib/comps/custom/Breadcrumb"; +import { useLocal } from "lib/utils/use-local"; export const MDHeader: FC<{ md: MDLocal; mdr: MDRef }> = ({ md, mdr }) => { - const [_, set] = useState({}); + const local = useLocal({ breads_length: 0 }); const head = mdr.item.edit.props?.header.value; const PassProp = mdr.PassProp; - md.header.render = () => set({}); + md.header.render = local.render; const prefix = breadcrumbPrefix(md); + let breads: (BreadItem & { url: string })[] = []; if (md.selected && md.header.child.breadcrumb) { - md.header.breadcrumb = [...prefix, ...md.header.child.breadcrumb()]; + breads = [...prefix, ...md.header.child.breadcrumb()] as any; } else if (!md.selected && md.header.master.breadcrumb) { - md.header.breadcrumb = [...prefix, ...md.header.master.breadcrumb()]; + breads = [...prefix, ...md.header.master.breadcrumb()] as any; } + md.header.breadcrumb = []; + let overrideLabel = "" as ReactNode; + for (const v of breads) { + if (v.label === "--reset--") { + md.header.breadcrumb = []; + overrideLabel = ""; + continue; + } + + if (v.url === "--override--") { + overrideLabel = v.label; + continue; + } + + if (overrideLabel) { + v.label = overrideLabel; + overrideLabel = ""; + } + md.header.breadcrumb.push(v); + } + + useEffect(() => { + if (local.breads_length !== md.header.breadcrumb.length) { + local.breads_length = md.header.breadcrumb.length; + if (!md.selected && md.master.reload) md.master.reload(); + } + }, [md.header.breadcrumb.length]); + if (md.internal.reset_detail) return null; return {head}; }; diff --git a/comps/md/utils/md-hash.ts b/comps/md/utils/md-hash.ts index fa2f525..518b36c 100755 --- a/comps/md/utils/md-hash.ts +++ b/comps/md/utils/md-hash.ts @@ -81,7 +81,7 @@ export const masterDetailApplyParams = (md: MDLocal) => { }; export const breadcrumbPrefix = (md: MDLocal) => { - const prefix: BreadItem[] = []; + let prefix: (BreadItem & { url: string })[] = []; if (md.params.links && md.params.links.length > 0) { const hashes: string[] = []; for (const link of md.params.links) { @@ -93,6 +93,7 @@ export const breadcrumbPrefix = (md: MDLocal) => { for (const p of link.prefix) { prefix.push({ label: p.label, + url: p.url || link.url, onClick(ev) { let url = ""; @@ -103,7 +104,7 @@ export const breadcrumbPrefix = (md: MDLocal) => { if (p.md) { url = `${p.url || link.url}#${p.md.name}=${p.md.value}${lnk}`; } else { - url = `${p.url || link.url}${lnk}`; + url = `${p.url || link.url}${lnk}`; } if (url) { diff --git a/comps/ui/toast.tsx b/comps/ui/toast.tsx index 8ee32a6..33e1d12 100755 --- a/comps/ui/toast.tsx +++ b/comps/ui/toast.tsx @@ -12,6 +12,7 @@ export const toast = { sonner.dismiss(); } else { clearTimeout(timer.timeout); + timer.timeout = null; } }, loading: ( @@ -41,6 +42,8 @@ export const toast = { clearTimeout(timer.timeout); timer.timeout = setTimeout(() => { sonner.error(el, props); + clearTimeout(timer.timeout); + timer.timeout = null; }, timer.limit); }, diff --git a/exports.tsx b/exports.tsx index 1fe162b..ebcadf2 100755 --- a/exports.tsx +++ b/exports.tsx @@ -26,6 +26,10 @@ export const Typeahead = lazify( async () => (await import("@/comps/ui/typeahead")).Typeahead ); +export const ImgThumb = lazify( + async () => (await import("@/comps/form/field/type/FilePreview")).ImgThumb +); + /** Master - Detail - List - Form */ export const MasterDetail = lazify( async () => (await import("@/comps/md/MasterDetail")).MasterDetail