wip fix
This commit is contained in:
parent
1dc4da9a68
commit
ec2c0d55e5
|
|
@ -1,20 +1,20 @@
|
|||
import { Popover } from "@/comps/custom/Popover";
|
||||
import { Input } from "@/comps/ui/input";
|
||||
import { Skeleton } from "@/comps/ui/skeleton";
|
||||
import { useLocal } from "@/utils/use-local";
|
||||
import { ChevronDown } from "lucide-react";
|
||||
import { FC, useEffect } from "react";
|
||||
import type { ControllerRenderProps, FieldValues } from "react-hook-form";
|
||||
import { FieldListItem, FieldOptions } from "../type";
|
||||
import { FormHook } from "../utils/utils";
|
||||
import { Skeleton } from "@/comps/ui/skeleton";
|
||||
|
||||
export const Dropdown: FC<
|
||||
ControllerRenderProps<FieldValues, string> & {
|
||||
options: FieldOptions;
|
||||
form?: FormHook;
|
||||
name: string;
|
||||
}
|
||||
> = ({ value, options, form, name }) => {
|
||||
type DropdownProps = {
|
||||
value: string;
|
||||
options: FieldOptions;
|
||||
form?: FormHook;
|
||||
name: string;
|
||||
};
|
||||
|
||||
export const Dropdown: FC<DropdownProps> = ({ value, options, form, name }) => {
|
||||
const local = useLocal({
|
||||
status: "loading" as "loading" | "ready",
|
||||
open: false,
|
||||
|
|
@ -62,7 +62,7 @@ export const Dropdown: FC<
|
|||
|
||||
if (local.filter) {
|
||||
filtered = local.list.filter((e) => {
|
||||
if (e.value.toLowerCase().includes(local.filter)) return true;
|
||||
if (e.label.toLowerCase().includes(local.filter)) return true;
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,195 @@
|
|||
import { Popover } from "@/comps/custom/Popover";
|
||||
import { Input } from "@/comps/ui/input";
|
||||
import { Skeleton } from "@/comps/ui/skeleton";
|
||||
import { useLocal } from "@/utils/use-local";
|
||||
import { ChevronDown } from "lucide-react";
|
||||
import { FC, useEffect } from "react";
|
||||
import { FieldListItem, FieldOptions } from "../type";
|
||||
import { FormHook } from "../utils/utils";
|
||||
|
||||
type RelationProps = {
|
||||
value: string;
|
||||
relation: {
|
||||
table: string;
|
||||
fields: string[];
|
||||
query: () => Promise<any>;
|
||||
};
|
||||
form?: FormHook;
|
||||
name: string;
|
||||
};
|
||||
|
||||
export const Relation: FC<RelationProps> = ({
|
||||
relation,
|
||||
value,
|
||||
form,
|
||||
name,
|
||||
}) => {
|
||||
const local = useLocal({
|
||||
status: "loading" as "loading" | "ready",
|
||||
open: false,
|
||||
ref: { input: null as null | HTMLInputElement },
|
||||
list: [] as FieldListItem[],
|
||||
input: "",
|
||||
label: "",
|
||||
filter: "",
|
||||
pk: "",
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
if (form) {
|
||||
local.status = "loading";
|
||||
local.render();
|
||||
const table_fn = (db as any)[relation.table];
|
||||
const select = {} as any;
|
||||
local.pk = "";
|
||||
for (const f of relation.fields) {
|
||||
if (f.startsWith("::")) {
|
||||
select[f.substring(2)] = true;
|
||||
local.pk = f.substring(2);
|
||||
} else {
|
||||
select[f] = true;
|
||||
}
|
||||
}
|
||||
let q = {};
|
||||
|
||||
if (typeof relation.query === "function") {
|
||||
q = await relation.query();
|
||||
}
|
||||
|
||||
const list = await table_fn.findMany({ select, ...q });
|
||||
if (Array.isArray(list)) {
|
||||
local.list = list.map((item: any) => {
|
||||
let label = [];
|
||||
for (const [k, v] of Object.entries(item)) {
|
||||
if (k !== local.pk) label.push(v);
|
||||
}
|
||||
return { value: item[local.pk], label: label.join(" - ") };
|
||||
});
|
||||
}
|
||||
|
||||
const found = local.list.find((e) => e.value === value);
|
||||
if (found) {
|
||||
local.label = found.label;
|
||||
}
|
||||
|
||||
local.status = "ready";
|
||||
local.render();
|
||||
}
|
||||
})();
|
||||
}, [relation]);
|
||||
|
||||
let filtered = local.list;
|
||||
|
||||
if (local.filter) {
|
||||
filtered = local.list.filter((e) => {
|
||||
if (e.label.toLowerCase().includes(local.filter)) return true;
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<Popover
|
||||
open={local.open}
|
||||
onOpenChange={() => {
|
||||
local.open = false;
|
||||
local.render();
|
||||
}}
|
||||
arrow={false}
|
||||
className={cx("c-rounded-sm c-bg-white")}
|
||||
content={
|
||||
<div
|
||||
className={cx(
|
||||
"c-text-sm",
|
||||
css`
|
||||
width: ${local.ref.input?.clientWidth || 100}px;
|
||||
`
|
||||
)}
|
||||
>
|
||||
{local.status === "loading" && (
|
||||
<>
|
||||
<div className="c-flex c-flex-col c-space-y-1 c-px-3 c-py-2">
|
||||
<Skeleton className="c-h-[10px] c-w-[90px]" />
|
||||
<Skeleton className="c-h-[10px] c-w-[60px]" />
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{local.status === "ready" && (
|
||||
<>
|
||||
{filtered.map((item, idx) => {
|
||||
return (
|
||||
<div
|
||||
tabIndex={0}
|
||||
key={item.value + "_" + idx}
|
||||
className={cx(
|
||||
"c-px-3 c-py-1 cursor-pointer option-item",
|
||||
item.value === value
|
||||
? "c-bg-blue-600 c-text-white"
|
||||
: "hover:c-bg-blue-50",
|
||||
idx > 0 && "c-border-t",
|
||||
idx === 0 && "c-rounded-t-sm",
|
||||
idx === local.list.length - 1 && "c-rounded-b-sm"
|
||||
)}
|
||||
onClick={() => {
|
||||
if (form) {
|
||||
local.open = false;
|
||||
form.hook.setValue(name, item.value);
|
||||
form.render();
|
||||
}
|
||||
}}
|
||||
>
|
||||
{item.label}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<div
|
||||
className={cx(
|
||||
"c-relative",
|
||||
css`
|
||||
cursor: pointer !important;
|
||||
`
|
||||
)}
|
||||
tabIndex={0}
|
||||
onFocus={() => {
|
||||
local.open = true;
|
||||
local.input = local.label;
|
||||
local.filter = "";
|
||||
local.render();
|
||||
setTimeout(() => {
|
||||
local.ref.input?.focus();
|
||||
});
|
||||
}}
|
||||
>
|
||||
<div className="c-absolute c-pointer-events-none c-inset-0 c-left-auto c-flex c-items-center c-pr-4">
|
||||
<ChevronDown size={14} />
|
||||
</div>
|
||||
<Input
|
||||
spellCheck={false}
|
||||
value={local.open ? local.input : ""}
|
||||
className={cx(
|
||||
local.open ? "c-cursor-pointer" : "c-pointer-events-none"
|
||||
)}
|
||||
onChange={(e) => {
|
||||
local.input = e.currentTarget.value;
|
||||
local.filter = local.input.toLowerCase();
|
||||
local.render();
|
||||
}}
|
||||
ref={(el) => {
|
||||
local.ref.input = el;
|
||||
}}
|
||||
type="text"
|
||||
/>
|
||||
{!local.open && (
|
||||
<div className="c-absolute c-text-sm c-inset-0 c-px-3 c-flex c-items-center">
|
||||
{local.label}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Popover>
|
||||
);
|
||||
};
|
||||
|
|
@ -19,6 +19,7 @@ import { SliderOptions } from "./Slider/types";
|
|||
import { FormHook, modify } from "./utils/utils";
|
||||
import { Dropdown } from "./Dropdown";
|
||||
import { FieldOptions } from "./type";
|
||||
import { Relation } from "./Dropdown/relation";
|
||||
|
||||
export const Field: FC<{
|
||||
name: string;
|
||||
|
|
@ -30,6 +31,7 @@ export const Field: FC<{
|
|||
| "number"
|
||||
| "textarea"
|
||||
| "dropdown"
|
||||
| "relation"
|
||||
| "password"
|
||||
| "radio"
|
||||
| "date"
|
||||
|
|
@ -50,6 +52,9 @@ export const Field: FC<{
|
|||
label_alt:
|
||||
| ReactNode
|
||||
| FC<{ modify: typeof modify; data: any; current_name: string }>;
|
||||
rel_table: string;
|
||||
rel_fields: string[];
|
||||
rel_query: () => any;
|
||||
}> = ({
|
||||
name,
|
||||
form,
|
||||
|
|
@ -67,6 +72,9 @@ export const Field: FC<{
|
|||
child,
|
||||
placeholder,
|
||||
label_alt,
|
||||
rel_fields,
|
||||
rel_table,
|
||||
rel_query,
|
||||
}) => {
|
||||
const value = form?.hook.getValues()[name];
|
||||
const local = useLocal({
|
||||
|
|
@ -184,10 +192,11 @@ export const Field: FC<{
|
|||
|
||||
{["text", "number", "password"].includes(type) &&
|
||||
(suffix !== "" ? (
|
||||
<div className="c-relative">
|
||||
<div className="c-flex c-items-stretch">
|
||||
<Input
|
||||
{...field}
|
||||
type={type}
|
||||
className="c-flex-1 c-rounded-r-none focus:c-rounded-r-none c-pr-1 c-border-r-0"
|
||||
placeholder={placeholder}
|
||||
onChangeCapture={
|
||||
typeof on_change === "function"
|
||||
|
|
@ -197,7 +206,7 @@ export const Field: FC<{
|
|||
: undefined
|
||||
}
|
||||
></Input>
|
||||
<span className="c-p-[7px] c-absolute c-top-1/2 c-right-0 c-transform -c-translate-y-1/2 c-text-base c-rounded c-bg-[#D3D3D5]">
|
||||
<span className="c-p-[7px] c-rounded-r c-bg-[#D3D3D5]">
|
||||
{suffix || "-"}
|
||||
</span>
|
||||
</div>
|
||||
|
|
@ -221,10 +230,23 @@ export const Field: FC<{
|
|||
|
||||
{type === "dropdown" && (
|
||||
<Dropdown
|
||||
{...field}
|
||||
options={options}
|
||||
form={form}
|
||||
name={name}
|
||||
value={field.value}
|
||||
/>
|
||||
)}
|
||||
|
||||
{type === "relation" && (
|
||||
<Relation
|
||||
relation={{
|
||||
fields: rel_fields,
|
||||
table: rel_table,
|
||||
query: rel_query,
|
||||
}}
|
||||
form={form}
|
||||
name={name}
|
||||
value={field.value}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue