UNPKG

@onehat/ui

Version:
492 lines (471 loc) 13 kB
import { forwardRef, useState, useEffect, } from 'react'; import { ADD, EDIT, DELETE, VIEW, COPY, DUPLICATE, PRINT, UPLOAD_DOWNLOAD, DOWNLOAD, } from '../../Constants/Commands.js'; import Inflector from 'inflector-js'; import Clipboard from '../Icons/Clipboard.js'; import Duplicate from '../Icons/Duplicate.js'; import Edit from '../Icons/Edit.js'; import Eye from '../Icons/Eye.js'; import Trash from '../Icons/Trash.js'; import Plus from '../Icons/Plus.js'; import Print from '../Icons/Print.js'; import UploadDownload from '../Icons/UploadDownload.js'; import Download from '../Icons/Download.js'; import inArray from '../../Functions/inArray.js'; import UploadsDownloadsWindow from '../Window/UploadsDownloadsWindow.js'; import _ from 'lodash'; // Note: A 'present button' will create both a context menu item // and a toolbar button that match in text label, icon, and handler. const presetButtons = [ ADD, EDIT, DELETE, VIEW, COPY, DUPLICATE, // PRINT, UPLOAD_DOWNLOAD, DOWNLOAD, ]; export default function withPresetButtons(WrappedComponent, isGrid = false) { return forwardRef((props, ref) => { if (props.disablePresetButtons || props.alreadyHasWithPresetButtons) { // bypass everything return <WrappedComponent {...props} ref={ref} />; } const { // for local use contextMenuItems = [], additionalToolbarButtons = [], useUploadDownload = false, useDownload = false, uploadHeaders, uploadParams, onUpload, downloadHeaders, downloadParams, onChangeColumnsConfig, ...propsToPass } = props, { // extract and pass down isEditor = false, isTree = false, canDeleteRootNode = false, isSideEditor = false, canEditorViewOnly = false, canRecordBeEdited, // fn(selection) returns bool on if the current record(s) can be edited canRecordBeDeleted, // fn(selection) returns bool on if the current record(s) can be deleted canRecordBeDuplicated, // fn(selection) returns bool on if the current record(s) can be duplicated disableAdd = !isEditor, disableEdit = !isEditor, disableDelete = !isEditor, disableView = !isGrid, disableCopy = !isGrid, disableDuplicate = !isEditor, disablePrint = !isGrid, protectedValues, // records with these values cannot be edited or deleted addDisplayMsg, editDisplayMsg, deleteDisplayMsg, // withAlert showInfo, // withModal showModal, hideModal, // withComponent self, // withData model, Repository, // withPermissions canUser, // withEditor userCanEdit = true, // not permissions, but capability userCanView = true, onAdd, onEdit, onDelete, onView, onDuplicate, // withSelection selection, // parent container selectorId, selectorSelected, } = props, [isReady, setIsReady] = useState(false), [localContextMenuItems, setLocalContextMenuItems] = useState([]), [localAdditionalToolbarButtons, setLocalAdditionalToolbarButtons] = useState([]), [localColumnsConfig, setLocalColumnsConfig] = useState([]), onChangeColumnsConfigDecorator = (columnsConfig) => { setLocalColumnsConfig(columnsConfig); if (onChangeColumnsConfig) { onChangeColumnsConfig(columnsConfig); } }, isTypeDisabledCompletely = (type) => { let isDisabled = false; switch(type) { case ADD: if (disableAdd || canEditorViewOnly) { isDisabled = true; } else if (canUser && !canUser(ADD)) { // check Permissions isDisabled = true; } break; case EDIT: if (disableEdit || canEditorViewOnly || isSideEditor) { isDisabled = true; } else if (canUser && !canUser(EDIT)) { // check Permissions isDisabled = true; } break; case DELETE: if (disableDelete || canEditorViewOnly) { isDisabled = true; } else if (canUser && !canUser(DELETE)) { // check Permissions isDisabled = true; } break; case VIEW: if (disableView || isSideEditor) { isDisabled = true; } else if (canUser && !canUser(VIEW)) { // check Permissions isDisabled = true; } break; case COPY: if (disableCopy) { isDisabled = true; } else if (canUser && !canUser(COPY)) { // check Permissions isDisabled = true; } break; case DUPLICATE: if (disableDuplicate || canEditorViewOnly) { isDisabled = true; } else if (canUser && !canUser(DUPLICATE)) { // check Permissions isDisabled = true; } break; case PRINT: if (disablePrint) { isDisabled = true; } else if (canUser && !canUser(PRINT)) { // check Permissions isDisabled = true; } break; case UPLOAD_DOWNLOAD: if (!useUploadDownload) { isDisabled = true; } else if (canUser && !canUser(UPLOAD_DOWNLOAD)) { // check Permissions isDisabled = true; } break; case DOWNLOAD: if (!useDownload) { isDisabled = true; } else if (canUser && !(canUser(DOWNLOAD) || canUser(UPLOAD_DOWNLOAD))) { // check Permissions isDisabled = true; } break; default: } return isDisabled; }, isNoSelectorSelected = () => { return selectorId && !selectorSelected; }, isEmptySelection = () => { return _.isEmpty(selection); }, isMultiSelection = () => { return _.isArray(selection) && selection.length > 1; }, isProtectedValue = () => { if (!protectedValues) { return false; } const value = selection[0]?.id; if (_.isNil(value)) { return false; } return inArray(value, protectedValues); }, getPresetButtonProps = (type) => { let key, text, handler, icon = null, isDisabled = false; switch(type) { case ADD: key = 'addBtn'; if (addDisplayMsg) { text = addDisplayMsg; } else { text = 'Add'; if (model) { let inflected = Inflector.singularize(model); // can only add one at a time inflected = Inflector.camel2words(Inflector.humanize(Inflector.underscore(inflected))); // Separate with spaces, capitalize each word text += ' ' + inflected; } } handler = (parent, e) => { onAdd(); }; icon = Plus; if (isNoSelectorSelected() || (isTree && isEmptySelection()) ) { isDisabled = true; } break; case EDIT: key = 'editBtn'; if (editDisplayMsg) { text = editDisplayMsg; } else { text = 'Edit'; if (model) { let inflected = model; if (selection.length <= 1) { inflected = Inflector.singularize(inflected); } else { inflected = Inflector.pluralize(inflected); } inflected = Inflector.camel2words(Inflector.humanize(Inflector.underscore(inflected))); // Separate with spaces, capitalize each word text += ' ' + inflected; } } handler = (parent, e) => { onEdit(); }; icon = Edit; if (isNoSelectorSelected() || isEmptySelection() || isMultiSelection() || isProtectedValue() || (canRecordBeEdited && !canRecordBeEdited(selection)) ) { isDisabled = true; } break; case DELETE: key = 'deleteBtn'; if (deleteDisplayMsg) { text = deleteDisplayMsg; } else { text = 'Delete'; if (model) { let inflected = model; if (selection.length <= 1) { inflected = Inflector.singularize(inflected); } else { inflected = Inflector.pluralize(inflected); } inflected = Inflector.camel2words(Inflector.humanize(Inflector.underscore(inflected))); // Separate with spaces, capitalize each word text += ' ' + inflected; } } handler = onDelete; handler = (parent, e) => { onDelete(); }; icon = Trash; if (isNoSelectorSelected() || isEmptySelection() || isMultiSelection() || isProtectedValue() || (canRecordBeDeleted && !canRecordBeDeleted(selection)) ) { isDisabled = true; } if (isTree) { const isRootNode = !!_.find(selection, { isRoot: true, }); if (isRootNode && !canDeleteRootNode) { isDisabled = true; } } break; case VIEW: key = 'viewBtn'; text = 'View'; handler = (parent, e) => onView(); icon = Eye; isDisabled = !selection.length || selection.length !== 1; if (isNoSelectorSelected() || isEmptySelection() || isMultiSelection() ) { isDisabled = true; } break; case COPY: key = 'copyBtn'; text = 'Copy to Clipboard'; handler = (parent, e) => onCopyToClipboard(); icon = Clipboard; isDisabled = !selection.length; if (isNoSelectorSelected() || isEmptySelection() ) { isDisabled = true; } break; case DUPLICATE: key = 'duplicateBtn'; text = 'Duplicate'; handler = (parent, e) => { onDuplicate(); }; icon = Duplicate; isDisabled = !selection.length || selection.length !== 1; if (isNoSelectorSelected() || isEmptySelection() || isMultiSelection() || (canRecordBeDuplicated && !canRecordBeDuplicated(selection)) ) { isDisabled = true; } break; // case PRINT: // text = 'Print'; // handler = onPrint; // icon = Print; // break; case UPLOAD_DOWNLOAD: key = 'uploadDownloadBtn'; text = 'Upload/Download'; handler = (parent, e) => onUploadDownload(); icon = UploadDownload; break; case DOWNLOAD: key = 'downloadBtn'; text = 'Download'; handler = (parent, e) => onDownload(); icon = Download; break; default: } return { key, text, handler, icon, isDisabled, parent: self, reference: key, }; }, generatePresetButtons = () => { const localContextMenuItems = [], localAdditionalToolbarButtons = []; _.each(presetButtons, (type) => { if (isTypeDisabledCompletely(type)) { // i.e. not just temporarily disabled because of selection return; } if ((!userCanEdit && inArray(type, [ADD, EDIT, DELETE, DUPLICATE,])) || (!userCanView && type === VIEW)) { return; } const config = getPresetButtonProps(type); localContextMenuItems.push(config); localAdditionalToolbarButtons.push(config); }); setLocalContextMenuItems(localContextMenuItems); setLocalAdditionalToolbarButtons(localAdditionalToolbarButtons); }, onCopyToClipboard = () => { // Get text of all selected rows let text; if (selection.length) { const headerText = _.map(localColumnsConfig, (config) => config.header).join("\t"), rowTexts = _.map(selection, (entity) => { const values = []; _.each(localColumnsConfig, (config) => { values.push(entity[config.fieldName]); }); return values.join("\t"); }); text = [headerText, ...rowTexts].join("\n"); } else { text = 'Nothing selected to copy!'; } // Send it to clipboard navigator?.clipboard.writeText(text); if (showInfo) { showInfo('Copied to clipboard!'); } }, onUploadDownload = () => { showModal({ body: <UploadsDownloadsWindow reference="uploadsDownloads" onClose={hideModal} Repository={Repository} columnsConfig={props.columnsConfig} uploadHeaders={uploadHeaders} uploadParams={uploadParams} onUpload={onUpload} downloadHeaders={downloadHeaders} downloadParams={downloadParams} />, onCancel: hideModal, }); }, onDownload = () => { showModal({ body: <UploadsDownloadsWindow reference="downloads" onClose={hideModal} isDownloadOnly={true} Repository={Repository} columnsConfig={props.columnsConfig} downloadHeaders={downloadHeaders} downloadParams={downloadParams} />, onCancel: hideModal, }); }; // onPrint = () => { // debugger; // }; useEffect(() => { generatePresetButtons(); if (!isReady) { setIsReady(true); } }, [selection, selectorSelected, localColumnsConfig]); if (!isReady) { return null; } return <WrappedComponent {...propsToPass} disablePresetButtons={false} alreadyHasWithPresetButtons={true} ref={ref} contextMenuItems={[ ...localContextMenuItems, ...contextMenuItems, ]} additionalToolbarButtons={[ ...localAdditionalToolbarButtons, ...additionalToolbarButtons, ]} onChangeColumnsConfig={onChangeColumnsConfigDecorator} />; }); }