fix
This commit is contained in:
parent
ebc3e19935
commit
ac74e0770b
|
|
@ -1,7 +1,5 @@
|
|||
import { useLocal } from "@/utils/use-local";
|
||||
import { FC, ReactNode, useEffect } from "react";
|
||||
import { Skeleton } from "../ui/skeleton";
|
||||
import get from "lodash.get";
|
||||
import { FieldLoading } from "../ui/field-loading";
|
||||
|
||||
export type BreadItem = {
|
||||
|
|
|
|||
|
|
@ -47,9 +47,7 @@ export const BaseField = (prop: {
|
|||
w === "½" && "c-w-1/2",
|
||||
w === "⅓" && "c-w-1/3",
|
||||
w === "¼" && "c-w-1/4",
|
||||
field.type === "link"
|
||||
? "c-flex-row c-items-stretch c-min-h-[78px]"
|
||||
: "c-flex-col c-space-y-1",
|
||||
"c-flex-col c-space-y-1",
|
||||
field.focused && "focused",
|
||||
field.disabled && "disabled",
|
||||
typeof fm.data[name] !== "undefined" &&
|
||||
|
|
@ -65,9 +63,7 @@ export const BaseField = (prop: {
|
|||
!["toogle", "button", "radio", "checkbox"].includes(arg.sub_type)
|
||||
? cx(
|
||||
"field-outer c-overflow-hidden c-flex-1 c-flex c-flex-row c-text-sm c-bg-white",
|
||||
field.type === "link"
|
||||
? " c-items-center"
|
||||
: "c-border c-rounded "
|
||||
"c-border c-rounded "
|
||||
)
|
||||
: "",
|
||||
fm.status === "loading"
|
||||
|
|
@ -105,7 +101,6 @@ export const BaseField = (prop: {
|
|||
<div
|
||||
className={cx(
|
||||
"field-inner c-flex-1 c-flex c-items-center",
|
||||
field.type === "link" && "c-justify-end",
|
||||
field.focused && "focused",
|
||||
field.disabled && "c-pointer-events-none"
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -54,9 +54,7 @@ export const Field: FC<FieldProp> = (arg) => {
|
|||
w === "½" && "c-w-1/2",
|
||||
w === "⅓" && "c-w-1/3",
|
||||
w === "¼" && "c-w-1/4",
|
||||
field.type === "link"
|
||||
? "c-flex-row c-items-stretch c-min-h-[78px]"
|
||||
: "c-flex-col c-space-y-1"
|
||||
"c-flex-col c-space-y-1"
|
||||
)}
|
||||
{...props}
|
||||
ref={typeof arg.field_ref === "function" ? arg.field_ref : undefined}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import { TableEdit } from "./table-edit/TableEdit";
|
|||
import { FieldTypeInput, PropTypeInput } from "./type/TypeInput";
|
||||
import { MultiOption } from "./type/TypeMultiOption";
|
||||
import { SingleOption } from "./type/TypeSingleOption";
|
||||
import { FieldLink } from "./type/TypeLink";
|
||||
|
||||
const modify = {
|
||||
timeout: null as any,
|
||||
|
|
@ -108,7 +109,7 @@ export const FieldInput: FC<{
|
|||
!["toogle", "button", "radio", "checkbox"].includes(arg.sub_type)
|
||||
? cx(
|
||||
"field-outer c-overflow-hidden c-flex-1 c-flex c-flex-row c-text-sm c-bg-white",
|
||||
field.type === "link" ? " c-items-center" : "c-border c-rounded "
|
||||
"c-border c-rounded "
|
||||
)
|
||||
: "",
|
||||
fm.status === "loading"
|
||||
|
|
@ -145,7 +146,6 @@ export const FieldInput: FC<{
|
|||
<div
|
||||
className={cx(
|
||||
"field-inner c-flex-1 c-flex c-items-center",
|
||||
field.type === "link" && "c-justify-end",
|
||||
field.focused && "focused",
|
||||
disabled && "c-pointer-events-none c-bg-gray-50"
|
||||
)}
|
||||
|
|
@ -158,7 +158,9 @@ export const FieldInput: FC<{
|
|||
<>{custom}</>
|
||||
) : (
|
||||
<>
|
||||
{type_field === "link" && <>ini link</>}
|
||||
{type_field === "link" && (
|
||||
<FieldLink field={field} fm={fm} arg={arg} />
|
||||
)}
|
||||
{["date", "input"].includes(type_field) ? (
|
||||
<FieldTypeInput
|
||||
field={field}
|
||||
|
|
|
|||
|
|
@ -8,12 +8,7 @@ export const Label: FC<{ field: FieldLocal; fm: FMLocal }> = ({
|
|||
const errors = fm.error.get(field.name);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cx(
|
||||
"label c-text-sm c-flex c-items-center",
|
||||
field.type === "link" ? "" : "c-mt-3"
|
||||
)}
|
||||
>
|
||||
<div className={cx("label c-text-sm c-flex c-items-center", "c-mt-3")}>
|
||||
<span className={cx(errors.length > 0 && `c-text-red-600`)}>
|
||||
{field.label}
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,196 @@
|
|||
import { FieldLoading, Spinner } from "lib/comps/ui/field-loading";
|
||||
import { hashSum } from "lib/utils/hash-sum";
|
||||
import { getPathname } from "lib/utils/pathname";
|
||||
import { useLocal } from "lib/utils/use-local";
|
||||
import { ArrowUpRight } from "lucide-react";
|
||||
import { FC, ReactNode, useEffect } from "react";
|
||||
import { FMLocal, FieldLocal, FieldProp } from "../../typings";
|
||||
|
||||
export const FieldLink: FC<{
|
||||
field: FieldLocal;
|
||||
fm: FMLocal;
|
||||
arg: FieldProp;
|
||||
}> = ({ field, fm, arg }) => {
|
||||
const local = useLocal({
|
||||
text: "",
|
||||
init: false,
|
||||
navigating: false,
|
||||
custom: false,
|
||||
});
|
||||
|
||||
const Link = ({
|
||||
children,
|
||||
}: {
|
||||
children: (arg: { icon: any }) => ReactNode;
|
||||
}) => {
|
||||
return (
|
||||
<div
|
||||
className={cx(
|
||||
"c-flex-1 c-my-1 c-px-2 c-rounded-md c-flex c-items-center cursor-pointer c-space-x-1 c-transition-all",
|
||||
css`
|
||||
border: 1px solid #aaa;
|
||||
&:hover {
|
||||
background-color: #dcedfc;
|
||||
}
|
||||
`
|
||||
)}
|
||||
onClick={async () => {
|
||||
if (!isEditor) {
|
||||
local.navigating = true;
|
||||
local.render();
|
||||
if (!(await navigateLink(arg.link, field))) {
|
||||
local.navigating = false;
|
||||
local.render();
|
||||
}
|
||||
}
|
||||
}}
|
||||
>
|
||||
{children({
|
||||
icon: local.navigating ? (
|
||||
<Spinner
|
||||
className={cx(
|
||||
css`
|
||||
padding-left: 4px;
|
||||
`
|
||||
)}
|
||||
/>
|
||||
) : (
|
||||
<ArrowUpRight size={15} />
|
||||
),
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (arg.link && !local.init) {
|
||||
if (typeof arg.link.text === "string") {
|
||||
local.text = arg.link.text;
|
||||
local.init = true;
|
||||
} else if (typeof arg.link.text === "function") {
|
||||
const res = arg.link.text({
|
||||
field,
|
||||
Link,
|
||||
});
|
||||
if (res instanceof Promise) {
|
||||
res.then((text) => {
|
||||
local.text = text;
|
||||
local.init = true;
|
||||
local.render();
|
||||
});
|
||||
} else {
|
||||
local.text = res;
|
||||
local.init = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
local.render();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className={cx("c-px-2")}>
|
||||
{!local.init ? (
|
||||
<FieldLoading height="short" />
|
||||
) : (
|
||||
<Link>
|
||||
{({ icon }) => {
|
||||
return (
|
||||
<>
|
||||
<div>{local.text}</div>
|
||||
{icon}
|
||||
</>
|
||||
);
|
||||
}}
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export type LinkParam = {
|
||||
url: string;
|
||||
where: any;
|
||||
hash: any;
|
||||
prefix: {
|
||||
label: any;
|
||||
url: string;
|
||||
md?: { name: string; value: any };
|
||||
}[];
|
||||
};
|
||||
|
||||
const navigateLink = async (link: FieldProp["link"], field: FieldLocal) => {
|
||||
let params = link.params(field);
|
||||
|
||||
if (typeof params === "object") {
|
||||
if (params instanceof Promise) {
|
||||
params = await params;
|
||||
}
|
||||
}
|
||||
|
||||
const md = params.md;
|
||||
const where = params.where;
|
||||
|
||||
const prefix: LinkParam["prefix"] = [];
|
||||
|
||||
if (md) {
|
||||
md.header.render();
|
||||
if (md.header.breadcrumb.length > 0) {
|
||||
const path = getPathname({ hash: false });
|
||||
let i = 0;
|
||||
for (const b of md.header.breadcrumb) {
|
||||
prefix.push({
|
||||
label: b.label,
|
||||
url: `${path}`,
|
||||
md:
|
||||
i > 0
|
||||
? { name: md.name, value: md.selected[md.pk?.name || ""] }
|
||||
: undefined,
|
||||
});
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const values: LinkParam = {
|
||||
url: getPathname({ hash: false }),
|
||||
where,
|
||||
prefix,
|
||||
hash: "",
|
||||
};
|
||||
const vhash = hashSum(values);
|
||||
values.hash = vhash;
|
||||
|
||||
if (!link.url) {
|
||||
alert("No URL defined!");
|
||||
return false;
|
||||
}
|
||||
await api._kv("set", vhash, values);
|
||||
const lnk = location.hash.split("#").find((e) => e.startsWith("lnk="));
|
||||
let prev_link = "";
|
||||
if (lnk) {
|
||||
prev_link = lnk.split("=").pop() || "";
|
||||
if (prev_link) prev_link = prev_link + "+";
|
||||
}
|
||||
|
||||
navigate(`${link.url}#lnk=${prev_link + vhash}`);
|
||||
return true;
|
||||
};
|
||||
|
||||
export const parseLink = () => {
|
||||
const lnk = location.hash.split("#").find((e) => e.startsWith("lnk="));
|
||||
|
||||
if (lnk) {
|
||||
const res = lnk.split("=").pop() || "";
|
||||
if (res) {
|
||||
return res.split("+");
|
||||
}
|
||||
}
|
||||
return [];
|
||||
};
|
||||
export const fetchLinkParams = async (
|
||||
parsed_link?: ReturnType<typeof parseLink>
|
||||
) => {
|
||||
const parsed = parsed_link || parseLink();
|
||||
|
||||
return await Promise.all(parsed.map((e) => api._kv("get", e)));
|
||||
};
|
||||
|
|
@ -60,11 +60,7 @@ export const generateForm = async (
|
|||
after_load: `
|
||||
if (typeof md === "object") {
|
||||
opt.fm.status = "ready";
|
||||
if (item) {
|
||||
for (const [k,v] of Object.entries(item)) {
|
||||
md.selected[k] = v;
|
||||
}
|
||||
}
|
||||
md.selected = opt.fm.data;
|
||||
md.header.render();
|
||||
md.render();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { GFCol } from "@/gen/utils";
|
||||
import { MutableRefObject, ReactElement, ReactNode } from "react";
|
||||
import { FC, MutableRefObject, ReactElement, ReactNode } from "react";
|
||||
import { editorFormData } from "./utils/ed-data";
|
||||
import { MDLocal } from "../md/utils/typings";
|
||||
|
||||
export type FMProps = {
|
||||
on_init: (arg: { fm: FMLocal; submit: any; reload: any }) => any;
|
||||
|
|
@ -46,6 +47,18 @@ export type FieldProp = {
|
|||
label: string;
|
||||
desc?: string;
|
||||
props?: any;
|
||||
link: {
|
||||
text:
|
||||
| string
|
||||
| ((arg: {
|
||||
field: FieldLocal;
|
||||
Link: FC<{ children: any; }>;
|
||||
}) => Promise<string> | string);
|
||||
url: string;
|
||||
params: (
|
||||
field: FieldLocal
|
||||
) => { md: MDLocal; where: any } | Promise<{ md: MDLocal; where: any }>;
|
||||
};
|
||||
fm: FMLocal;
|
||||
type: FieldType | (() => FieldType);
|
||||
required: ("y" | "n") | (() => "y" | "n");
|
||||
|
|
@ -142,7 +155,7 @@ export type FMInternal = {
|
|||
soft_delete: {
|
||||
field: any;
|
||||
};
|
||||
has_fields_container: boolean,
|
||||
has_fields_container: boolean;
|
||||
};
|
||||
export type FMLocal = FMInternal & { render: () => void };
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ import {
|
|||
} from "./utils/md-hash";
|
||||
import { mdRenderLoop } from "./utils/md-render-loop";
|
||||
import { MDLocalInternal, MDProps } from "./utils/typings";
|
||||
import { any } from "zod";
|
||||
|
||||
export const MasterDetail: FC<MDProps> = (arg) => {
|
||||
const {
|
||||
|
|
@ -36,8 +35,8 @@ export const MasterDetail: FC<MDProps> = (arg) => {
|
|||
status: isEditor ? "init" : "ready",
|
||||
actions: [],
|
||||
header: {
|
||||
loading: false,
|
||||
breadcrumb: [],
|
||||
internalRender() {},
|
||||
render: () => {},
|
||||
master: { prefix: null, suffix: null },
|
||||
child: { prefix: null, suffix: null },
|
||||
|
|
@ -60,6 +59,7 @@ export const MasterDetail: FC<MDProps> = (arg) => {
|
|||
item: _item,
|
||||
},
|
||||
params: {
|
||||
links: [],
|
||||
hash: {},
|
||||
tabs: {},
|
||||
parse: () => {
|
||||
|
|
@ -93,7 +93,9 @@ export const MasterDetail: FC<MDProps> = (arg) => {
|
|||
if (pk) {
|
||||
const value = md.params.hash[md.name];
|
||||
if (value) {
|
||||
if (!md.selected) {
|
||||
md.selected = { [pk.name]: value };
|
||||
}
|
||||
const tab = md.params.tabs[md.name];
|
||||
if (tab && md.tab.list.includes(tab)) {
|
||||
md.tab.active = tab;
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ export const generateMDForm = async (
|
|||
if (Object.keys(md.selected).length === 0){
|
||||
breads.push({ label: "Add New" });
|
||||
} else {
|
||||
breads.push({ label: "Edit" });
|
||||
breads.push({ label: guessLabel(md.selected) || "Detail" });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { GenFn } from "lib/gen/utils";
|
||||
import { generateMDForm } from "./md-form";
|
||||
import { generateMDList } from "./md-list";
|
||||
|
||||
import capitalize from "lodash.capitalize";
|
||||
const w = window as any;
|
||||
export const generateMasterDetail: GenFn<{
|
||||
item: PrasiItem;
|
||||
|
|
@ -16,7 +16,9 @@ export const generateMasterDetail: GenFn<{
|
|||
);
|
||||
const title = fn_title();
|
||||
if (!title && item.edit.props?.gen_table) {
|
||||
item.edit.setProp('title', item.edit.props?.gen_table)
|
||||
const table = { ...item.edit.props?.gen_table };
|
||||
table.value = `${capitalize(table.value as string)}`;
|
||||
item.edit.setProp("title", table);
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
import { useLocal } from "lib/utils/use-local";
|
||||
import { FC, useEffect } from "react";
|
||||
import { breadcrumbPrefix } from "../utils/md-hash";
|
||||
import { MDLocal, MDRef } from "../utils/typings";
|
||||
import { MDHeader } from "./MDHeader";
|
||||
import { useLocal } from "lib/utils/use-local";
|
||||
|
||||
export const should_show_tab = (md: MDLocal) => {
|
||||
if (isEditor) {
|
||||
|
|
@ -114,19 +115,8 @@ export const MDRenderTab: FC<{
|
|||
on_init: () => MDLocal;
|
||||
breadcrumb: () => Array<any>;
|
||||
}> = ({ child, on_init, breadcrumb }) => {
|
||||
const local = useLocal({ md: null as null | MDLocal });
|
||||
if (!local.md) {
|
||||
local.md = on_init();
|
||||
}
|
||||
const md = local.md;
|
||||
|
||||
md.header.render = () => {
|
||||
md.header.breadcrumb = breadcrumb();
|
||||
md.header.internalRender();
|
||||
};
|
||||
useEffect(() => {
|
||||
md.header.render();
|
||||
}, Object.values(md.deps || {}) || []);
|
||||
const md = on_init();
|
||||
md.header.child.breadcrumb = breadcrumb;
|
||||
|
||||
return <>{child}</>;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,11 +1,19 @@
|
|||
import { FC, useState } from "react";
|
||||
import { breadcrumbPrefix } from "../utils/md-hash";
|
||||
import { MDLocal, MDRef } from "../utils/typings";
|
||||
|
||||
export const MDHeader: FC<{ md: MDLocal; mdr: MDRef }> = ({ md, mdr }) => {
|
||||
const [_, set] = useState({});
|
||||
const head = mdr.item.edit.props?.header.value;
|
||||
const PassProp = mdr.PassProp;
|
||||
md.header.internalRender = () => set({});
|
||||
md.header.render = () => set({});
|
||||
|
||||
const prefix = breadcrumbPrefix(md);
|
||||
if (md.selected && md.header.child.breadcrumb) {
|
||||
md.header.breadcrumb = [...prefix, ...md.header.child.breadcrumb()];
|
||||
} else if (!md.selected && md.header.master.breadcrumb) {
|
||||
md.header.breadcrumb = [...prefix, ...md.header.master.breadcrumb()];
|
||||
}
|
||||
|
||||
return <PassProp md={md}>{head}</PassProp>;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { useLocal } from "lib/utils/use-local";
|
||||
import { FC, useEffect } from "react";
|
||||
import { MDLocal, MDRef } from "../utils/typings";
|
||||
import { MDHeader } from "./MDHeader";
|
||||
import { useLocal } from "lib/utils/use-local";
|
||||
|
||||
const w = window as unknown as {
|
||||
md_panel_master: any;
|
||||
|
|
@ -18,14 +18,9 @@ export const MDRenderMaster: FC<{
|
|||
local.md = on_init();
|
||||
}
|
||||
const md = local.md;
|
||||
|
||||
md.header.render = () => {
|
||||
md.header.breadcrumb = breadcrumb();
|
||||
md.header.internalRender();
|
||||
};
|
||||
md.header.master.breadcrumb = breadcrumb;
|
||||
|
||||
useEffect(() => {
|
||||
md.header.render();
|
||||
if (md) {
|
||||
let width = 0;
|
||||
let min_width = 0;
|
||||
|
|
|
|||
|
|
@ -40,7 +40,6 @@ export const editorMDInit = (md: MDLocal, mdr: MDRef, arg: MDProps) => {
|
|||
];
|
||||
md.status = "unready";
|
||||
} else {
|
||||
// md.header.breadcrumb = [];
|
||||
md.status = "ready";
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
import { fetchLinkParams, parseLink } from "lib/comps/form/field/type/TypeLink";
|
||||
import { MDLocal } from "./typings";
|
||||
import { BreadItem } from "lib/comps/custom/Breadcrumb";
|
||||
|
||||
export const masterDetailParseHash = (md: MDLocal) => {
|
||||
let raw_hash = decodeURIComponent(location.hash);
|
||||
|
|
@ -21,6 +23,27 @@ export const masterDetailParseHash = (md: MDLocal) => {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
const parsed_link = parseLink();
|
||||
let changed = parsed_link.length !== md.params.links.length;
|
||||
|
||||
if (!changed) {
|
||||
for (let i = 0; i < parsed_link.length; i++) {
|
||||
if (parsed_link[i] !== md.params.links[i].hash) {
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
md.params.links = [];
|
||||
md.header.loading = true;
|
||||
fetchLinkParams(parsed_link).then((links) => {
|
||||
md.params.links = links;
|
||||
md.header.loading = false;
|
||||
md.header.render();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export const masterDetailApplyParams = (md: MDLocal) => {
|
||||
|
|
@ -56,3 +79,40 @@ export const masterDetailApplyParams = (md: MDLocal) => {
|
|||
location.hash = hash;
|
||||
}
|
||||
};
|
||||
|
||||
export const breadcrumbPrefix = (md: MDLocal) => {
|
||||
const prefix: BreadItem[] = [];
|
||||
if (md.params.links && md.params.links.length > 0) {
|
||||
const hashes: string[] = [];
|
||||
for (const link of md.params.links) {
|
||||
if (!hashes.includes(link.hash)) {
|
||||
hashes.push(link.hash);
|
||||
}
|
||||
}
|
||||
for (const link of md.params.links) {
|
||||
for (const p of link.prefix) {
|
||||
prefix.push({
|
||||
label: p.label,
|
||||
onClick(ev) {
|
||||
let url = "";
|
||||
|
||||
const hashIndex = hashes.indexOf(link.hash);
|
||||
const link_hashes = hashes.slice(0, hashIndex).join("+");
|
||||
const lnk = link_hashes ? `#lnk=${link_hashes}` : ``;
|
||||
|
||||
if (p.md) {
|
||||
url = `${link.url}#${p.md.name}=${p.md.value}${lnk}`;
|
||||
} else {
|
||||
url = `${link.url}${lnk}`;
|
||||
}
|
||||
|
||||
if (url) {
|
||||
navigate(url);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
return prefix;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { BreadItem } from "@/comps/custom/Breadcrumb";
|
||||
import { FMLocal } from "@/comps/form/typings";
|
||||
import { GFCol } from "@/gen/utils";
|
||||
import { LinkParam } from "lib/comps/form/field/type/TypeLink";
|
||||
import { ReactNode } from "react";
|
||||
|
||||
type ID_MASTER_DETAIL = string;
|
||||
|
|
@ -38,11 +39,11 @@ export type MDLocalInternal = {
|
|||
title: string;
|
||||
status: "init" | "unready" | "ready";
|
||||
header: {
|
||||
loading: boolean;
|
||||
breadcrumb: BreadItem[];
|
||||
internalRender: () => void;
|
||||
render: () => void;
|
||||
master: { prefix: any; suffix: any };
|
||||
child: { prefix: any; suffix: any };
|
||||
master: { prefix: any; suffix: any; breadcrumb?: () => BreadItem[] };
|
||||
child: { prefix: any; suffix: any; breadcrumb?: () => BreadItem[] };
|
||||
};
|
||||
actions: MDActions;
|
||||
selected: any;
|
||||
|
|
@ -53,7 +54,8 @@ export type MDLocalInternal = {
|
|||
internal: { action_should_refresh: boolean };
|
||||
master: { render: () => void };
|
||||
params: {
|
||||
hash: any;
|
||||
links: LinkParam[];
|
||||
hash: Record<string, any>;
|
||||
tabs: any;
|
||||
parse: () => void;
|
||||
apply: () => void;
|
||||
|
|
|
|||
|
|
@ -1,28 +1,41 @@
|
|||
import { Skeleton } from "@/comps/ui/skeleton";
|
||||
import { cn } from "lib/utils";
|
||||
import { Loader2 } from "lucide-react";
|
||||
import { FC } from "react";
|
||||
|
||||
export const FieldLoading = () => {
|
||||
export const FieldLoading: FC<{ height?: "normal" | "short" }> = (prop) => {
|
||||
let height = "10px";
|
||||
if (prop.height === "short") height = "6px";
|
||||
return (
|
||||
<div className="field-inner c-flex c-flex-col c-space-y-1 c-p-1 c-justify-center">
|
||||
<div className="c-flex c-space-x-1">
|
||||
<Skeleton
|
||||
className={css`
|
||||
width: 50px;
|
||||
height: 10px;
|
||||
height: ${height};
|
||||
`}
|
||||
/>
|
||||
<Skeleton
|
||||
className={css`
|
||||
width: 50px;
|
||||
height: 10px;
|
||||
height: ${height};
|
||||
`}
|
||||
/>
|
||||
</div>
|
||||
<Skeleton
|
||||
className={css`
|
||||
width: 80px;
|
||||
height: 10px;
|
||||
height: ${height};
|
||||
`}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const Spinner = ({ className }: { className?: string }) => {
|
||||
return (
|
||||
<div className={className}>
|
||||
<Loader2 className={cx("c-h-4 c-w-4 c-animate-spin")} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
export { FieldLoading } from "@/comps/ui/field-loading";
|
||||
import { lazify, lazifyMany } from "@/utils/lazify";
|
||||
export { guessLabel } from "./utils/guess-label";
|
||||
export { fetchLinkParams } from "./comps/form/field/type/TypeLink";
|
||||
export { prasi_gen } from "./gen/prasi_gen";
|
||||
|
||||
export const Popover = lazify(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
import { validate } from "uuid";
|
||||
|
||||
export const guessLabel = (_obj: Record<string, any>, key?: string) => {
|
||||
let label = "";
|
||||
let obj = _obj;
|
||||
if (key) obj = _obj[key];
|
||||
|
||||
const label_key =
|
||||
Object.keys(obj)
|
||||
.map((e) => e.toLowerCase())
|
||||
.find((e) => e.includes("name") || e.includes("nama")) || "";
|
||||
|
||||
label = obj[label_key];
|
||||
if (obj.length > 1) {
|
||||
for (const v of Object.values(obj)) {
|
||||
if (typeof v === "string" && v.length >= 2 && !validate(v)) {
|
||||
label = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof label === "string" && label.length > 10) {
|
||||
label = label.substring(0, 10) + "…";
|
||||
}
|
||||
|
||||
return label;
|
||||
};
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
export const getPathname = (url?: string) => {
|
||||
export const getPathname = (opt?: { hash?: boolean }) => {
|
||||
if (
|
||||
["prasi.avolut.com"].includes(location.hostname) ||
|
||||
location.host === "localhost:4550"
|
||||
|
|
@ -9,11 +9,8 @@ export const getPathname = (url?: string) => {
|
|||
location.pathname.startsWith("/deploy")
|
||||
) {
|
||||
const hash = location.hash;
|
||||
if (url?.startsWith("/prod")) {
|
||||
return "/" + url.split("/").slice(3).join("/");
|
||||
}
|
||||
|
||||
if (hash !== "") {
|
||||
if (hash !== "" && opt?.hash !== false) {
|
||||
return "/" + location.pathname.split("/").slice(3).join("/") + hash;
|
||||
} else {
|
||||
return "/" + location.pathname.split("/").slice(3).join("/");
|
||||
|
|
|
|||
Loading…
Reference in New Issue