import { parseGenField } from "lib/gen/utils"; import { useLocal } from "lib/utils/use-local"; import get from "lodash.get"; import { AlertTriangle, ChevronDown, ChevronRight, Loader2, Sticker, } from "lucide-react"; import { ChangeEvent, FC, MouseEvent, useEffect } from "react"; import { DataGrid, ColumnOrColumnGroup, Row } from "react-data-grid"; import "react-data-grid/lib/styles.css"; import { createPortal } from "react-dom"; import { getFilter } from "../filter/utils/get-filter"; import { Skeleton } from "../ui/skeleton"; import { toast, Toaster } from "../ui/toast"; import { TableListProp, useTableListLocal } from "./TableListLocal"; import { TLList } from "./TLList"; import { TLSlider } from "./TLSlider"; import { sortTree } from "./utils/sort-tree"; let EMPTY_SET = new Set() as ReadonlySet; const w = window as any; const selectCellClassname = css` display: flex; align-items: center; justify-content: center; > input { margin: 0; } `; export const TableList: FC = (props) => { const { name, on_load, child, PassProp, mode: _mode, on_init, _item, gen_fields, row_click, selected, id_parent, feature, filter_name, row_height: rowHeight, render_col, show_header, list, value, paging, cache_row, __props, md, } = props; let mode = _mode; if (mode === "auto") { if (w.isMobile) { mode = "list"; } else { mode = "table"; } } const local = useTableListLocal(props); const reload = local.reload; if (md) { md.master.list = local; md.master.reload = reload; } if (filter_name) { const f = getFilter(filter_name); if (f) { f.list.ref[_item.id] = { reload }; } } // 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); if (isEditor && local.data.length === 0 && local.status === "ready") { reload(); } else { local.render(); } return; } (async () => { on_init(local); if ( (local.status === "init" || local.status === "reload") && typeof on_load === "function" ) { reload(); } })(); }, [local.status, on_load, local.sort.orderBy]); const raw_childs = get( child, "props.meta.item.component.props.child.content.childs" ); let childs: any[] = []; let sub_name: string[] = []; switch (mode) { case "table": sub_name = ["tbl-col", "table: columns"]; break; case "list": sub_name = ["list-row", "list: fields"]; break; } // passing local data ke variable baru (biar gak panjang nulisnya hehe) let rowData = local.data; // function untuk menghandle checkbox di header (digunakan untuk check all) const headerCheckboxClick = (e: ChangeEvent) => { if (e.target.checked && Array.isArray(rowData)) { // jika checbox checked, maka semua rowData akan dimasukkan ke dalam local selected rows rowData.forEach((data) => { local.selectedRows.push({ pk: data[pk], rows: data, }); }); local.selectedAllRows = true; local.render(); } else { // jika tidak, maka local selected rows akan dikosongkan local.selectedAllRows = false; local.selectedRows = []; local.render(); } }; // function untuk menghandle checkbox pada setiap row (digunakan untuk check setiap rowData) const checkboxClick = (rowId: any) => (e: MouseEvent) => { e.preventDefault(); e.stopPropagation(); const checked = !!local.selectedRows.find((data) => data.pk === rowId); if (!checked) { // jika checkbox tercheck, maka rowData akan diambil jika memiliki id yang sama dengan rowId yang dikirim const checkedRowData = rowData.filter((row) => row[pk] === rowId); local.selectedRows.push({ pk: rowId, rows: checkedRowData, }); local.render(); } else { // jika tidak, maka akan dihapus local.selectedRows = local.selectedRows.filter( (data) => data.pk !== rowId ); local.selectedAllRows = false; local.render(); } }; const mode_child = raw_childs.find( (e: any) => sub_name.includes(e.name) || e.name === mode ); if (mode_child) { const tbl = _item.edit.childs[0].edit.childs.find( (e: any) => get(e, "id") === mode_child.id ); const meta = tbl; if (meta && meta.childs) { childs = meta.childs; } } let columns: ColumnOrColumnGroup[] = []; let isCheckbox = false; let isTree = false; try { if (feature?.find((e) => e === "checkbox")) isCheckbox = true; if (feature?.find((e) => e === "tree")) isTree = true; } catch (e) {} if (local.status === "init") { const fields = parseGenField(gen_fields); for (const field of fields) { if (field.is_pk) { local.pk = field; } } } if (typeof value !== "undefined") { local.data = value; local.status = "ready" as any; } else { if (isEditor && local.status !== "ready") { if (local.data.length === 0) { const load_args: any = { async reload() {}, where: {}, paging: { take: local.paging.take > 0 ? local.paging.take : undefined, skip: local.paging.skip, }, }; if (id_parent) load_args.paging = {}; if (typeof on_load === "function") { let res = on_load({ ...load_args, mode: "query" }) as any; if (typeof res === "object" && res instanceof Promise) { res.then((e) => { local.data = e; }); } else { local.data = res; } } } local.status = "ready"; } } let data = Array.isArray(local.data) ? local.data : []; if (typeof local.data === "string") { console.error(local.data); local.data = []; } if (isEditor) { if (data.length > 0) { w.prasi_table_list_temp_data = data; } else if ( w.prasi_table_list_temp_data && w.prasi_table_list_temp_data.length > 0 ) { data = w.prasi_table_list_temp_data; } } if (isTree && id_parent && local.pk && local.sort.columns.length === 0) { data = sortTree(local.data, id_parent, local.pk.name).filter((e) => { if (local.pk && local.collapsed.has(e?.__parent?.[local.pk.name])) { return false; } return true; }); } let first = true; for (const child of childs) { let key = getProp(child, "name", {}); const name = getProp(child, "title", ""); const sort = getProp(child, "sortir", ""); let show = getProp(child, "show", ""); if (typeof show === "function") { show = show(); if (typeof show === "object" && show instanceof Promise) { show.then((e) => { show = e; }); } } show = show === "n" ? false : show; if (show) { const type = getProp(child, "type", ""); const width = parseInt(getProp(child, "width", {})); if (type === "checkbox") { columns.push({ key, name, width: 35, minWidth: 45, resizable: true, sortable: sort === "n" ? false : true, frozen: true, renderHeaderCell(props) { return (
{isCheckbox ? ( ) : ( <> )}
); }, renderCell(props) { if (typeof render_col === "function") return render_col({ props, tbl: local, child, }); if (isCheckbox) { const isChecked = local.selectedRows.some( (checked) => checked.pk === props.row.id ); return (
); } return ( {child} ); }, }); } else { columns.push({ key, name, width: width > 0 ? width : undefined, resizable: true, sortable: sort === "n" ? false : true, renderHeaderCell(props) { return (
{ const msg = `The ${props?.column?.name} column cannot be sorted!`; if (!props?.column?.sortable) { toast.dismiss(); toast.error(
{msg}
, { dismissible: true, className: css` background: #ffecec; border: 2px solid red; `, } ); } }} > {props?.column?.name} {props.sortDirection ? ( <> {" "}
{props.sortDirection === "ASC" ? ( ) : ( )}
) : ( <> )}
); }, renderCell(props) { if (typeof render_col === "function") return render_col({ props, tbl: local, child, }); return ( <> {isTree && local.firstKey === key && local.pk && (
{ if (!local.pk) return; if (props?.row?.__children?.length > 0) { e.stopPropagation(); if (!local.collapsed.has(props.row?.[local.pk.name])) { local.collapsed.add(props.row?.[local.pk.name]); } else { local.collapsed.delete(props.row?.[local.pk.name]); } local.render(); } }} >
{props.row?.__children?.length > 0 && ( <> {local.collapsed.has(props.row?.[local.pk.name]) ? ( ) : ( )} )}
{props.row?.__parent && (props.row?.__children || []).length === 0 && (
)}
)} {child} ); }, }); if (first) { first = false; local.firstKey = key; } } } } if (mode === "list") { if (columns.length > 1) columns = columns.slice(0, 0 + 1); } if (!isEditor) { let should_toast = true; if (md) { if (md.props.mode !== "full") { should_toast = false; } } if (should_toast) { if (local.status === "loading") { toast.dismiss(); toast.loading( <> Loading Data ... , { dismissible: true } ); } else { toast.dismiss(); } } } 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]; if (mode === "table") { if (local.status === "resizing" && !isEditor) { local.status = "ready"; local.render(); return null; } return ( <>
{local.status !== "ready" && (
)}
{ local.el = e; if (e) local.height = e.offsetHeight; }} > {toaster_el && createPortal(, toaster_el)} {local.status === "init" ? ( { local.status = "reload"; local.render(); }, 100); return <>; }, }, ]} rows={genRows(200)} /> ) : ( <> { local.grid_ref = e?.element as any; }} onScroll={(e) => { local.paging.scroll(e.currentTarget); }} selectedRows={EMPTY_SET} onSelectedCellChange={() => {}} onSelectedRowsChange={() => {}} headerRowHeight={show_header === false ? 0 : undefined} renderers={ local.status !== "ready" ? undefined : { renderRow(key, props) { if ( cache_row === true && local.cached_row.has(props.row) ) { return local.cached_row.get(props.row); } const isSelect = local.selectedAllRows ? true : selected({ idx: props.rowIdx, row: props.row, rows: local.data, select: local.selectedAllRows ? true : local.selectedRows.some( (checked) => checked.pk === props.row.id ), data: local.selectedAllRows ? local.data : local.selectedRows, }); const child_row = ( { if ( !isEditor && typeof row_click === "function" ) { row_click({ event: ev, idx: props.rowIdx, row: props.row, rows: local.data, }); } }} isRowSelected={true} className={cx( props.className, (isSelect || (md?.selected?.[local.pk?.name || ""] === props.row[local.pk?.name || ""] && props.row[local.pk?.name || ""])) && "row-selected" )} /> ); if (cache_row) { local.cached_row.set(props.row, child_row); } return child_row; }, noRowsFallback: (
No Data
{local.filtering && (
{local.filtering}
)}
), } } /> )}
); } else if (mode === "list") { return ( <> {toaster_el && createPortal(, toaster_el)} {list.type !== "slider" && list.type !== "grid" && ( )} {list.type === "slider" && ( )} ); } }; const CheckboxList: FC<{ on_click: (e: any) => void; checked?: boolean; value?: boolean; }> = ({ value, checked, on_click }) => { const local = useLocal({ checked: false as any, value: false as boolean, }); useEffect(() => { local.checked = checked; local.render(); }, []); return (
{ local.checked = !local.checked; on_click(typeof value === "boolean" ? !local.checked : value); local.render(); }} className="c-flex c-flex-row c-space-x-1 cursor-pointer c-items-center rounded-full p-0.5" > {local.checked ? ( ) : ( )}
); }; const genRows = (total: number) => { const result = [] as any[]; for (let i = 0; i < total; i++) { result.push({ _: i }); } return result; }; const dataGridStyle = (local: { el: null | HTMLDivElement }) => { return css` .rdg { display: grid !important; .rdg-cell, .rdg-header-sort-name { display: flex; flex-direction: row; align-items: stretch; &.rdg-header-sort-name { align-items: center; } } } .rdg { height: ${local.el?.clientHeight || 100}px; } div[role="row"]:hover { background: #e2f1ff; .num-edit { display: flex; } .num-idx { display: none; } } div[role="columnheader"] span svg { margin: 12px 2px; /* color: #ffffff */ } div[aria-selected="true"] { outline: none; } div[role="gridcell"] { padding-inline: 0px; } .row-selected { background: #bddfff !important; } `; }; function getProp(child: any, name: string, defaultValue?: any) { const fn = new Function( `return ${get(child, `component.props.${name}.valueBuilt`) || `null`}` ); return fn() || defaultValue; }