import React, { useRef, useState } from "react"; import PropTypes from "prop-types"; import FileUploadButton from "./FileUploadButton"; import DragElement from "./ImageGrid/DragElement"; import FilePlaceholder from "./ImageGrid/FilePlaceholder"; import GridImage from "./ImageGrid/GridImage"; import useToastStore from "../stores/useToastStore"; import { post } from "../lib/request"; import { createDraggable, collectionOrder, useDragCollection, useDragUploader } from "./drag"; function filterFiles(files) { const validMimeTypes = [ "image/gif", "image/jpeg", "image/pjpeg", "image/png", "image/tiff" ]; return files.filter((f) => validMimeTypes.indexOf(f.type) !== -1); } function draggedImageOrder(primaryCollection, imagesCollection, dragState) { 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]; } function initRecords(props) { const primary = props.enablePrimary ? props.records.filter((r) => r.primary).slice(0, 1) : []; return [primary, props.records.filter((r) => primary.indexOf(r) === -1)]; } export default function ImageGrid(props) { const [initPrimary, initImages] = initRecords(props); const primary = useDragCollection(initPrimary); const images = useDragCollection(initImages); const [deleted, setDeleted] = useState([]); const error = useToastStore((state) => state.error); const containerRef = useRef(); const dispatchAll = (action) => { primary.dispatch(action); images.dispatch(action); }; const dragEnd = (dragState, files) => { 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) => { return ( [ ...primary.draggables.map((d) => d.record), ...images.draggables.map((d) => d.record), ...deleted ].indexOf(record) + 1 ); }; const attrName = (record) => { return `${props.attribute}[${position(record)}]`; }; const uploadImage = (file) => { const draggable = createDraggable({ image: null, file: file }); let data = new FormData(); data.append("image[file]", file); post("/admin/images.json", data).then((json) => { if (json.status === "error") { error("Error uploading image: " + json.error); dispatchAll({ type: "remove", payload: draggable }); } else { dispatchAll({ type: "update", payload: { ...draggable, record: { image: json } } }); } }); return draggable; }; const update = (draggable) => (image) => { const { record } = draggable; const updated = { ...draggable, record: { ...record, image: { ...record.image, ...image } } }; dispatchAll({ type: "update", payload: updated }); }; const remove = (draggable) => () => { dispatchAll({ type: "remove", payload: draggable }); if (draggable.record.id) { setDeleted([...deleted, draggable.record]); } }; const renderImage = (draggable, isPrimary) => { const { dragging } = dragState; if (draggable === "Files") { return ; } return ( ); }; const uploadPrimary = (files) => { 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) => { images.dispatch({ type: "append", payload: filterFiles(files).map((f) => uploadImage(f)) }); }; let 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) => ( ))}
); } ImageGrid.propTypes = { attribute: PropTypes.string, locale: PropTypes.string, locales: PropTypes.object, records: PropTypes.array, enablePrimary: PropTypes.bool, primaryAttribute: PropTypes.string, showEmbed: PropTypes.bool };