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;