fix table list

This commit is contained in:
rizky 2024-08-19 04:44:36 -07:00
parent 66ce2ab145
commit 52105ef69c
8 changed files with 275 additions and 114 deletions

113
comps/list/TLList.tsx Executable file
View File

@ -0,0 +1,113 @@
import { FC } from "react";
import { Skeleton } from "../ui/skeleton";
import { OnRowClick } from "./utils/type";
import { Sticker } from "lucide-react";
export const TLList: FC<{
local: Record<string, any> & {
el: null | HTMLDivElement;
render: () => void;
};
data: any[];
PassProp: any;
row_click: OnRowClick;
mode_child: any;
dataGridStyle: (local: { el: null | HTMLDivElement }) => string;
}> = ({ local, data, dataGridStyle, mode_child, PassProp, row_click }) => {
return (
<div
className={cx(
"c-w-full c-h-full c-flex-1 c-relative c-overflow-hidden",
dataGridStyle(local)
)}
ref={(el) => {
if (!local.el && el) {
local.el = el;
}
}}
>
{local.status !== "ready" ? (
<div className="c-flex c-flex-col c-space-y-2 c-m-4 c-absolute c-left-0 c-top-0">
<Skeleton className={cx("c-w-[200px] c-h-[11px]")} />
<Skeleton className={cx("c-w-[170px] c-h-[11px]")} />
<Skeleton className={cx("c-w-[180px] c-h-[11px]")} />
</div>
) : (
<div
className={cx(
"c-absolute c-inset-0",
!isEditor &&
css`
@keyframes flasher {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0px);
}
}
.list-row {
animation: flasher 0.5s;
}
`
)}
>
<>
{Array.isArray(data) && data.length > 0 ? (
<div
className="w-full h-full overflow-y-auto c-flex-col"
ref={(e) => {
local.grid_ref = e;
}}
onScroll={(e) => local.paging.scroll(e.currentTarget)}
>
{data.map((e, idx) => {
return (
<div
className={cx("list-row c-flex-grow c-flex")}
onClick={(ev) => {
if (!isEditor && typeof row_click === "function") {
row_click({
event: ev,
idx: idx,
row: e,
rows: local.data,
});
}
}}
>
<PassProp idx={idx} row={e} col={{}} rows={local.data}>
{mode_child}
</PassProp>
</div>
);
})}
</div>
) : (
<div className="c-flex c-items-center c-justify-center c-flex-1 w-full h-full c-flex-col ">
<Sticker size={35} strokeWidth={1} />
<div className="c-pt-1 c-text-center">
No&nbsp;Data
<br />
{local.filtering && (
<div
className={css`
color: gray;
font-style: italic;
font-size: 90%;
`}
>
{local.filtering}
</div>
)}
</div>
</div>
)}
</>
</div>
)}
</div>
);
};

107
comps/list/TLSlider.tsx Executable file
View File

@ -0,0 +1,107 @@
import { FC } from "react";
import { Skeleton } from "../ui/skeleton";
import { OnRowClick } from "./utils/type";
import { Sticker } from "lucide-react";
export const TLSlider: FC<{
local: Record<string, any> & {
el: null | HTMLDivElement;
render: () => void;
};
data: any[];
PassProp: any;
row_click: OnRowClick;
mode_child: any;
item_w: string;
dataGridStyle: (local: { el: null | HTMLDivElement }) => string;
}> = ({
local,
data,
dataGridStyle,
item_w,
mode_child,
PassProp,
row_click,
}) => {
return (
<>
{local.status !== "ready" ? (
<div className="c-flex c-flex-col c-space-y-2 c-m-4 c-absolute c-left-0 c-top-0">
<Skeleton className={cx("c-w-[200px] c-h-[11px]")} />
<Skeleton className={cx("c-w-[170px] c-h-[11px]")} />
<Skeleton className={cx("c-w-[180px] c-h-[11px]")} />
</div>
) : (
<>
{Array.isArray(data) && data.length > 0 ? (
<div
className={cx(
"c-overflow-x-auto c-snap-x c-h-full c-w-full c-flex",
css`
padding-right: 50px;
`
)}
ref={(e) => {
local.grid_ref = e;
}}
onScroll={(e) => local.paging.scroll(e.currentTarget)}
>
{data.map((e, idx) => {
return (
<div
className={cx(
"list-item c-snap-start c-flex c-shrink-0",
css`
width: ${item_w}px;
`
)}
onClick={(ev) => {
if (!isEditor && typeof row_click === "function") {
row_click({
event: ev,
idx: idx,
row: e,
rows: local.data,
});
}
}}
>
<PassProp
idx={idx}
item_w={item_w}
is_last={idx == data.length - 1}
row={e}
col={{}}
rows={local.data}
>
{mode_child}
</PassProp>
</div>
);
})}
</div>
) : (
<div className="c-flex c-items-center c-justify-center c-flex-1 w-full h-full c-flex-col ">
<Sticker size={35} strokeWidth={1} />
<div className="c-pt-1 c-text-center">
No&nbsp;Data
<br />
{local.filtering && (
<div
className={css`
color: gray;
font-style: italic;
font-size: 90%;
`}
>
{local.filtering}
</div>
)}
</div>
</div>
)}
</>
)}
</>
);
};

