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 };