image upload preview

This commit is contained in:
rizky 2024-08-09 04:01:56 -07:00
parent c30dee6181
commit 3d810d5d41
5 changed files with 122 additions and 162 deletions

View File

@ -0,0 +1,102 @@
import { ExternalLink } from "lucide-react";
export const FilePreview = ({ url }: { url: string }) => {
const file = getFileName(url);
const color = darkenColor(generateRandomColor(file.extension));
let content = (
<div
className={cx(
css`
border: 1px solid ${color};
color: ${color};
border-radius: 3px;
text-transform: uppercase;
padding: 0px 5px;
font-size: 9px;
height: 15px;
margin-right: 5px;
`,
"c-flex c-items-center"
)}
>
{file.extension}
</div>
);
if (url.startsWith("_file/")) {
if ([".png", ".jpeg", ".jpg", ".webp"].find((e) => url.endsWith(e))) {
content = (
<img
className="c-py-1 c-rounded-md"
src={siteurl(`/_img/${url.substring("_file/".length)}?w=100&h=20`)}
/>
);
}
}
return (
<>
{file.extension && (
<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"
onClick={() => {
let _url = siteurl(url || "");
window.open(_url, "_blank");
}}
>
{content}
<div className="c-ml-2">
<ExternalLink size="12px" />
</div>
</div>
)}
</>
);
};
function darkenColor(color: string, factor: number = 0.5): string {
const rgb = hexToRgb(color);
const r = Math.floor(rgb.r * factor);
const g = Math.floor(rgb.g * factor);
const b = Math.floor(rgb.b * factor);
return rgbToHex(r, g, b);
}
function hexToRgb(hex: string): { r: number; g: number; b: number } {
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result
? {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16),
}
: { r: 0, g: 0, b: 0 };
}
function rgbToHex(r: number, g: number, b: number): string {
return `#${r.toString(16).padStart(2, "0")}${g
.toString(16)
.padStart(2, "0")}${b.toString(16).padStart(2, "0")}`;
}
function generateRandomColor(str: string): string {
let hash = 0;
if (str.length === 0) return hash.toString(); // Return a string representation of the hash
for (let i = 0; i < str.length; i++) {
hash = str.charCodeAt(i) + ((hash << 5) - hash);
hash = hash & hash;
}
let color = "#";
for (let i = 0; i < 3; i++) {
const value = (hash >> (i * 8)) & 255;
color += ("00" + value.toString(16)).substr(-2);
}
return color;
}
const getFileName = (url: string) => {
const fileName = url.substring(url.lastIndexOf("/") + 1);
const dotIndex = fileName.lastIndexOf(".");
const fullname = fileName;
if (dotIndex === -1) {
return { name: fileName, extension: "", fullname };
}
const name = fileName.substring(0, dotIndex);
const extension = fileName.substring(dotIndex + 1);
return { name, extension, fullname };
};

View File

