UNPKG

@orfeas126/box-ui-elements

Version:
161 lines 8.03 kB
/** * @file Redesigned Metadata sidebar component * @author Box */ import * as React from 'react'; import flow from 'lodash/flow'; import { FormattedMessage, useIntl } from 'react-intl'; import { InlineError, LoadingIndicator } from '@box/blueprint-web'; import { AddMetadataTemplateDropdown, AutofillContextProvider, MetadataEmptyState, MetadataInstanceList } from '@box/metadata-editor'; import SidebarContent from './SidebarContent'; import { withAPIContext } from '../common/api-context'; import { withErrorBoundary } from '../common/error-boundary'; import { withLogger } from '../common/logger'; import { useFeatureEnabled } from '../common/feature-checking'; import { ORIGIN_METADATA_SIDEBAR_REDESIGN, SIDEBAR_VIEW_METADATA } from '../../constants'; import { EVENT_JS_READY } from '../common/logger/constants'; import { mark } from '../../utils/performance'; import useSidebarMetadataFetcher, { STATUS } from './hooks/useSidebarMetadataFetcher'; import messages from '../common/messages'; import './MetadataSidebarRedesign.scss'; import MetadataInstanceEditor from './MetadataInstanceEditor'; import { convertTemplateToTemplateInstance } from './utils/convertTemplateToTemplateInstance'; import { isExtensionSupportedForMetadataSuggestions } from './utils/isExtensionSupportedForMetadataSuggestions'; import { metadataTaxonomyFetcher, metadataTaxonomyNodeAncestorsFetcher } from './fetchers/metadataTaxonomyFetcher'; const MARK_NAME_JS_READY = `${ORIGIN_METADATA_SIDEBAR_REDESIGN}_${EVENT_JS_READY}`; mark(MARK_NAME_JS_READY); function MetadataSidebarRedesign({ api, elementId, fileId, onError, isFeatureEnabled }) { const { extractSuggestions, file, handleCreateMetadataInstance, handleDeleteMetadataInstance, handleUpdateMetadataInstance, templates, errorMessage, status, templateInstances } = useSidebarMetadataFetcher(api, fileId, onError, isFeatureEnabled); const { formatMessage } = useIntl(); const isBoxAiSuggestionsEnabled = useFeatureEnabled('metadata.aiSuggestions.enabled'); const [editingTemplate, setEditingTemplate] = React.useState(null); const [isUnsavedChangesModalOpen, setIsUnsavedChangesModalOpen] = React.useState(false); const [isDeleteButtonDisabled, setIsDeleteButtonDisabled] = React.useState(false); const [selectedTemplates, setSelectedTemplates] = React.useState(templateInstances); const [pendingTemplateToEdit, setPendingTemplateToEdit] = React.useState(null); React.useEffect(() => { // disable only pre-existing template instances from dropdown if not editing or editing pre-exiting one const isEditingTemplateAlreadyExisting = editingTemplate && templateInstances.some(t => t.templateKey === editingTemplate.templateKey && t.scope === editingTemplate.scope); if (!editingTemplate || isEditingTemplateAlreadyExisting) { setSelectedTemplates(templateInstances); } else { setSelectedTemplates([...templateInstances, editingTemplate]); } }, [editingTemplate, templateInstances, templateInstances.length]); const handleTemplateSelect = selectedTemplate => { if (editingTemplate) { setPendingTemplateToEdit(convertTemplateToTemplateInstance(file, selectedTemplate)); setIsUnsavedChangesModalOpen(true); } else { setEditingTemplate(convertTemplateToTemplateInstance(file, selectedTemplate)); setIsDeleteButtonDisabled(true); } }; const handleCancel = () => { setEditingTemplate(null); }; const handleDiscardUnsavedChanges = () => { // check if user tried to edit another template before unsaved changes modal if (pendingTemplateToEdit) { setEditingTemplate(pendingTemplateToEdit); setIsDeleteButtonDisabled(true); setPendingTemplateToEdit(null); } else { handleCancel(); } setIsUnsavedChangesModalOpen(false); }; const handleDeleteInstance = async metadataInstance => { try { await handleDeleteMetadataInstance(metadataInstance); } catch { // ignore error, handled in useSidebarMetadataFetcher } setEditingTemplate(null); }; const isExistingMetadataInstance = () => { return editingTemplate && !!templateInstances.find(templateInstance => templateInstance.id === editingTemplate.id); }; const handleSubmit = async (values, operations) => { if (isExistingMetadataInstance()) { await handleUpdateMetadataInstance(values.metadata, operations, () => setEditingTemplate(null)); } else { await handleCreateMetadataInstance(values.metadata, () => setEditingTemplate(null)); } }; const metadataDropdown = status === STATUS.SUCCESS && templates && /*#__PURE__*/React.createElement(AddMetadataTemplateDropdown, { availableTemplates: templates, selectedTemplates: selectedTemplates, onSelect: handleTemplateSelect }); const errorMessageDisplay = status === STATUS.ERROR && errorMessage && /*#__PURE__*/React.createElement(InlineError, { className: "bcs-MetadataSidebarRedesign-inline-error" }, /*#__PURE__*/React.createElement(FormattedMessage, errorMessage)); const showTemplateInstances = file && templates && templateInstances; const showLoading = status === STATUS.LOADING; const showEmptyState = !showLoading && showTemplateInstances && templateInstances.length === 0 && !editingTemplate; const showEditor = !showEmptyState && editingTemplate; const showList = !showEditor && templateInstances.length > 0 && !editingTemplate; const areAiSuggestionsAvailable = isExtensionSupportedForMetadataSuggestions(file?.extension ?? ''); const taxonomyOptionsFetcher = async (scope, templateKey, fieldKey, level, options) => metadataTaxonomyFetcher(api, fileId, scope, templateKey, fieldKey, level, options); const taxonomyNodeFetcher = async (scope, taxonomyKey, nodeID) => metadataTaxonomyNodeAncestorsFetcher(api, fileId, scope, taxonomyKey, nodeID); return /*#__PURE__*/React.createElement(SidebarContent, { actions: metadataDropdown, className: 'bcs-MetadataSidebarRedesign', elementId: elementId, sidebarView: SIDEBAR_VIEW_METADATA, title: formatMessage(messages.sidebarMetadataTitle) }, /*#__PURE__*/React.createElement("div", { className: "bcs-MetadataSidebarRedesign-content" }, errorMessageDisplay, showLoading && /*#__PURE__*/React.createElement(LoadingIndicator, { "aria-label": formatMessage(messages.loading) }), showEmptyState && /*#__PURE__*/React.createElement(MetadataEmptyState, { level: 'file', isBoxAiSuggestionsFeatureEnabled: isBoxAiSuggestionsEnabled }), /*#__PURE__*/React.createElement(AutofillContextProvider, { fetchSuggestions: extractSuggestions, isAiSuggestionsFeatureEnabled: isBoxAiSuggestionsEnabled }, editingTemplate && /*#__PURE__*/React.createElement(MetadataInstanceEditor, { areAiSuggestionsAvailable: areAiSuggestionsAvailable, isBoxAiSuggestionsEnabled: isBoxAiSuggestionsEnabled, isDeleteButtonDisabled: isDeleteButtonDisabled, isUnsavedChangesModalOpen: isUnsavedChangesModalOpen, onCancel: handleCancel, onDelete: handleDeleteInstance, onDiscardUnsavedChanges: handleDiscardUnsavedChanges, onSubmit: handleSubmit, setIsUnsavedChangesModalOpen: setIsUnsavedChangesModalOpen, taxonomyOptionsFetcher: taxonomyOptionsFetcher, template: editingTemplate }), showList && /*#__PURE__*/React.createElement(MetadataInstanceList, { areAiSuggestionsAvailable: areAiSuggestionsAvailable, isAiSuggestionsFeatureEnabled: isBoxAiSuggestionsEnabled, onEdit: templateInstance => { setEditingTemplate(templateInstance); setIsDeleteButtonDisabled(false); }, templateInstances: templateInstances, taxonomyNodeFetcher: taxonomyNodeFetcher })))); } export { MetadataSidebarRedesign as MetadataSidebarRedesignComponent }; export default flow([withLogger(ORIGIN_METADATA_SIDEBAR_REDESIGN), withErrorBoundary(ORIGIN_METADATA_SIDEBAR_REDESIGN), withAPIContext])(MetadataSidebarRedesign); //# sourceMappingURL=MetadataSidebarRedesign.js.map