import React, { useRef } from "react"; import FileUploadButton from "../FileUploadButton"; import DragElement from "./DragElement"; import FilePlaceholder from "./FilePlaceholder"; import GridImage from "./GridImage"; import useToastStore from "../../stores/useToastStore"; import { post } from "../../lib/request"; import * as Drag from "../../types/Drag"; import * as Images from "../../types/Images"; import { createDraggable, collectionOrder, useDragUploader } from "../drag"; interface Props extends Images.GridOptions { state: Images.GridState; } function filterFiles(files: File[]): File[] { const validMimeTypes = [ "image/gif", "image/jpeg", "image/pjpeg", "image/png", "image/tiff" ]; return files.filter((f) => validMimeTypes.indexOf(f.type) !== -1); } function draggedImageOrder( primaryCollection: Drag.Collection, imagesCollection: Drag.Collection, dragState: Drag.State ): [Drag.Item, Drag.Item[]] { const [primary, ...rest] = collectionOrder(primaryCollection, dragState); let images = [...rest, ...collectionOrder(imagesCollection, dragState)]; if ( dragState.dragging && [primary, ...images].indexOf(dragState.dragging) === -1 ) { if ( dragState.y < imagesCollection.ref.current.getBoundingClientRect().top ) { images = [dragState.dragging, ...images]; } else { images.push(dragState.dragging); } } return [primary, images]; } export default function Grid(props: Props) { const { primary, images, deleted, setDeleted } = props.state; const containerRef = useRef(); const error = useToastStore((state) => state.error); const dispatchAll = (action) => { primary.dispatch(action); images.dispatch(action); }; const dragEnd = (dragState: Drag.State, files: File[]) => { const [draggedPrimary, draggedImages] = draggedImageOrder( primary, images, dragState ); primary.dispatch({ type: "reorder", payload: draggedPrimary ? [draggedPrimary] : [] }); images.dispatch({ type: "reorder", payload: draggedImages }); if (files) { const uploads = filterFiles(files).map((f) => uploadImage(f)); dispatchAll({ type: "insertFiles", payload: uploads }); } }; const [dragState, dragStart, listeners] = useDragUploader( [primary, images], dragEnd ); const position = (record: Images.Record) => { return ( [ ...primary.draggables.map( (d: Drag.Draggable) => d.record ), ...images.draggables.map( (d: Drag.Draggable) => d.record ), ...deleted ].indexOf(record) + 1 ); }; const attrName = (record: Images.Record) => { return `${props.attribute}[${position(record)}]`; }; const uploadImage = (file: File) => { const draggable = createDraggable({ image: null, file: file }); const data = new FormData(); data.append("image[file]", file); void post("/admin/images.json", data).then((json: Images.Response) => { if ("status" in json && json.status === "error") { error(`Error uploading image: ${json.error}`); dispatchAll({ type: "remove", payload: draggable }); } else { dispatchAll({ type: "update", payload: { ...draggable, record: { image: json } } as Drag.Draggable }); } }); return draggable; }; const update = (draggable: Drag.Draggable) => (image: Images.Resource) => { const { record } = draggable; const updated = { ...draggable, record: { ...record, image: { ...record.image, ...image } } }; dispatchAll({ type: "update", payload: updated }); }; const remove = (draggable: Drag.Draggable) => () => { dispatchAll({ type: "remove", payload: draggable }); if (draggable.record.id) { setDeleted([...deleted, draggable.record]); } }; const renderImage = ( draggable: Drag.Item, isPrimary: boolean ) => { const { dragging } = dragState; if (draggable === "Files") { return ; } return ( ); }; const uploadPrimary = (files: File[]) => { const [first, ...rest] = filterFiles(files).map((f) => uploadImage(f)); if (first) { images.dispatch({ type: "prepend", payload: [...primary.draggables, ...rest] }); primary.dispatch({ type: "replace", payload: [first] }); } }; const uploadAdditional = (files: File[]) => { images.dispatch({ type: "append", payload: filterFiles(files).map((f) => uploadImage(f)) }); }; const classNames = ["image-grid"]; if (props.enablePrimary) { classNames.push("with-primary-image"); } const [draggedPrimary, draggedImages] = draggedImageOrder( primary, images, dragState ); return (
{dragState.dragging && ( )} {props.enablePrimary && (

Main image

{draggedPrimary && ( <> {renderImage(draggedPrimary, true)} {props.primaryAttribute && ( )} )} {!draggedPrimary && (
)}
)}

{props.enablePrimary ? "More images" : "Images"}

{draggedImages.map((r) => renderImage(r, false))}
{deleted.map((r) => ( ))}
); }