@atlaskit/editor-plugin-insert-block
Version:
Insert block plugin for @atlaskit/editor-core
273 lines (272 loc) • 10.9 kB
JavaScript
/* RegisteredInsertMenuList.tsx generated by @compiled/babel-plugin v3.0.2 */
import _extends from "@babel/runtime/helpers/extends";
import "./RegisteredInsertMenuList.compiled.css";
import { ax, ix } from "@compiled/react/runtime";
import React, { useCallback, useLayoutEffect, useMemo, useRef } from 'react';
import { CellMeasurer, CellMeasurerCache } from 'react-virtualized/dist/commonjs/CellMeasurer';
import { List } from 'react-virtualized/dist/commonjs/List';
import { QuickInsertEmptyState } from '@atlaskit/editor-common/quick-insert/empty-state';
import { getQuickInsertMenuRows } from '@atlaskit/editor-common/quick-insert/registered-menu-model';
import { useMenuListHeight } from '@atlaskit/editor-common/quick-insert/use-menu-list-height';
// eslint-disable-next-line @atlaskit/platform/no-barrel-entry-imports
import { Box } from '@atlaskit/primitives/compiled/box';
import { RegisteredInsertMenuItem, RegisteredInsertMenuPreviewContext } from './RegisteredInsertMenuItem';
const ESTIMATED_ROW_HEIGHT = 44;
// 44px item + 1px separator.
const MENU_FOOTER_HEIGHT = 45;
const MENU_SEARCH_HEIGHT = 64;
const LIST_WIDTH = 350;
const listAriaProps = {
// @ts-expect-error react-virtualized forwards null to omit its grid-only default; its types exclude null.
'aria-readonly': null
};
const styles = {
content: "_1e0c1txw _2lx21bp4 _1tkeidpf",
scrollable: "_16jlkb7n _1tkeidpf _18m91wug",
footer: "_1o9zidpf",
footerWithDivider: "_1i531l7x _15a5nqa1 _uwhke4h9 _1o9zidpf",
externalControls: "_1i531l7x _15a5nqa1 _uwhke4h9 _1q51u2gc _85i5u2gc"
};
const RegisteredInsertMenuItemRow = ({
editorView,
id,
isOffline,
isSelected,
itemIndex,
menuOpenId,
onClose,
onItemFocus,
onItemBlur,
onItemHover,
onItemLeave,
registration,
surfaceContext
}) => {
useLayoutEffect(() => () => onItemLeave(registration.key), [onItemLeave, registration.key]);
const onBlur = useCallback(event => {
if (!event.currentTarget.contains(event.relatedTarget)) {
onItemBlur(registration.key);
}
}, [onItemBlur, registration.key]);
return /*#__PURE__*/React.createElement("div", {
onBlur: onBlur,
onFocus: () => onItemFocus(itemIndex, registration.key),
onMouseLeave: () => onItemLeave(registration.key),
onMouseMove: () => onItemHover(itemIndex, registration.key)
}, /*#__PURE__*/React.createElement(RegisteredInsertMenuItem, {
editorView: editorView,
id: id,
isOffline: isOffline,
isSelected: isSelected,
menuOpenId: menuOpenId,
onClose: onClose,
registration: registration,
surfaceContext: surfaceContext
}));
};
export const RegisteredInsertMenuList = ({
editorView,
emptyStateHandler,
isOffline,
listId,
listLabel,
maxHeight,
menuOpenId,
model,
onClose,
onItemFocus,
onItemBlur,
query,
onItemHover,
onItemLeave,
onItemVisibilityChange,
onRenderedItemsChange,
selectedItemIndex,
surfaceContext,
activePreviewItemKey
}) => {
var _model$root$component, _model$root;
const Root = (_model$root$component = (_model$root = model.root) === null || _model$root === void 0 ? void 0 : _model$root.component) !== null && _model$root$component !== void 0 ? _model$root$component : React.Fragment;
const listRef = useRef(null);
const setListRef = useCallback(list => {
listRef.current = list;
}, []);
const rows = useMemo(() => getQuickInsertMenuRows(model), [model]);
const cache = useMemo(() => new CellMeasurerCache({
defaultHeight: ESTIMATED_ROW_HEIGHT,
fixedWidth: true
}), []);
const itemIndexByRowIndex = useMemo(() => {
const indexes = new Map();
let itemIndex = 0;
rows.forEach((registration, rowIndex) => {
if (registration.type === 'menu-item') {
indexes.set(rowIndex, itemIndex++);
}
});
return indexes;
}, [rows]);
const selectedRowIndex = useMemo(() => {
for (const [rowIndex, itemIndex] of itemIndexByRowIndex) {
if (itemIndex === selectedItemIndex) {
return rowIndex;
}
}
return -1;
}, [itemIndexByRowIndex, selectedItemIndex]);
const rowKeySignature = useMemo(() => rows.map(({
key,
type
}) => `${type}:${key}`).join('\0'), [rows]);
const {
height: measuredHeight,
updateHeight
} = useMenuListHeight(cache, rows.length, rowKeySignature);
useLayoutEffect(() => {
var _listRef$current, _listRef$current2;
cache.clearAll();
(_listRef$current = listRef.current) === null || _listRef$current === void 0 ? void 0 : _listRef$current.recomputeRowHeights();
(_listRef$current2 = listRef.current) === null || _listRef$current2 === void 0 ? void 0 : _listRef$current2.scrollToPosition(0);
}, [cache, rowKeySignature]);
const onRowsRendered = useCallback(({
overscanStartIndex,
overscanStopIndex,
startIndex,
stopIndex
}) => {
var _indexes$, _indexes;
if (activePreviewItemKey) {
const previewRowIndex = rows.findIndex(({
key
}) => key === activePreviewItemKey);
if (previewRowIndex < startIndex || previewRowIndex > stopIndex) {
onItemVisibilityChange(activePreviewItemKey, false);
}
}
updateHeight();
const indexes = [...itemIndexByRowIndex].filter(([rowIndex]) => rowIndex >= overscanStartIndex && rowIndex <= overscanStopIndex).map(([, itemIndex]) => itemIndex);
onRenderedItemsChange({
startIndex: (_indexes$ = indexes[0]) !== null && _indexes$ !== void 0 ? _indexes$ : -1,
stopIndex: (_indexes = indexes[indexes.length - 1]) !== null && _indexes !== void 0 ? _indexes : -1
});
}, [activePreviewItemKey, itemIndexByRowIndex, onItemVisibilityChange, onRenderedItemsChange, rows, updateHeight]);
const onScrollableContentScroll = useCallback(event => {
if (event.target === event.currentTarget && activePreviewItemKey) {
onItemVisibilityChange(activePreviewItemKey, false);
}
}, [activePreviewItemKey, onItemVisibilityChange]);
const renderRow = useCallback(({
index,
key,
parent,
style
}) => {
var _registration$compone;
const registration = rows[index];
if (!registration) {
return null;
}
const itemIndex = itemIndexByRowIndex.get(index);
const rowContent = registration.type === 'menu-section' ? ( /*#__PURE__*/React.createElement((_registration$compone = registration.component) !== null && _registration$compone !== void 0 ? _registration$compone : React.Fragment, null, null)) : /*#__PURE__*/React.createElement(RegisteredInsertMenuItemRow, {
editorView: editorView,
id: `${listId}-${itemIndex}`,
isOffline: isOffline,
isSelected: itemIndex === selectedItemIndex,
itemIndex: itemIndex !== null && itemIndex !== void 0 ? itemIndex : 0,
menuOpenId: menuOpenId,
onClose: onClose,
onItemFocus: onItemFocus,
onItemBlur: onItemBlur,
onItemHover: onItemHover,
onItemLeave: onItemLeave,
registration: registration,
surfaceContext: surfaceContext
});
return /*#__PURE__*/React.createElement(CellMeasurer, {
key: key,
cache: cache,
parent: parent,
columnIndex: 0,
rowIndex: index
}, ({
registerChild
}) => /*#__PURE__*/React.createElement("div", {
ref: element => registerChild === null || registerChild === void 0 ? void 0 : registerChild(element !== null && element !== void 0 ? element : undefined)
// eslint-disable-next-line @atlaskit/ui-styling-standard/enforce-style-prop
,
style: style
}, /*#__PURE__*/React.createElement(Box, {
paddingBlockEnd: index === rows.length - 1 ? 'space.100' : undefined
}, rowContent)));
}, [cache, editorView, isOffline, itemIndexByRowIndex, listId, menuOpenId, onClose, onItemFocus, onItemBlur, onItemHover, onItemLeave, rows, selectedItemIndex, surfaceContext]);
const footerItemIndex = itemIndexByRowIndex.size;
const footerItemId = `${listId}-${footerItemIndex}`;
const hasExternalControls = model.fallbackItems !== undefined && emptyStateHandler !== undefined;
const hasNoSearchResults = query !== '' && rows.length === 0;
const externalControls = hasExternalControls && emptyStateHandler ? emptyStateHandler({
mode: 'inline',
searchTerm: query,
showNoResultsMessage: false
}) : null;
const showFooterDivider = !hasNoSearchResults || Boolean(externalControls);
const listMaxHeight = Math.max(0, maxHeight - MENU_SEARCH_HEIGHT - (model.footer ? MENU_FOOTER_HEIGHT : 0));
const contentStyle = {
maxHeight: Math.max(0, maxHeight - MENU_SEARCH_HEIGHT)
};
const onFooterItemHover = useCallback(() => onItemHover(footerItemIndex), [footerItemIndex, onItemHover]);
const onFooterItemFocus = useCallback(() => onItemFocus(footerItemIndex), [footerItemIndex, onItemFocus]);
const footer = model.footer ? /*#__PURE__*/React.createElement(Box, {
testId: "registered-insert-menu-footer",
xcss: showFooterDivider ? styles.footerWithDivider : styles.footer,
onFocus: onFooterItemFocus,
onMouseMove: onFooterItemHover
}, /*#__PURE__*/React.createElement(RegisteredInsertMenuItem, {
editorView: editorView,
id: footerItemId,
isOffline: isOffline,
isSelected: footerItemIndex === selectedItemIndex,
menuOpenId: menuOpenId,
onClose: onClose,
registration: model.footer,
surfaceContext: surfaceContext
})) : null;
if (!model.root) {
return null;
}
return /*#__PURE__*/React.createElement(Root, null, /*#__PURE__*/React.createElement(RegisteredInsertMenuPreviewContext.Provider, {
value: activePreviewItemKey
}, /*#__PURE__*/React.createElement(Box, {
xcss: styles.content,
style: contentStyle
}, /*#__PURE__*/React.createElement(Box, {
xcss: styles.scrollable,
onScroll: onScrollableContentScroll
}, /*#__PURE__*/React.createElement(Box, {
"aria-label": listLabel,
"aria-owns": model.footer ? footerItemId : undefined,
id: listId,
role: "listbox"
}, rows.length > 0 && /*#__PURE__*/React.createElement(List
// eslint-disable-next-line react/jsx-props-no-spreading -- Omits the library's grid-only ARIA default without declaring it on the group role.
, _extends({}, listAriaProps, {
tabIndex: null,
containerRole: "presentation",
height: Math.min(measuredHeight, listMaxHeight),
overscanRowCount: 3,
onRowsRendered: onRowsRendered,
ref: setListRef,
role: "group",
rowCount: rows.length,
rowHeight: cache.rowHeight,
rowRenderer: renderRow,
scrollToAlignment: "auto",
scrollToIndex: selectedRowIndex,
width: LIST_WIDTH
}))), hasNoSearchResults && /*#__PURE__*/React.createElement(QuickInsertEmptyState, {
mode: "inline",
spacing: "compact"
}), externalControls && /*#__PURE__*/React.createElement(Box, {
testId: "registered-insert-menu-external-controls",
xcss: styles.externalControls
}, externalControls)), footer)));
};