@orfeas126/box-ui-elements
Version:
Box UI Elements
161 lines • 8.03 kB
JavaScript
/**
* @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