import * as React from 'react'; import { useEffect, useState } from 'react'; import { List, ListItem, ListItemButton, ListItemText, Menu, MenuItem, } from '@mui/material'; import { styled } from '@mui/material/styles'; import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown'; import { useTranslate } from 'ra-core'; import clsx from 'clsx'; import { useTiptapEditor } from '../useTiptapEditor'; export const LevelSelect = (props: LevelSelectProps) => { const translate = useTranslate(); const editor = useTiptapEditor(); const [anchorElement, setAnchorElement] = useState( null ); const { size } = props; const [selectedOption, setSelectedOption] = useState(options[0]); const handleMenuItemClick = ( event: React.MouseEvent, index: number ) => { setAnchorElement(null); const selectedItem = options[index]; if (selectedItem.value === 'paragraph') { editor.chain().focus().setParagraph().run(); } else if (selectedItem.value === 'heading') { editor .chain() .focus() .setHeading({ level: selectedItem.level }) .run(); } }; const handleClickListItem = ( event: React.MouseEvent ) => { setAnchorElement(event.currentTarget); }; const handleClose = (_event: React.MouseEvent) => { setAnchorElement(null); }; useEffect(() => { const handleUpdate = () => { setSelectedOption(currentOption => options.reduce((acc, option) => { if (editor) { if ( option.value === 'paragraph' && editor.isActive('paragraph') ) { return option; } if ( editor.isActive('heading', { level: (option as HeadingLevelOption).level, }) ) { return option; } } return acc; }, currentOption) ); }; if (editor) { editor.on('update', handleUpdate); editor.on('selectionUpdate', handleUpdate); } return () => { if (editor) { editor.off('update', handleUpdate); editor.off('selectionUpdate', handleUpdate); } }; }, [editor]); return ( {options.map((option, index) => ( { handleMenuItemClick(event, index); }} > {translate(option.label, { _: option.defaultLabel })} ))} ); }; type LevelOption = ParagraphLevelOption | HeadingLevelOption; type ParagraphLevelOption = { label: string; defaultLabel: string; value: 'paragraph'; }; type HeadingLevelOption = { label: string; defaultLabel: string; value: 'heading'; level: 1 | 2 | 3 | 4 | 5 | 6; }; const options: Array = [ { label: 'ra.tiptap.paragraph', defaultLabel: 'Normal', value: 'paragraph', }, { label: 'ra.tiptap.heading1', defaultLabel: 'Heading 1', value: 'heading', level: 1, }, { label: 'ra.tiptap.heading2', defaultLabel: 'Heading 2', value: 'heading', level: 2, }, { label: 'ra.tiptap.heading3', defaultLabel: 'Heading 3', value: 'heading', level: 3, }, { label: 'ra.tiptap.heading4', defaultLabel: 'Heading 4', value: 'heading', level: 4, }, { label: 'ra.tiptap.heading5', defaultLabel: 'Heading 5', value: 'heading', level: 5, }, { label: 'ra.tiptap.heading6', defaultLabel: 'Heading 6', value: 'heading', level: 6, }, ]; const PREFIX = 'RaRichTextInputLevelSelect'; const classes = { list: `${PREFIX}-list`, sizeSmall: `${PREFIX}-sizeSmall`, sizeLarge: `${PREFIX}-sizeLarge`, }; const Root = styled('div')(({ theme }) => ({ [`&.${classes.list}`]: { borderRadius: theme.shape.borderRadius, border: `1px solid color-mix(in srgb, ${(theme.vars || theme).palette.action.active}, transparent 12%)`, }, [`& .${classes.sizeSmall}`]: { paddingTop: 1, paddingBottom: 1, '& .MuiTypography-root': { fontSize: theme.typography.pxToRem(13), }, }, [`& .${classes.sizeLarge}`]: { paddingTop: 8, paddingBottom: 8, '& .MuiTypography-root': { fontSize: theme.typography.pxToRem(15), }, }, })); export type LevelSelectProps = { size?: 'small' | 'medium' | 'large'; };