fix lib
This commit is contained in:
parent
c35c4c41f8
commit
cf2b1c02e5
|
|
@ -18,7 +18,7 @@ export function AutoHeightTextarea({
|
|||
paddingBottom,
|
||||
paddingTop,
|
||||
} = window.getComputedStyle(ref.current);
|
||||
ref.current.style.height = lineHeight; // set height temporarily to a single row to obtain scrollHeight, disregarding empty space after text (otherwise, scrollHeight would be equal to the height of the element) - this solves auto-shrinking of the textarea (it's not needed for auto-growing it)
|
||||
ref.current.style.minHeight = lineHeight; // set height temporarily to a single row to obtain scrollHeight, disregarding empty space after text (otherwise, scrollHeight would be equal to the height of the element) - this solves auto-shrinking of the textarea (it's not needed for auto-growing it)
|
||||
const { scrollHeight } = ref.current; // scrollHeight = content height + padding top + padding bottom
|
||||
|
||||
if (boxSizing === "border-box") {
|
||||
|
|
@ -32,12 +32,12 @@ export function AutoHeightTextarea({
|
|||
scrollHeight +
|
||||
parseFloat(borderTopWidth) +
|
||||
parseFloat(borderBottomWidth);
|
||||
ref.current.style.height = `${Math.max(minHeight, allTextHeight)}px`;
|
||||
ref.current.style.minHeight = `${Math.max(minHeight, allTextHeight)}px`;
|
||||
} else if (boxSizing === "content-box") {
|
||||
const minHeight = parseFloat(lineHeight) * minRows;
|
||||
const allTextHeight =
|
||||
scrollHeight - parseFloat(paddingTop) - parseFloat(paddingBottom);
|
||||
ref.current.style.height = `${Math.max(minHeight, allTextHeight)}px`;
|
||||
ref.current.style.minHeight = `${Math.max(minHeight, allTextHeight)}px`;
|
||||
} else {
|
||||
console.error("Unknown box-sizing value.");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ export const Breadcrumb: FC<BreadcrumbProps> = (_arg) => {
|
|||
|
||||
if (local.status === "init") {
|
||||
let should_load = true;
|
||||
local.status = "loading";
|
||||
|
||||
if (isEditor && item && breadcrumbData[item.id]) {
|
||||
local.list = breadcrumbData[item.id];
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
import { useLocal } from "@/utils/use-local";
|
||||
import get from "lodash.get";
|
||||
import { FC, useRef } from "react";
|
||||
import { FC, useEffect, useRef } from "react";
|
||||
import { createPortal } from "react-dom";
|
||||
import { Toaster } from "sonner";
|
||||
import { FMInternal, FMProps } from "./typings";
|
||||
import { editorFormData } from "./utils/ed-data";
|
||||
import { formInit } from "./utils/init";
|
||||
import { formReload } from "./utils/reload";
|
||||
import { getPathname } from "../../..";
|
||||
|
||||
const editorFormWidth = {} as Record<string, { w: number; f: any }>;
|
||||
|
||||
|
|
@ -83,6 +84,13 @@ export const Form: FC<FMProps> = (props) => {
|
|||
}),
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (fm.status === "ready") {
|
||||
fm.status = "init";
|
||||
fm.render();
|
||||
}
|
||||
}, [getPathname()]);
|
||||
|
||||
if (fm.status === "init") {
|
||||
formInit(fm, props);
|
||||
fm.reload();
|
||||
|
|
@ -95,6 +103,14 @@ export const Form: FC<FMProps> = (props) => {
|
|||
}
|
||||
const toaster_el = document.getElementsByClassName("prasi-toaster")[0];
|
||||
|
||||
if (fm.status === "resizing") {
|
||||
setTimeout(() => {
|
||||
fm.status = "ready";
|
||||
fm.render();
|
||||
}, 100);
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<form
|
||||
onSubmit={(e) => {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,58 @@
|
|||
import { FMLocal } from "../typings";
|
||||
import { BaseLabel } from "./BaseLabel";
|
||||
import { BaseFieldProps } from "./utils/type/field";
|
||||
import { useField } from "./utils/use-field";
|
||||
|
||||
export const BaseField = <T extends Record<string, any>>(
|
||||
arg: BaseFieldProps<T> & { fm: FMLocal }
|
||||
) => {
|
||||
const field = useField<T>(arg);
|
||||
const fm = arg.fm;
|
||||
const mode = fm.props.label_mode || "vertical";
|
||||
const props = arg.props;
|
||||
const w = field.width;
|
||||
const errors = fm.error.get(field.name);
|
||||
|
||||
return (
|
||||
<label
|
||||
className={cx(
|
||||
"field",
|
||||
"c-flex",
|
||||
css`
|
||||
padding: 5px 0px 0px 10px;
|
||||
`,
|
||||
w === "auto" && fm.size.field === "full" && "c-w-full",
|
||||
w === "auto" && fm.size.field === "half" && "c-w-1/2",
|
||||
w === "full" && "c-w-full",
|
||||
w === "¾" && "c-w-3/4",
|
||||
w === "½" && "c-w-1/2",
|
||||
w === "⅓" && "c-w-1/3",
|
||||
w === "¼" && "c-w-1/4",
|
||||
mode === "horizontal" && "c-flex-row c-items-center",
|
||||
mode === "vertical" && "c-flex-col c-space-y-1"
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{mode !== "hidden" && <BaseLabel field={field} fm={fm} />}
|
||||
<div className="field-inner c-flex c-flex-1 c-flex-col">
|
||||
{field.desc && (
|
||||
<div className={cx("c-p-2 c-text-xs", errors.length > 0 && "c-pb-1")}>
|
||||
{field.desc}
|
||||
</div>
|
||||
)}
|
||||
{errors.length > 0 && (
|
||||
<div
|
||||
className={cx(
|
||||
"c-p-2 c-text-xs c-text-red-600",
|
||||
field.desc && "c-pt-0"
|
||||
)}
|
||||
>
|
||||
{errors.map((err) => {
|
||||
return <div>{err}</div>;
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</label>
|
||||
);
|
||||
};
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
import { useLocal } from "@/utils/use-local";
|
||||
import { FC } from "react";
|
||||
import { BaseField } from "./BaseField";
|
||||
import { initSimpleForm as initBaseForm } from "./utils/init";
|
||||
import { BaseFieldProps, BaseFormProps } from "./utils/type/field";
|
||||
|
||||
type Children<T extends Record<string, any>> = Exclude<
|
||||
BaseFormProps<T>["children"],
|
||||
undefined
|
||||
>;
|
||||
|
||||
export const BaseForm = <T extends Record<string, any>>(
|
||||
arg: BaseFormProps<T>
|
||||
) => {
|
||||
const fm = useLocal<FMInternal>({ status: "init" } as any);
|
||||
const local = useLocal({
|
||||
Field: null as null | FC<BaseFieldProps<T>>,
|
||||
children: null as unknown as Children<T>,
|
||||
});
|
||||
|
||||
if (fm.status === "init") {
|
||||
local.Field = (props) => {
|
||||
return <BaseField fm={fm} {...props} />;
|
||||
};
|
||||
if (arg.children) local.children = arg.children;
|
||||
else {
|
||||
local.children = ({ Field }) => {
|
||||
const data = Object.entries(fm.data);
|
||||
return (
|
||||
<>
|
||||
{data.map(([key, value]) => {
|
||||
return <Field name={key} />;
|
||||
})}
|
||||
</>
|
||||
);
|
||||
};
|
||||
}
|
||||
initBaseForm(fm, arg);
|
||||
}
|
||||
|
||||
return (
|
||||
<form
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
fm.submit();
|
||||
}}
|
||||
>
|
||||
{local.Field && local.children({ Field: local.Field })}
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
import { FC } from "react";
|
||||
import { BaseFieldLocal } from "./utils/type/field";
|
||||
import { FMLocal } from "../typings";
|
||||
|
||||
export const BaseLabel = <T extends Record<string, any>>({
|
||||
field,
|
||||
fm,
|
||||
}: {
|
||||
field: BaseFieldLocal<T>;
|
||||
fm: FMLocal;
|
||||
}) => {
|
||||
const errors = fm.error.get(field.name);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cx(
|
||||
"label c-text-sm c-flex c-items-center",
|
||||
fm.props.label_mode === "horizontal" &&
|
||||
css`
|
||||
width: ${fm.props.label_width}px;
|
||||
`,
|
||||
fm.props.label_mode === "vertical" && "c-mt-3"
|
||||
)}
|
||||
>
|
||||
<span className={cx(errors.length > 0 && `c-text-red-600`)}>
|
||||
{field.label}
|
||||
</span>
|
||||
{field.required && (
|
||||
<span className="c-text-red-600 c-mb-2 c-ml-1">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="12"
|
||||
height="12"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
<path d="M12 6v12" />
|
||||
<path d="M17.196 9 6.804 15" />
|
||||
<path d="m6.804 9 10.392 6" />
|
||||
</svg>
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
import { FMLocal } from "../../typings";
|
||||
import { formError } from "../../utils/error";
|
||||
import { reloadBaseForm } from "./reload";
|
||||
import { submitBaseForm } from "./submit";
|
||||
import { BaseFormProps } from "./type/field";
|
||||
|
||||
export const initSimpleForm = <T extends Record<string, any>>(
|
||||
fm: FMLocal,
|
||||
arg: BaseFormProps<T>
|
||||
) => {
|
||||
fm.data = {};
|
||||
fm.error = formError(fm);
|
||||
fm.events = {
|
||||
on_change(name, new_value) {},
|
||||
};
|
||||
fm.field_def = {};
|
||||
fm.fields = {};
|
||||
fm.internal = {
|
||||
submit: { done: [], promises: [], timeout: null as any },
|
||||
reload: { done: [], promises: [], timeout: null as any },
|
||||
};
|
||||
fm.props = {} as any;
|
||||
fm.reload = async () => {
|
||||
await reloadBaseForm(fm, arg);
|
||||
};
|
||||
fm.size = {
|
||||
width: 0,
|
||||
height: 0,
|
||||
field: "full",
|
||||
};
|
||||
fm.submit = () => {
|
||||
return submitBaseForm(fm, arg);
|
||||
};
|
||||
fm.status = "ready";
|
||||
};
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
import { FMLocal } from "../../typings";
|
||||
import { BaseFormProps } from "./type/field";
|
||||
|
||||
export const reloadBaseForm = async <T extends Record<string, any>>(
|
||||
fm: FMLocal,
|
||||
arg: BaseFormProps<T>
|
||||
) => {};
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
import { FMLocal } from "../../typings";
|
||||
import { BaseFormProps } from "./type/field";
|
||||
|
||||
export const submitBaseForm = async <T extends Record<string, any>>(
|
||||
fm: FMLocal,
|
||||
arg: BaseFormProps<T>
|
||||
) => {
|
||||
return true;
|
||||
};
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
import { FC, ReactNode } from "react";
|
||||
|
||||
|
||||
export type BaseFieldWidth =
|
||||
| "auto"
|
||||
| "full"
|
||||
| "¾"
|
||||
| "½"
|
||||
| "⅓"
|
||||
| "¼"
|
||||
| "1/2"
|
||||
| "1/3"
|
||||
| "1/4"
|
||||
| "3/4";
|
||||
|
||||
export type BaseFieldType = "text" | "relation";
|
||||
export type BaseFieldProps<T extends Record<string, any>> = {
|
||||
name: keyof T;
|
||||
label?: string;
|
||||
type?: BaseFieldType;
|
||||
props?: any;
|
||||
desc?: string;
|
||||
on_change?: (arg: { value: any }) => void | Promise<void>;
|
||||
|
||||
prefix?: any;
|
||||
suffix?: any;
|
||||
|
||||
required?: boolean;
|
||||
required_msg?: (name: string) => string;
|
||||
disabled?: boolean;
|
||||
|
||||
width?: BaseFieldWidth;
|
||||
};
|
||||
|
||||
export type BaseFieldInternal<T extends Record<string, any>> = {
|
||||
name: keyof T;
|
||||
label: ReactNode;
|
||||
type: BaseFieldType;
|
||||
desc: string;
|
||||
on_change: (arg: { value: any }) => void | Promise<void>;
|
||||
|
||||
prefix: any;
|
||||
suffix: any;
|
||||
|
||||
required: boolean;
|
||||
required_msg: (name: string) => string;
|
||||
disabled: boolean;
|
||||
|
||||
width: BaseFieldWidth;
|
||||
status: "init" | "loading" | "ready";
|
||||
|
||||
PassProp: any;
|
||||
child: any;
|
||||
};
|
||||
|
||||
export type BaseFieldLocal<T extends Record<string, any>> =
|
||||
BaseFieldInternal<T>;
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
import { FC, ReactNode } from "react";
|
||||
import { BaseFieldProps } from "./field";
|
||||
|
||||
export type BaseFormProps<T extends Record<string, any>> = {
|
||||
onLoad: () => Promise<T | null>;
|
||||
onSubmit: (arg: { data: T | null }) => Promise<boolean>;
|
||||
children?: (arg: { Field: FC<BaseFieldProps<T>> }) => ReactNode;
|
||||
};
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
import { useLocal } from "@/utils/use-local";
|
||||
import { FMLocal } from "../../typings";
|
||||
import {
|
||||
BaseFieldInternal,
|
||||
BaseFieldLocal,
|
||||
BaseFieldProps,
|
||||
} from "./type/field";
|
||||
import { formatName } from "@/gen/utils";
|
||||
|
||||
export const useField = <T extends Record<string, any>>(
|
||||
arg: BaseFieldProps<T> & { fm: FMLocal }
|
||||
) => {
|
||||
const fm = arg.fm;
|
||||
const local = useLocal<BaseFieldInternal<T>>({
|
||||
status: "init",
|
||||
} as any) as BaseFieldLocal<T>;
|
||||
|
||||
if (local.status === "init") {
|
||||
local.name = arg.name;
|
||||
local.label = arg.label || formatName(arg.name as string);
|
||||
local.type = arg.type || "text";
|
||||
local.desc = arg.desc || "";
|
||||
local.status = "ready";
|
||||
local.width = arg.width || fm.size.field === "full" ? "full" : "1/2";
|
||||
}
|
||||
|
||||
return local as BaseFieldLocal<T>;
|
||||
};
|
||||
|
|
@ -2,9 +2,10 @@ import { FC } from "react";
|
|||
import { FMLocal, FieldLocal } from "../../typings";
|
||||
import { useLocal } from "@/utils/use-local";
|
||||
import parser from "any-date-parser";
|
||||
import { AutoHeightTextarea } from "@/comps/custom/AutoHeightTextarea";
|
||||
|
||||
export type PropTypeText = {
|
||||
type: "text" | "password" | "number" | "date" | "datetime";
|
||||
type: "text" | "password" | "number" | "date" | "datetime" | "textarea";
|
||||
};
|
||||
|
||||
const parse = parser.exportAsFunctionAny("en-US");
|
||||
|
|
@ -31,6 +32,26 @@ export const FieldTypeText: FC<{
|
|||
|
||||
return (
|
||||
<>
|
||||
{prop.type === "textarea" ? (
|
||||
<AutoHeightTextarea
|
||||
onChange={(ev) => {
|
||||
fm.data[field.name] = ev.currentTarget.value;
|
||||
fm.render();
|
||||
}}
|
||||
value={value || ""}
|
||||
disabled={field.disabled}
|
||||
className="c-flex-1 c-bg-transparent c-outline-none c-p-2 c-text-sm c-w-full"
|
||||
spellCheck={false}
|
||||
onFocus={() => {
|
||||
field.focused = true;
|
||||
field.render();
|
||||
}}
|
||||
onBlur={() => {
|
||||
field.focused = false;
|
||||
field.render();
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<input
|
||||
type={prop.type}
|
||||
onChange={(ev) => {
|
||||
|
|
@ -50,6 +71,7 @@ export const FieldTypeText: FC<{
|
|||
field.render();
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -73,9 +73,9 @@ export type FMInternal = {
|
|||
error: {
|
||||
readonly object: Record<string, string>;
|
||||
readonly list: { name: string; error: string[] }[];
|
||||
set: (name: string, error: string[]) => void;
|
||||
get: (name: string) => string[];
|
||||
clear: (name?: string) => void;
|
||||
set: (name: string | number | symbol, error: string[]) => void;
|
||||
get: (name: string | number | symbol) => string[];
|
||||
clear: (name?: string | number | symbol) => void;
|
||||
};
|
||||
internal: {
|
||||
reload: {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
export const getPathname = () => {
|
||||
export const getPathname = (url?: string) => {
|
||||
// if (["localhost", "prasi.avolut.com"].includes(location.hostname)) {
|
||||
if (
|
||||
location.pathname.startsWith("/vi") ||
|
||||
|
|
@ -6,6 +6,9 @@ export const getPathname = () => {
|
|||
location.pathname.startsWith("/deploy")
|
||||
) {
|
||||
const hash = location.hash;
|
||||
if (url?.startsWith("/prod")) {
|
||||
return "/" + url.split("/").slice(3).join("/");
|
||||
}
|
||||
|
||||
if (hash !== "") {
|
||||
return "/" + location.pathname.split("/").slice(3).join("/") + hash;
|
||||
|
|
|
|||
Loading…
Reference in New Issue