UNPKG

@pdftron/webviewer-react-toolkit

Version:

A React component library for integrating with PDFTron WebViewer API.

164 lines (163 loc) 9.51 kB
import { __assign, __rest } from "tslib"; import classnames from 'classnames'; import React, { createRef, Fragment, useCallback, useEffect, useImperativeHandle, useRef, useState, } from 'react'; import { focusableElementDomString, getRowAndColumnIndex, getSibling, isScrolledIntoView, THUMBNAIL_WIDTH, } from '../../utils'; import { DndMultiProvider } from '../DndMultiProvider'; import { Draggable } from '../Draggable'; import { DragLayer } from '../DragLayer'; import { MemoAutoSizer } from './MemoAutoSizer'; var defaultSize = { width: THUMBNAIL_WIDTH, height: THUMBNAIL_WIDTH }; export function FileOrganizer(_a) { var files = _a.files, onMove = _a.onMove, onDragChange = _a.onDragChange, onDeselectAll = _a.onDeselectAll, onSelectAll = _a.onSelectAll, onRenderThumbnail = _a.onRenderThumbnail, onRenderDragLayer = _a.onRenderDragLayer, disableMove = _a.disableMove, preventArrowsToMove = _a.preventArrowsToMove, preventClickAwayDeselect = _a.preventClickAwayDeselect, draggingIds = _a.draggingIds, padding = _a.padding, _gridRef = _a.gridRef, thumbnailSize = _a.thumbnailSize, noProvider = _a.noProvider, className = _a.className, onClick = _a.onClick, onKeyDown = _a.onKeyDown, style = _a.style, divProps = __rest(_a, ["files", "onMove", "onDragChange", "onDeselectAll", "onSelectAll", "onRenderThumbnail", "onRenderDragLayer", "disableMove", "preventArrowsToMove", "preventClickAwayDeselect", "draggingIds", "padding", "gridRef", "thumbnailSize", "noProvider", "className", "onClick", "onKeyDown", "style"]); var fileOrganizerRef = useRef(null); var gridRef = useRef(null); useImperativeHandle(_gridRef, function () { return gridRef.current; }); var _b = useState(0), columnCount = _b[0], setColumnCount = _b[1]; var _c = useState(), editingId = _c[0], setEditingId = _c[1]; var _d = useState(), draggingId = _d[0], setDraggingId = _d[1]; // Get the width of the first item, or default if no first item found. var hasFiles = files.length > 0; var getSize = useCallback(function () { if (!hasFiles) return defaultSize; if (!fileOrganizerRef.current) return defaultSize; var draggableWrapper = fileOrganizerRef.current.querySelector('div[draggable="true"]'); var draggableElement = draggableWrapper === null || draggableWrapper === void 0 ? void 0 : draggableWrapper.firstChild; var firstItem = draggableElement === null || draggableElement === void 0 ? void 0 : draggableElement.firstChild; if (!firstItem) return defaultSize; return firstItem.getBoundingClientRect(); }, [hasFiles]); // Detect size of first item and use as size throughout. var _e = useState(function () { return thumbnailSize || getSize(); }), size = _e[0], setSize = _e[1]; // Update size when getWidth ref changes (when hasFiles changes). useEffect(function () { if (thumbnailSize) return setSize(thumbnailSize); if (files.length === 0) return setSize(defaultSize); setSize(function (prev) { var _a = getSize(), width = _a.width, height = _a.height; if (prev.width === width && prev.height === height) return prev; return { width: width, height: height }; }); // Watches all files to continuously check width and height. }, [files, getSize, thumbnailSize]); var handleOnDragChange = useCallback(function (id) { onDragChange === null || onDragChange === void 0 ? void 0 : onDragChange(id); setDraggingId(id); }, [onDragChange]); var handleItemKeyDown = useCallback(function (event, index, _file, draggableRef) { var indexDiff = 0; switch (event.key) { case 'ArrowLeft': indexDiff = -1; break; case 'ArrowRight': indexDiff = 1; break; case 'ArrowUp': indexDiff = -1 * columnCount; break; case 'ArrowDown': indexDiff = columnCount; break; default: return; // Return if not one of above keys } event.preventDefault(); var hasMoved = false; // If meta key was pressed, move to new location. if (!preventArrowsToMove && (event.metaKey || event.ctrlKey) && !disableMove && editingId === undefined && onMove) { hasMoved = true; onMove(index, index + indexDiff); } if (!gridRef.current) return; var siblingAtLocation = getSibling(draggableRef.current, indexDiff); // If no meta key was pressed, focus item in direction of keys. if (siblingAtLocation && !(event.metaKey || event.ctrlKey)) { var focusable_1 = siblingAtLocation.querySelector(focusableElementDomString); if (focusable_1) { hasMoved = true; requestAnimationFrame(function () { focusable_1.focus(); }); } } if (!hasMoved) return; var isVisible = isScrolledIntoView(siblingAtLocation, fileOrganizerRef.current).isVisible; if (isVisible) return; // Use react-window scrollToItem api for virtualized items. var rowIndex = getRowAndColumnIndex(index + indexDiff, columnCount).rowIndex; gridRef.current.scrollToItem({ align: 'smart', rowIndex: rowIndex }); }, [columnCount, disableMove, editingId, onMove, preventArrowsToMove]); var pad = Math.max(1, padding || 0); var renderItem = useCallback(function (file, index, style) { if (!file) return React.createElement(Fragment, { key: '__null' }, null); var isEditing = editingId === file.id; var otherDragging = !!((draggingId && draggingId !== file.id) || (draggingIds && draggingIds.length && !draggingIds.includes(file.id))); var draggableRef = createRef(); var isInDragGroup = draggingIds ? draggingIds.includes(file.id) : false; return (React.createElement(Draggable, { "data-file-id": file.id, key: file.id, index: index, style: style && __assign(__assign({}, style), { top: typeof style.top === 'number' ? style.top + pad : style.top, left: typeof style.left === 'number' ? style.left + pad : style.left }), ref: draggableRef, hideDragPreview: !!onRenderDragLayer, preventAnimation: isInDragGroup, onDragChange: function (isDragging) { return handleOnDragChange(isDragging ? file.id : undefined); }, disableDrag: isEditing || disableMove, onMove: onMove, onKeyDown: function (e) { return handleItemKeyDown(e, index, file, draggableRef); }, onRenderChildren: function (isDragging) { return onRenderThumbnail({ onRenderThumbnailProps: { file: file, dragging: isDragging || isInDragGroup, otherDragging: otherDragging, onEditingChange: function (editing) { return setEditingId(editing ? file.id : undefined); }, }, id: file.id, index: index, }); } })); }, [ editingId, draggingId, draggingIds, pad, onRenderDragLayer, disableMove, onMove, handleOnDragChange, handleItemKeyDown, onRenderThumbnail, ]); var handleOnClick = useCallback(function (event) { onClick === null || onClick === void 0 ? void 0 : onClick(event); if (!preventClickAwayDeselect) onDeselectAll === null || onDeselectAll === void 0 ? void 0 : onDeselectAll(); }, [onDeselectAll, onClick, preventClickAwayDeselect]); var handleOnKeyDown = useCallback(function (event) { onKeyDown === null || onKeyDown === void 0 ? void 0 : onKeyDown(event); if (event.key === 'Escape') return onDeselectAll === null || onDeselectAll === void 0 ? void 0 : onDeselectAll(); if (event.key === 'a' && (event.metaKey || event.ctrlKey)) { onSelectAll === null || onSelectAll === void 0 ? void 0 : onSelectAll(); event.preventDefault(); } }, [onDeselectAll, onSelectAll, onKeyDown]); var customDragLayerTranslate = useCallback(function (_a) { var mousePosition = _a.mousePosition; var x = mousePosition.x - THUMBNAIL_WIDTH / 2; var y = mousePosition.y - THUMBNAIL_WIDTH / 2; return { x: x, y: y }; }, []); var fileOrganizerClass = classnames('ui__base ui__fileOrganizer', className); var Wrapper = noProvider ? React.Fragment : DndMultiProvider; return (React.createElement(Wrapper, null, React.createElement("div", __assign({}, divProps, { className: fileOrganizerClass, ref: fileOrganizerRef, onClick: handleOnClick, onKeyDown: handleOnKeyDown, style: style, role: "grid", tabIndex: 0 }), React.createElement(MemoAutoSizer, { ref: gridRef, files: files, padding: pad, size: size, renderItem: renderItem, onColumnCountChange: setColumnCount }), onRenderDragLayer ? (React.createElement(DragLayer, { customTranslate: customDragLayerTranslate }, React.createElement("div", { className: "ui__fileOrganizer__draglayer", style: { height: THUMBNAIL_WIDTH, width: THUMBNAIL_WIDTH } }, onRenderDragLayer()))) : undefined))); }