diff --git a/comps/form/field/type/FilePreview.tsx b/comps/form/field/type/FilePreview.tsx
index aaf333e..fdd19a6 100755
--- a/comps/form/field/type/FilePreview.tsx
+++ b/comps/form/field/type/FilePreview.tsx
@@ -3,10 +3,10 @@ import { ReactElement } from "react";
export const ThumbPreview = ({
url,
- del,
+ options,
}: {
url: string;
- del: ReactElement;
+ options: ReactElement;
}) => {
const file = getFileName(url);
if (typeof file === "string") return;
@@ -25,16 +25,23 @@ export const ThumbPreview = ({
font-size: 14px;
font-weight: black;
padding: 3px 7px;
- height: 26px;
+
+ width: 60px;
+ height: 60px;
+
+ &:hover {
+ border: 1px solid #1c4ed8;
+ outline: 1px solid #1c4ed8;
+ }
`,
- "c-flex c-items-center"
+ "c-flex c-justify-center c-items-center"
)}
+ onClick={() => {
+ let _url = siteurl(url || "");
+ window.open(_url, "_blank");
+ }}
>
{file.extension}
-
-
{
- let _url = siteurl(url || "");
- window.open(_url, "_blank");
- }}
>
{content}
- {del}
+ {options}
)}
>
diff --git a/comps/form/field/type/TypeUploadMulti.tsx b/comps/form/field/type/TypeUploadMulti.tsx
index 132fc13..7f1ceb6 100755
--- a/comps/form/field/type/TypeUploadMulti.tsx
+++ b/comps/form/field/type/TypeUploadMulti.tsx
@@ -1,11 +1,12 @@
import { useLocal } from "@/utils/use-local";
+import { Spinner } from "lib/comps/ui/field-loading";
+import { Tooltip } from "lib/comps/ui/tooltip";
import get from "lodash.get";
-import { Trash2, Upload } from "lucide-react";
+import { Check, Trash2, Upload } from "lucide-react";
import { ChangeEvent, FC } from "react";
import { FMLocal, FieldLocal, FieldProp } from "../../typings";
+import { ThumbPreview } from "./FilePreview";
import { PropTypeInput } from "./TypeInput";
-import { FilePreview, ThumbPreview } from "./FilePreview";
-import { Spinner } from "lib/comps/ui/field-loading";
const w = window as unknown as {
serverurl: string;
};
@@ -30,6 +31,11 @@ export const FieldUploadMulti: FC<{
style: "inline" as "inline" | "full",
});
+ const cover = {
+ field: field.prop.upload?.cover_field || "",
+ text: field.prop.upload?.cover_text,
+ };
+
const parse_list = () => {
let list: string[] = [];
if (value.startsWith("[")) {
@@ -114,46 +120,118 @@ export const FieldUploadMulti: FC<{
`
)}
>
- {list.map((value, idx) => {
- return (
- void;
+ delay?: number;
+ asChild?: boolean;
+}
+
+export function useTooltip({
+ initialOpen = false,
+ placement = "top",
+ open: controlledOpen,
+ onOpenChange: setControlledOpen,
+ delay = 1000,
+ offset: tooltipOffset,
+}: TooltipOptions = {}) {
+ const [uncontrolledOpen, setUncontrolledOpen] = React.useState(initialOpen);
+
+ const arrowRef = React.useRef(null);
+ const open = controlledOpen ?? uncontrolledOpen;
+ const setOpen = setControlledOpen ?? setUncontrolledOpen;
+
+ const data = useFloating({
+ placement,
+ open,
+ onOpenChange: setOpen,
+ whileElementsMounted: autoUpdate,
+ middleware: [
+ offset(typeof tooltipOffset === "undefined" ? 5 : tooltipOffset),
+ flip({
+ fallbackAxisSideDirection: "start",
+ padding: 5,
+ }),
+ shift({ padding: 5 }),
+ arrow({ element: arrowRef }),
+ ],
+ });
+
+ const context = data.context;
+
+ const hover = useHover(context, {
+ move: false,
+ delay,
+ enabled: controlledOpen == null,
+ });
+ const focus = useFocus(context, {
+ enabled: controlledOpen == null,
+ });
+ const dismiss = useDismiss(context);
+ const role = useRole(context, { role: "tooltip" });
+
+ const interactions = useInteractions([hover, focus, dismiss, role]);
+
+ return React.useMemo(
+ () => ({
+ open,
+ setOpen,
+ arrowRef,
+ ...interactions,
+ ...data,
+ }),
+ [open, setOpen, arrowRef, interactions, data]
+ );
+}
+
+type ContextType = ReturnType
| null;
+
+const TooltipContext = React.createContext(null);
+
+export const useTooltipContext = () => {
+ const context = React.useContext(TooltipContext);
+
+ if (context == null) {
+ throw new Error("Tooltip components must be wrapped in ");
+ }
+
+ return context;
+};
+
+export function Tooltip({
+ children,
+ content,
+ className,
+ onClick,
+ onPointerEnter,
+ onPointerLeave,
+ asChild,
+ ...options
+}: {
+ children: React.ReactNode;
+ content: React.ReactNode;
+ className?: string;
+ onClick?: (e: React.MouseEvent) => void;
+ onPointerEnter?: (e: React.MouseEvent) => void;
+ onPointerLeave?: (e: React.MouseEvent) => void;
+} & TooltipOptions) {
+ // This can accept any props as options, e.g. `placement`,
+ // or other positioning options.
+ const tooltip = useTooltip(options);
+
+ if (!content)
+ return (
+
+ {children}
+
+ );
+
+ return (
+
+
+ {children}
+
+
+ {content}
+
+
+
+ );
+}
+
+function TooltipArrow() {
+ const context = useTooltipContext();
+ const { x: arrowX, y: arrowY } = context.middlewareData.arrow || {
+ x: 0,
+ y: 0,
+ };
+ const staticSide = mapPlacementSideToCSSProperty(context.placement) as string;
+
+ return (
+
+ );
+}
+
+function mapPlacementSideToCSSProperty(placement: Placement) {
+ const staticPosition = placement.split("-")[0];
+
+ const staticSide = {
+ top: "bottom",
+ right: "left",
+ bottom: "top",
+ left: "right",
+ }[staticPosition];
+
+ return staticSide;
+}
+
+export const TooltipTrigger = React.forwardRef<
+ HTMLElement,
+ React.HTMLProps & { asChild?: boolean }
+>(function TooltipTrigger({ children, asChild = false, ...props }, propRef) {
+ const context = useTooltipContext();
+ const childrenRef = (children as any).ref;
+ const ref = useMergeRefs([context.refs.setReference, propRef, childrenRef]);
+
+ // `asChild` allows the user to pass any element as the anchor
+ if (asChild && React.isValidElement(children)) {
+ return React.cloneElement(
+ children,
+ context.getReferenceProps({
+ ref,
+ ...props,
+ ...children.props,
+ "data-state": context.open ? "open" : "closed",
+ })
+ );
+ }
+
+ return (
+
+ {children}
+
+ );
+});
+
+export const TooltipContent = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLProps
+>(function TooltipContent(props, propRef) {
+ const context = useTooltipContext();
+ const ref = useMergeRefs([context.refs.setFloating, propRef]);
+
+ if (!context.open) return null;
+
+ return (
+
+
+
+ );
+});
diff --git a/gen/prop/gen_prop_fields.ts b/gen/prop/gen_prop_fields.ts
index 2d0bb52..9bc4edd 100755
--- a/gen/prop/gen_prop_fields.ts
+++ b/gen/prop/gen_prop_fields.ts
@@ -67,6 +67,7 @@ const get_layer = async (
table: string
) => {
const { cols, rels } = await loadSingle(id_site, table);
+
const options = [];
if (cols) {
for (const [k, v] of Object.entries(cols)) {
@@ -125,6 +126,7 @@ const loadSingle = async (id_site: string, table: string) => {
const idb_key = `${id_site}-${table}`;
let cached_raw = localStorage.getItem(ls_key);
let cached_keys: string[] = [];
+
if (cached_raw) {
try {
let res = JSON.parse(cached_raw);