fix multi-file upload
This commit is contained in:
parent
2122f74cc3
commit
b802de4505
|
|
@ -1,12 +1,38 @@
|
||||||
import { ExternalLink } from "lucide-react";
|
import { ExternalLink } from "lucide-react";
|
||||||
|
|
||||||
export const FilePreview = ({ url }: { url: string }) => {
|
export const FilePreview = ({
|
||||||
|
url,
|
||||||
|
variant,
|
||||||
|
}: {
|
||||||
|
url: string;
|
||||||
|
variant?: "thumb";
|
||||||
|
}) => {
|
||||||
const file = getFileName(url);
|
const file = getFileName(url);
|
||||||
|
if (typeof file === "string")
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={cx(
|
||||||
|
css`
|
||||||
|
border-radius: 3px;
|
||||||
|
padding: 0px 5px;
|
||||||
|
height: 20px;
|
||||||
|
margin-right: 5px;
|
||||||
|
height: 20px;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
background: white;
|
||||||
|
`,
|
||||||
|
"c-flex c-items-center c-text-sm"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{file}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
const color = darkenColor(generateRandomColor(file.extension));
|
const color = darkenColor(generateRandomColor(file.extension));
|
||||||
let content = (
|
let content = (
|
||||||
<div
|
<div
|
||||||
className={cx(
|
className={cx(
|
||||||
css`
|
css`
|
||||||
|
background: white;
|
||||||
border: 1px solid ${color};
|
border: 1px solid ${color};
|
||||||
color: ${color};
|
color: ${color};
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
|
|
@ -22,12 +48,46 @@ export const FilePreview = ({ url }: { url: string }) => {
|
||||||
{file.extension}
|
{file.extension}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (variant === "thumb") {
|
||||||
|
content = (
|
||||||
|
<div
|
||||||
|
className={cx(
|
||||||
|
css`
|
||||||
|
background: white;
|
||||||
|
color: ${color};
|
||||||
|
border: 1px solid ${color};
|
||||||
|
color: ${color};
|
||||||
|
border-radius: 3px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: black;
|
||||||
|
padding: 3px 7px;
|
||||||
|
margin-left: 5px;
|
||||||
|
height: 30px;
|
||||||
|
`,
|
||||||
|
"c-flex c-items-center"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{file.extension}
|
||||||
|
|
||||||
|
<div className="c-ml-1">
|
||||||
|
<ExternalLink size="12px" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (url.startsWith("_file/")) {
|
if (url.startsWith("_file/")) {
|
||||||
if ([".png", ".jpeg", ".jpg", ".webp"].find((e) => url.endsWith(e))) {
|
if ([".png", ".jpeg", ".jpg", ".webp"].find((e) => url.endsWith(e))) {
|
||||||
content = (
|
content = (
|
||||||
<img
|
<img
|
||||||
className="c-py-1 c-rounded-md"
|
className="c-py-1 c-rounded-md"
|
||||||
src={siteurl(`/_img/${url.substring("_file/".length)}?w=100&h=20`)}
|
src={siteurl(
|
||||||
|
`/_img/${url.substring("_file/".length)}?${
|
||||||
|
variant === "thumb" ? "w=95&h=95" : "w=100&h=20"
|
||||||
|
}`
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -36,16 +96,33 @@ export const FilePreview = ({ url }: { url: string }) => {
|
||||||
<>
|
<>
|
||||||
{file.extension && (
|
{file.extension && (
|
||||||
<div
|
<div
|
||||||
className="c-flex c-border c-rounded c-items-center c-px-1 c-pr-2 c-bg-white hover:c-bg-blue-50 c-cursor-pointer"
|
className={cx(
|
||||||
|
"c-flex c-border c-rounded c-items-center c-px-1 c-bg-white c-cursor-pointer",
|
||||||
|
variant !== "thumb"
|
||||||
|
? "c-pr-2"
|
||||||
|
: css`
|
||||||
|
width: 95px;
|
||||||
|
max-height: 95px;
|
||||||
|
min-height: 50px;
|
||||||
|
`,
|
||||||
|
css`
|
||||||
|
&:hover {
|
||||||
|
border: 1px solid #1c4ed8;
|
||||||
|
outline: 1px solid #1c4ed8;
|
||||||
|
}
|
||||||
|
`
|
||||||
|
)}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
let _url = siteurl(url || "");
|
let _url = siteurl(url || "");
|
||||||
window.open(_url, "_blank");
|
window.open(_url, "_blank");
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{content}
|
{content}
|
||||||
|
{variant !== "thumb" && (
|
||||||
<div className="c-ml-2">
|
<div className="c-ml-2">
|
||||||
<ExternalLink size="12px" />
|
<ExternalLink size="12px" />
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
|
@ -90,6 +167,17 @@ function generateRandomColor(str: string): string {
|
||||||
return color;
|
return color;
|
||||||
}
|
}
|
||||||
const getFileName = (url: string) => {
|
const getFileName = (url: string) => {
|
||||||
|
if (url.startsWith("[")) {
|
||||||
|
try {
|
||||||
|
const list = JSON.parse(url);
|
||||||
|
if (list.length === 0) return "Empty";
|
||||||
|
return `${list.length} File${list.length > 1 ? "s" : ""}`;
|
||||||
|
} catch (e) {
|
||||||
|
console.error(`Error parsing multi-file: ${url}`);
|
||||||
|
}
|
||||||
|
return "Unknown File";
|
||||||
|
}
|
||||||
|
|
||||||
const fileName = url.substring(url.lastIndexOf("/") + 1);
|
const fileName = url.substring(url.lastIndexOf("/") + 1);
|
||||||
const dotIndex = fileName.lastIndexOf(".");
|
const dotIndex = fileName.lastIndexOf(".");
|
||||||
const fullname = fileName;
|
const fullname = fileName;
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,6 @@ export const FieldTypeInput: FC<{
|
||||||
|
|
||||||
// let value: any = "2024-05-14T05:58:01.376Z" // case untuk date time
|
// let value: any = "2024-05-14T05:58:01.376Z" // case untuk date time
|
||||||
field.input = input;
|
field.input = input;
|
||||||
if (!field.prop) field.prop = prop;
|
|
||||||
if (["date", "datetime", "datetime-local", "time"].includes(type_field)) {
|
if (["date", "datetime", "datetime-local", "time"].includes(type_field)) {
|
||||||
if (typeof value === "string" || value instanceof Date) {
|
if (typeof value === "string" || value instanceof Date) {
|
||||||
let date = parse(value);
|
let date = parse(value);
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,8 @@
|
||||||
import { useLocal } from "@/utils/use-local";
|
|
||||||
import get from "lodash.get";
|
|
||||||
import { Loader2, Paperclip, Trash2, Upload } from "lucide-react";
|
|
||||||
import { FC } from "react";
|
import { FC } from "react";
|
||||||
import * as XLSX from "xlsx";
|
|
||||||
import { FMLocal, FieldLocal, FieldProp } from "../../typings";
|
import { FMLocal, FieldLocal, FieldProp } from "../../typings";
|
||||||
import { FilePreview } from "./FilePreview";
|
|
||||||
import { PropTypeInput } from "./TypeInput";
|
import { PropTypeInput } from "./TypeInput";
|
||||||
const w = window as unknown as {
|
import { FieldUploadMulti } from "./TypeUploadMulti";
|
||||||
serverurl: string;
|
import { FieldUploadSingle } from "./TypeUploadSingle";
|
||||||
};
|
|
||||||
|
|
||||||
export const FieldUpload: FC<{
|
export const FieldUpload: FC<{
|
||||||
field: FieldLocal;
|
field: FieldLocal;
|
||||||
|
|
@ -17,7 +11,11 @@ export const FieldUpload: FC<{
|
||||||
styling?: string;
|
styling?: string;
|
||||||
arg: FieldProp;
|
arg: FieldProp;
|
||||||
on_change: (e: any) => void | Promise<void>;
|
on_change: (e: any) => void | Promise<void>;
|
||||||
}> = ({ field, fm, prop, on_change, arg }) => {
|
}> = (pass) => {
|
||||||
console.log(field.prop.upload);
|
const { field, fm, prop, on_change, arg } = pass;
|
||||||
return <></>;
|
let mode = field.prop.upload?.mode || "single-file";
|
||||||
|
if (mode === "single-file") {
|
||||||
|
return <FieldUploadSingle {...pass} />;
|
||||||
|
}
|
||||||
|
return <FieldUploadMulti {...pass} />;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,223 @@
|
||||||
|
import { useLocal } from "@/utils/use-local";
|
||||||
|
import get from "lodash.get";
|
||||||
|
import { Trash2, Upload } from "lucide-react";
|
||||||
|
import { ChangeEvent, FC } from "react";
|
||||||
|
import { FMLocal, FieldLocal, FieldProp } from "../../typings";
|
||||||
|
import { PropTypeInput } from "./TypeInput";
|
||||||
|
import { FilePreview } from "./FilePreview";
|
||||||
|
import { Spinner } from "lib/comps/ui/field-loading";
|
||||||
|
const w = window as unknown as {
|
||||||
|
serverurl: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const FieldUploadMulti: FC<{
|
||||||
|
field: FieldLocal;
|
||||||
|
fm: FMLocal;
|
||||||
|
prop: PropTypeInput;
|
||||||
|
styling?: string;
|
||||||
|
arg: FieldProp;
|
||||||
|
on_change: (e: any) => void | Promise<void>;
|
||||||
|
}> = ({ field, fm, prop, on_change, arg }) => {
|
||||||
|
let value: string = (fm.data[field.name] || "").trim();
|
||||||
|
// let type_upload =
|
||||||
|
const input = useLocal({
|
||||||
|
value: 0 as any,
|
||||||
|
display: false as any,
|
||||||
|
ref: null as any,
|
||||||
|
drop: false as boolean,
|
||||||
|
uploading: new Set<File>(),
|
||||||
|
fase: value ? "preview" : ("start" as "start" | "upload" | "preview"),
|
||||||
|
style: "inline" as "inline" | "full",
|
||||||
|
});
|
||||||
|
|
||||||
|
const parse_list = () => {
|
||||||
|
let list: string[] = [];
|
||||||
|
if (value.startsWith("[")) {
|
||||||
|
try {
|
||||||
|
list = JSON.parse(value);
|
||||||
|
} catch (e) {}
|
||||||
|
} else if (typeof value === "string" && value) {
|
||||||
|
list.push(value);
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
};
|
||||||
|
|
||||||
|
const on_upload = async (event: ChangeEvent<HTMLInputElement>) => {
|
||||||
|
const upload_single = async (file: File) => {
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append("file", file);
|
||||||
|
|
||||||
|
let url = siteurl("/_upload");
|
||||||
|
if (
|
||||||
|
location.hostname === "prasi.avolut.com" ||
|
||||||
|
location.host === "localhost:4550"
|
||||||
|
) {
|
||||||
|
const newurl = new URL(location.href);
|
||||||
|
newurl.pathname = `/_proxy/${url}`;
|
||||||
|
url = newurl.toString();
|
||||||
|
}
|
||||||
|
const response = await fetch(url, {
|
||||||
|
method: "POST",
|
||||||
|
body: formData,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
const contentType: any = response.headers.get("content-type");
|
||||||
|
let result;
|
||||||
|
if (contentType.includes("application/json")) {
|
||||||
|
result = await response.json();
|
||||||
|
} else if (contentType.includes("text/plain")) {
|
||||||
|
result = await response.text();
|
||||||
|
} else {
|
||||||
|
result = await response.blob();
|
||||||
|
}
|
||||||
|
if (Array.isArray(result)) {
|
||||||
|
return `_file${get(result, "[0]")}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new Error("Upload Failed");
|
||||||
|
};
|
||||||
|
|
||||||
|
if (event.target.files) {
|
||||||
|
const list = parse_list();
|
||||||
|
|
||||||
|
for (let i = 0; i < event.target.files.length; i++) {
|
||||||
|
const file = event.target?.files?.item(i);
|
||||||
|
if (file) {
|
||||||
|
input.uploading.add(file);
|
||||||
|
upload_single(file).then((path) => {
|
||||||
|
input.uploading.delete(file);
|
||||||
|
list.push(path);
|
||||||
|
fm.data[field.name] = JSON.stringify(list);
|
||||||
|
fm.render();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
input.render();
|
||||||
|
}
|
||||||
|
if (input.ref) {
|
||||||
|
input.ref.value = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (isEditor) input.fase = "start";
|
||||||
|
|
||||||
|
const list = parse_list();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="c-flex-grow c-flex-col c-flex c-w-full c-h-full c-items-stretch c-p-1">
|
||||||
|
<div
|
||||||
|
className={cx(
|
||||||
|
"c-flex c-flex-row c-flex-wrap",
|
||||||
|
css`
|
||||||
|
flex-flow: row wrap;
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{list.map((value, idx) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="c-py-1 c-pr-2"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
e.preventDefault();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={cx(
|
||||||
|
"c-relative",
|
||||||
|
css`
|
||||||
|
.del {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
.del {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<FilePreview url={value || ""} variant="thumb" />
|
||||||
|
<div
|
||||||
|
onClick={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
if (confirm("Remove this file ?")) {
|
||||||
|
list.splice(idx, 1);
|
||||||
|
fm.data[field.name] = JSON.stringify(list);
|
||||||
|
fm.render();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
className={cx(
|
||||||
|
"c-flex c-flex-row c-items-center c-px-1 c-rounded c-bg-white c-cursor-pointer hover:c-bg-red-100 c-absolute c-top-0 c-right-0 del transition-all",
|
||||||
|
css`
|
||||||
|
border: 1px solid red;
|
||||||
|
width: 25px;
|
||||||
|
height: 25px;
|
||||||
|
margin: 5px;
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<Trash2 className="c-text-red-500 c-h-4 c-w-4 " />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
{input.uploading.size > 0 && (
|
||||||
|
<div className="c-flex c-space-x-1 c-p-2 c-border">
|
||||||
|
<Spinner /> <div>Uploading</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="c-flex">
|
||||||
|
<div className={cx("c-flex c-border c-rounded ")}>
|
||||||
|
<div
|
||||||
|
className={cx(
|
||||||
|
"c-flex c-flex-row c-relative c-py-1 c-flex-grow c-pr-2 c-items-center c-cursor-pointer hover:c-bg-blue-50",
|
||||||
|
css`
|
||||||
|
input[type="file"],
|
||||||
|
input[type="file"]::-webkit-file-upload-button {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{!isEditor && (
|
||||||
|
<input
|
||||||
|
ref={(ref) => {
|
||||||
|
if (!input.ref) {
|
||||||
|
input.ref = ref;
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
type="file"
|
||||||
|
multiple={true}
|
||||||
|
accept={field.prop.upload?.accept}
|
||||||
|
onChange={on_upload}
|
||||||
|
className={cx(
|
||||||
|
"c-absolute c-w-full c-h-full c-cursor-pointer c-top-0 c-left-0 c-opacity-0"
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<div className="c-items-center c-flex c-text-base c-px-1 c-outline-none c-rounded c-cursor-pointer ">
|
||||||
|
<div className="c-flex c-flex-row c-items-center c-px-2">
|
||||||
|
<Upload className="c-h-4 c-w-4" />
|
||||||
|
</div>
|
||||||
|
<div className="c-flex c-flex-row c-items-center c-text-sm">
|
||||||
|
Upload File
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="c-flex-1"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
e.preventDefault();
|
||||||
|
}}
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { useLocal } from "@/utils/use-local";
|
import { useLocal } from "@/utils/use-local";
|
||||||
import get from "lodash.get";
|
import get from "lodash.get";
|
||||||
import { Loader2, Paperclip, Trash2, Upload } from "lucide-react";
|
import { Loader2, Paperclip, Trash2, Upload } from "lucide-react";
|
||||||
import { FC } from "react";
|
import { ChangeEvent, FC } from "react";
|
||||||
import * as XLSX from "xlsx";
|
import * as XLSX from "xlsx";
|
||||||
import { FMLocal, FieldLocal, FieldProp } from "../../typings";
|
import { FMLocal, FieldLocal, FieldProp } from "../../typings";
|
||||||
import { FilePreview } from "./FilePreview";
|
import { FilePreview } from "./FilePreview";
|
||||||
|
|
@ -19,7 +19,6 @@ export const FieldUploadSingle: FC<{
|
||||||
on_change: (e: any) => void | Promise<void>;
|
on_change: (e: any) => void | Promise<void>;
|
||||||
}> = ({ field, fm, prop, on_change, arg }) => {
|
}> = ({ field, fm, prop, on_change, arg }) => {
|
||||||
const styling = arg.upload_style ? arg.upload_style : "full";
|
const styling = arg.upload_style ? arg.upload_style : "full";
|
||||||
let type_field = prop.sub_type;
|
|
||||||
let value: any = fm.data[field.name];
|
let value: any = fm.data[field.name];
|
||||||
// let type_upload =
|
// let type_upload =
|
||||||
const input = useLocal({
|
const input = useLocal({
|
||||||
|
|
@ -31,30 +30,46 @@ export const FieldUploadSingle: FC<{
|
||||||
style: "inline" as "inline" | "full",
|
style: "inline" as "inline" | "full",
|
||||||
});
|
});
|
||||||
|
|
||||||
const on_upload = async (event: any) => {
|
const on_upload = async (event: ChangeEvent<HTMLInputElement>) => {
|
||||||
let file = null;
|
let file = null;
|
||||||
try {
|
try {
|
||||||
file = event.target.files[0];
|
file = event.target?.files?.[0];
|
||||||
} catch (ex) {}
|
} catch (ex) {}
|
||||||
if (type_field === "import") {
|
if (prop.model_upload === "import") {
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
|
|
||||||
reader.onload = (e: any) => {
|
function arrayBufferToBinaryString(buffer: ArrayBuffer): string {
|
||||||
const binaryStr = e.target.result;
|
const bytes = new Uint8Array(buffer);
|
||||||
|
return String.fromCharCode.apply(null, Array.from(bytes));
|
||||||
|
}
|
||||||
|
|
||||||
|
reader.onload = (e: ProgressEvent<FileReader>) => {
|
||||||
|
if (e.target && e.target.result) {
|
||||||
|
const binaryStr =
|
||||||
|
typeof e.target.result === "string"
|
||||||
|
? e.target.result
|
||||||
|
: arrayBufferToBinaryString(e.target.result);
|
||||||
const workbook = XLSX.read(binaryStr, { type: "binary" });
|
const workbook = XLSX.read(binaryStr, { type: "binary" });
|
||||||
|
|
||||||
const worksheet = workbook.Sheets[workbook.SheetNames[0]];
|
const worksheet = workbook.Sheets[workbook.SheetNames[0]];
|
||||||
const jsonData = XLSX.utils.sheet_to_json(worksheet);
|
const jsonData = XLSX.utils.sheet_to_json(worksheet);
|
||||||
if (typeof on_change === "function") {
|
if (typeof on_change === "function") {
|
||||||
const res = on_change({
|
on_change({
|
||||||
value: jsonData,
|
value: jsonData,
|
||||||
file: file,
|
file: file,
|
||||||
binnary: e.target.result,
|
binnary: e.target.result,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
reader.readAsBinaryString(file);
|
if (file) {
|
||||||
|
if (typeof reader.readAsArrayBuffer === "function") {
|
||||||
|
reader.readAsArrayBuffer(file);
|
||||||
} else {
|
} else {
|
||||||
|
reader.readAsBinaryString(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (file) {
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append("file", file);
|
formData.append("file", file);
|
||||||
|
|
||||||
|
|
@ -132,6 +147,7 @@ export const FieldUploadSingle: FC<{
|
||||||
ref={(ref) => (input.ref = ref)}
|
ref={(ref) => (input.ref = ref)}
|
||||||
type="file"
|
type="file"
|
||||||
multiple={false}
|
multiple={false}
|
||||||
|
accept={field.prop.upload?.accept}
|
||||||
onChange={on_upload}
|
onChange={on_upload}
|
||||||
className={cx(
|
className={cx(
|
||||||
"c-absolute c-w-full c-h-full c-cursor-pointer c-top-0 c-left-0 c-opacity-0"
|
"c-absolute c-w-full c-h-full c-cursor-pointer c-top-0 c-left-0 c-opacity-0"
|
||||||
|
|
@ -152,7 +168,7 @@ export const FieldUploadSingle: FC<{
|
||||||
<div className="c-flex c-flex-row c-items-center c-px-2">
|
<div className="c-flex c-flex-row c-items-center c-px-2">
|
||||||
<Upload className="c-h-4 c-w-4" />
|
<Upload className="c-h-4 c-w-4" />
|
||||||
</div>
|
</div>
|
||||||
<div className="c-flex c-flex-row c-items-center">
|
<div className="c-flex c-flex-row c-items-center c-text-sm">
|
||||||
Upload File
|
Upload File
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -173,7 +189,7 @@ export const FieldUploadSingle: FC<{
|
||||||
}}
|
}}
|
||||||
className={cx(
|
className={cx(
|
||||||
input.drop ? "c-bg-gray-100" : "",
|
input.drop ? "c-bg-gray-100" : "",
|
||||||
"hover:c-bg-gray-100 c-flex-grow c-m-1 c-relative c-flex-grow c-p-4 c-items-center c-flex c-flex-row c-text-gray-400 c-border c-border-gray-200 c-border-dashed c-rounded c-cursor-pointer"
|
"hover:c-bg-gray-100 c-m-1 c-relative c-flex-grow c-p-4 c-items-center c-flex c-flex-row c-text-gray-400 c-border c-border-gray-200 c-border-dashed c-rounded c-cursor-pointer"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div className="c-flex-row c-flex c-flex-grow c-space-x-2">
|
<div className="c-flex-row c-flex c-flex-grow c-space-x-2">
|
||||||
|
|
@ -217,9 +233,7 @@ export const FieldUploadSingle: FC<{
|
||||||
) : input.fase === "preview" ? (
|
) : input.fase === "preview" ? (
|
||||||
<div className="c-flex c-justify-between c-flex-1 c-p-1">
|
<div className="c-flex c-justify-between c-flex-1 c-p-1">
|
||||||
<FilePreview url={value || ""} />
|
<FilePreview url={value || ""} />
|
||||||
<div className="c-flex c-flex-row c-items-center c-border c-px-2 c-rounded c-cursor-pointer hover:c-bg-red-100">
|
<div
|
||||||
<Trash2
|
|
||||||
className="c-text-red-500 c-h-4 c-w-4"
|
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
@ -229,7 +243,9 @@ export const FieldUploadSingle: FC<{
|
||||||
fm.render();
|
fm.render();
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
className="c-flex c-flex-row c-items-center c-border c-px-2 c-rounded c-cursor-pointer hover:c-bg-red-100"
|
||||||
|
>
|
||||||
|
<Trash2 className="c-text-red-500 c-h-4 c-w-4" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ export type FieldProp = {
|
||||||
label: string;
|
label: string;
|
||||||
desc?: string;
|
desc?: string;
|
||||||
props?: any;
|
props?: any;
|
||||||
upload?: { mode: "single-file" | "multi-file" };
|
upload?: { mode: "single-file" | "multi-file"; accept: string };
|
||||||
link: {
|
link: {
|
||||||
text:
|
text:
|
||||||
| string
|
| string
|
||||||
|
|
@ -187,7 +187,7 @@ export type FieldInternal<T extends FieldProp["type"]> = {
|
||||||
name: string;
|
name: string;
|
||||||
fm: FMLocal;
|
fm: FMLocal;
|
||||||
}) => void | Promise<void>;
|
}) => void | Promise<void>;
|
||||||
prop?: FieldProp;
|
prop: FieldProp;
|
||||||
max_date?: FieldProp["max_date"];
|
max_date?: FieldProp["max_date"];
|
||||||
min_date?: FieldProp["min_date"];
|
min_date?: FieldProp["min_date"];
|
||||||
error?: any;
|
error?: any;
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,12 @@ export const formInit = (fm: FMLocal, props: FMProps) => {
|
||||||
<Button
|
<Button
|
||||||
variant={"link"}
|
variant={"link"}
|
||||||
size={"xs"}
|
size={"xs"}
|
||||||
className="c-cursor-pointer"
|
className={cx(
|
||||||
|
css`
|
||||||
|
color: gray !important;
|
||||||
|
`,
|
||||||
|
"c-cursor-pointer"
|
||||||
|
)}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
const md = fm.deps.md as MDLocal;
|
const md = fm.deps.md as MDLocal;
|
||||||
toast.dismiss();
|
toast.dismiss();
|
||||||
|
|
@ -72,9 +77,14 @@ export const formInit = (fm: FMLocal, props: FMProps) => {
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
variant={"default"}
|
variant={"outline"}
|
||||||
|
className={cx(
|
||||||
|
css`
|
||||||
|
color: green;
|
||||||
|
`,
|
||||||
|
"c-cursor-pointer"
|
||||||
|
)}
|
||||||
size={"xs"}
|
size={"xs"}
|
||||||
className="c-cursor-pointer"
|
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
const md = fm.deps.md as MDLocal;
|
const md = fm.deps.md as MDLocal;
|
||||||
toast.dismiss();
|
toast.dismiss();
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ export const useField = (
|
||||||
field.render();
|
field.render();
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
field.prop = arg;
|
field.prop = arg as any;
|
||||||
|
|
||||||
return field;
|
return field;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue