import React, {
useState,
useCallback,
ComponentClass,
ReactElement,
} from 'react';
import styled, { ThemeProvider } from 'styled-components';
import { QuickInsertItem } from '@atlaskit/editor-common/provider-factory';
import Item, { itemThemeNamespace } from '@atlaskit/item';
// AFP-2532 TODO: Fix automatic suppressions below
// eslint-disable-next-line @atlassian/tangerine/import/entry-points
import { borderRadius, gridSize } from '@atlaskit/theme';
import { themed } from '@atlaskit/theme/components';
import { DN50, N0, N30A, N60A } from '@atlaskit/theme/colors';
import {
IconCode,
IconDate,
IconDecision,
IconDivider,
IconExpand,
IconPanel,
IconQuote,
IconStatus,
} from '../../plugins/quick-insert/assets';
import { QuickInsertPluginState } from '../../plugins/quick-insert/types';
import withOuterListeners from '../with-outer-listeners';
import WithPluginState from '../WithPluginState';
import { pluginKey } from '../../plugins/quick-insert/plugin-key';
import {
getFeaturedQuickInsertItems,
searchQuickInsertItems,
} from '../../plugins/quick-insert/search';
import { insertItem } from '../../plugins/quick-insert/commands';
import ElementBrowser from './components/ElementBrowserLoader';
import { MenuItem } from '../DropdownMenu/types';
import { ELEMENT_ITEM_HEIGHT } from './constants';
import { InsertMenuProps, SvgGetterParams } from './types';
const InsertMenu = ({
editorView,
dropdownItems,
onInsert,
toggleVisiblity,
}: InsertMenuProps) => {
const [itemCount, setItemCount] = useState(0);
const transform = useCallback(
(item: MenuItem): QuickInsertItem => ({
title: item.content as string,
description: item.tooltipDescription,
keyshortcut: item.shortcut,
icon: () =>
getSvgIconForItem({
name: item.value.name,
}) || (item.elemBefore as ReactElement),
action: () => onInsert({ item }),
// "insertInsertMenuItem" expects these 2 properties.
onClick: item.onClick,
value: item.value,
}),
[onInsert],
);
const quickInsertDropdownItems = dropdownItems.map(transform);
const viewMoreItem = quickInsertDropdownItems.pop();
const onInsertItem = useCallback(
(item) => {
toggleVisiblity();
if (!editorView.hasFocus()) {
editorView.focus();
}
insertItem(item)(editorView.state, editorView.dispatch);
},
[editorView, toggleVisiblity],
);
const getItems = useCallback(
(quickInsertState: QuickInsertPluginState) => (
query?: string,
category?: string,
) => {
let result;
if (query) {
result = searchQuickInsertItems(quickInsertState, {})(query, category);
} else {
result = quickInsertDropdownItems.concat(
getFeaturedQuickInsertItems(quickInsertState, {})(),
) as QuickInsertItem[];
}
setItemCount(result.length);
return result;
},
[quickInsertDropdownItems],
);
const render = useCallback(
({ quickInsertState }) => (
),
[getItems, onInsertItem, quickInsertDropdownItems.length, toggleVisiblity],
);
return (
{itemCount > 0 && viewMoreItem && }
);
};
const ViewMore = ({ item }: { item: QuickInsertItem }) => {
const onKeyPress = useCallback(
(e: React.KeyboardEvent) => {
const SPACE_KEY = 32;
const ENTER_KEY = 13;
if (e.which === ENTER_KEY || e.which === SPACE_KEY) {
// @ts-ignore We manually transformed "view more" to a quickInsert item
// action would always toggle the ModalElementBrowser
item.action();
}
},
[item],
);
return (
- {item.icon!()}}
aria-describedby={item.title}
data-testid="view-more-elements-item"
onKeyPress={onKeyPress}
>
{item.title}
);
};
const getSvgIconForItem = ({
name,
}: SvgGetterParams): ReactElement | undefined => {
type IconType = { [key: string]: ComponentClass<{ label: string }> };
const Icon = ({
codeblock: IconCode,
panel: IconPanel,
blockquote: IconQuote,
decision: IconDecision,
horizontalrule: IconDivider,
expand: IconExpand,
date: IconDate,
status: IconStatus,
} as IconType)[name];
return Icon ? : undefined;
};
const getInsertMenuHeight = ({ itemCount }: { itemCount: number }) => {
// Figure based on visuals to exclude the searchbar, padding/margin, and the ViewMore item.
const EXTRA_SPACE_EXCLUDING_ELEMENTLIST = 112;
if (itemCount > 0 && itemCount < 6) {
return itemCount * ELEMENT_ITEM_HEIGHT + EXTRA_SPACE_EXCLUDING_ELEMENTLIST;
}
return 560; // For showing 6 Elements.
};
const InsertMenuWrapper = styled.div`
display: flex;
flex-direction: column;
width: 320px;
height: ${getInsertMenuHeight}px;
background-color: ${themed({ light: N0, dark: DN50 })()};
border-radius: ${borderRadius()}px;
box-shadow: 0 0 0 1px ${N30A}, 0 2px 1px ${N30A}, 0 0 20px -6px ${N60A};
`;
const ItemBefore = styled.div`
width: 40px;
height: 40px;
box-sizing: border-box;
display: flex;
justify-content: center;
align-items: center;
margin-right: ${gridSize() / 2}px;
`;
const PADDING_LEFT = 14;
const HEIGHT = 40;
const viewMoreItemTheme = {
[itemThemeNamespace]: {
padding: {
default: {
left: PADDING_LEFT,
},
},
height: HEIGHT,
},
};
const FlexWrapper = styled.div`
display: flex;
flex: 1;
box-sizing: border-box;
overflow: hidden;
`;
const ElementBrowserWrapper = withOuterListeners(FlexWrapper);
export default InsertMenu;