@ -3,12 +3,11 @@ import { useLocal } from "@/utils/use-local";
import parser from "any-date-parser";
import Datepicker from "lib/comps/custom/Datepicker";
import { EyeIcon, EyeOff } from "lucide-react";
import { FC, FocusEvent, KeyboardEvent, MouseEvent } from "react";
import { FC, FocusEvent } from "react";
import { FMLocal, FieldLocal, FieldProp } from "../../typings";
import { FieldMoney } from "./TypeMoney";
import { FieldRichText } from "./TypeRichText";
import { FieldUpload } from "./TypeUpload";
import { FieldToggle } from "./TypeToggle";
export type PropTypeInput = {
type: "input";
@ -25,7 +24,6 @@ export type PropTypeInput = {
| "toggle"
| "rich-text"
| "upload"
| "file"
| "search"
| "password"
| "import"

View File

@ -14,6 +14,7 @@ import {
Upload,
} from "lucide-react";
import { Spinner } from "lib/comps/ui/field-loading";
import { FilePreview } from "./FilePreview";
const w = window as unknown as {
serverurl: string;
};
@ -38,9 +39,7 @@ export const FieldUpload: FC<{
fase: value ? "preview" : ("start" as "start" | "upload" | "preview"),
style: "inline" as "inline" | "full",
});
let display: any = null;
const disabled =
typeof field.disabled === "function" ? field.disabled() : field.disabled;
const on_upload = async (event: any) => {
let file = null;
try {
@ -226,23 +225,15 @@ export const FieldUpload: FC<{
</div>
) : input.fase === "preview" ? (
<div className="c-flex c-justify-between c-flex-1 c-p-1">
<div
className="c-flex c-border c-rounded c-items-center c-px-2 hover:c-bg-blue-50 c-cursor-pointer"
onClick={() => {
let url = siteurl(value);
window.open(url, "_blank");
}}
>
<Filename url={siteurl(value)} />
<div className="ml-2">
<ExternalLink size="12px" />
</div>
</div>
<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">
<Trash2
className="c-text-red-500 c-h-4 c-w-4"
onClick={() => {
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
if (confirm("Clear this file ?")) {
input.fase = "start";
fm.data[field.name] = null;
fm.render();
}
@ -255,126 +246,6 @@ export const FieldUpload: FC<{
)}
</div>
);
return (
<div className="c-flex-grow c-flex-row c-flex c-w-full c-h-full c-items-center">
<div className="c-flex c-flex-row c-p-2 c-items-center">
<IconFile
type={
getFileName("https://www.example.com/path/to/your/file.txt")
.extension
}
/>
</div>
<div className="c-line-clamp-1 c-flex-grow c-items-center">
{getFileName("https://www.example.com/path/to/your/file.txt").fullname}
</div>
<div className="c-flex c-flex-row c-items-center">
<div className="c-flex c-flex-row c-space-x-1 c-px-2">
<SquareArrowOutUpRight
className="c-h-5 c-w-5"
onClick={() => {
let url = siteurl(value);
window.open(url, "_blank");
}}
/>
<Trash2
className="c-text-red-500 c-h-5 c-w-5"
onClick={() => {
fm.data[field.name] = null;
fm.render();
}}
/>
</div>
</div>
</div>
);
};
const Filename = ({ url }: { url: string }) => {
const file = getFileName(url);
const color = darkenColor(generateRandomColor(file.extension));
return (
<>
{file.extension && (
<div
className={cx(
css`
border: 1px solid ${color};
color: ${color};
border-radius: 3px;
text-transform: uppercase;
padding: 0px 5px;
font-size: 9px;
height: 15px;
margin-right: 5px;
`,
"c-flex c-items-center"
)}
>
{file.extension}
</div>
)}
<div
className={cx(
css`
font-size: 13px;
`
)}
>
View File in New Tab
</div>
</>
);
};
function darkenColor(color: string, factor: number = 0.5): string {
const rgb = hexToRgb(color);
const r = Math.floor(rgb.r * factor);
const g = Math.floor(rgb.g * factor);
const b = Math.floor(rgb.b * factor);
return rgbToHex(r, g, b);
}
function hexToRgb(hex: string): { r: number; g: number; b: number } {
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result
? {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16),
}
: { r: 0, g: 0, b: 0 };
}
function rgbToHex(r: number, g: number, b: number): string {
return `#${r.toString(16).padStart(2, "0")}${g
.toString(16)
.padStart(2, "0")}${b.toString(16).padStart(2, "0")}`;
}
function generateRandomColor(str: string): string {
let hash = 0;
if (str.length === 0) return hash.toString(); // Return a string representation of the hash
for (let i = 0; i < str.length; i++) {
hash = str.charCodeAt(i) + ((hash << 5) - hash);
hash = hash & hash;
}
let color = "#";
for (let i = 0; i < 3; i++) {
const value = (hash >> (i * 8)) & 255;
color += ("00" + value.toString(16)).substr(-2);
}
return color;
}
const getFileName = (url: string) => {
const fileName = url.substring(url.lastIndexOf("/") + 1);
const dotIndex = fileName.lastIndexOf(".");
const fullname = fileName;
if (dotIndex === -1) {
return { name: fileName, extension: "", fullname };
}
const name = fileName.substring(0, dotIndex);
const extension = fileName.substring(dotIndex + 1);
return { name, extension, fullname };
};
const IconFile: FC<{ type: string }> = ({ type }) => {

View File

@ -60,7 +60,7 @@ export const newField = async (
field.name.includes(e)
)
) {
sub_type = "file";
sub_type = "upload";
}
return createItem({

View File

@ -1,9 +1,10 @@
import { GFCol } from "@/gen/utils";
import dayjs from "dayjs";
import { formatDate } from "lib/comps/custom/Datepicker/helpers";
import { FilePreview } from "lib/comps/form/field/type/FilePreview";
import { formatMoney } from "lib/comps/form/field/type/TypeMoney";
import { FC } from "react";
import { isEmptyString } from "./is-empty-string";
import { formatDate } from "lib/comps/custom/Datepicker/helpers";
import dayjs from "dayjs";
import { formatMoney } from "lib/comps/form/field/type/TypeMoney";
export const fields_map = new Map<string, (GFCol & { checked?: GFCol[] })[]>();
@ -121,25 +122,13 @@ export const FormatValue: FC<{
}
}
// let prefix = <></>;
// if (typeof tree_depth === "number" && tree_depth > 0) {
// prefix = (
// <div
// className={css`
// padding-left: ${tree_depth * 5}px;
// `}
// >
// <div
// className={cx(
// " c-border-l c-border-b c-border-black c-w-[10px] c-h-[15px]",
// css`
// margin-top: -10px;
// `
// )}
// ></div>
// </div>
// );
// }
if (
["attachment", "file", "img", "image"].find((e) =>
name.toLowerCase().includes(e)
)
) {
return <FilePreview url={value || ""} />;
}
return (
<div className="c-flex c-space-x-2 c-items-center">