View File

@ -36,19 +36,17 @@ import { MDLocal } from "../md/utils/typings";
import { Skeleton } from "../ui/skeleton"; import { Skeleton } from "../ui/skeleton";
import { toast } from "../ui/toast"; import { toast } from "../ui/toast";
import { sortTree } from "./utils/sort-tree"; import { sortTree } from "./utils/sort-tree";
import { TLList } from "./TLList";
import { OnRowClick } from "./utils/type";
import { TLSlider } from "./TLSlider";
type OnRowClick = (arg: {
row: any;
rows: any[];
idx: any;
event: React.MouseEvent<HTMLDivElement>;
}) => void;
let EMPTY_SET = new Set() as ReadonlySet<any>; let EMPTY_SET = new Set() as ReadonlySet<any>;
type SelectedRow = (arg: { row: any; rows: any[]; idx: any }) => boolean; type SelectedRow = (arg: { row: any; rows: any[]; idx: any }) => boolean;
type TableListProp = { type TableListProp = {
child: any; child: any;
PassProp: any; PassProp: any;
list: { type: string; item_w: string };
name: string; name: string;
value?: any[]; value?: any[];
on_load?: (arg: { on_load?: (arg: {
@ -75,7 +73,6 @@ type TableListProp = {
tbl: any; tbl: any;
child: any; child: any;
}) => ReactNode; }) => ReactNode;
softdel_field?: string;
gen_table?: string; gen_table?: string;
softdel_type?: string; softdel_type?: string;
paging?: boolean; paging?: boolean;
@ -110,6 +107,7 @@ export const TableList: FC<TableListProp> = ({
row_height: rowHeight, row_height: rowHeight,
render_col, render_col,
show_header, show_header,
list,
value, value,
paging, paging,
cache_row, cache_row,
@ -163,6 +161,7 @@ export const TableList: FC<TableListProp> = ({
last_length: 0, last_length: 0,
scroll: (currentTarget: HTMLDivElement) => { scroll: (currentTarget: HTMLDivElement) => {
if ( if (
isEditor ||
local.data.length < local.paging.take || local.data.length < local.paging.take ||
local.data.length === 0 || local.data.length === 0 ||
local.status !== "ready" || local.status !== "ready" ||
@ -259,7 +258,11 @@ export const TableList: FC<TableListProp> = ({
} }
const result = on_load({ ...load_args, mode: "query" }); const result = on_load({ ...load_args, mode: "query" });
const callback = (data: any[]) => { const callback = (data: any[]) => {
if (!local.paging || (local.paging && !local.paging.take)) { if (
id_parent ||
!local.paging ||
(local.paging && !local.paging.take)
) {
local.data = data; local.data = data;
} else { } else {
local.data = [...local.data, ...data]; local.data = [...local.data, ...data];
@ -414,6 +417,9 @@ export const TableList: FC<TableListProp> = ({
useEffect(() => { useEffect(() => {
if (isEditor || value) { if (isEditor || value) {
on_init(local); on_init(local);
if (isEditor && local.data.length === 0 && local.status === "ready") {
reload();
}
return; return;
} }
(async () => { (async () => {
@ -935,107 +941,35 @@ export const TableList: FC<TableListProp> = ({
); );
} else if (mode === "list") { } else if (mode === "list") {
return ( return (
<div <>
className={cx(
"c-w-full c-h-full c-flex-1 c-relative c-overflow-hidden",
dataGridStyle(local)
)}
ref={(el) => {
if (!local.el && el) {
local.el = el;
}
}}
>
{toaster_el && {toaster_el &&
createPortal( createPortal(
<Toaster position={toast.position} cn={cn} />, <Toaster position={toast.position} cn={cn} />,
toaster_el toaster_el
)} )}
{list.type !== "slider" && list.type !== "grid" && (
{local.status !== "ready" ? ( <TLList
<div className="c-flex c-flex-col c-space-y-2 c-m-4 c-absolute c-left-0 c-top-0"> row_click={row_click}
<Skeleton className={cx("c-w-[200px] c-h-[11px]")} /> PassProp={PassProp}
<Skeleton className={cx("c-w-[170px] c-h-[11px]")} /> local={local}
<Skeleton className={cx("c-w-[180px] c-h-[11px]")} /> mode_child={mode_child}
</div> data={data}
) : ( dataGridStyle={dataGridStyle}
<div />
className={cx(
"c-absolute c-inset-0",
css`
@keyframes flasher {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0px);
}
}
.list-row {
animation: flasher 0.5s;
}
`
)} )}
> {list.type === "slider" && (
<> <TLSlider
{Array.isArray(data) && data.length > 0 ? ( row_click={row_click}
<div PassProp={PassProp}
className="w-full h-full overflow-y-auto c-flex-col" local={local}
ref={(e) => { mode_child={mode_child}
local.grid_ref = e; data={data}
}} item_w={list.item_w}
onScroll={(e) => local.paging.scroll(e.currentTarget)} dataGridStyle={dataGridStyle}
> />
{data.map((e, idx) => {
return (
<div
className={cx("list-row c-flex-grow c-flex")}
onClick={(ev) => {
if (!isEditor && typeof row_click === "function") {
row_click({
event: ev,
idx: idx,
row: e,
rows: local.data,
});
}
}}
>
<PassProp idx={idx} row={e} col={{}} rows={local.data}>
{mode_child}
</PassProp>
</div>
);
})}
</div>
) : (
<div className="c-flex c-items-center c-justify-center c-flex-1 w-full h-full c-flex-col ">
<Sticker size={35} strokeWidth={1} />
<div className="c-pt-1 c-text-center">
No&nbsp;Data
<br />
{local.filtering && (
<div
className={css`
color: gray;
font-style: italic;
font-size: 90%;
`}
>
{local.filtering}
</div>
)}
</div>
</div>
)} )}
</> </>
</div>
)}
</div>
); );
} else {
} }
}; };
const CheckboxList: FC<{ const CheckboxList: FC<{

6
comps/list/utils/type.tsx Executable file
View File

@ -0,0 +1,6 @@
export type OnRowClick = (arg: {
row: any;
rows: any[];
idx: any;
event: React.MouseEvent<HTMLDivElement>;
}) => void;

View File

@ -129,11 +129,11 @@ const genList = async (opt: GenOpt) => {
}, },
adv: { adv: {
js: `\ js: `\
<div {...props} className={cx(props.className, \`s-\${_item?.edit?.parent?.item?.id}\` , "list-field")}> <div {...props} className={cx(props.className, _item?.edit?.parent?.item?.id && \`s-\${_item?.edit?.parent?.item?.id}\` , "list-field")}>
<FormatValue value={_get(row, name)} name={name} gen_fields={gen__fields} /> <FormatValue value={_get(row, name)} name={name} gen_fields={gen__fields} />
</div>`, </div>`,
jsBuilt: `\ jsBuilt: `\
render(React.createElement("div", Object.assign({}, props, { className: cx(props.className, \`s-\${_item?.edit?.parent?.item?.id}\` , "") }),React.createElement(FormatValue, { value: _get(row, name), name: name, gen_fields: gen__fields }))); render(React.createElement("div", Object.assign({}, props, { className: cx(props.className, _item?.edit?.parent?.item?.id && \`s-\${_item?.edit?.parent?.item?.id}\` , "") }),React.createElement(FormatValue, { value: _get(row, name), name: name, gen_fields: gen__fields })));
`, `,
}, },
}), }),
@ -189,11 +189,11 @@ const genTable = async (opt: GenOpt) => {
title: formatName(e.name), title: formatName(e.name),
child: createItem({ child: createItem({
name: "cell", name: "cell",
"layout": { layout: {
"dir": "col", dir: "col",
"align": "left", align: "left",
"gap": 0, gap: 0,
"wrap": "flex-nowrap" wrap: "flex-nowrap",
}, },
padding: { padding: {
l: 8, l: 8,
@ -203,11 +203,11 @@ const genTable = async (opt: GenOpt) => {
}, },
adv: { adv: {
js: `\ js: `\
<div {...props} className={cx(props.className, \`s-\${_item?.edit?.parent?.item?.id}\` , "table-col")}> <div {...props} className={cx(props.className, _item?.edit?.parent?.item?.id && \`s-\${_item?.edit?.parent?.item?.id}\` , "table-col")}>
<FormatValue value={col.value} name={col.name} gen_fields={gen__fields} /> <FormatValue value={col.value} name={col.name} gen_fields={gen__fields} />
</div>`, </div>`,
jsBuilt: `\ jsBuilt: `\
render(React.createElement("div", Object.assign({}, props, { className: cx(props.className, \`s-\${_item?.edit?.parent?.item?.id}\` , "") }),React.createElement(FormatValue, { value: col.value, name: col.name, gen_fields: gen__fields }))); render(React.createElement("div", Object.assign({}, props, { className: cx(props.className, _item?.edit?.parent?.item?.id && \`s-\${_item?.edit?.parent?.item?.id}\` , "") }),React.createElement(FormatValue, { value: col.value, name: col.name, gen_fields: gen__fields })));
`, `,
}, },
}), }),

View File

@ -40,7 +40,7 @@ async (arg: TableOnLoad) => {
if (isEditor) if (isEditor)
return [${JSON.stringify(sample)}]; return [${JSON.stringify(sample)}];
let where = arg.where; let where = arg.where as Prisma.${table}WhereInput;
if (arg.mode === "count") { if (arg.mode === "count") {
return await db.${table}.count({ return await db.${table}.count({
where: { where: {

View File

@ -81,6 +81,7 @@ export const Layout: FC<LYTChild> = (props) => {
const newurl = new URL(`${url.protocol}//${url.host}${_href}`); const newurl = new URL(`${url.protocol}//${url.host}${_href}`);
const pathname = newurl.pathname; const pathname = newurl.pathname;
_href = baseurl(_href);
if (params) { if (params) {
const prefix: LinkParam["prefix"] = const prefix: LinkParam["prefix"] =
params.breads?.map((e) => { params.breads?.map((e) => {