From 038f62ff3e67b96906a1dd5ad025e5e259cfd6a4 Mon Sep 17 00:00:00 2001 From: lumixraku Date: Mon, 12 May 2025 21:56:11 +0800 Subject: [PATCH 01/43] feat: floating toolbar style1 --- .../toolbar/selection-toolbar/SelectionToolbar.tsx | 6 +++--- .../selection-toolbar/actions/AIAssistant.tsx | 14 +++++++------- .../selection-toolbar/actions/ActionButton.tsx | 13 +++++++------ .../toolbar/selection-toolbar/actions/Bold.tsx | 2 +- .../toolbar/selection-toolbar/actions/Heading.tsx | 4 ++-- .../toolbar/selection-toolbar/actions/Italic.tsx | 2 +- .../selection-toolbar/actions/Underline.tsx | 2 +- src/components/main/AppTheme.tsx | 2 +- 8 files changed, 23 insertions(+), 22 deletions(-) diff --git a/src/components/editor/components/toolbar/selection-toolbar/SelectionToolbar.tsx b/src/components/editor/components/toolbar/selection-toolbar/SelectionToolbar.tsx index 7619ff676..3bdcacf3c 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/SelectionToolbar.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/SelectionToolbar.tsx @@ -44,7 +44,7 @@ export function SelectionToolbar() {
{ // prevent toolbar from taking focus away from editor @@ -52,10 +52,10 @@ export function SelectionToolbar() { e.stopPropagation(); }} > - +
); } -export default SelectionToolbar; \ No newline at end of file +export default SelectionToolbar; diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/AIAssistant.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/AIAssistant.tsx index 5106069e6..3df402167 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/AIAssistant.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/AIAssistant.tsx @@ -32,7 +32,7 @@ function AIAssistant() { const addReplaceStyle = useCallback(() => { const range = editor.selection; - if(!range) return; + if (!range) return; addDecorate?.(range, 'line-through text-text-caption', 'ai-writer'); }, [addDecorate, editor.selection]); @@ -40,7 +40,7 @@ function AIAssistant() { const addHighLightStyle = useCallback(() => { const range = editor.selection; - if(!range) return; + if (!range) return; addDecorate?.(range, 'bg-content-blue-100', 'ai-writer'); }, [addDecorate, editor.selection]); @@ -56,7 +56,7 @@ function AIAssistant() { }, []); const onItemClicked = useCallback((type: AIAssistantType) => { - if([AIAssistantType.ImproveWriting, AIAssistantType.FixSpelling, AIAssistantType.MakeLonger, AIAssistantType.MakeShorter].includes(type)) { + if ([AIAssistantType.ImproveWriting, AIAssistantType.FixSpelling, AIAssistantType.MakeLonger, AIAssistantType.MakeShorter].includes(type)) { addReplaceStyle(); } else { addHighLightStyle(); @@ -66,7 +66,7 @@ function AIAssistant() { }, [addHighLightStyle, addReplaceStyle, editor]); useEffect(() => { - if(!toolbarVisible) { + if (!toolbarVisible) { setOpen(false); } }, [toolbarVisible]); @@ -96,8 +96,8 @@ function AIAssistant() { tooltip={t('editor.askAI')} >
- - + +
@@ -107,4 +107,4 @@ function AIAssistant() { ); } -export default AIAssistant; \ No newline at end of file +export default AIAssistant; diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/ActionButton.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/ActionButton.tsx index b6aa91cd4..b84454935 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/ActionButton.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/ActionButton.tsx @@ -20,17 +20,18 @@ const ActionButton = forwardRef< - {children} +
+ {children} +
); diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/Bold.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/Bold.tsx index bd2de7f67..79405f126 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/Bold.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/Bold.tsx @@ -32,7 +32,7 @@ export function Bold() { } > - + ); } diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/Heading.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/Heading.tsx index 94191be1b..61873094d 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/Heading.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/Heading.tsx @@ -25,7 +25,7 @@ const popoverProps: Partial = { }, slotProps: { paper: { - className: 'bg-[var(--fill-toolbar)] rounded-[6px]', + className: 'bg-[var(--surface-primary)] rounded-[6px]', }, }, }; @@ -109,7 +109,7 @@ export function Heading() { >
{getActiveButton()} - +
{toolbarVisible && ( diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/Italic.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/Italic.tsx index 6e7312c3f..cbad734e1 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/Italic.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/Italic.tsx @@ -31,7 +31,7 @@ export function Italic() { } > - + ); } diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/Underline.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/Underline.tsx index e084940b9..318e16420 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/Underline.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/Underline.tsx @@ -31,7 +31,7 @@ export function Underline() { } > - + ); } diff --git a/src/components/main/AppTheme.tsx b/src/components/main/AppTheme.tsx index 55306a35c..83c95c497 100644 --- a/src/components/main/AppTheme.tsx +++ b/src/components/main/AppTheme.tsx @@ -191,7 +191,7 @@ function AppTheme({ children }: { children: React.ReactNode }) { MuiTooltip: { styleOverrides: { arrow: { - color: 'var(--fill-toolbar)', + color: 'var(--surface-primary)', }, tooltip: { backgroundColor: 'var(--fill-toolbar)', From bc8a350896caac6af1e2baaad21792c5fb2b0ad0 Mon Sep 17 00:00:00 2001 From: lumixraku Date: Tue, 13 May 2025 00:30:53 +0800 Subject: [PATCH 02/43] fix: heading popover --- .../selection-toolbar/actions/AIAssistant.tsx | 5 +- .../actions/ActionButton.tsx | 2 +- .../selection-toolbar/actions/Heading.tsx | 119 +++++++++++++++--- 3 files changed, 103 insertions(+), 23 deletions(-) diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/AIAssistant.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/AIAssistant.tsx index 3df402167..c2a88af65 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/AIAssistant.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/AIAssistant.tsx @@ -95,11 +95,10 @@ function AIAssistant() { className={'!text-ai-primary !hover:text-billing-primary '} tooltip={t('editor.askAI')} > -
- +
+
- diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/ActionButton.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/ActionButton.tsx index b84454935..266bf9b5a 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/ActionButton.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/ActionButton.tsx @@ -29,7 +29,7 @@ const ActionButton = forwardRef< className={`${className ?? '' } bg-transparent px-1.5 py-1.5 text-icon-primary hover:bg-[var(--fill-content-hover)] hover:text-fill-default`} > -
+
{children}
diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/Heading.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/Heading.tsx index 61873094d..4e519dc6f 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/Heading.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/Heading.tsx @@ -13,6 +13,7 @@ import { ReactComponent as Heading1 } from '@/assets/icons/h1.svg'; import { ReactComponent as Heading2 } from '@/assets/icons/h2.svg'; import { ReactComponent as Heading3 } from '@/assets/icons/h3.svg'; import { ReactComponent as DownArrow } from '@/assets/icons/triangle_down.svg'; +import Button from '@mui/material/Button'; const popoverProps: Partial = { anchorOrigin: { @@ -20,12 +21,13 @@ const popoverProps: Partial = { horizontal: 'center', }, transformOrigin: { - vertical: -8, + vertical: 'top', horizontal: 'center', }, slotProps: { paper: { - className: 'bg-[var(--surface-primary)] rounded-[6px]', + className: 'bg-[var(--surface-primary)] rounded-[8px]', + style: { marginTop: '6px' } }, }, }; @@ -73,18 +75,18 @@ export function Heading() { const getActiveButton = useCallback(() => { if (isActivated(1)) { - return ; + return ; } if (isActivated(2)) { - return ; + return ; } if (isActivated(3)) { - return ; + return ; } - return ; + return ; }, [isActivated]); const [open, setOpen] = useState(false); @@ -107,7 +109,7 @@ export function Heading() { }} tooltip={'Heading'} > -
+
{getActiveButton()}
@@ -124,20 +126,99 @@ export function Heading() { anchorEl={ref.current} {...popoverProps} > -
- - - - - - - - - +
+ + +
- )} -
+ ) + } +
); } From e63747fd9e35dbd320b0f8e8d77b526e8af07a22 Mon Sep 17 00:00:00 2001 From: lumixraku Date: Tue, 13 May 2025 12:32:20 +0800 Subject: [PATCH 03/43] fix: change icon size from w-4 to w-5 --- .../selection-toolbar/actions/AIAssistant.tsx | 2 +- .../toolbar/selection-toolbar/actions/Align.tsx | 14 +++++++------- .../selection-toolbar/actions/BulletedList.tsx | 2 +- .../toolbar/selection-toolbar/actions/Color.tsx | 2 +- .../toolbar/selection-toolbar/actions/Formula.tsx | 2 +- .../toolbar/selection-toolbar/actions/Href.tsx | 2 +- .../selection-toolbar/actions/InlineCode.tsx | 2 +- .../selection-toolbar/actions/NumberedList.tsx | 2 +- .../toolbar/selection-toolbar/actions/Quote.tsx | 2 +- .../selection-toolbar/actions/StrikeThrough.tsx | 2 +- 10 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/AIAssistant.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/AIAssistant.tsx index c2a88af65..7ccbf3aa7 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/AIAssistant.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/AIAssistant.tsx @@ -78,7 +78,7 @@ function AIAssistant() { onClick={onClickImproveWriting} tooltip={t('editor.improveWriting')} > - + ; + return ; case 'center': - return ; + return ; case 'right': - return ; + return ; default: - return ; + return ; } }, [getAlign]); @@ -142,21 +142,21 @@ export function Align({ blockId, enabled = true }: { blockId?: string; enabled?: tooltip={t('document.plugins.optionAction.left')} onClick={toggleAlign(AlignType.Left)} > - + - + - +
diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/BulletedList.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/BulletedList.tsx index 780646902..7ff5b7280 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/BulletedList.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/BulletedList.tsx @@ -32,7 +32,7 @@ export function BulletedList() { return ( - + ); } diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/Color.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/Color.tsx index b23c05a35..485f17fc0 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/Color.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/Color.tsx @@ -215,7 +215,7 @@ function Color() { return ( <> - + {toolbarVisible && ( - + ); } diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/Href.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/Href.tsx index 03c22c38b..9617b0a40 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/Href.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/Href.tsx @@ -93,7 +93,7 @@ export function Href() { return ( <> - + } > - + ); } diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/NumberedList.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/NumberedList.tsx index 17268fdcf..6fc11ebb5 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/NumberedList.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/NumberedList.tsx @@ -32,7 +32,7 @@ export function NumberedList() { return ( - + ); } diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/Quote.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/Quote.tsx index e44983125..8b383c9be 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/Quote.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/Quote.tsx @@ -32,7 +32,7 @@ export function Quote() { return ( - + ); } diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/StrikeThrough.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/StrikeThrough.tsx index 9dcb9ffc0..f7d8aa6e4 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/StrikeThrough.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/StrikeThrough.tsx @@ -31,7 +31,7 @@ export function StrikeThrough() { } > - + ); } From 1d88d60df0b164b6fcebc4e47b797227fc8e24c7 Mon Sep 17 00:00:00 2001 From: lumixraku Date: Tue, 13 May 2025 14:00:03 +0800 Subject: [PATCH 04/43] fix: align popover style --- .../selection-toolbar/actions/Align.tsx | 107 ++++++++++++++---- 1 file changed, 87 insertions(+), 20 deletions(-) diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/Align.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/Align.tsx index 9c7a07cd9..f029eb8b0 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/Align.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/Align.tsx @@ -13,6 +13,7 @@ import { useTranslation } from 'react-i18next'; import { useSlateStatic } from 'slate-react'; import { Element } from 'slate'; import ActionButton from './ActionButton'; +import Button from '@mui/material/Button'; const popoverProps: Partial = { anchorOrigin: { @@ -25,7 +26,7 @@ const popoverProps: Partial = { }, slotProps: { paper: { - className: 'bg-[var(--fill-toolbar)] rounded-[6px]', + className: 'bg-[var(--surface-primary)] rounded-[6px]', }, }, }; @@ -136,28 +137,94 @@ export function Align({ blockId, enabled = true }: { blockId?: string; enabled?: anchorEl={ref.current} {...popoverProps} > -
- +
From f0569471ade4042b105f3246c39a5551dc50f58a Mon Sep 17 00:00:00 2001 From: lumixraku Date: Tue, 13 May 2025 14:32:07 +0800 Subject: [PATCH 05/43] fix: AI button style --- .../toolbar/selection-toolbar/actions/AIAssistant.tsx | 2 -- .../selection-toolbar/actions/ActionButton.tsx | 11 ++++++----- .../toolbar/selection-toolbar/actions/Paragraph.tsx | 2 +- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/AIAssistant.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/AIAssistant.tsx index 7ccbf3aa7..da738551d 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/AIAssistant.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/AIAssistant.tsx @@ -74,7 +74,6 @@ function AIAssistant() { return ( <> @@ -92,7 +91,6 @@ function AIAssistant() { setContent(CustomEditor.getSelectionContent(editor)); setOpen(prev => !prev); }} - className={'!text-ai-primary !hover:text-billing-primary '} tooltip={t('editor.askAI')} >
diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/ActionButton.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/ActionButton.tsx index 266bf9b5a..cfe4cd3cc 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/ActionButton.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/ActionButton.tsx @@ -1,6 +1,7 @@ import React, { forwardRef } from 'react'; import IconButton, { IconButtonProps } from '@mui/material/IconButton'; import { Tooltip } from '@mui/material'; +import { cn } from '@/lib/utils'; const ActionButton = forwardRef< HTMLButtonElement, @@ -21,13 +22,13 @@ const ActionButton = forwardRef< ref={ref} onClick={onClick} size={'large'} - style={{ - color: active ? 'var(--fill-default)' : disabled ? 'var(--text-caption)' : 'var(--icon-primary)', - }} disabled={disabled} {...props} - className={`${className ?? '' - } bg-transparent px-1.5 py-1.5 text-icon-primary hover:bg-[var(--fill-content-hover)] hover:text-fill-default`} + className={cn( + className, + 'bg-transparent px-1.5 py-1.5 text-icon-primary hover:bg-[var(--fill-content-hover)] hover:text-fill-default', + active && 'bg-[var(--fill-theme-select)]' + )} >
{children} diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/Paragraph.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/Paragraph.tsx index 32eeb26da..d1c20a876 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/Paragraph.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/Paragraph.tsx @@ -24,7 +24,7 @@ export function Paragraph() { return ( - + ); } From 8e78761ceac159981f1cfdb74e23163cbf030c8a Mon Sep 17 00:00:00 2001 From: lumixraku Date: Tue, 13 May 2025 19:39:35 +0800 Subject: [PATCH 06/43] test: host to 0.0.0.0 --- vite.config.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/vite.config.ts b/vite.config.ts index bb36c046b..8ba4649c2 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -99,6 +99,9 @@ export default defineConfig({ server: { port: process.env.PORT ? parseInt(process.env.PORT) : 3000, strictPort: true, + host: '0.0.0.0', + // origin: 'http://*.appflowy.com', + origin: 'http://app-flowy-web.vercel.app', watch: { ignored: ['node_modules'], }, @@ -125,7 +128,7 @@ export default defineConfig({ entryFileNames: 'static/js/[name]-[hash].js', assetFileNames: 'static/[ext]/[name]-[hash].[ext]', manualChunks(id) { - if( + if ( // id.includes('/react@') || // id.includes('/react-dom@') || id.includes('/react-is@') || From e4da3dd70a7a1fbfdfbb4fd5891f6606ee29c125 Mon Sep 17 00:00:00 2001 From: lumixraku Date: Tue, 13 May 2025 22:09:32 +0800 Subject: [PATCH 07/43] fix: align popover style fix: toolbar shadow style --- .../toolbar/selection-toolbar/SelectionToolbar.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/editor/components/toolbar/selection-toolbar/SelectionToolbar.tsx b/src/components/editor/components/toolbar/selection-toolbar/SelectionToolbar.tsx index 3bdcacf3c..32099b3c8 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/SelectionToolbar.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/SelectionToolbar.tsx @@ -44,8 +44,11 @@ export function SelectionToolbar() {
{ // prevent toolbar from taking focus away from editor e.preventDefault(); From c9eec2e13eb4ae8ad94c8ab8f3d5050c16a16f52 Mon Sep 17 00:00:00 2001 From: lumixraku Date: Tue, 13 May 2025 22:21:36 +0800 Subject: [PATCH 08/43] fix: dark mode for Href input --- .../components/leaf/href/HrefPopover.tsx | 52 +++++++++++++++++-- .../selection-toolbar/actions/Align.tsx | 3 ++ .../selection-toolbar/actions/Heading.tsx | 3 ++ 3 files changed, 53 insertions(+), 5 deletions(-) diff --git a/src/components/editor/components/leaf/href/HrefPopover.tsx b/src/components/editor/components/leaf/href/HrefPopover.tsx index 6e51dbd10..7b793db53 100644 --- a/src/components/editor/components/leaf/href/HrefPopover.tsx +++ b/src/components/editor/components/leaf/href/HrefPopover.tsx @@ -152,7 +152,20 @@ function HrefPopover({ setUrlValid(true); }} - size={'small'} fullWidth={true} placeholder={t('toolbar.addLink')}/> + size={'small'} fullWidth={true} placeholder={t('toolbar.addLink')} className="text-primary" sx={{ + '& .MuiOutlinedInput-input': { + color: 'var(--text-primary)', + }, + '& .MuiOutlinedInput-notchedOutline': { + borderColor: 'var(--line-divider)', + }, + '&:hover .MuiOutlinedInput-notchedOutline': { + borderColor: 'var(--line-divider)', + }, + '&.Mui-focused .MuiOutlinedInput-notchedOutline': { + borderColor: 'var(--line-divider)', + }, + }} /> {urlValid ? null : (
{t('editor.incorrectLink')} @@ -223,6 +236,21 @@ function HrefPopover({ size={'small'} fullWidth={true} placeholder={t('toolbar.addLink')} + className="text-primary" + sx={{ + '& .MuiOutlinedInput-input': { + color: 'var(--text-primary)', + }, + '& .MuiOutlinedInput-notchedOutline': { + borderColor: 'var(--line-divider)', + }, + '&:hover .MuiOutlinedInput-notchedOutline': { + borderColor: 'var(--line-divider)', + }, + '&.Mui-focused .MuiOutlinedInput-notchedOutline': { + borderColor: 'var(--line-divider)', + }, + }} /> {urlValid ? null : (
@@ -252,11 +280,25 @@ function HrefPopover({ } }} size={'small'} - fullWidth={true} placeholder={t('toolbar.addLink')}/> + fullWidth={true} placeholder={t('toolbar.addLink')} className="text-primary" sx={{ + '& .MuiOutlinedInput-input': { + color: 'var(--text-primary)', + }, + '& .MuiOutlinedInput-notchedOutline': { + borderColor: 'var(--line-divider)', + }, + '&:hover .MuiOutlinedInput-notchedOutline': { + borderColor: 'var(--line-divider)', + }, + '&.Mui-focused .MuiOutlinedInput-notchedOutline': { + borderColor: 'var(--line-divider)', + }, + }} + />
- +
- ) - } -
+ )} +
); } From efadc3375fd668272a6f219cf7ccb87fcf81b682 Mon Sep 17 00:00:00 2001 From: lumixraku Date: Wed, 14 May 2025 12:04:23 +0800 Subject: [PATCH 10/43] fix: hotkey to nav in list items hook --- .../selection-toolbar/actions/Align.tsx | 33 ++++++--- .../selection-toolbar/actions/Heading.tsx | 73 ++++--------------- .../hooks/useKeyboardNavigation.ts | 67 +++++++++++++++++ 3 files changed, 105 insertions(+), 68 deletions(-) create mode 100644 src/components/editor/components/toolbar/selection-toolbar/hooks/useKeyboardNavigation.ts diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/Align.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/Align.tsx index 8ff20b331..58f0a1f92 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/Align.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/Align.tsx @@ -14,6 +14,7 @@ import { useSlateStatic } from 'slate-react'; import { Element } from 'slate'; import ActionButton from './ActionButton'; import Button from '@mui/material/Button'; +import { useKeyboardNavigation } from '../hooks/useKeyboardNavigation'; const popoverProps: Partial = { anchorOrigin: { @@ -33,7 +34,6 @@ const popoverProps: Partial = { export function Align({ blockId, enabled = true }: { blockId?: string; enabled?: boolean }) { const [open, setOpen] = useState(false); - const ref = useRef(null); const { t } = useTranslation(); const editor = useSlateStatic() as YjsEditor; @@ -105,6 +105,16 @@ export function Align({ blockId, enabled = true }: { blockId?: string; enabled?: [getNode, editor, handleClose, rePosition] ); + const { getButtonProps, selectedIndex } = useKeyboardNavigation({ + itemCount: 3, + isOpen: open, + onSelect: (index) => { + const align = [AlignType.Left, AlignType.Center, AlignType.Right][index]; + toggleAlign(align)(); + }, + onClose: handleClose + }); + useEffect(() => { if (!enabled) { setOpen(false); @@ -139,6 +149,7 @@ export function Align({ blockId, enabled = true }: { blockId?: string; enabled?: >
+ + + +
+ +
+ ); +} + +export default TurnInfo; \ No newline at end of file From b4d92e0446769f1bb19c7a49a9cc1c5b25825480 Mon Sep 17 00:00:00 2001 From: lumixraku Date: Wed, 14 May 2025 19:33:42 +0800 Subject: [PATCH 16/43] feat: use menu instead of popover --- .../selection-toolbar/actions/TurnInto.tsx | 296 ++++++++++-------- 1 file changed, 161 insertions(+), 135 deletions(-) diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/TurnInto.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/TurnInto.tsx index 803ef39ab..6607b82c8 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/TurnInto.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/TurnInto.tsx @@ -1,7 +1,6 @@ import React, { useRef, useState } from 'react'; -import { Button, PopoverProps } from '@mui/material'; +import { Button, Menu, MenuItem, PopoverProps, Typography } from '@mui/material'; import ActionButton from './ActionButton'; -import { Popover } from '@/components/_shared/popover'; import { ReactComponent as BulletedListSvg } from '@/assets/icons/bulleted_list.svg'; import { ReactComponent as NumberedListSvg } from '@/assets/icons/numbered_list.svg'; import { ReactComponent as QuoteSvg } from '@/assets/icons/quote.svg'; @@ -13,38 +12,82 @@ import { YjsEditor } from '@/application/slate-yjs'; import { useTranslation } from 'react-i18next'; import { getBlockEntry } from '@/application/slate-yjs/utils/editor'; import { BlockType } from '@/application/types'; - -const popoverProps: Partial = { - anchorOrigin: { - vertical: 'bottom', - horizontal: 'left', - }, - transformOrigin: { - vertical: 'top', - horizontal: 'left', - }, - slotProps: { - paper: { - className: 'bg-[var(--surface-primary)] rounded-[8px]', - style: { marginTop: '6px' } - }, - }, -}; +import { useSelectionToolbarContext } from '../SelectionToolbar.hooks'; +import { useKeyboardNavigation } from '../hooks/useKeyboardNavigation'; function TurnInfo() { - const [open, setOpen] = useState(false); + const [anchorEl, setAnchorEl] = useState(null); const [selectedType, setSelectedType] = useState<'quote' | 'bulleted' | 'numbered' | null>(null); const ref = useRef(null); const editor = useSlateStatic() as YjsEditor; const selectedText = CustomEditor.getSelectionContent(editor)?.trim() || ''; const { t } = useTranslation(); - const handleClose = () => setOpen(false); + const { forceShow } = useSelectionToolbarContext(); + const open = Boolean(anchorEl); + const handleClose = () => { + setAnchorEl(null); + forceShow(false); + }; let displayText = 'Text'; if (selectedType === 'quote') displayText = String(t('toolbar.quote', { returnObjects: false, defaultValue: 'Quote' })); if (selectedType === 'bulleted') displayText = String(t('toolbar.bulletList', { returnObjects: false, defaultValue: 'Bulleted List' })); if (selectedType === 'numbered') displayText = String(t('toolbar.numberedList', { returnObjects: false, defaultValue: 'Numbered List' })); + const { getButtonProps, selectedIndex } = useKeyboardNavigation({ + itemCount: 3, + isOpen: open, + onSelect: (index) => { + if (index === 0) { + // Quote + try { + const [node] = getBlockEntry(editor); + if (!node) return; + if (node.type === BlockType.QuoteBlock) { + CustomEditor.turnToBlock(editor, node.blockId as string, BlockType.Paragraph, {}); + } else { + CustomEditor.turnToBlock(editor, node.blockId as string, BlockType.QuoteBlock, {}); + } + setSelectedType('quote'); + handleClose(); + } catch (e) { + handleClose(); + } + } else if (index === 1) { + // Bulleted List + try { + const [node] = getBlockEntry(editor); + if (!node) return; + if (node.type === BlockType.BulletedListBlock) { + CustomEditor.turnToBlock(editor, node.blockId as string, BlockType.Paragraph, {}); + } else { + CustomEditor.turnToBlock(editor, node.blockId as string, BlockType.BulletedListBlock, {}); + } + setSelectedType('bulleted'); + handleClose(); + } catch (e) { + handleClose(); + } + } else if (index === 2) { + // Numbered List + try { + const [node] = getBlockEntry(editor); + if (!node) return; + if (node.type === BlockType.NumberedListBlock) { + CustomEditor.turnToBlock(editor, node.blockId as string, BlockType.Paragraph, {}); + } else { + CustomEditor.turnToBlock(editor, node.blockId as string, BlockType.NumberedListBlock, {}); + } + setSelectedType('numbered'); + handleClose(); + } catch (e) { + handleClose(); + } + } + }, + onClose: handleClose, + }); + return (
{ e.preventDefault(); e.stopPropagation(); - setOpen(true); + setAnchorEl(e.currentTarget); + forceShow(true); }} tooltip={displayText} > @@ -60,7 +104,6 @@ function TurnInfo() {
- -
- - - -
-
+ setSelectedType('numbered'); + handleClose(); + } catch (e) { + handleClose(); + } + }} + > + + {String(t('toolbar.numberedList', { returnObjects: false, defaultValue: 'Numbered List' }))} + +
); } From 2688f32d589328e2207176e377d915d74070cb24 Mon Sep 17 00:00:00 2001 From: lumixraku Date: Thu, 15 May 2025 11:34:41 +0800 Subject: [PATCH 17/43] chore: better code --- src/@types/translations/en.json | 10 +- .../selection-toolbar/actions/TurnInto.tsx | 256 +++++++++--------- 2 files changed, 132 insertions(+), 134 deletions(-) diff --git a/src/@types/translations/en.json b/src/@types/translations/en.json index 0903aa25a..554ff1bdb 100644 --- a/src/@types/translations/en.json +++ b/src/@types/translations/en.json @@ -298,7 +298,13 @@ "highlight": "Highlight", "color": "Color", "addLink": "Add Link", - "link": "Link" + "link": "Link", + "suggestion": "Suggestion", + "turninto": "Turn into", + "paragraph": "Paragraph", + "heading1": "Heading 1", + "heading2": "Heading 2", + "heading3": "Heading 3" }, "tooltip": { "lightMode": "Switch to Light mode", @@ -3120,4 +3126,4 @@ "joining": "Joining...", "hasJoined": "You’ve joined to\n{{workspaceName}}" } -} +} \ No newline at end of file diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/TurnInto.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/TurnInto.tsx index 6607b82c8..32327bb31 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/TurnInto.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/TurnInto.tsx @@ -14,10 +14,71 @@ import { getBlockEntry } from '@/application/slate-yjs/utils/editor'; import { BlockType } from '@/application/types'; import { useSelectionToolbarContext } from '../SelectionToolbar.hooks'; import { useKeyboardNavigation } from '../hooks/useKeyboardNavigation'; +import { ReactComponent as ParagraphSvg } from '@/assets/icons/text.svg'; +import { ReactComponent as Heading1 } from '@/assets/icons/h1.svg'; +import { ReactComponent as Heading2 } from '@/assets/icons/h2.svg'; +import { ReactComponent as Heading3 } from '@/assets/icons/h3.svg'; +import type { HeadingBlockData } from '@/application/types'; + +type BlockOption = { + type: 'paragraph' | 'heading1' | 'heading2' | 'heading3' | 'quote' | 'bulleted' | 'numbered'; + icon: React.FC>; + label: string; + blockType: BlockType; + data?: any; +}; + +const blockOptions: BlockOption[] = [ + { + type: 'paragraph', + icon: ParagraphSvg, + label: 'editor.text', + blockType: BlockType.Paragraph, + }, + { + type: 'heading1', + icon: Heading1, + label: 'document.slashMenu.name.heading1', + blockType: BlockType.HeadingBlock, + data: { level: 1 }, + }, + { + type: 'heading2', + icon: Heading2, + label: 'document.slashMenu.name.heading2', + blockType: BlockType.HeadingBlock, + data: { level: 2 }, + }, + { + type: 'heading3', + icon: Heading3, + label: 'document.slashMenu.name.heading3', + blockType: BlockType.HeadingBlock, + data: { level: 3 }, + }, + { + type: 'quote', + icon: QuoteSvg, + label: 'toolbar.quote', + blockType: BlockType.QuoteBlock, + }, + { + type: 'bulleted', + icon: BulletedListSvg, + label: 'toolbar.bulletList', + blockType: BlockType.BulletedListBlock, + }, + { + type: 'numbered', + icon: NumberedListSvg, + label: 'toolbar.numberedList', + blockType: BlockType.NumberedListBlock, + }, +]; function TurnInfo() { const [anchorEl, setAnchorEl] = useState(null); - const [selectedType, setSelectedType] = useState<'quote' | 'bulleted' | 'numbered' | null>(null); + const [selectedType, setSelectedType] = useState(null); const ref = useRef(null); const editor = useSlateStatic() as YjsEditor; const selectedText = CustomEditor.getSelectionContent(editor)?.trim() || ''; @@ -29,62 +90,53 @@ function TurnInfo() { forceShow(false); }; - let displayText = 'Text'; - if (selectedType === 'quote') displayText = String(t('toolbar.quote', { returnObjects: false, defaultValue: 'Quote' })); - if (selectedType === 'bulleted') displayText = String(t('toolbar.bulletList', { returnObjects: false, defaultValue: 'Bulleted List' })); - if (selectedType === 'numbered') displayText = String(t('toolbar.numberedList', { returnObjects: false, defaultValue: 'Numbered List' })); + // Helper: get current block type and heading level + let currentType: string | null = null; + let currentLevel: number | null = null; + try { + const [node] = getBlockEntry(editor); + if (node.type === BlockType.Paragraph) currentType = 'paragraph'; + else if (node.type === BlockType.HeadingBlock) { + currentType = 'heading'; + currentLevel = (node.data as HeadingBlockData).level; + } else if (node.type === BlockType.QuoteBlock) currentType = 'quote'; + else if (node.type === BlockType.BulletedListBlock) currentType = 'bulleted'; + else if (node.type === BlockType.NumberedListBlock) currentType = 'numbered'; + } catch (e) { } + + const getDisplayText = () => { + if (currentType === 'paragraph') return t('editor.text', { defaultValue: 'Text' }); + if (currentType === 'heading' && currentLevel) { + return t(`document.slashMenu.name.heading${currentLevel}`, { defaultValue: `Heading ${currentLevel}` }); + } + if (currentType === 'quote') return t('toolbar.quote', { returnObjects: false, defaultValue: 'Quote' }); + if (currentType === 'bulleted') return t('toolbar.bulletList', { returnObjects: false, defaultValue: 'Bulleted List' }); + if (currentType === 'numbered') return t('toolbar.numberedList', { returnObjects: false, defaultValue: 'Numbered List' }); + + const option = blockOptions.find(opt => opt.type === selectedType); + return option ? t(option.label, { defaultValue: option.label }) : 'Text'; + }; + + const handleBlockChange = (option: BlockOption) => { + try { + const [node] = getBlockEntry(editor); + if (!node) return; + + if (node.type === option.blockType && + (!option.data || (node.type === BlockType.HeadingBlock && (node.data as HeadingBlockData).level === option.data.level))) { + CustomEditor.turnToBlock(editor, node.blockId as string, BlockType.Paragraph, {}); + } else { + CustomEditor.turnToBlock(editor, node.blockId as string, option.blockType, option.data || {}); + } + setSelectedType(option.type); + handleClose(); + } catch (e) { handleClose(); } + }; const { getButtonProps, selectedIndex } = useKeyboardNavigation({ - itemCount: 3, + itemCount: blockOptions.length, isOpen: open, - onSelect: (index) => { - if (index === 0) { - // Quote - try { - const [node] = getBlockEntry(editor); - if (!node) return; - if (node.type === BlockType.QuoteBlock) { - CustomEditor.turnToBlock(editor, node.blockId as string, BlockType.Paragraph, {}); - } else { - CustomEditor.turnToBlock(editor, node.blockId as string, BlockType.QuoteBlock, {}); - } - setSelectedType('quote'); - handleClose(); - } catch (e) { - handleClose(); - } - } else if (index === 1) { - // Bulleted List - try { - const [node] = getBlockEntry(editor); - if (!node) return; - if (node.type === BlockType.BulletedListBlock) { - CustomEditor.turnToBlock(editor, node.blockId as string, BlockType.Paragraph, {}); - } else { - CustomEditor.turnToBlock(editor, node.blockId as string, BlockType.BulletedListBlock, {}); - } - setSelectedType('bulleted'); - handleClose(); - } catch (e) { - handleClose(); - } - } else if (index === 2) { - // Numbered List - try { - const [node] = getBlockEntry(editor); - if (!node) return; - if (node.type === BlockType.NumberedListBlock) { - CustomEditor.turnToBlock(editor, node.blockId as string, BlockType.Paragraph, {}); - } else { - CustomEditor.turnToBlock(editor, node.blockId as string, BlockType.NumberedListBlock, {}); - } - setSelectedType('numbered'); - handleClose(); - } catch (e) { - handleClose(); - } - } - }, + onSelect: (index) => handleBlockChange(blockOptions[index]), onClose: handleClose, }); @@ -98,7 +150,7 @@ function TurnInfo() { setAnchorEl(e.currentTarget); forceShow(true); }} - tooltip={displayText} + tooltip={getDisplayText()} >
- {displayText} + {getDisplayText()}
@@ -123,39 +175,14 @@ function TurnInfo() { transformOrigin={{ vertical: 'top', horizontal: 'left' }} PaperProps={{ className: 'bg-[var(--surface-primary)] rounded-[8px]', - style: { marginTop: '6px', minWidth: 200, padding: 'var(--spacing-spacing-m)' } + style: { marginTop: '6px', minWidth: 200, paddingLeft: 'var(--spacing-spacing-m)', paddingRight: 'var(--spacing-spacing-m)' } }} > - {/* Group 1: Quote */} - getButtonProps(0).ref?.(el as any)} - selected={selectedIndex === 0} - sx={getButtonProps(0).sx} - onClick={() => { - try { - const [node] = getBlockEntry(editor); - if (!node) return; - if (node.type === BlockType.QuoteBlock) { - CustomEditor.turnToBlock(editor, node.blockId as string, BlockType.Paragraph, {}); - } else { - CustomEditor.turnToBlock(editor, node.blockId as string, BlockType.QuoteBlock, {}); - } - setSelectedType('quote'); - handleClose(); - } catch (e) { - handleClose(); - } - }} - > - - {String(t('toolbar.quote', { returnObjects: false, defaultValue: 'Quote' }))} - - {/* Group label */} + {/* Group label: Text */} - {t('toolbar.listGroup', { defaultValue: 'List' })} + {t('toolbar.suggestion', { defaultValue: 'Suggestion' })} - {/* Group 2: Bulleted List & Numbered List */} - getButtonProps(1).ref?.(el as any)} - selected={selectedIndex === 1} - sx={getButtonProps(1).sx} - onClick={() => { - try { - const [node] = getBlockEntry(editor); - if (!node) return; - if (node.type === BlockType.BulletedListBlock) { - CustomEditor.turnToBlock(editor, node.blockId as string, BlockType.Paragraph, {}); - } else { - CustomEditor.turnToBlock(editor, node.blockId as string, BlockType.BulletedListBlock, {}); - } - setSelectedType('bulleted'); - handleClose(); - } catch (e) { - handleClose(); - } - }} - > - - {String(t('toolbar.bulletList', { returnObjects: false, defaultValue: 'Bulleted List' }))} - - getButtonProps(2).ref?.(el as any)} - selected={selectedIndex === 2} - sx={getButtonProps(2).sx} - onClick={() => { - try { - const [node] = getBlockEntry(editor); - if (!node) return; - if (node.type === BlockType.NumberedListBlock) { - CustomEditor.turnToBlock(editor, node.blockId as string, BlockType.Paragraph, {}); - } else { - CustomEditor.turnToBlock(editor, node.blockId as string, BlockType.NumberedListBlock, {}); - } - setSelectedType('numbered'); - handleClose(); - } catch (e) { - handleClose(); - } - }} - > - - {String(t('toolbar.numberedList', { returnObjects: false, defaultValue: 'Numbered List' }))} - + {blockOptions.map((option, index) => ( + getButtonProps(index).ref?.(el as any)} + selected={selectedIndex === index} + sx={getButtonProps(index).sx} + onClick={() => handleBlockChange(option)} + > + + {t(option.label, { defaultValue: option.label })} + + ))} ); From c163f8573e294db1a11003fabc816936f9793886 Mon Sep 17 00:00:00 2001 From: lumixraku Date: Thu, 15 May 2025 12:20:11 +0800 Subject: [PATCH 18/43] test: group list item --- .../selection-toolbar/actions/TurnInto.tsx | 39 +++++++++++++++++-- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/TurnInto.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/TurnInto.tsx index 32327bb31..b8872301a 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/TurnInto.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/TurnInto.tsx @@ -18,10 +18,14 @@ import { ReactComponent as ParagraphSvg } from '@/assets/icons/text.svg'; import { ReactComponent as Heading1 } from '@/assets/icons/h1.svg'; import { ReactComponent as Heading2 } from '@/assets/icons/h2.svg'; import { ReactComponent as Heading3 } from '@/assets/icons/h3.svg'; +import { ReactComponent as ToggleListIcon } from '@/assets/icons/toggle_list.svg'; +import { ReactComponent as ToggleHeading1Icon } from '@/assets/icons/toggle_h1.svg'; +import { ReactComponent as ToggleHeading2Icon } from '@/assets/icons/toggle_h2.svg'; +import { ReactComponent as ToggleHeading3Icon } from '@/assets/icons/toggle_h3.svg'; import type { HeadingBlockData } from '@/application/types'; type BlockOption = { - type: 'paragraph' | 'heading1' | 'heading2' | 'heading3' | 'quote' | 'bulleted' | 'numbered'; + type: 'paragraph' | 'heading1' | 'heading2' | 'heading3' | 'quote' | 'bulleted' | 'numbered' | 'toggleHeading1' | 'toggleHeading2' | 'toggleHeading3' | 'toggle'; icon: React.FC>; label: string; blockType: BlockType; @@ -56,6 +60,33 @@ const blockOptions: BlockOption[] = [ blockType: BlockType.HeadingBlock, data: { level: 3 }, }, + { + type: 'toggle', + icon: ToggleListIcon, + label: 'document.slashMenu.name.toggleList', + blockType: BlockType.ToggleListBlock, + }, + { + type: 'toggleHeading1', + icon: ToggleHeading1Icon, + label: 'document.slashMenu.name.toggleHeading1', + blockType: BlockType.ToggleListBlock, + data: { level: 1 }, + }, + { + type: 'toggleHeading2', + icon: ToggleHeading2Icon, + label: 'document.slashMenu.name.toggleHeading2', + blockType: BlockType.ToggleListBlock, + data: { level: 2 }, + }, + { + type: 'toggleHeading3', + icon: ToggleHeading3Icon, + label: 'document.slashMenu.name.toggleHeading3', + blockType: BlockType.ToggleListBlock, + data: { level: 3 }, + }, { type: 'quote', icon: QuoteSvg, @@ -71,7 +102,7 @@ const blockOptions: BlockOption[] = [ { type: 'numbered', icon: NumberedListSvg, - label: 'toolbar.numberedList', + label: 'editor.numberedList', blockType: BlockType.NumberedListBlock, }, ]; @@ -178,7 +209,7 @@ function TurnInfo() { style: { marginTop: '6px', minWidth: 200, paddingLeft: 'var(--spacing-spacing-m)', paddingRight: 'var(--spacing-spacing-m)' } }} > - {/* Group label: Text */} + {/* Group label: Turn into */} - {t('toolbar.suggestion', { defaultValue: 'Suggestion' })} + {t('document.plugins.optionAction.turnInto', { defaultValue: 'Turn into' })} {blockOptions.map((option, index) => ( Date: Thu, 15 May 2025 12:54:52 +0800 Subject: [PATCH 19/43] feat: suggest & turninto --- .../selection-toolbar/actions/TurnInto.tsx | 186 +++++++++++++++--- 1 file changed, 163 insertions(+), 23 deletions(-) diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/TurnInto.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/TurnInto.tsx index b8872301a..46c351e9e 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/TurnInto.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/TurnInto.tsx @@ -23,6 +23,7 @@ import { ReactComponent as ToggleHeading1Icon } from '@/assets/icons/toggle_h1.s import { ReactComponent as ToggleHeading2Icon } from '@/assets/icons/toggle_h2.svg'; import { ReactComponent as ToggleHeading3Icon } from '@/assets/icons/toggle_h3.svg'; import type { HeadingBlockData } from '@/application/types'; +import { ReactComponent as TickIcon } from '@/assets/icons/tick.svg'; type BlockOption = { type: 'paragraph' | 'heading1' | 'heading2' | 'heading3' | 'quote' | 'bulleted' | 'numbered' | 'toggleHeading1' | 'toggleHeading2' | 'toggleHeading3' | 'toggle'; @@ -30,6 +31,7 @@ type BlockOption = { label: string; blockType: BlockType; data?: any; + group: 'text' | 'list' | 'toggle' | 'other'; }; const blockOptions: BlockOption[] = [ @@ -38,6 +40,7 @@ const blockOptions: BlockOption[] = [ icon: ParagraphSvg, label: 'editor.text', blockType: BlockType.Paragraph, + group: 'text', }, { type: 'heading1', @@ -45,6 +48,7 @@ const blockOptions: BlockOption[] = [ label: 'document.slashMenu.name.heading1', blockType: BlockType.HeadingBlock, data: { level: 1 }, + group: 'text', }, { type: 'heading2', @@ -52,6 +56,7 @@ const blockOptions: BlockOption[] = [ label: 'document.slashMenu.name.heading2', blockType: BlockType.HeadingBlock, data: { level: 2 }, + group: 'text', }, { type: 'heading3', @@ -59,12 +64,28 @@ const blockOptions: BlockOption[] = [ label: 'document.slashMenu.name.heading3', blockType: BlockType.HeadingBlock, data: { level: 3 }, + group: 'text', + }, + { + type: 'bulleted', + icon: BulletedListSvg, + label: 'toolbar.bulletList', + blockType: BlockType.BulletedListBlock, + group: 'list', + }, + { + type: 'numbered', + icon: NumberedListSvg, + label: 'editor.numberedList', + blockType: BlockType.NumberedListBlock, + group: 'list', }, { type: 'toggle', icon: ToggleListIcon, label: 'document.slashMenu.name.toggleList', blockType: BlockType.ToggleListBlock, + group: 'toggle', }, { type: 'toggleHeading1', @@ -72,6 +93,7 @@ const blockOptions: BlockOption[] = [ label: 'document.slashMenu.name.toggleHeading1', blockType: BlockType.ToggleListBlock, data: { level: 1 }, + group: 'toggle', }, { type: 'toggleHeading2', @@ -79,6 +101,7 @@ const blockOptions: BlockOption[] = [ label: 'document.slashMenu.name.toggleHeading2', blockType: BlockType.ToggleListBlock, data: { level: 2 }, + group: 'toggle', }, { type: 'toggleHeading3', @@ -86,24 +109,14 @@ const blockOptions: BlockOption[] = [ label: 'document.slashMenu.name.toggleHeading3', blockType: BlockType.ToggleListBlock, data: { level: 3 }, + group: 'toggle', }, { type: 'quote', icon: QuoteSvg, label: 'toolbar.quote', blockType: BlockType.QuoteBlock, - }, - { - type: 'bulleted', - icon: BulletedListSvg, - label: 'toolbar.bulletList', - blockType: BlockType.BulletedListBlock, - }, - { - type: 'numbered', - icon: NumberedListSvg, - label: 'editor.numberedList', - blockType: BlockType.NumberedListBlock, + group: 'other', }, ]; @@ -124,15 +137,34 @@ function TurnInfo() { // Helper: get current block type and heading level let currentType: string | null = null; let currentLevel: number | null = null; + let currentGroup: BlockOption['group'] | null = null; try { const [node] = getBlockEntry(editor); - if (node.type === BlockType.Paragraph) currentType = 'paragraph'; + if (node.type === BlockType.Paragraph) { + currentType = 'paragraph'; + currentGroup = 'text'; + } else if (node.type === BlockType.HeadingBlock) { currentType = 'heading'; currentLevel = (node.data as HeadingBlockData).level; - } else if (node.type === BlockType.QuoteBlock) currentType = 'quote'; - else if (node.type === BlockType.BulletedListBlock) currentType = 'bulleted'; - else if (node.type === BlockType.NumberedListBlock) currentType = 'numbered'; + currentGroup = 'text'; + } + else if (node.type === BlockType.QuoteBlock) { + currentType = 'quote'; + currentGroup = 'other'; + } + else if (node.type === BlockType.BulletedListBlock) { + currentType = 'bulleted'; + currentGroup = 'list'; + } + else if (node.type === BlockType.NumberedListBlock) { + currentType = 'numbered'; + currentGroup = 'list'; + } + else if (node.type === BlockType.ToggleListBlock) { + currentType = 'toggle'; + currentGroup = 'toggle'; + } } catch (e) { } const getDisplayText = () => { @@ -164,13 +196,53 @@ function TurnInfo() { } catch (e) { handleClose(); } }; + const getSuggestionOptions = () => { + if (!currentGroup) return []; + + // Get all options from the same group except the current one + return blockOptions.filter(option => + option.group === currentGroup && + (option.type !== currentType && + !(currentType === 'heading' && option.type === `heading${currentLevel}`)) + ); + }; + + const getTurnIntoOptions = () => { + const suggestionOptions = getSuggestionOptions(); + // Filter out options that are already in suggestions + return blockOptions.filter(option => + !suggestionOptions.some(suggestion => suggestion.type === option.type) + ); + }; + + const suggestionOptions = getSuggestionOptions(); + const turnIntoOptions = getTurnIntoOptions(); + const { getButtonProps, selectedIndex } = useKeyboardNavigation({ - itemCount: blockOptions.length, + itemCount: suggestionOptions.length + turnIntoOptions.length, isOpen: open, - onSelect: (index) => handleBlockChange(blockOptions[index]), + onSelect: (index) => { + const options = [...suggestionOptions, ...turnIntoOptions]; + handleBlockChange(options[index]); + }, onClose: handleClose, }); + function isOptionActive(option: BlockOption, currentType: string | null, currentLevel: number | null) { + if (!currentType) return false; + if (option.type === 'paragraph' && currentType === 'paragraph') return true; + if (option.type.startsWith('heading') && currentType === 'heading') { + // heading1/2/3 匹配 level + return option.data && option.data.level === currentLevel; + } + if (option.type === 'bulleted' && currentType === 'bulleted') return true; + if (option.type === 'numbered' && currentType === 'numbered') return true; + if (option.type === 'toggle' && currentType === 'toggle') return true; + if (option.type.startsWith('toggleHeading') && currentType === 'toggle' && option.data && option.data.level === currentLevel) return true; + if (option.type === 'quote' && currentType === 'quote') return true; + return false; + } + return (
- {/* Group label: Turn into */} + {suggestionOptions.length > 0 && ( + <> + + {t('toolbar.suggestion', { defaultValue: 'Suggestion' })} + + {suggestionOptions.map((option, index) => ( + getButtonProps(index).ref?.(el as any)} + selected={selectedIndex === index} + className="text--text-primary" + sx={{ + ...getButtonProps(index).sx, + fontSize: '14px', + fontStyle: 'normal', + fontWeight: 400, + lineHeight: '20px', + '&.Mui-selected, &.Mui-selected:hover': { + backgroundColor: 'var(--fill-list-hover) !important', + color: 'inherit', + }, + '&:hover': { + backgroundColor: 'var(--fill-list-hover)', + }, + }} + onClick={() => handleBlockChange(option)} + > + + {t(option.label, { defaultValue: option.label })} + {isOptionActive(option, currentType, currentLevel) && ( + + + + )} + + ))} + + )} {t('document.plugins.optionAction.turnInto', { defaultValue: 'Turn into' })} - {blockOptions.map((option, index) => ( + {turnIntoOptions.map((option, index) => ( getButtonProps(index).ref?.(el as any)} - selected={selectedIndex === index} - sx={getButtonProps(index).sx} + ref={el => getButtonProps(index + suggestionOptions.length).ref?.(el as any)} + selected={selectedIndex === index + suggestionOptions.length} + className="text--text-primary" + sx={{ + ...getButtonProps(index + suggestionOptions.length).sx, + fontSize: '14px', + fontStyle: 'normal', + fontWeight: 400, + lineHeight: '20px', + '&.Mui-selected, &.Mui-selected:hover': { + backgroundColor: 'var(--fill-list-hover) !important', + color: 'inherit', + }, + '&:hover': { + backgroundColor: 'var(--fill-list-hover)', + }, + }} onClick={() => handleBlockChange(option)} > {t(option.label, { defaultValue: option.label })} + {isOptionActive(option, currentType, currentLevel) && ( + + + + )} ))} From e9fecb574577d952efab8ea8f22834a720f446b4 Mon Sep 17 00:00:00 2001 From: lumixraku Date: Thu, 15 May 2025 15:05:26 +0800 Subject: [PATCH 20/43] chore: better code --- .../selection-toolbar/actions/Heading.tsx | 186 ++++++------------ 1 file changed, 62 insertions(+), 124 deletions(-) diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/Heading.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/Heading.tsx index 028f6f76e..bd5ab0a89 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/Heading.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/Heading.tsx @@ -34,6 +34,33 @@ const popoverProps: Partial = { }, }; +const headingOptions = [ + { + icon: , + labelKey: 'editor.text', + isActive: (isParagraph: () => boolean, isActivated: (level: number) => boolean) => isParagraph(), + onClick: (toParagraph: () => void, toHeading: (level: number) => () => void, setOpen: (v: boolean) => void) => () => { toParagraph(); setOpen(false); }, + }, + { + icon: , + labelKey: 'document.slashMenu.name.heading1', + isActive: (isParagraph: () => boolean, isActivated: (level: number) => boolean) => isActivated(1), + onClick: (toParagraph: () => void, toHeading: (level: number) => () => void, setOpen: (v: boolean) => void) => () => { toHeading(1)(); setOpen(false); }, + }, + { + icon: , + labelKey: 'document.slashMenu.name.heading2', + isActive: (isParagraph: () => boolean, isActivated: (level: number) => boolean) => isActivated(2), + onClick: (toParagraph: () => void, toHeading: (level: number) => () => void, setOpen: (v: boolean) => void) => () => { toHeading(2)(); setOpen(false); }, + }, + { + icon: , + labelKey: 'document.slashMenu.name.heading3', + isActive: (isParagraph: () => boolean, isActivated: (level: number) => boolean) => isActivated(3), + onClick: (toParagraph: () => void, toHeading: (level: number) => () => void, setOpen: (v: boolean) => void) => () => { toHeading(3)(); setOpen(false); }, + }, +]; + export function Heading() { const { t } = useTranslation(); const editor = useSlateStatic() as YjsEditor; @@ -167,130 +194,41 @@ export function Heading() { {...popoverProps} >
- - - - + {headingOptions.map((opt, idx) => ( + + ))}
)} From 9f7fcd958f5f743736b674dedf42f64546f79d90 Mon Sep 17 00:00:00 2001 From: lumixraku Date: Thu, 15 May 2025 15:38:30 +0800 Subject: [PATCH 21/43] add more options --- .../selection-toolbar/ToolbarActions.tsx | 9 ++ .../selection-toolbar/actions/MoreOptions.tsx | 143 ++++++++++++++++++ 2 files changed, 152 insertions(+) create mode 100644 src/components/editor/components/toolbar/selection-toolbar/actions/MoreOptions.tsx diff --git a/src/components/editor/components/toolbar/selection-toolbar/ToolbarActions.tsx b/src/components/editor/components/toolbar/selection-toolbar/ToolbarActions.tsx index 88d0a88b9..a7a4c1f96 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/ToolbarActions.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/ToolbarActions.tsx @@ -24,6 +24,14 @@ import { Editor, Element, Path } from 'slate'; import Paragraph from './actions/Paragraph'; import React, { useMemo } from 'react'; import { useSlate } from 'slate-react'; +import { ReactComponent as MoreIcon } from '@/assets/icons/more.svg'; +import Popover from '@/components/_shared/popover/Popover'; +import { useTranslation } from 'react-i18next'; +import ActionButton from './actions/ActionButton'; +import { ReactComponent as StrikeThroughSvg } from '@/assets/icons/strikethrough.svg'; +import { ReactComponent as FormulaSvg } from '@/assets/icons/formula.svg'; +import { Button } from '@mui/material'; +import MoreOptions from './actions/MoreOptions'; function ToolbarActions() { const editor = useSlate() as YjsEditor; @@ -110,6 +118,7 @@ function ToolbarActions() { } {!isCodeBlock && } + {!isCodeBlock && }
); } diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/MoreOptions.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/MoreOptions.tsx new file mode 100644 index 000000000..d2563f57a --- /dev/null +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/MoreOptions.tsx @@ -0,0 +1,143 @@ +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import { useSlate } from 'slate-react'; +import { ReactComponent as MoreIcon } from '@/assets/icons/more.svg'; +import { ReactComponent as StrikeThroughSvg } from '@/assets/icons/strikethrough.svg'; +import { ReactComponent as FormulaSvg } from '@/assets/icons/formula.svg'; +import Popover from '@/components/_shared/popover/Popover'; +import ActionButton from './ActionButton'; +import { Button } from '@mui/material'; +import { useKeyboardNavigation } from '../hooks/useKeyboardNavigation'; +import { CustomEditor } from '@/application/slate-yjs/command'; +import { EditorMarkFormat } from '@/application/slate-yjs/types'; +import { Transforms, Text, Editor } from 'slate'; + +const options = [ + { + icon: , + labelKey: 'editor.strikethrough', + onClick: (editor: any, setOpen: (v: boolean) => void) => { + CustomEditor.toggleMark(editor, { + key: EditorMarkFormat.StrikeThrough, + value: true, + }); + setOpen(false); + }, + }, + { + icon: , + labelKey: 'document.plugins.createInlineMathEquation', + onClick: (editor: any, setOpen: (v: boolean) => void) => { + const selection = editor.selection; + if (!selection) return; + const isActivated = CustomEditor.isMarkActive(editor, EditorMarkFormat.Formula); + if (!isActivated) { + const text = editor.string(selection); + editor.delete(); + editor.insertText('$'); + const newSelection = editor.selection; + if (!newSelection) return; + Transforms.select(editor, { + anchor: { + path: newSelection.anchor.path, + offset: newSelection.anchor.offset - 1, + }, + focus: newSelection.focus, + }); + CustomEditor.addMark(editor, { + key: EditorMarkFormat.Formula, + value: text, + }); + } else { + const [entry] = editor.nodes({ + at: selection, + match: (n: any) => !Editor.isEditor(n) && Text.isText(n) && (n as any).formula !== undefined, + }); + if (!entry) return; + const [node, path] = entry; + const formula = (node as any).formula; + if (!formula) return; + editor.select(path); + CustomEditor.removeMark(editor, EditorMarkFormat.Formula); + editor.delete(); + editor.insertText(formula); + } + setOpen(false); + }, + }, +]; + +export default function MoreOptions() { + const [open, setOpen] = React.useState(false); + const ref = React.useRef(null); + const { t } = useTranslation(); + const editor = useSlate(); + + const { getButtonProps, selectedIndex } = useKeyboardNavigation({ + itemCount: options.length, + isOpen: open, + onSelect: (index) => options[index].onClick(editor, setOpen), + onClose: () => setOpen(false), + }); + + return ( + <> + { + e.preventDefault(); + e.stopPropagation(); + setOpen(true); + }} + tooltip={t('toolbar.moreOptions', { defaultValue: 'More options' })} + > + + + setOpen(false)} + open={open} + anchorEl={ref.current} + anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }} + transformOrigin={{ vertical: 'top', horizontal: 'center' }} + > +
+ {options.map((opt, idx) => ( + + ))} +
+
+ + ); +} \ No newline at end of file From 8637f3470b56c3f8714c936d74a0291cfcd3d7bd Mon Sep 17 00:00:00 2001 From: lumixraku Date: Thu, 15 May 2025 17:05:48 +0800 Subject: [PATCH 22/43] chore: style fix --- .../selection-toolbar/ToolbarActions.tsx | 2 +- .../actions/ActionButton.tsx | 2 +- .../selection-toolbar/actions/Heading.tsx | 21 ++++++++++++------- .../selection-toolbar/actions/MoreOptions.tsx | 21 +++++++++++++++++-- .../selection-toolbar/actions/TurnInto.tsx | 12 +++++++---- 5 files changed, 42 insertions(+), 16 deletions(-) diff --git a/src/components/editor/components/toolbar/selection-toolbar/ToolbarActions.tsx b/src/components/editor/components/toolbar/selection-toolbar/ToolbarActions.tsx index a7a4c1f96..3e0a1085e 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/ToolbarActions.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/ToolbarActions.tsx @@ -99,7 +99,7 @@ function ToolbarActions() { {/* */} } - {/* {!isCodeBlock && } */} + {!isCodeBlock && } {/* {!isCodeBlock && !isAcrossBlock && } */} { !isAcrossBlock && !isCodeBlock && <> diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/ActionButton.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/ActionButton.tsx index 434454d8a..cd1c652d5 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/ActionButton.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/ActionButton.tsx @@ -26,7 +26,7 @@ const ActionButton = forwardRef< {...props} className={cn( className, - 'bg-transparent px-1.5 py-1.5 text-icon-primary hover:bg-[var(--fill-content-hover)] hover:text-fill-default', + 'bg-transparent px-1.5 py-1.5 text-icon-primary hover:bg-[var(--fill-content-hover)]', active && 'bg-[var(--fill-theme-select)]' )} > diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/Heading.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/Heading.tsx index bd5ab0a89..beb2205a6 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/Heading.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/Heading.tsx @@ -16,6 +16,8 @@ import { ReactComponent as DownArrow } from '@/assets/icons/triangle_down.svg'; import Button from '@mui/material/Button'; import { useKeyboardNavigation } from '../hooks/useKeyboardNavigation'; import { ReactComponent as ParagraphSvg } from '@/assets/icons/text.svg'; +import { ReactComponent as TickIcon } from '@/assets/icons/tick.svg'; +import { ReactComponent as TextFormatSvg } from '@/assets/icons/text_format.svg'; const popoverProps: Partial = { anchorOrigin: { @@ -126,19 +128,19 @@ export function Heading() { const getActiveButton = useCallback(() => { if (isParagraph()) { - return ; + return ; } if (isActivated(1)) { - return ; + return ; } if (isActivated(2)) { - return ; + return ; } if (isActivated(3)) { - return ; + return ; } return ; @@ -167,6 +169,7 @@ export function Heading() { return (
+ { @@ -177,7 +180,7 @@ export function Heading() { tooltip={t('editor.text')} >
- {getActiveButton()} +
@@ -218,15 +221,17 @@ export function Heading() { fontStyle: 'normal', fontWeight: 400, lineHeight: '20px', - ...(opt.isActive(isParagraph, isActivated) && { - backgroundColor: 'var(--fill-list-active)' - }), ...(selectedIndex === idx && { backgroundColor: 'var(--fill-list-hover)' }) }} > {String(t(opt.labelKey as any))} + {opt.isActive(isParagraph, isActivated) && ( + + + + )} ))}
diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/MoreOptions.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/MoreOptions.tsx index d2563f57a..d30246e1b 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/MoreOptions.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/MoreOptions.tsx @@ -11,6 +11,7 @@ import { useKeyboardNavigation } from '../hooks/useKeyboardNavigation'; import { CustomEditor } from '@/application/slate-yjs/command'; import { EditorMarkFormat } from '@/application/slate-yjs/types'; import { Transforms, Text, Editor } from 'slate'; +import { PopoverProps } from '@mui/material/Popover'; const options = [ { @@ -67,6 +68,23 @@ const options = [ }, ]; +const popoverProps: Partial = { + anchorOrigin: { + vertical: 'bottom', + horizontal: 'center', + }, + transformOrigin: { + vertical: 'top', + horizontal: 'center', + }, + slotProps: { + paper: { + className: 'bg-[var(--surface-primary)] rounded-[8px]', + style: { marginTop: '6px' } + }, + }, +}; + export default function MoreOptions() { const [open, setOpen] = React.useState(false); const ref = React.useRef(null); @@ -100,8 +118,7 @@ export default function MoreOptions() { onClose={() => setOpen(false)} open={open} anchorEl={ref.current} - anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }} - transformOrigin={{ vertical: 'top', horizontal: 'center' }} + {...popoverProps} >
{options.map((opt, idx) => ( diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/TurnInto.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/TurnInto.tsx index 46c351e9e..323cf89f7 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/TurnInto.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/TurnInto.tsx @@ -320,8 +320,10 @@ function TurnInfo() { }} onClick={() => handleBlockChange(option)} > - - {t(option.label, { defaultValue: option.label })} + + + {t(option.label, { defaultValue: option.label })} + {isOptionActive(option, currentType, currentLevel) && ( @@ -368,8 +370,10 @@ function TurnInfo() { }} onClick={() => handleBlockChange(option)} > - - {t(option.label, { defaultValue: option.label })} + + + {t(option.label, { defaultValue: option.label })} + {isOptionActive(option, currentType, currentLevel) && ( From 69e7ec2f02f609f47ac662729f63834aed5871b4 Mon Sep 17 00:00:00 2001 From: lumixraku Date: Thu, 15 May 2025 19:58:42 +0800 Subject: [PATCH 23/43] feat: color & highlight color --- src/assets/icons/text_color.svg | 2 +- src/assets/icons/text_highlight.svg | 2 +- .../selection-toolbar/ToolbarActions.tsx | 2 + .../selection-toolbar/actions/Color.tsx | 100 ++------- .../actions/ColorHighlight.tsx | 190 ++++++++++++++++++ 5 files changed, 214 insertions(+), 82 deletions(-) create mode 100644 src/components/editor/components/toolbar/selection-toolbar/actions/ColorHighlight.tsx diff --git a/src/assets/icons/text_color.svg b/src/assets/icons/text_color.svg index 123da8c04..eeac9edc6 100644 --- a/src/assets/icons/text_color.svg +++ b/src/assets/icons/text_color.svg @@ -1,4 +1,4 @@ - + diff --git a/src/assets/icons/text_highlight.svg b/src/assets/icons/text_highlight.svg index 2c2cb2ca8..f2056c2fe 100644 --- a/src/assets/icons/text_highlight.svg +++ b/src/assets/icons/text_highlight.svg @@ -1,4 +1,4 @@ - + diff --git a/src/components/editor/components/toolbar/selection-toolbar/ToolbarActions.tsx b/src/components/editor/components/toolbar/selection-toolbar/ToolbarActions.tsx index 3e0a1085e..5da0bd31b 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/ToolbarActions.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/ToolbarActions.tsx @@ -7,6 +7,7 @@ import Bold from '@/components/editor/components/toolbar/selection-toolbar/actio import BulletedList from '@/components/editor/components/toolbar/selection-toolbar/actions/BulletedList'; import Href from '@/components/editor/components/toolbar/selection-toolbar/actions/Href'; import Color from '@/components/editor/components/toolbar/selection-toolbar/actions/Color'; +import ColorHighlight from '@/components/editor/components/toolbar/selection-toolbar/actions/ColorHighlight'; import Formula from '@/components/editor/components/toolbar/selection-toolbar/actions/Formula'; import Heading from '@/components/editor/components/toolbar/selection-toolbar/actions/Heading'; import InlineCode from '@/components/editor/components/toolbar/selection-toolbar/actions/InlineCode'; @@ -96,6 +97,7 @@ function ToolbarActions() { + {/* */} } diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/Color.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/Color.tsx index 485f17fc0..73ee4d19c 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/Color.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/Color.tsx @@ -5,7 +5,7 @@ import { Popover } from '@/components/_shared/popover'; import { useSelectionToolbarContext } from '@/components/editor/components/toolbar/selection-toolbar/SelectionToolbar.hooks'; import { ColorEnum, renderColor } from '@/utils/color'; import { Tooltip } from '@mui/material'; -import React, { useCallback, useEffect, useMemo } from 'react'; +import React, { useCallback, useEffect, useMemo, useRef } from 'react'; import ActionButton from './ActionButton'; import { useTranslation } from 'react-i18next'; import { useSlateStatic } from 'slate-react'; @@ -26,12 +26,26 @@ function Color() { const [anchorEl, setAnchorEl] = React.useState(null); const open = Boolean(anchorEl); + const wrapperRef = useRef(null); + useEffect(() => { if (!toolbarVisible) { setAnchorEl(null); } }, [toolbarVisible]); + useEffect(() => { + if (wrapperRef.current) { + const svg = wrapperRef.current.querySelector('svg'); + if (svg) { + const bar = svg.querySelector('[class*="color-bar"]'); + if (bar) { + bar.setAttribute('stroke', activeFontColor || 'currentColor'); + } + } + } + }, [activeFontColor]); + const onClick = useCallback((e: React.MouseEvent) => { e.stopPropagation(); e.preventDefault(); @@ -101,54 +115,9 @@ function Color() { ]; }, [t]); - const editorBgColors = useMemo(() => { - return [ - { - label: t('editor.backgroundColorDefault'), - color: '', - }, - { - label: t('editor.backgroundColorLime'), - color: ColorEnum.Lime, - }, - { - label: t('editor.backgroundColorAqua'), - color: ColorEnum.Aqua, - }, - { - label: t('editor.backgroundColorOrange'), - color: ColorEnum.Orange, - }, - { - label: t('editor.backgroundColorYellow'), - color: ColorEnum.Yellow, - }, - { - label: t('editor.backgroundColorGreen'), - color: ColorEnum.Green, - }, - { - label: t('editor.backgroundColorBlue'), - color: ColorEnum.Blue, - }, - { - label: t('editor.backgroundColorPurple'), - color: ColorEnum.Purple, - }, - { - label: t('editor.backgroundColorPink'), - color: ColorEnum.Pink, - }, - { - label: t('editor.backgroundColorRed'), - color: ColorEnum.LightPink, - }, - ]; - }, [t]); - const popoverContent = useMemo(() => { return ( -
+
{t('editor.textColor')}
@@ -177,45 +146,16 @@ function Color() { })}
-
-
{t('editor.backgroundColor')}
-
- {editorBgColors.map((color, index) => { - return ( - -
handlePickedColor(EditorMarkFormat.BgColor, color.color)} - > -
-
-
- - ); - })} -
-
); - }, [activeBgColor, activeFontColor, editorBgColors, editorTextColors, handlePickedColor, t]); + }, [activeFontColor, editorTextColors, handlePickedColor, t]); return ( <> - + + + {toolbarVisible && ( mark[EditorMarkFormat.BgColor])?.[EditorMarkFormat.BgColor]; + + const [anchorEl, setAnchorEl] = React.useState(null); + const open = Boolean(anchorEl); + + const wrapperRef = useRef(null); + + useEffect(() => { + if (wrapperRef.current) { + const svg = wrapperRef.current.querySelector('svg'); + if (svg) { + const bar = svg.querySelector('[class*="color-bar"]'); + if (bar) { + bar.setAttribute('stroke', activeBgColor ? renderColor(activeBgColor) : 'currentColor'); + } + } + } + }, [activeBgColor]); + + useEffect(() => { + if (!toolbarVisible) { + setAnchorEl(null); + } + }, [toolbarVisible]); + + const onClick = useCallback((e: React.MouseEvent) => { + e.stopPropagation(); + e.preventDefault(); + setAnchorEl(e.currentTarget); + }, []); + + const handleClose = useCallback(() => { + setAnchorEl(null); + }, []); + + const handlePickedColor = useCallback( + (format: EditorMarkFormat, color: string) => { + if (color) { + CustomEditor.addMark(editor, { + key: format, + value: color, + }); + } else { + CustomEditor.removeMark(editor, format); + } + }, + [editor] + ); + + const editorBgColors = useMemo(() => { + return [ + { + label: t('editor.backgroundColorDefault'), + color: '', + }, + { + label: t('editor.backgroundColorLime'), + color: ColorEnum.Lime, + }, + { + label: t('editor.backgroundColorAqua'), + color: ColorEnum.Aqua, + }, + { + label: t('editor.backgroundColorOrange'), + color: ColorEnum.Orange, + }, + { + label: t('editor.backgroundColorYellow'), + color: ColorEnum.Yellow, + }, + { + label: t('editor.backgroundColorGreen'), + color: ColorEnum.Green, + }, + { + label: t('editor.backgroundColorBlue'), + color: ColorEnum.Blue, + }, + { + label: t('editor.backgroundColorPurple'), + color: ColorEnum.Purple, + }, + { + label: t('editor.backgroundColorPink'), + color: ColorEnum.Pink, + }, + { + label: t('editor.backgroundColorRed'), + color: ColorEnum.LightPink, + }, + ]; + }, [t]); + + const popoverContent = useMemo(() => { + return ( +
+
+
{t('editor.backgroundColor')}
+
+ {editorBgColors.map((color, index) => { + return ( + +
handlePickedColor(EditorMarkFormat.BgColor, color.color)} + > +
+
+
+ + ); + })} +
+
+
+ ); + }, [activeBgColor, editorBgColors, handlePickedColor, t]); + + return ( + <> + + + + + + {toolbarVisible && ( + { + e.preventDefault(); + e.stopPropagation(); + }} + onMouseUp={(e) => { + e.stopPropagation(); + }} + disableRestoreFocus={true} + disableAutoFocus={true} + disableEnforceFocus={true} + open={open} + onClose={handleClose} + anchorEl={anchorEl} + anchorOrigin={{ + vertical: 'bottom', + horizontal: 'center', + }} + transformOrigin={{ + vertical: -8, + horizontal: 'center', + }} + > + {popoverContent} + + )} + + ); +} + +export default ColorHighlight; \ No newline at end of file From 61e1eaf564375bf03ed08d20a642df7356f204df Mon Sep 17 00:00:00 2001 From: lumixraku Date: Fri, 16 May 2025 15:00:45 +0800 Subject: [PATCH 24/43] fix: eslint fix --- .eslintrc.cjs | 54 +- package.json | 4 + pnpm-lock.yaml | 1285 ++++++++++++++++- .../components/leaf/href/HrefPopover.tsx | 21 +- .../selection-toolbar/SelectionToolbar.tsx | 4 +- .../selection-toolbar/ToolbarActions.tsx | 29 +- .../selection-toolbar/actions/AIAssistant.tsx | 20 +- .../actions/ActionButton.tsx | 11 +- .../selection-toolbar/actions/Align.tsx | 18 +- .../selection-toolbar/actions/Bold.tsx | 10 +- .../actions/BulletedList.tsx | 12 +- .../selection-toolbar/actions/Color.tsx | 25 +- .../actions/ColorHighlight.tsx | 20 +- .../selection-toolbar/actions/Formula.tsx | 14 +- .../selection-toolbar/actions/Heading.tsx | 63 +- .../selection-toolbar/actions/Href.tsx | 20 +- .../selection-toolbar/actions/InlineCode.tsx | 10 +- .../selection-toolbar/actions/Italic.tsx | 10 +- .../selection-toolbar/actions/MoreOptions.tsx | 34 +- .../actions/NumberedList.tsx | 12 +- .../selection-toolbar/actions/Paragraph.tsx | 8 +- .../selection-toolbar/actions/Quote.tsx | 12 +- .../actions/StrikeThrough.tsx | 10 +- .../selection-toolbar/actions/TurnInto.tsx | 48 +- .../selection-toolbar/actions/Underline.tsx | 10 +- .../hooks/useKeyboardNavigation.ts | 3 +- src/components/main/AppTheme.tsx | 10 +- 27 files changed, 1500 insertions(+), 277 deletions(-) diff --git a/.eslintrc.cjs b/.eslintrc.cjs index be02b0d02..63e94f1bf 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -5,7 +5,12 @@ module.exports = { es6: true, node: true, }, - extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended'], + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:import/recommended', + 'plugin:import/typescript' + ], parser: '@typescript-eslint/parser', parserOptions: { project: 'tsconfig.json', @@ -13,8 +18,49 @@ module.exports = { tsconfigRootDir: __dirname, extraFileExtensions: ['.json'], }, - plugins: ['@typescript-eslint', 'react-hooks'], + plugins: ['@typescript-eslint', 'react-hooks', 'import'], + settings: { + 'import/resolver': { + typescript: { + alwaysTryTypes: true, + project: 'tsconfig.json', + }, + }, + }, rules: { + // Import rules + 'import/order': [ + 'error', + { + 'groups': ['builtin', 'external', 'internal', 'parent', 'sibling', 'index', 'type'], + 'pathGroups': [ + { + 'pattern': '@/**', + 'group': 'internal', + 'position': 'before' + } + ], + 'newlines-between': 'always', + 'alphabetize': { 'order': 'asc', 'caseInsensitive': true } + } + ], + 'import/no-unresolved': 'error', + 'import/no-duplicates': 'error', + 'import/no-named-as-default': 'off', + 'import/no-named-as-default-member': 'off', + + // TypeScript rules + '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/ban-types': ['error', { + 'types': { + 'Object': false, + 'object': false, + 'Function': false, + }, + 'extendDefaults': true + }], + + // Existing rules 'react-hooks/rules-of-hooks': 'error', 'react-hooks/exhaustive-deps': 'error', '@typescript-eslint/adjacent-overload-signatures': 'error', @@ -34,7 +80,6 @@ module.exports = { eqeqeq: ['error', 'always'], 'no-cond-assign': 'error', 'no-duplicate-case': 'error', - 'no-duplicate-imports': 'error', 'no-empty': [ 'error', { @@ -66,8 +111,7 @@ module.exports = { { blankLine: 'any', prev: 'import', next: 'import' }, { blankLine: 'always', prev: 'block-like', next: '*' }, { blankLine: 'always', prev: 'block', next: '*' }, - ], }, ignorePatterns: ['src/**/*.test.ts', '**/__tests__/**/*.json', 'package.json', '__mocks__/*.ts'], -}; +}; \ No newline at end of file diff --git a/package.json b/package.json index cd7975d74..8350a7502 100644 --- a/package.json +++ b/package.json @@ -181,9 +181,13 @@ "cypress-image-snapshot": "^4.0.1", "cypress-real-events": "^1.13.0", "eslint": "^8.57.0", + "eslint-import-resolver-typescript": "^4.3.4", + "eslint-plugin-import": "^2.31.0", "eslint-plugin-react": "^7.32.2", "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-refresh": "^0.4.6", + "eslint-plugin-simple-import-sort": "^12.1.1", + "eslint-plugin-unused-imports": "^4.1.4", "istanbul-lib-coverage": "^3.2.2", "jest-environment-jsdom": "^29.6.2", "jest-node-exports-resolver": "^1.1.6", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 18ae10573..762984fea 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -468,6 +468,12 @@ devDependencies: eslint: specifier: ^8.57.0 version: 8.57.0 + eslint-import-resolver-typescript: + specifier: ^4.3.4 + version: 4.3.4(eslint-plugin-import@2.31.0)(eslint@8.57.0) + eslint-plugin-import: + specifier: ^2.31.0 + version: 2.31.0(@typescript-eslint/parser@7.2.0)(eslint-import-resolver-typescript@4.3.4)(eslint@8.57.0) eslint-plugin-react: specifier: ^7.32.2 version: 7.32.2(eslint@8.57.0) @@ -477,6 +483,12 @@ devDependencies: eslint-plugin-react-refresh: specifier: ^0.4.6 version: 0.4.6(eslint@8.57.0) + eslint-plugin-simple-import-sort: + specifier: ^12.1.1 + version: 12.1.1(eslint@8.57.0) + eslint-plugin-unused-imports: + specifier: ^4.1.4 + version: 4.1.4(@typescript-eslint/eslint-plugin@7.2.0)(eslint@8.57.0) istanbul-lib-coverage: specifier: ^3.2.2 version: 3.2.2 @@ -967,7 +979,7 @@ packages: '@babel/core': 7.24.3 '@babel/helper-compilation-targets': 7.24.7 '@babel/helper-plugin-utils': 7.24.7 - debug: 4.3.7 + debug: 4.4.1 lodash.debounce: 4.0.8 resolve: 1.22.8 transitivePeerDependencies: @@ -2342,7 +2354,7 @@ packages: '@babel/helper-split-export-declaration': 7.22.6 '@babel/parser': 7.24.1 '@babel/types': 7.24.0 - debug: 4.3.7 + debug: 4.4.1 globals: 11.12.0 transitivePeerDependencies: - supports-color @@ -2359,7 +2371,7 @@ packages: '@babel/helper-split-export-declaration': 7.24.7 '@babel/parser': 7.24.7 '@babel/types': 7.24.7 - debug: 4.3.7 + debug: 4.4.1 globals: 11.12.0 transitivePeerDependencies: - supports-color @@ -2525,6 +2537,31 @@ packages: - supports-color dev: true + /@emnapi/core@1.4.3: + resolution: {integrity: sha512-4m62DuCE07lw01soJwPiBGC0nAww0Q+RY70VZ+n49yDIO13yyinhbWCeNnaob0lakDtWQzSdtNWzJeOJt2ma+g==} + requiresBuild: true + dependencies: + '@emnapi/wasi-threads': 1.0.2 + tslib: 2.6.2 + dev: true + optional: true + + /@emnapi/runtime@1.4.3: + resolution: {integrity: sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==} + requiresBuild: true + dependencies: + tslib: 2.6.2 + dev: true + optional: true + + /@emnapi/wasi-threads@1.0.2: + resolution: {integrity: sha512-5n3nTJblwRi8LlXkJ9eBzu+kZR8Yxcc7ubakyQTFzPMtIhFpUBRbsnc2Dv88IZDIbCDlBiWrknhB4Lsz7mg6BA==} + requiresBuild: true + dependencies: + tslib: 2.6.2 + dev: true + optional: true + /@emoji-mart/data@1.2.1: resolution: {integrity: sha512-no2pQMWiBy6gpBEiqGeU77/bFejDqUTRY7KX+0+iur13op3bqUsXdnwoZs6Xb1zbv0gAj5VvS1PWoUUckSr5Dw==} dev: false @@ -3055,7 +3092,7 @@ packages: '@antfu/install-pkg': 0.4.1 '@antfu/utils': 0.7.10 '@iconify/types': 2.0.0 - debug: 4.3.7 + debug: 4.4.1 kolorist: 1.8.0 local-pkg: 0.5.1 mlly: 1.7.3 @@ -3813,6 +3850,16 @@ packages: - '@types/react' dev: false + /@napi-rs/wasm-runtime@0.2.9: + resolution: {integrity: sha512-OKRBiajrrxB9ATokgEQoG87Z25c67pCpYcCwmXYX8PBftC9pBfN18gnm/fh1wurSLEKIAt+QRFLFCQISrb66Jg==} + requiresBuild: true + dependencies: + '@emnapi/core': 1.4.3 + '@emnapi/runtime': 1.4.3 + '@tybys/wasm-util': 0.9.0 + dev: true + optional: true + /@nodelib/fs.scandir@2.1.5: resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -5119,6 +5166,10 @@ packages: requiresBuild: true optional: true + /@rtsao/scc@1.1.0: + resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} + dev: true + /@sinclair/typebox@0.27.8: resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} @@ -5445,6 +5496,14 @@ packages: resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} dev: true + /@tybys/wasm-util@0.9.0: + resolution: {integrity: sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==} + requiresBuild: true + dependencies: + tslib: 2.6.2 + dev: true + optional: true + /@types/aria-query@5.0.4: resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} dev: true @@ -5756,6 +5815,10 @@ packages: resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} dev: true + /@types/json5@0.0.29: + resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} + dev: true + /@types/katex@0.16.0: resolution: {integrity: sha512-hz+S3nV6Mym5xPbT9fnO8dDhBFQguMYpY0Ipxv06JMi1ORgnEM4M1ymWDUhUNer3ElLmT583opRo4RzxKmh9jw==} dev: true @@ -6125,6 +6188,144 @@ packages: /@ungap/structured-clone@1.2.0: resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} + /@unrs/resolver-binding-darwin-arm64@1.7.2: + resolution: {integrity: sha512-vxtBno4xvowwNmO/ASL0Y45TpHqmNkAaDtz4Jqb+clmcVSSl8XCG/PNFFkGsXXXS6AMjP+ja/TtNCFFa1QwLRg==} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@unrs/resolver-binding-darwin-x64@1.7.2: + resolution: {integrity: sha512-qhVa8ozu92C23Hsmv0BF4+5Dyyd5STT1FolV4whNgbY6mj3kA0qsrGPe35zNR3wAN7eFict3s4Rc2dDTPBTuFQ==} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@unrs/resolver-binding-freebsd-x64@1.7.2: + resolution: {integrity: sha512-zKKdm2uMXqLFX6Ac7K5ElnnG5VIXbDlFWzg4WJ8CGUedJryM5A3cTgHuGMw1+P5ziV8CRhnSEgOnurTI4vpHpg==} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@unrs/resolver-binding-linux-arm-gnueabihf@1.7.2: + resolution: {integrity: sha512-8N1z1TbPnHH+iDS/42GJ0bMPLiGK+cUqOhNbMKtWJ4oFGzqSJk/zoXFzcQkgtI63qMcUI7wW1tq2usZQSb2jxw==} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@unrs/resolver-binding-linux-arm-musleabihf@1.7.2: + resolution: {integrity: sha512-tjYzI9LcAXR9MYd9rO45m1s0B/6bJNuZ6jeOxo1pq1K6OBuRMMmfyvJYval3s9FPPGmrldYA3mi4gWDlWuTFGA==} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@unrs/resolver-binding-linux-arm64-gnu@1.7.2: + resolution: {integrity: sha512-jon9M7DKRLGZ9VYSkFMflvNqu9hDtOCEnO2QAryFWgT6o6AXU8du56V7YqnaLKr6rAbZBWYsYpikF226v423QA==} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@unrs/resolver-binding-linux-arm64-musl@1.7.2: + resolution: {integrity: sha512-c8Cg4/h+kQ63pL43wBNaVMmOjXI/X62wQmru51qjfTvI7kmCy5uHTJvK/9LrF0G8Jdx8r34d019P1DVJmhXQpA==} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@unrs/resolver-binding-linux-ppc64-gnu@1.7.2: + resolution: {integrity: sha512-A+lcwRFyrjeJmv3JJvhz5NbcCkLQL6Mk16kHTNm6/aGNc4FwPHPE4DR9DwuCvCnVHvF5IAd9U4VIs/VvVir5lg==} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@unrs/resolver-binding-linux-riscv64-gnu@1.7.2: + resolution: {integrity: sha512-hQQ4TJQrSQW8JlPm7tRpXN8OCNP9ez7PajJNjRD1ZTHQAy685OYqPrKjfaMw/8LiHCt8AZ74rfUVHP9vn0N69Q==} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@unrs/resolver-binding-linux-riscv64-musl@1.7.2: + resolution: {integrity: sha512-NoAGbiqrxtY8kVooZ24i70CjLDlUFI7nDj3I9y54U94p+3kPxwd2L692YsdLa+cqQ0VoqMWoehDFp21PKRUoIQ==} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@unrs/resolver-binding-linux-s390x-gnu@1.7.2: + resolution: {integrity: sha512-KaZByo8xuQZbUhhreBTW+yUnOIHUsv04P8lKjQ5otiGoSJ17ISGYArc+4vKdLEpGaLbemGzr4ZeUbYQQsLWFjA==} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@unrs/resolver-binding-linux-x64-gnu@1.7.2: + resolution: {integrity: sha512-dEidzJDubxxhUCBJ/SHSMJD/9q7JkyfBMT77Px1npl4xpg9t0POLvnWywSk66BgZS/b2Hy9Y1yFaoMTFJUe9yg==} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@unrs/resolver-binding-linux-x64-musl@1.7.2: + resolution: {integrity: sha512-RvP+Ux3wDjmnZDT4XWFfNBRVG0fMsc+yVzNFUqOflnDfZ9OYujv6nkh+GOr+watwrW4wdp6ASfG/e7bkDradsw==} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@unrs/resolver-binding-wasm32-wasi@1.7.2: + resolution: {integrity: sha512-y797JBmO9IsvXVRCKDXOxjyAE4+CcZpla2GSoBQ33TVb3ILXuFnMrbR/QQZoauBYeOFuu4w3ifWLw52sdHGz6g==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + requiresBuild: true + dependencies: + '@napi-rs/wasm-runtime': 0.2.9 + dev: true + optional: true + + /@unrs/resolver-binding-win32-arm64-msvc@1.7.2: + resolution: {integrity: sha512-gtYTh4/VREVSLA+gHrfbWxaMO/00y+34htY7XpioBTy56YN2eBjkPrY1ML1Zys89X3RJDKVaogzwxlM1qU7egg==} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@unrs/resolver-binding-win32-ia32-msvc@1.7.2: + resolution: {integrity: sha512-Ywv20XHvHTDRQs12jd3MY8X5C8KLjDbg/jyaal/QLKx3fAShhJyD4blEANInsjxW3P7isHx1Blt56iUDDJO3jg==} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@unrs/resolver-binding-win32-x64-msvc@1.7.2: + resolution: {integrity: sha512-friS8NEQfHaDbkThxopGk+LuE5v3iY0StruifjQEt7SLbA46OnfgMO15sOTkbpJkol6RB+1l1TYPXh0sCddpvA==} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + /@vitejs/plugin-react@4.2.1(vite@5.2.0): resolution: {integrity: sha512-oojO9IDc4nCUUi8qIR11KoQm0XFFLIwsRBwHRR4d/88IWghn1y6ckz/bJ8GHDCsYEJee8mDzqtJxh15/cisJNQ==} engines: {node: ^14.18.0 || >=16.0.0} @@ -6339,7 +6540,7 @@ packages: resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} engines: {node: '>= 6.0.0'} dependencies: - debug: 4.3.7 + debug: 4.4.1 transitivePeerDependencies: - supports-color dev: true @@ -6512,8 +6713,16 @@ packages: resolution: {integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.7 - is-array-buffer: 3.0.4 + call-bind: 1.0.8 + is-array-buffer: 3.0.5 + dev: true + + /array-buffer-byte-length@1.0.2: + resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} + engines: {node: '>= 0.4'} + dependencies: + call-bound: 1.0.4 + is-array-buffer: 3.0.5 dev: true /array-includes@3.1.8: @@ -6533,6 +6742,19 @@ packages: engines: {node: '>=8'} dev: true + /array.prototype.findlastindex@1.2.6: + resolution: {integrity: sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.23.9 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + es-shim-unscopables: 1.1.0 + dev: true + /array.prototype.flat@1.3.2: resolution: {integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==} engines: {node: '>= 0.4'} @@ -6567,14 +6789,27 @@ packages: resolution: {integrity: sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==} engines: {node: '>= 0.4'} dependencies: - array-buffer-byte-length: 1.0.1 - call-bind: 1.0.7 + array-buffer-byte-length: 1.0.2 + call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.23.3 + es-abstract: 1.23.9 es-errors: 1.3.0 - get-intrinsic: 1.2.4 - is-array-buffer: 3.0.4 - is-shared-array-buffer: 1.0.3 + get-intrinsic: 1.3.0 + is-array-buffer: 3.0.5 + is-shared-array-buffer: 1.0.4 + dev: true + + /arraybuffer.prototype.slice@1.0.4: + resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} + engines: {node: '>= 0.4'} + dependencies: + array-buffer-byte-length: 1.0.2 + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.23.9 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + is-array-buffer: 3.0.5 dev: true /asn1@0.2.6: @@ -6591,6 +6826,11 @@ packages: engines: {node: '>=8'} dev: true + /async-function@1.0.0: + resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} + engines: {node: '>= 0.4'} + dev: true + /async-retry@1.3.3: resolution: {integrity: sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==} dependencies: @@ -6947,6 +7187,13 @@ packages: write-file-atomic: 3.0.3 dev: true + /call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + /call-bind@1.0.7: resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} engines: {node: '>= 0.4'} @@ -6956,6 +7203,24 @@ packages: function-bind: 1.1.2 get-intrinsic: 1.2.4 set-function-length: 1.2.2 + dev: true + + /call-bind@1.0.8: + resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} + engines: {node: '>= 0.4'} + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.0 + get-intrinsic: 1.2.4 + set-function-length: 1.2.2 + + /call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + dev: true /callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} @@ -7938,29 +8203,56 @@ packages: resolution: {integrity: sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 es-errors: 1.3.0 is-data-view: 1.0.1 dev: true + /data-view-buffer@1.0.2: + resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + dev: true + /data-view-byte-length@1.0.1: resolution: {integrity: sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 es-errors: 1.3.0 is-data-view: 1.0.1 dev: true + /data-view-byte-length@1.0.2: + resolution: {integrity: sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + dev: true + /data-view-byte-offset@1.0.0: resolution: {integrity: sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 es-errors: 1.3.0 is-data-view: 1.0.1 dev: true + /data-view-byte-offset@1.0.1: + resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + dev: true + /date-arithmetic@4.1.0: resolution: {integrity: sha512-QWxYLR5P/6GStZcdem+V1xoto6DMadYWpMXU82ES3/RfR3Wdwr3D0+be7mgOJ+Ov0G9D5Dmb9T17sNLQYj9XOg==} dev: false @@ -8005,8 +8297,8 @@ packages: ms: 2.1.2 supports-color: 8.1.1 - /debug@4.3.7: - resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} + /debug@4.4.1: + resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} engines: {node: '>=6.0'} peerDependencies: supports-color: '*' @@ -8293,6 +8585,14 @@ packages: engines: {node: '>=12'} dev: true + /dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + /duplexify@3.7.1: resolution: {integrity: sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==} dependencies: @@ -8388,13 +8688,13 @@ packages: array-buffer-byte-length: 1.0.1 arraybuffer.prototype.slice: 1.0.3 available-typed-arrays: 1.0.7 - call-bind: 1.0.7 + call-bind: 1.0.8 data-view-buffer: 1.0.1 data-view-byte-length: 1.0.1 data-view-byte-offset: 1.0.0 es-define-property: 1.0.0 es-errors: 1.3.0 - es-object-atoms: 1.0.0 + es-object-atoms: 1.1.1 es-set-tostringtag: 2.0.3 es-to-primitive: 1.2.1 function.prototype.name: 1.1.6 @@ -8433,12 +8733,73 @@ packages: which-typed-array: 1.1.15 dev: true + /es-abstract@1.23.9: + resolution: {integrity: sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==} + engines: {node: '>= 0.4'} + dependencies: + array-buffer-byte-length: 1.0.2 + arraybuffer.prototype.slice: 1.0.4 + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.4 + data-view-buffer: 1.0.2 + data-view-byte-length: 1.0.2 + data-view-byte-offset: 1.0.1 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + es-set-tostringtag: 2.1.0 + es-to-primitive: 1.3.0 + function.prototype.name: 1.1.8 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + get-symbol-description: 1.1.0 + globalthis: 1.0.4 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + has-proto: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + internal-slot: 1.1.0 + is-array-buffer: 3.0.5 + is-callable: 1.2.7 + is-data-view: 1.0.2 + is-regex: 1.2.1 + is-shared-array-buffer: 1.0.4 + is-string: 1.1.1 + is-typed-array: 1.1.15 + is-weakref: 1.1.1 + math-intrinsics: 1.1.0 + object-inspect: 1.13.4 + object-keys: 1.1.1 + object.assign: 4.1.7 + own-keys: 1.0.1 + regexp.prototype.flags: 1.5.4 + safe-array-concat: 1.1.3 + safe-push-apply: 1.0.0 + safe-regex-test: 1.1.0 + set-proto: 1.0.0 + string.prototype.trim: 1.2.10 + string.prototype.trimend: 1.0.9 + string.prototype.trimstart: 1.0.8 + typed-array-buffer: 1.0.3 + typed-array-byte-length: 1.0.3 + typed-array-byte-offset: 1.0.4 + typed-array-length: 1.0.7 + unbox-primitive: 1.1.0 + which-typed-array: 1.1.19 + dev: true + /es-define-property@1.0.0: resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==} engines: {node: '>= 0.4'} dependencies: get-intrinsic: 1.2.4 + /es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + /es-errors@1.3.0: resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} engines: {node: '>= 0.4'} @@ -8458,11 +8819,27 @@ packages: es-errors: 1.3.0 dev: true + /es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + dependencies: + es-errors: 1.3.0 + /es-set-tostringtag@2.0.3: resolution: {integrity: sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==} engines: {node: '>= 0.4'} dependencies: - get-intrinsic: 1.2.4 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + dev: true + + /es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 has-tostringtag: 1.0.2 hasown: 2.0.2 dev: true @@ -8473,6 +8850,13 @@ packages: hasown: 2.0.2 dev: true + /es-shim-unscopables@1.1.0: + resolution: {integrity: sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==} + engines: {node: '>= 0.4'} + dependencies: + hasown: 2.0.2 + dev: true + /es-to-primitive@1.2.1: resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} engines: {node: '>= 0.4'} @@ -8482,6 +8866,15 @@ packages: is-symbol: 1.0.4 dev: true + /es-to-primitive@1.3.0: + resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} + engines: {node: '>= 0.4'} + dependencies: + is-callable: 1.2.7 + is-date-object: 1.0.5 + is-symbol: 1.0.4 + dev: true + /es6-error@4.1.1: resolution: {integrity: sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==} dev: true @@ -8549,22 +8942,124 @@ packages: source-map: 0.6.1 dev: true - /eslint-plugin-react-hooks@4.6.0(eslint@8.57.0): - resolution: {integrity: sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==} - engines: {node: '>=10'} - peerDependencies: - eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 + /eslint-import-resolver-node@0.3.9: + resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} dependencies: - eslint: 8.57.0 + debug: 3.2.7(supports-color@8.1.1) + is-core-module: 2.16.1 + resolve: 1.22.8 + transitivePeerDependencies: + - supports-color dev: true - /eslint-plugin-react-refresh@0.4.6(eslint@8.57.0): - resolution: {integrity: sha512-NjGXdm7zgcKRkKMua34qVO9doI7VOxZ6ancSvBELJSSoX97jyndXcSoa8XBh69JoB31dNz3EEzlMcizZl7LaMA==} + /eslint-import-resolver-typescript@4.3.4(eslint-plugin-import@2.31.0)(eslint@8.57.0): + resolution: {integrity: sha512-buzw5z5VtiQMysYLH9iW9BV04YyZebsw+gPi+c4FCjfS9i6COYOrEWw9t3m3wA9PFBfqcBCqWf32qrXLbwafDw==} + engines: {node: ^16.17.0 || >=18.6.0} peerDependencies: - eslint: '>=7' + eslint: '*' + eslint-plugin-import: '*' + eslint-plugin-import-x: '*' + peerDependenciesMeta: + eslint-plugin-import: + optional: true + eslint-plugin-import-x: + optional: true dependencies: + debug: 4.4.1 eslint: 8.57.0 - dev: true + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@7.2.0)(eslint-import-resolver-typescript@4.3.4)(eslint@8.57.0) + get-tsconfig: 4.10.0 + is-bun-module: 2.0.0 + stable-hash: 0.0.5 + tinyglobby: 0.2.13 + unrs-resolver: 1.7.2 + transitivePeerDependencies: + - supports-color + dev: true + + /eslint-module-utils@2.12.0(@typescript-eslint/parser@7.2.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@4.3.4)(eslint@8.57.0): + resolution: {integrity: sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: '*' + eslint-import-resolver-node: '*' + eslint-import-resolver-typescript: '*' + eslint-import-resolver-webpack: '*' + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + eslint: + optional: true + eslint-import-resolver-node: + optional: true + eslint-import-resolver-typescript: + optional: true + eslint-import-resolver-webpack: + optional: true + dependencies: + '@typescript-eslint/parser': 7.2.0(eslint@8.57.0)(typescript@4.9.5) + debug: 3.2.7(supports-color@8.1.1) + eslint: 8.57.0 + eslint-import-resolver-node: 0.3.9 + eslint-import-resolver-typescript: 4.3.4(eslint-plugin-import@2.31.0)(eslint@8.57.0) + transitivePeerDependencies: + - supports-color + dev: true + + /eslint-plugin-import@2.31.0(@typescript-eslint/parser@7.2.0)(eslint-import-resolver-typescript@4.3.4)(eslint@8.57.0): + resolution: {integrity: sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9 + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + dependencies: + '@rtsao/scc': 1.1.0 + '@typescript-eslint/parser': 7.2.0(eslint@8.57.0)(typescript@4.9.5) + array-includes: 3.1.8 + array.prototype.findlastindex: 1.2.6 + array.prototype.flat: 1.3.2 + array.prototype.flatmap: 1.3.2 + debug: 3.2.7(supports-color@8.1.1) + doctrine: 2.1.0 + eslint: 8.57.0 + eslint-import-resolver-node: 0.3.9 + eslint-module-utils: 2.12.0(@typescript-eslint/parser@7.2.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@4.3.4)(eslint@8.57.0) + hasown: 2.0.2 + is-core-module: 2.16.1 + is-glob: 4.0.3 + minimatch: 3.1.2 + object.fromentries: 2.0.8 + object.groupby: 1.0.3 + object.values: 1.2.0 + semver: 6.3.1 + string.prototype.trimend: 1.0.8 + tsconfig-paths: 3.15.0 + transitivePeerDependencies: + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - supports-color + dev: true + + /eslint-plugin-react-hooks@4.6.0(eslint@8.57.0): + resolution: {integrity: sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==} + engines: {node: '>=10'} + peerDependencies: + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 + dependencies: + eslint: 8.57.0 + dev: true + + /eslint-plugin-react-refresh@0.4.6(eslint@8.57.0): + resolution: {integrity: sha512-NjGXdm7zgcKRkKMua34qVO9doI7VOxZ6ancSvBELJSSoX97jyndXcSoa8XBh69JoB31dNz3EEzlMcizZl7LaMA==} + peerDependencies: + eslint: '>=7' + dependencies: + eslint: 8.57.0 + dev: true /eslint-plugin-react@7.32.2(eslint@8.57.0): resolution: {integrity: sha512-t2fBMa+XzonrrNkyVirzKlvn5RXzzPwRHtMvLAtVZrt8oxgnTQaYbU6SXTOO1mwQgp1y5+toMSKInnzGr0Knqg==} @@ -8590,6 +9085,27 @@ packages: string.prototype.matchall: 4.0.11 dev: true + /eslint-plugin-simple-import-sort@12.1.1(eslint@8.57.0): + resolution: {integrity: sha512-6nuzu4xwQtE3332Uz0to+TxDQYRLTKRESSc2hefVT48Zc8JthmN23Gx9lnYhu0FtkRSL1oxny3kJ2aveVhmOVA==} + peerDependencies: + eslint: '>=5.0.0' + dependencies: + eslint: 8.57.0 + dev: true + + /eslint-plugin-unused-imports@4.1.4(@typescript-eslint/eslint-plugin@7.2.0)(eslint@8.57.0): + resolution: {integrity: sha512-YptD6IzQjDardkl0POxnnRBhU1OEePMV0nd6siHaRBbd+lyh6NAhFEobiznKU7kTsSsDeSD62Pe7kAM1b7dAZQ==} + peerDependencies: + '@typescript-eslint/eslint-plugin': ^8.0.0-0 || ^7.0.0 || ^6.0.0 || ^5.0.0 + eslint: ^9.0.0 || ^8.0.0 + peerDependenciesMeta: + '@typescript-eslint/eslint-plugin': + optional: true + dependencies: + '@typescript-eslint/eslint-plugin': 7.2.0(@typescript-eslint/parser@7.2.0)(eslint@8.57.0)(typescript@4.9.5) + eslint: 8.57.0 + dev: true + /eslint-scope@5.1.1: resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} engines: {node: '>=8.0.0'} @@ -8885,6 +9401,17 @@ packages: pend: 1.2.0 dev: true + /fdir@6.4.4(picomatch@4.0.2): + resolution: {integrity: sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + dependencies: + picomatch: 4.0.2 + dev: true + /figures@3.2.0: resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==} engines: {node: '>=8'} @@ -8999,6 +9526,13 @@ packages: is-callable: 1.2.7 dev: true + /for-each@0.3.5: + resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} + engines: {node: '>= 0.4'} + dependencies: + is-callable: 1.2.7 + dev: true + /foreground-child@2.0.0: resolution: {integrity: sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==} engines: {node: '>=8.0.0'} @@ -9107,10 +9641,22 @@ packages: resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.23.9 + functions-have-names: 1.2.3 + dev: true + + /function.prototype.name@1.1.8: + resolution: {integrity: sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 define-properties: 1.2.1 - es-abstract: 1.23.3 functions-have-names: 1.2.3 + hasown: 2.0.2 + is-callable: 1.2.7 dev: true /functions-have-names@1.2.3: @@ -9134,6 +9680,21 @@ packages: has-symbols: 1.0.3 hasown: 2.0.2 + /get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + math-intrinsics: 1.1.0 + /get-node-dimensions@1.2.1: resolution: {integrity: sha512-2MSPMu7S1iOTL+BOa6K1S62hB2zUAYNF/lV0gSVlOaacd087lc6nR1H1r0e3B1CerTo+RceOmi1iJW+vp21xcQ==} dev: false @@ -9163,6 +9724,13 @@ packages: through: 2.3.8 dev: false + /get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + /get-stdin@5.0.1: resolution: {integrity: sha512-jZV7n6jGE3Gt7fgSTJoz91Ak5MuTLwMwkoYdjxuJ/AmjIsE1UC03y/IWkZCQGEvVNS9qoRNwy5BCqxImv0FVeA==} engines: {node: '>=0.12.0'} @@ -9190,9 +9758,24 @@ packages: resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 es-errors: 1.3.0 - get-intrinsic: 1.2.4 + get-intrinsic: 1.3.0 + dev: true + + /get-symbol-description@1.1.0: + resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} + engines: {node: '>= 0.4'} + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + dev: true + + /get-tsconfig@4.10.0: + resolution: {integrity: sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A==} + dependencies: + resolve-pkg-maps: 1.0.0 dev: true /getos@3.2.1: @@ -9272,6 +9855,14 @@ packages: define-properties: 1.2.1 dev: true + /globalthis@1.0.4: + resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} + engines: {node: '>= 0.4'} + dependencies: + define-properties: 1.2.1 + gopd: 1.2.0 + dev: true + /globby@11.1.0: resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} engines: {node: '>=10'} @@ -9303,7 +9894,11 @@ packages: /gopd@1.0.1: resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} dependencies: - get-intrinsic: 1.2.4 + get-intrinsic: 1.3.0 + + /gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} /graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} @@ -9370,10 +9965,21 @@ packages: resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==} engines: {node: '>= 0.4'} + /has-proto@1.2.0: + resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==} + engines: {node: '>= 0.4'} + dependencies: + dunder-proto: 1.0.1 + dev: true + /has-symbols@1.0.3: resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} engines: {node: '>= 0.4'} + /has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + /has-tostringtag@1.0.2: resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} engines: {node: '>= 0.4'} @@ -9604,7 +10210,7 @@ packages: dependencies: '@tootallnate/once': 2.0.0 agent-base: 6.0.2 - debug: 4.3.7 + debug: 4.4.1 transitivePeerDependencies: - supports-color dev: true @@ -9632,7 +10238,7 @@ packages: engines: {node: '>= 6'} dependencies: agent-base: 6.0.2 - debug: 4.3.7 + debug: 4.4.1 transitivePeerDependencies: - supports-color dev: true @@ -9737,6 +10343,15 @@ packages: side-channel: 1.0.6 dev: true + /internal-slot@1.1.0: + resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} + engines: {node: '>= 0.4'} + dependencies: + es-errors: 1.3.0 + hasown: 2.0.2 + side-channel: 1.1.0 + dev: true + /internmap@1.0.1: resolution: {integrity: sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==} dev: false @@ -9760,7 +10375,7 @@ packages: resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 has-tostringtag: 1.0.2 dev: false @@ -9768,19 +10383,46 @@ packages: resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.7 - get-intrinsic: 1.2.4 + call-bind: 1.0.8 + get-intrinsic: 1.3.0 + dev: true + + /is-array-buffer@3.0.5: + resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 dev: true /is-arrayish@0.2.1: resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + /is-async-function@2.1.1: + resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==} + engines: {node: '>= 0.4'} + dependencies: + async-function: 1.0.0 + call-bound: 1.0.4 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + dev: true + /is-bigint@1.0.4: resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} dependencies: has-bigints: 1.0.2 dev: true + /is-bigint@1.1.0: + resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==} + engines: {node: '>= 0.4'} + dependencies: + has-bigints: 1.0.2 + dev: true + /is-binary-path@2.1.0: resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} engines: {node: '>=8'} @@ -9791,7 +10433,15 @@ packages: resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 + has-tostringtag: 1.0.2 + dev: true + + /is-boolean-object@1.2.2: + resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} + engines: {node: '>= 0.4'} + dependencies: + call-bound: 1.0.4 has-tostringtag: 1.0.2 dev: true @@ -9804,6 +10454,12 @@ packages: engines: {node: '>=4'} dev: true + /is-bun-module@2.0.0: + resolution: {integrity: sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==} + dependencies: + semver: 7.7.2 + dev: true + /is-callable@1.2.7: resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} engines: {node: '>= 0.4'} @@ -9820,6 +10476,13 @@ packages: resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==} dependencies: hasown: 2.0.2 + dev: true + + /is-core-module@2.16.1: + resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} + engines: {node: '>= 0.4'} + dependencies: + hasown: 2.0.2 /is-data-view@1.0.1: resolution: {integrity: sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==} @@ -9828,12 +10491,29 @@ packages: is-typed-array: 1.1.13 dev: true + /is-data-view@1.0.2: + resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==} + engines: {node: '>= 0.4'} + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + is-typed-array: 1.1.15 + dev: true + /is-date-object@1.0.5: resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} engines: {node: '>= 0.4'} dependencies: has-tostringtag: 1.0.2 + /is-date-object@1.1.0: + resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} + engines: {node: '>= 0.4'} + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + dev: true + /is-deflate@1.0.0: resolution: {integrity: sha512-YDoFpuZWu1VRXlsnlYMzKyVRITXj7Ej/V9gXQ2/pAe7X1J7M/RNOqaIYi6qUn+B7nGyB9pDXrv02dsB58d2ZAQ==} dev: true @@ -9848,6 +10528,13 @@ packages: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} + /is-finalizationregistry@1.1.1: + resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==} + engines: {node: '>= 0.4'} + dependencies: + call-bound: 1.0.4 + dev: true + /is-fullwidth-code-point@3.0.0: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} @@ -9856,6 +10543,16 @@ packages: resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==} engines: {node: '>=6'} + /is-generator-function@1.1.0: + resolution: {integrity: sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bound: 1.0.4 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + dev: true + /is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} @@ -9879,6 +10576,11 @@ packages: is-path-inside: 3.0.3 dev: true + /is-map@2.0.3: + resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} + engines: {node: '>= 0.4'} + dev: true + /is-negative-zero@2.0.3: resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} engines: {node: '>= 0.4'} @@ -9891,6 +10593,14 @@ packages: has-tostringtag: 1.0.2 dev: true + /is-number-object@1.1.1: + resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} + engines: {node: '>= 0.4'} + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + dev: true + /is-number@7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} @@ -9925,14 +10635,36 @@ packages: resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 + has-tostringtag: 1.0.2 + + /is-regex@1.2.1: + resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} + engines: {node: '>= 0.4'} + dependencies: + call-bound: 1.0.4 + gopd: 1.2.0 has-tostringtag: 1.0.2 + hasown: 2.0.2 + dev: true + + /is-set@2.0.3: + resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} + engines: {node: '>= 0.4'} + dev: true /is-shared-array-buffer@1.0.3: resolution: {integrity: sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 + dev: true + + /is-shared-array-buffer@1.0.4: + resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==} + engines: {node: '>= 0.4'} + dependencies: + call-bound: 1.0.4 dev: true /is-stream@1.1.0: @@ -9951,11 +10683,28 @@ packages: has-tostringtag: 1.0.2 dev: true + /is-string@1.1.1: + resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==} + engines: {node: '>= 0.4'} + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + dev: true + /is-symbol@1.0.4: resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} engines: {node: '>= 0.4'} dependencies: - has-symbols: 1.0.3 + has-symbols: 1.1.0 + dev: true + + /is-symbol@1.1.1: + resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==} + engines: {node: '>= 0.4'} + dependencies: + call-bound: 1.0.4 + has-symbols: 1.1.0 + safe-regex-test: 1.1.0 dev: true /is-typed-array@1.1.13: @@ -9965,6 +10714,13 @@ packages: which-typed-array: 1.1.15 dev: true + /is-typed-array@1.1.15: + resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} + engines: {node: '>= 0.4'} + dependencies: + which-typed-array: 1.1.19 + dev: true + /is-typedarray@1.0.0: resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==} @@ -9973,10 +10729,30 @@ packages: engines: {node: '>=10'} dev: true + /is-weakmap@2.0.2: + resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} + engines: {node: '>= 0.4'} + dev: true + /is-weakref@1.0.2: resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 + dev: true + + /is-weakref@1.1.1: + resolution: {integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==} + engines: {node: '>= 0.4'} + dependencies: + call-bound: 1.0.4 + dev: true + + /is-weakset@2.0.4: + resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 dev: true /is-windows@1.0.2: @@ -10057,7 +10833,7 @@ packages: '@babel/parser': 7.24.1 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.2 - semver: 7.6.0 + semver: 7.7.2 transitivePeerDependencies: - supports-color @@ -10085,7 +10861,7 @@ packages: resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==} engines: {node: '>=10'} dependencies: - debug: 4.3.4(supports-color@8.1.1) + debug: 4.4.1 istanbul-lib-coverage: 3.2.2 source-map: 0.6.1 transitivePeerDependencies: @@ -10486,7 +11262,7 @@ packages: jest-util: 29.7.0 natural-compare: 1.4.0 pretty-format: 29.7.0 - semver: 7.6.0 + semver: 7.7.2 transitivePeerDependencies: - supports-color @@ -10680,6 +11456,13 @@ packages: /json-stringify-safe@5.0.1: resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} + /json5@1.0.2: + resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} + hasBin: true + dependencies: + minimist: 1.2.8 + dev: true + /json5@2.2.3: resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} engines: {node: '>=6'} @@ -10982,6 +11765,7 @@ packages: engines: {node: '>=10'} dependencies: yallist: 4.0.0 + dev: true /lucide-react@0.468.0(react@18.2.0): resolution: {integrity: sha512-6koYRhnM2N0GGZIdXzSeiNwguv1gt/FAjZOiPl76roBi3xKEXa4WmfpxgQwTTL4KipXjefrnf3oV4IsYhi4JFA==} @@ -11041,7 +11825,7 @@ packages: resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} engines: {node: '>=10'} dependencies: - semver: 7.6.0 + semver: 7.7.2 /make-error@1.3.6: resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} @@ -11066,6 +11850,10 @@ packages: resolution: {integrity: sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg==} dev: false + /math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + /mdast-util-find-and-replace@3.0.2: resolution: {integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==} dependencies: @@ -11481,7 +12269,7 @@ packages: resolution: {integrity: sha512-eBPdkcoCNvYcxQOAKAlceo5SNdzZWfF+FcSupREAzdAh9rRmE239CEQAiTwIgblwnoM8zzj35sZ5ZwvSEOF6Kw==} dependencies: '@types/debug': 4.1.12 - debug: 4.3.7 + debug: 4.4.1 decode-named-character-reference: 1.0.2 devlop: 1.1.0 micromark-core-commonmark: 2.0.2 @@ -11634,6 +12422,12 @@ packages: hasBin: true dev: false + /napi-postinstall@0.2.4: + resolution: {integrity: sha512-ZEzHJwBhZ8qQSbknHqYcdtQVr8zUgGyM/q6h6qAyhtyVMNrSgDhrC4disf03dYW0e+czXyLnZINnCTEkWy0eJg==} + engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + hasBin: true + dev: true + /natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} @@ -11809,11 +12603,16 @@ packages: resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==} dev: true + /object-inspect@1.13.4: + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + engines: {node: '>= 0.4'} + dev: true + /object-is@1.1.6: resolution: {integrity: sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 define-properties: 1.2.1 dev: false @@ -11825,12 +12624,24 @@ packages: resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 define-properties: 1.2.1 has-symbols: 1.0.3 object-keys: 1.1.1 dev: true + /object.assign@4.1.7: + resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + has-symbols: 1.1.0 + object-keys: 1.1.1 + dev: true + /object.entries@1.1.8: resolution: {integrity: sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==} engines: {node: '>= 0.4'} @@ -11850,6 +12661,15 @@ packages: es-object-atoms: 1.0.0 dev: true + /object.groupby@1.0.3: + resolution: {integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + dev: true + /object.hasown@1.1.4: resolution: {integrity: sha512-FZ9LZt9/RHzGySlBARE3VF+gE26TxR38SdmqOqliuTnl9wrKulaQs+4dee1V+Io8VfxqzAfHu6YuRgUy8OHoTg==} engines: {node: '>= 0.4'} @@ -11913,6 +12733,15 @@ packages: resolution: {integrity: sha512-o6E5qJV5zkAbIDNhGSIlyOhScKXgQrSRMilfph0clDfM0nEnBOlKlH4sWDmG95BW/CvwNz0vmm7dJVtU2KlMiA==} dev: true + /own-keys@1.0.1: + resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} + engines: {node: '>= 0.4'} + dependencies: + get-intrinsic: 1.3.0 + object-keys: 1.1.1 + safe-push-apply: 1.0.0 + dev: true + /p-finally@1.0.0: resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==} engines: {node: '>=4'} @@ -12135,6 +12964,11 @@ packages: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} + /picomatch@4.0.2: + resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} + engines: {node: '>=12'} + dev: true + /pify@2.3.0: resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} engines: {node: '>=0.10.0'} @@ -12592,7 +13426,7 @@ packages: resolution: {integrity: sha512-OQiU+C+Ds5qiH91qh/mg0w+8nwQuLjM4F4M/PbmhDOoYehPh+Fb0bDjtR1sOvy7YKxvj28Y/M0PhP5uVX0kB+g==} engines: {node: '>=0.6'} dependencies: - side-channel: 1.0.6 + side-channel: 1.1.0 dev: true /qs@6.5.3: @@ -13311,6 +14145,20 @@ packages: resolution: {integrity: sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==} dev: false + /reflect.getprototypeof@1.0.10: + resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.23.9 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + which-builtin-type: 1.2.1 + dev: true + /regenerate-unicode-properties@10.1.1: resolution: {integrity: sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q==} engines: {node: '>=4'} @@ -13339,11 +14187,23 @@ packages: resolution: {integrity: sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 define-properties: 1.2.1 es-errors: 1.3.0 set-function-name: 2.0.2 + /regexp.prototype.flags@1.5.4: + resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-errors: 1.3.0 + get-proto: 1.0.1 + gopd: 1.2.0 + set-function-name: 2.0.2 + dev: true + /regexpu-core@5.3.2: resolution: {integrity: sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==} engines: {node: '>=4'} @@ -13462,6 +14322,10 @@ packages: resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} engines: {node: '>=8'} + /resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + dev: true + /resolve.exports@2.0.2: resolution: {integrity: sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==} engines: {node: '>=10'} @@ -13470,7 +14334,7 @@ packages: resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} hasBin: true dependencies: - is-core-module: 2.13.1 + is-core-module: 2.16.1 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 @@ -13589,12 +14453,23 @@ packages: resolution: {integrity: sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==} engines: {node: '>=0.4'} dependencies: - call-bind: 1.0.7 - get-intrinsic: 1.2.4 + call-bind: 1.0.8 + get-intrinsic: 1.3.0 has-symbols: 1.0.3 isarray: 2.0.5 dev: true + /safe-array-concat@1.1.3: + resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==} + engines: {node: '>=0.4'} + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + has-symbols: 1.1.0 + isarray: 2.0.5 + dev: true + /safe-buffer@5.1.2: resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} @@ -13602,15 +14477,32 @@ packages: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} dev: true + /safe-push-apply@1.0.0: + resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==} + engines: {node: '>= 0.4'} + dependencies: + es-errors: 1.3.0 + isarray: 2.0.5 + dev: true + /safe-regex-test@1.0.3: resolution: {integrity: sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 es-errors: 1.3.0 is-regex: 1.1.4 dev: true + /safe-regex-test@1.1.0: + resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} + engines: {node: '>= 0.4'} + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-regex: 1.2.1 + dev: true + /safe-stable-stringify@2.4.3: resolution: {integrity: sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==} engines: {node: '>=10'} @@ -13696,6 +14588,12 @@ packages: hasBin: true dependencies: lru-cache: 6.0.0 + dev: true + + /semver@7.7.2: + resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} + engines: {node: '>=10'} + hasBin: true /sentence-case@3.0.4: resolution: {integrity: sha512-8LS0JInaQMCRoQ7YUytAo/xUu5W2XnQxV2HI/6uM6U7CITS1RqPElr30V6uIqyMKM9lJGRVFy5/4CuzcixNYSg==} @@ -13735,6 +14633,15 @@ packages: functions-have-names: 1.2.3 has-property-descriptors: 1.0.2 + /set-proto@1.0.0: + resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} + engines: {node: '>= 0.4'} + dependencies: + dunder-proto: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + dev: true + /shallow-clone@3.0.1: resolution: {integrity: sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==} engines: {node: '>=8'} @@ -13768,14 +14675,54 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} + /side-channel-list@1.0.0: + resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} + engines: {node: '>= 0.4'} + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + dev: true + + /side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + dev: true + + /side-channel-weakmap@1.0.2: + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + side-channel-map: 1.0.1 + dev: true + /side-channel@1.0.6: resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 es-errors: 1.3.0 - get-intrinsic: 1.2.4 - object-inspect: 1.13.1 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + dev: true + + /side-channel@1.1.0: + resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} + engines: {node: '>= 0.4'} + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list: 1.0.0 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 dev: true /signal-exit@3.0.7: @@ -13969,6 +14916,10 @@ packages: resolution: {integrity: sha512-Aj6Jl2z6oDmgYFFbQqK7fght19bXdOxY7Tj03nF+03M9gCBAjeIiO8/PlEGMfKDwYpw4q6iBqVq2YuREorGg/g==} dev: true + /stable-hash@0.0.5: + resolution: {integrity: sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==} + dev: true + /stack-utils@2.0.6: resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} engines: {node: '>=10'} @@ -14029,14 +14980,27 @@ packages: side-channel: 1.0.6 dev: true + /string.prototype.trim@1.2.10: + resolution: {integrity: sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-data-property: 1.1.4 + define-properties: 1.2.1 + es-abstract: 1.23.9 + es-object-atoms: 1.1.1 + has-property-descriptors: 1.0.2 + dev: true + /string.prototype.trim@1.2.9: resolution: {integrity: sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.23.3 - es-object-atoms: 1.0.0 + es-abstract: 1.23.9 + es-object-atoms: 1.1.1 dev: true /string.prototype.trimend@1.0.8: @@ -14047,13 +15011,23 @@ packages: es-object-atoms: 1.0.0 dev: true + /string.prototype.trimend@1.0.9: + resolution: {integrity: sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + dev: true + /string.prototype.trimstart@1.0.8: resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 define-properties: 1.2.1 - es-object-atoms: 1.0.0 + es-object-atoms: 1.1.1 dev: true /string_decoder@1.1.1: @@ -14418,6 +15392,14 @@ packages: resolution: {integrity: sha512-WiCJLEECkO18gwqIp6+hJg0//p23HXp4S+gGtAKu3mI2F2/sXC4FvHvXvB0zJVVaTPhx1/tOwdbRsa1sOBIKqQ==} dev: false + /tinyglobby@0.2.13: + resolution: {integrity: sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==} + engines: {node: '>=12.0.0'} + dependencies: + fdir: 6.4.4(picomatch@4.0.2) + picomatch: 4.0.2 + dev: true + /tmp@0.2.3: resolution: {integrity: sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==} engines: {node: '>=14.14'} @@ -14619,6 +15601,15 @@ packages: resolution: {integrity: sha512-YKhUKqbteklNppC2NqL7dv1cWF8eEobgHVD5kjF1y9Q4ocqpBiaDlYslQ9eMhtbqIPRrA68RIEXqknEjlxdwxw==} dev: true + /tsconfig-paths@3.15.0: + resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} + dependencies: + '@types/json5': 0.0.29 + json5: 1.0.2 + minimist: 1.2.8 + strip-bom: 3.0.0 + dev: true + /tsconfig@7.0.0: resolution: {integrity: sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw==} dependencies: @@ -14672,20 +15663,40 @@ packages: resolution: {integrity: sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 es-errors: 1.3.0 is-typed-array: 1.1.13 dev: true + /typed-array-buffer@1.0.3: + resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} + engines: {node: '>= 0.4'} + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-typed-array: 1.1.15 + dev: true + /typed-array-byte-length@1.0.1: resolution: {integrity: sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 for-each: 0.3.3 - gopd: 1.0.1 - has-proto: 1.0.3 - is-typed-array: 1.1.13 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + dev: true + + /typed-array-byte-length@1.0.3: + resolution: {integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.8 + for-each: 0.3.3 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 dev: true /typed-array-byte-offset@1.0.2: @@ -14693,18 +15704,31 @@ packages: engines: {node: '>= 0.4'} dependencies: available-typed-arrays: 1.0.7 - call-bind: 1.0.7 + call-bind: 1.0.8 for-each: 0.3.3 - gopd: 1.0.1 - has-proto: 1.0.3 - is-typed-array: 1.1.13 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + dev: true + + /typed-array-byte-offset@1.0.4: + resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==} + engines: {node: '>= 0.4'} + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + for-each: 0.3.3 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + reflect.getprototypeof: 1.0.10 dev: true /typed-array-length@1.0.6: resolution: {integrity: sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 for-each: 0.3.3 gopd: 1.0.1 has-proto: 1.0.3 @@ -14712,6 +15736,18 @@ packages: possible-typed-array-names: 1.0.0 dev: true + /typed-array-length@1.0.7: + resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.8 + for-each: 0.3.3 + gopd: 1.2.0 + is-typed-array: 1.1.15 + possible-typed-array-names: 1.0.0 + reflect.getprototypeof: 1.0.10 + dev: true + /typedarray-to-buffer@3.1.5: resolution: {integrity: sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==} dependencies: @@ -14734,12 +15770,22 @@ packages: /unbox-primitive@1.0.2: resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 has-bigints: 1.0.2 has-symbols: 1.0.3 which-boxed-primitive: 1.0.2 dev: true + /unbox-primitive@1.1.0: + resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} + engines: {node: '>= 0.4'} + dependencies: + call-bound: 1.0.4 + has-bigints: 1.0.2 + has-symbols: 1.1.0 + which-boxed-primitive: 1.1.1 + dev: true + /uncontrollable@7.2.1(react@18.2.0): resolution: {integrity: sha512-svtcfoTADIB0nT9nltgjujTi7BzVmwjZClOmskKu/E8FW9BXzg9os8OLr4f8Dlnk0rYWJIWr4wv9eKUXiQvQwQ==} peerDependencies: @@ -14854,6 +15900,31 @@ packages: engines: {node: '>= 10.0.0'} dev: true + /unrs-resolver@1.7.2: + resolution: {integrity: sha512-BBKpaylOW8KbHsu378Zky/dGh4ckT/4NW/0SHRABdqRLcQJ2dAOjDo9g97p04sWflm0kqPqpUatxReNV/dqI5A==} + requiresBuild: true + dependencies: + napi-postinstall: 0.2.4 + optionalDependencies: + '@unrs/resolver-binding-darwin-arm64': 1.7.2 + '@unrs/resolver-binding-darwin-x64': 1.7.2 + '@unrs/resolver-binding-freebsd-x64': 1.7.2 + '@unrs/resolver-binding-linux-arm-gnueabihf': 1.7.2 + '@unrs/resolver-binding-linux-arm-musleabihf': 1.7.2 + '@unrs/resolver-binding-linux-arm64-gnu': 1.7.2 + '@unrs/resolver-binding-linux-arm64-musl': 1.7.2 + '@unrs/resolver-binding-linux-ppc64-gnu': 1.7.2 + '@unrs/resolver-binding-linux-riscv64-gnu': 1.7.2 + '@unrs/resolver-binding-linux-riscv64-musl': 1.7.2 + '@unrs/resolver-binding-linux-s390x-gnu': 1.7.2 + '@unrs/resolver-binding-linux-x64-gnu': 1.7.2 + '@unrs/resolver-binding-linux-x64-musl': 1.7.2 + '@unrs/resolver-binding-wasm32-wasi': 1.7.2 + '@unrs/resolver-binding-win32-arm64-msvc': 1.7.2 + '@unrs/resolver-binding-win32-ia32-msvc': 1.7.2 + '@unrs/resolver-binding-win32-x64-msvc': 1.7.2 + dev: true + /unsplash-js@7.0.19: resolution: {integrity: sha512-j6qT2floy5Q2g2d939FJpwey1yw/GpQecFiSouyJtsHQPj3oqmqq3K4rI+GF8vU1zwGCT7ZwIGQd2dtCQLjYJw==} engines: {node: '>=10'} @@ -15326,10 +16397,50 @@ packages: is-bigint: 1.0.4 is-boolean-object: 1.1.2 is-number-object: 1.0.7 - is-string: 1.0.7 + is-string: 1.1.1 is-symbol: 1.0.4 dev: true + /which-boxed-primitive@1.1.1: + resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} + engines: {node: '>= 0.4'} + dependencies: + is-bigint: 1.1.0 + is-boolean-object: 1.2.2 + is-number-object: 1.1.1 + is-string: 1.1.1 + is-symbol: 1.1.1 + dev: true + + /which-builtin-type@1.2.1: + resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==} + engines: {node: '>= 0.4'} + dependencies: + call-bound: 1.0.4 + function.prototype.name: 1.1.8 + has-tostringtag: 1.0.2 + is-async-function: 2.1.1 + is-date-object: 1.1.0 + is-finalizationregistry: 1.1.1 + is-generator-function: 1.1.0 + is-regex: 1.2.1 + is-weakref: 1.1.1 + isarray: 2.0.5 + which-boxed-primitive: 1.1.1 + which-collection: 1.0.2 + which-typed-array: 1.1.19 + dev: true + + /which-collection@1.0.2: + resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} + engines: {node: '>= 0.4'} + dependencies: + is-map: 2.0.3 + is-set: 2.0.3 + is-weakmap: 2.0.2 + is-weakset: 2.0.4 + dev: true + /which-module@2.0.1: resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==} dev: true @@ -15339,12 +16450,25 @@ packages: engines: {node: '>= 0.4'} dependencies: available-typed-arrays: 1.0.7 - call-bind: 1.0.7 + call-bind: 1.0.8 for-each: 0.3.3 gopd: 1.0.1 has-tostringtag: 1.0.2 dev: true + /which-typed-array@1.1.19: + resolution: {integrity: sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==} + engines: {node: '>= 0.4'} + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.4 + for-each: 0.3.5 + get-proto: 1.0.1 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + dev: true + /which@1.3.1: resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} hasBin: true @@ -15468,6 +16592,7 @@ packages: /yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + dev: true /yaml@1.10.2: resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} diff --git a/src/components/editor/components/leaf/href/HrefPopover.tsx b/src/components/editor/components/leaf/href/HrefPopover.tsx index b2d0dcf9e..de5080741 100644 --- a/src/components/editor/components/leaf/href/HrefPopover.tsx +++ b/src/components/editor/components/leaf/href/HrefPopover.tsx @@ -1,16 +1,17 @@ -import React, { useCallback, useEffect, useMemo, useRef } from 'react'; import { Button, Divider, OutlinedInput, PopoverPosition } from '@mui/material'; +import { PopoverOrigin } from '@mui/material/Popover/Popover'; +import { debounce } from 'lodash-es'; +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { ReactEditor, useSlateStatic } from 'slate-react'; + +import { YjsEditor } from '@/application/slate-yjs'; import { CustomEditor } from '@/application/slate-yjs/command'; import { EditorMarkFormat } from '@/application/slate-yjs/types'; import { ReactComponent as DeleteIcon } from '@/assets/icons/delete.svg'; import { Popover } from '@/components/_shared/popover'; -import { ReactEditor, useSlateStatic } from 'slate-react'; -import { useTranslation } from 'react-i18next'; import { getRangeRect } from '@/components/editor/components/toolbar/selection-toolbar/utils'; -import { YjsEditor } from '@/application/slate-yjs'; import { createHotkey, HOT_KEY_NAME } from '@/utils/hotkeys'; -import { debounce } from 'lodash-es'; -import { PopoverOrigin } from '@mui/material/Popover/Popover'; import { processUrl } from '@/utils/url'; const defaultOrigin: PopoverOrigin = { @@ -31,9 +32,9 @@ function HrefPopover({ }: HrefPopoverProps) { const editor = useSlateStatic() as YjsEditor; const { t } = useTranslation(); - const [isActivated, setIsActivated] = React.useState(CustomEditor.isMarkActive(editor, EditorMarkFormat.Href)); - const [popoverType, setPopoverType] = React.useState<'add' | 'update' | undefined>(undefined); - const [anchorPosition, setAnchorPosition] = React.useState(undefined); + const [isActivated, setIsActivated] = useState(CustomEditor.isMarkActive(editor, EditorMarkFormat.Href)); + const [popoverType, setPopoverType] = useState<'add' | 'update' | undefined>(undefined); + const [anchorPosition, setAnchorPosition] = useState(undefined); useEffect(() => { if (isActivated) { @@ -125,7 +126,7 @@ function HrefPopover({ const urlRef = useRef(null); const textRef = useRef(null); const buttonRef = useRef(null); - const [urlValid, setUrlValid] = React.useState(true); + const [urlValid, setUrlValid] = useState(true); const addLink = useMemo(() => { if (!open || popoverType !== 'add') return null; return ( diff --git a/src/components/editor/components/toolbar/selection-toolbar/SelectionToolbar.tsx b/src/components/editor/components/toolbar/selection-toolbar/SelectionToolbar.tsx index 97901de77..abdb66099 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/SelectionToolbar.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/SelectionToolbar.tsx @@ -1,6 +1,8 @@ +import { useCallback, useEffect, useRef } from 'react'; + import ToolbarActions from '@/components/editor/components/toolbar/selection-toolbar/ToolbarActions'; + import { SelectionToolbarContext, useToolbarPosition, useVisible } from './SelectionToolbar.hooks'; -import React, { useCallback, useEffect, useRef } from 'react'; export function SelectionToolbar() { const { visible, forceShow, getDecorateState } = useVisible(); diff --git a/src/components/editor/components/toolbar/selection-toolbar/ToolbarActions.tsx b/src/components/editor/components/toolbar/selection-toolbar/ToolbarActions.tsx index 5da0bd31b..d66994ab7 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/ToolbarActions.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/ToolbarActions.tsx @@ -1,39 +1,30 @@ +import { Divider } from '@mui/material'; +import { useMemo } from 'react'; +import { Editor, Element, Path } from 'slate'; +import { useSlate } from 'slate-react'; + import { YjsEditor } from '@/application/slate-yjs'; import { getBlockEntry } from '@/application/slate-yjs/utils/editor'; import { BlockType } from '@/application/types'; import AIAssistant from '@/components/editor/components/toolbar/selection-toolbar/actions/AIAssistant'; import Align from '@/components/editor/components/toolbar/selection-toolbar/actions/Align'; import Bold from '@/components/editor/components/toolbar/selection-toolbar/actions/Bold'; -import BulletedList from '@/components/editor/components/toolbar/selection-toolbar/actions/BulletedList'; -import Href from '@/components/editor/components/toolbar/selection-toolbar/actions/Href'; import Color from '@/components/editor/components/toolbar/selection-toolbar/actions/Color'; import ColorHighlight from '@/components/editor/components/toolbar/selection-toolbar/actions/ColorHighlight'; -import Formula from '@/components/editor/components/toolbar/selection-toolbar/actions/Formula'; import Heading from '@/components/editor/components/toolbar/selection-toolbar/actions/Heading'; +import Href from '@/components/editor/components/toolbar/selection-toolbar/actions/Href'; import InlineCode from '@/components/editor/components/toolbar/selection-toolbar/actions/InlineCode'; import Italic from '@/components/editor/components/toolbar/selection-toolbar/actions/Italic'; -import NumberedList from '@/components/editor/components/toolbar/selection-toolbar/actions/NumberedList'; -import Quote from '@/components/editor/components/toolbar/selection-toolbar/actions/Quote'; -import StrikeThrough from '@/components/editor/components/toolbar/selection-toolbar/actions/StrikeThrough'; -import Underline from '@/components/editor/components/toolbar/selection-toolbar/actions/Underline'; import TurnInto from '@/components/editor/components/toolbar/selection-toolbar/actions/TurnInto'; +import Underline from '@/components/editor/components/toolbar/selection-toolbar/actions/Underline'; import { useSelectionToolbarContext, } from '@/components/editor/components/toolbar/selection-toolbar/SelectionToolbar.hooks'; -import { Divider } from '@mui/material'; -import { Editor, Element, Path } from 'slate'; -import Paragraph from './actions/Paragraph'; -import React, { useMemo } from 'react'; -import { useSlate } from 'slate-react'; -import { ReactComponent as MoreIcon } from '@/assets/icons/more.svg'; -import Popover from '@/components/_shared/popover/Popover'; -import { useTranslation } from 'react-i18next'; -import ActionButton from './actions/ActionButton'; -import { ReactComponent as StrikeThroughSvg } from '@/assets/icons/strikethrough.svg'; -import { ReactComponent as FormulaSvg } from '@/assets/icons/formula.svg'; -import { Button } from '@mui/material'; + import MoreOptions from './actions/MoreOptions'; + + function ToolbarActions() { const editor = useSlate() as YjsEditor; const selection = editor.selection; diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/AIAssistant.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/AIAssistant.tsx index da738551d..eed192259 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/AIAssistant.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/AIAssistant.tsx @@ -1,23 +1,25 @@ +import { AIAssistantType,AIWriterMenu, useAIWriter } from '@appflowyinc/ai-chat'; +import { useCallback, useEffect, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { ReactEditor, useSlate } from 'slate-react'; + import { YjsEditor } from '@/application/slate-yjs'; import { CustomEditor } from '@/application/slate-yjs/command'; +import { ReactComponent as AskAIIcon } from '@/assets/icons/ai.svg'; +import { ReactComponent as ImproveWritingIcon } from '@/assets/icons/ai_improve_writing.svg'; +import { ReactComponent as TriangleDownIcon } from '@/assets/icons/triangle_down.svg'; import ActionButton from '@/components/editor/components/toolbar/selection-toolbar/actions/ActionButton'; import { useSelectionToolbarContext, } from '@/components/editor/components/toolbar/selection-toolbar/SelectionToolbar.hooks'; import { useEditorContext } from '@/components/editor/EditorContext'; -import { useAIWriter, AIWriterMenu, AIAssistantType } from '@appflowyinc/ai-chat'; -import React, { useCallback, useEffect } from 'react'; -import { ReactComponent as AskAIIcon } from '@/assets/icons/ai.svg'; -import { ReactComponent as ImproveWritingIcon } from '@/assets/icons/ai_improve_writing.svg'; -import { useTranslation } from 'react-i18next'; -import { ReactEditor, useSlate } from 'slate-react'; -import { ReactComponent as TriangleDownIcon } from '@/assets/icons/triangle_down.svg'; + function AIAssistant() { const { t } = useTranslation(); const editor = useSlate() as YjsEditor; - const [open, setOpen] = React.useState(false); + const [open, setOpen] = useState(false); const { addDecorate, } = useEditorContext(); @@ -27,7 +29,7 @@ function AIAssistant() { const { improveWriting, } = useAIWriter(); - const [content, setContent] = React.useState(''); + const [content, setContent] = useState(''); const addReplaceStyle = useCallback(() => { const range = editor.selection; diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/ActionButton.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/ActionButton.tsx index cd1c652d5..5236368f5 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/ActionButton.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/ActionButton.tsx @@ -1,14 +1,15 @@ -import React, { forwardRef } from 'react'; -import IconButton, { IconButtonProps } from '@mui/material/IconButton'; import { Tooltip } from '@mui/material'; +import IconButton, { IconButtonProps } from '@mui/material/IconButton'; +import { forwardRef, ReactNode, MouseEvent } from 'react'; + import { cn } from '@/lib/utils'; const ActionButton = forwardRef< HTMLButtonElement, { - tooltip?: string | React.ReactNode; - onClick?: (e: React.MouseEvent) => void; - children: React.ReactNode; + tooltip?: string | ReactNode; + onClick?: (e: MouseEvent) => void; + children: ReactNode; active?: boolean; } & IconButtonProps >(({ tooltip, onClick, disabled, children, active, className, ...props }, ref) => { diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/Align.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/Align.tsx index 58f0a1f92..18a69765f 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/Align.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/Align.tsx @@ -1,3 +1,10 @@ +import Button from '@mui/material/Button'; +import { PopoverProps } from '@mui/material/Popover'; +import { useCallback, useEffect, useRef, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { Element } from 'slate'; +import { useSlateStatic } from 'slate-react'; + import { YjsEditor } from '@/application/slate-yjs'; import { CustomEditor } from '@/application/slate-yjs/command'; import { findSlateEntryByBlockId, getBlockEntry } from '@/application/slate-yjs/utils/editor'; @@ -7,15 +14,11 @@ import { ReactComponent as AlignLeftSvg } from '@/assets/icons/align_left.svg'; import { ReactComponent as AlignRightSvg } from '@/assets/icons/align_right.svg'; import { Popover } from '@/components/_shared/popover'; import { useSelectionToolbarContext } from '@/components/editor/components/toolbar/selection-toolbar/SelectionToolbar.hooks'; -import { PopoverProps } from '@mui/material/Popover'; -import React, { useCallback, useEffect, useRef, useState } from 'react'; -import { useTranslation } from 'react-i18next'; -import { useSlateStatic } from 'slate-react'; -import { Element } from 'slate'; -import ActionButton from './ActionButton'; -import Button from '@mui/material/Button'; + import { useKeyboardNavigation } from '../hooks/useKeyboardNavigation'; +import ActionButton from './ActionButton'; + const popoverProps: Partial = { anchorOrigin: { vertical: 'bottom', @@ -110,6 +113,7 @@ export function Align({ blockId, enabled = true }: { blockId?: string; enabled?: isOpen: open, onSelect: (index) => { const align = [AlignType.Left, AlignType.Center, AlignType.Right][index]; + toggleAlign(align)(); }, onClose: handleClose diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/Bold.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/Bold.tsx index 79405f126..bb0f0a773 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/Bold.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/Bold.tsx @@ -1,12 +1,14 @@ +import { useCallback, useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useSlateStatic } from 'slate-react'; + import { YjsEditor } from '@/application/slate-yjs'; import { CustomEditor } from '@/application/slate-yjs/command'; import { EditorMarkFormat } from '@/application/slate-yjs/types'; +import { ReactComponent as BoldSvg } from '@/assets/icons/bold.svg'; import ActionButton from '@/components/editor/components/toolbar/selection-toolbar/actions/ActionButton'; import { createHotKeyLabel, HOT_KEY_NAME } from '@/utils/hotkeys'; -import React, { useCallback, useMemo } from 'react'; -import { useTranslation } from 'react-i18next'; -import { useSlateStatic } from 'slate-react'; -import { ReactComponent as BoldSvg } from '@/assets/icons/bold.svg'; + export function Bold() { const { t } = useTranslation(); diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/BulletedList.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/BulletedList.tsx index 7ff5b7280..90565e020 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/BulletedList.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/BulletedList.tsx @@ -1,13 +1,17 @@ +import { useCallback } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useSlateStatic } from 'slate-react'; + import { YjsEditor } from '@/application/slate-yjs'; import { CustomEditor } from '@/application/slate-yjs/command'; import { getBlockEntry } from '@/application/slate-yjs/utils/editor'; import { BlockType } from '@/application/types'; -import React, { useCallback } from 'react'; -import ActionButton from './ActionButton'; -import { useTranslation } from 'react-i18next'; -import { useSlateStatic } from 'slate-react'; import { ReactComponent as BulletedListSvg } from '@/assets/icons/bulleted_list.svg'; +import ActionButton from './ActionButton'; + + + export function BulletedList() { const { t } = useTranslation(); const editor = useSlateStatic() as YjsEditor; diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/Color.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/Color.tsx index 73ee4d19c..f08e516a9 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/Color.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/Color.tsx @@ -1,16 +1,20 @@ +import { Tooltip } from '@mui/material'; +import { useCallback, useEffect, useMemo, useRef, useState, MouseEvent } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useSlateStatic } from 'slate-react'; + import { YjsEditor } from '@/application/slate-yjs'; import { CustomEditor } from '@/application/slate-yjs/command'; import { EditorMarkFormat } from '@/application/slate-yjs/types'; +import { ReactComponent as TextSvg } from '@/assets/icons/format_text.svg'; +import { ReactComponent as ColorSvg } from '@/assets/icons/text_color.svg'; import { Popover } from '@/components/_shared/popover'; import { useSelectionToolbarContext } from '@/components/editor/components/toolbar/selection-toolbar/SelectionToolbar.hooks'; -import { ColorEnum, renderColor } from '@/utils/color'; -import { Tooltip } from '@mui/material'; -import React, { useCallback, useEffect, useMemo, useRef } from 'react'; +import { renderColor } from '@/utils/color'; + import ActionButton from './ActionButton'; -import { useTranslation } from 'react-i18next'; -import { useSlateStatic } from 'slate-react'; -import { ReactComponent as ColorSvg } from '@/assets/icons/text_color.svg'; -import { ReactComponent as TextSvg } from '@/assets/icons/format_text.svg'; + + function Color() { const { t } = useTranslation(); @@ -20,10 +24,9 @@ function Color() { CustomEditor.isMarkActive(editor, EditorMarkFormat.BgColor) || CustomEditor.isMarkActive(editor, EditorMarkFormat.FontColor); const marks = CustomEditor.getAllMarks(editor); - const activeBgColor = marks.find((mark) => mark[EditorMarkFormat.BgColor])?.[EditorMarkFormat.BgColor]; const activeFontColor = marks.find((mark) => mark[EditorMarkFormat.FontColor])?.[EditorMarkFormat.FontColor]; - const [anchorEl, setAnchorEl] = React.useState(null); + const [anchorEl, setAnchorEl] = useState(null); const open = Boolean(anchorEl); const wrapperRef = useRef(null); @@ -37,8 +40,10 @@ function Color() { useEffect(() => { if (wrapperRef.current) { const svg = wrapperRef.current.querySelector('svg'); + if (svg) { const bar = svg.querySelector('[class*="color-bar"]'); + if (bar) { bar.setAttribute('stroke', activeFontColor || 'currentColor'); } @@ -46,7 +51,7 @@ function Color() { } }, [activeFontColor]); - const onClick = useCallback((e: React.MouseEvent) => { + const onClick = useCallback((e: MouseEvent) => { e.stopPropagation(); e.preventDefault(); setAnchorEl(e.currentTarget); diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/ColorHighlight.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/ColorHighlight.tsx index 439594e30..eed27fbdc 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/ColorHighlight.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/ColorHighlight.tsx @@ -1,15 +1,19 @@ +import { Tooltip } from '@mui/material'; +import { useCallback, useEffect, useMemo, useRef, useState, MouseEvent } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useSlateStatic } from 'slate-react'; + import { YjsEditor } from '@/application/slate-yjs'; import { CustomEditor } from '@/application/slate-yjs/command'; import { EditorMarkFormat } from '@/application/slate-yjs/types'; +import { ReactComponent as HighlightSvg } from '@/assets/icons/text_highlight.svg'; import { Popover } from '@/components/_shared/popover'; import { useSelectionToolbarContext } from '@/components/editor/components/toolbar/selection-toolbar/SelectionToolbar.hooks'; import { ColorEnum, renderColor } from '@/utils/color'; -import { Tooltip } from '@mui/material'; -import React, { useCallback, useMemo, useRef, useEffect } from 'react'; + import ActionButton from './ActionButton'; -import { useTranslation } from 'react-i18next'; -import { useSlateStatic } from 'slate-react'; -import { ReactComponent as HighlightSvg } from '@/assets/icons/text_highlight.svg'; + + function ColorHighlight() { const { t } = useTranslation(); @@ -19,7 +23,7 @@ function ColorHighlight() { const marks = CustomEditor.getAllMarks(editor); const activeBgColor = marks.find((mark) => mark[EditorMarkFormat.BgColor])?.[EditorMarkFormat.BgColor]; - const [anchorEl, setAnchorEl] = React.useState(null); + const [anchorEl, setAnchorEl] = useState(null); const open = Boolean(anchorEl); const wrapperRef = useRef(null); @@ -27,8 +31,10 @@ function ColorHighlight() { useEffect(() => { if (wrapperRef.current) { const svg = wrapperRef.current.querySelector('svg'); + if (svg) { const bar = svg.querySelector('[class*="color-bar"]'); + if (bar) { bar.setAttribute('stroke', activeBgColor ? renderColor(activeBgColor) : 'currentColor'); } @@ -42,7 +48,7 @@ function ColorHighlight() { } }, [toolbarVisible]); - const onClick = useCallback((e: React.MouseEvent) => { + const onClick = useCallback((e: MouseEvent) => { e.stopPropagation(); e.preventDefault(); setAnchorEl(e.currentTarget); diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/Formula.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/Formula.tsx index bb4ed5fcc..fced87607 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/Formula.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/Formula.tsx @@ -1,19 +1,21 @@ +import { useCallback, useEffect, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { Editor,Text, Transforms } from 'slate'; +import { useSlate } from 'slate-react'; + import { YjsEditor } from '@/application/slate-yjs'; import { CustomEditor } from '@/application/slate-yjs/command'; import { EditorMarkFormat } from '@/application/slate-yjs/types'; +import { ReactComponent as MathSvg } from '@/assets/icons/formula.svg'; import ActionButton from '@/components/editor/components/toolbar/selection-toolbar/actions/ActionButton'; import { useSelectionToolbarContext } from '@/components/editor/components/toolbar/selection-toolbar/SelectionToolbar.hooks'; -import React, { useCallback, useEffect } from 'react'; -import { useTranslation } from 'react-i18next'; -import { Transforms, Text, Editor } from 'slate'; -import { useSlate } from 'slate-react'; -import { ReactComponent as MathSvg } from '@/assets/icons/formula.svg'; + function Formula() { const { t } = useTranslation(); const editor = useSlate() as YjsEditor; const { visible } = useSelectionToolbarContext(); - const [state, setState] = React.useState({ + const [state, setState] = useState({ isActivated: false, hasFormulaActivated: false, hasMentionActivated: false, diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/Heading.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/Heading.tsx index beb2205a6..cc18f6666 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/Heading.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/Heading.tsx @@ -1,23 +1,26 @@ +import Button from '@mui/material/Button'; +import { PopoverProps } from '@mui/material/Popover'; +import { useCallback, useEffect, useRef, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useSlateStatic } from 'slate-react'; + import { YjsEditor } from '@/application/slate-yjs'; import { CustomEditor } from '@/application/slate-yjs/command'; import { getBlockEntry } from '@/application/slate-yjs/utils/editor'; import { BlockType, HeadingBlockData } from '@/application/types'; -import { Popover } from '@/components/_shared/popover'; -import { useSelectionToolbarContext } from '@/components/editor/components/toolbar/selection-toolbar/SelectionToolbar.hooks'; -import { PopoverProps } from '@mui/material/Popover'; -import React, { useCallback, useEffect, useRef, useState } from 'react'; -import ActionButton from './ActionButton'; -import { useTranslation } from 'react-i18next'; -import { useSlateStatic } from 'slate-react'; import { ReactComponent as Heading1 } from '@/assets/icons/h1.svg'; import { ReactComponent as Heading2 } from '@/assets/icons/h2.svg'; import { ReactComponent as Heading3 } from '@/assets/icons/h3.svg'; -import { ReactComponent as DownArrow } from '@/assets/icons/triangle_down.svg'; -import Button from '@mui/material/Button'; -import { useKeyboardNavigation } from '../hooks/useKeyboardNavigation'; import { ReactComponent as ParagraphSvg } from '@/assets/icons/text.svg'; -import { ReactComponent as TickIcon } from '@/assets/icons/tick.svg'; import { ReactComponent as TextFormatSvg } from '@/assets/icons/text_format.svg'; +import { ReactComponent as TickIcon } from '@/assets/icons/tick.svg'; +import { ReactComponent as DownArrow } from '@/assets/icons/triangle_down.svg'; +import { Popover } from '@/components/_shared/popover'; +import { useSelectionToolbarContext } from '@/components/editor/components/toolbar/selection-toolbar/SelectionToolbar.hooks'; + +import { useKeyboardNavigation } from '../hooks/useKeyboardNavigation'; + +import ActionButton from './ActionButton'; const popoverProps: Partial = { anchorOrigin: { @@ -40,26 +43,26 @@ const headingOptions = [ { icon: , labelKey: 'editor.text', - isActive: (isParagraph: () => boolean, isActivated: (level: number) => boolean) => isParagraph(), - onClick: (toParagraph: () => void, toHeading: (level: number) => () => void, setOpen: (v: boolean) => void) => () => { toParagraph(); setOpen(false); }, + isActive: (isParagraph: () => boolean, _isActivated: (level: number) => boolean) => isParagraph(), + onClick: (toParagraph: () => void, _toHeading: (level: number) => () => void, setOpen: (v: boolean) => void) => () => { toParagraph(); setOpen(false); }, }, { icon: , labelKey: 'document.slashMenu.name.heading1', - isActive: (isParagraph: () => boolean, isActivated: (level: number) => boolean) => isActivated(1), - onClick: (toParagraph: () => void, toHeading: (level: number) => () => void, setOpen: (v: boolean) => void) => () => { toHeading(1)(); setOpen(false); }, + isActive: (_isParagraph: () => boolean, isActivated: (level: number) => boolean) => isActivated(1), + onClick: (_toParagraph: () => void, toHeading: (level: number) => () => void, setOpen: (v: boolean) => void) => () => { toHeading(1)(); setOpen(false); }, }, { icon: , labelKey: 'document.slashMenu.name.heading2', - isActive: (isParagraph: () => boolean, isActivated: (level: number) => boolean) => isActivated(2), - onClick: (toParagraph: () => void, toHeading: (level: number) => () => void, setOpen: (v: boolean) => void) => () => { toHeading(2)(); setOpen(false); }, + isActive: (_isParagraph: () => boolean, isActivated: (level: number) => boolean) => isActivated(2), + onClick: (_toParagraph: () => void, toHeading: (level: number) => () => void, setOpen: (v: boolean) => void) => () => { toHeading(2)(); setOpen(false); }, }, { icon: , labelKey: 'document.slashMenu.name.heading3', - isActive: (isParagraph: () => boolean, isActivated: (level: number) => boolean) => isActivated(3), - onClick: (toParagraph: () => void, toHeading: (level: number) => () => void, setOpen: (v: boolean) => void) => () => { toHeading(3)(); setOpen(false); }, + isActive: (_isParagraph: () => boolean, isActivated: (level: number) => boolean) => isActivated(3), + onClick: (_toParagraph: () => void, toHeading: (level: number) => () => void, setOpen: (v: boolean) => void) => () => { toHeading(3)(); setOpen(false); }, }, ]; @@ -110,6 +113,7 @@ export function Heading() { const isParagraph = useCallback(() => { try { const [node] = getBlockEntry(editor); + return node && node.type === BlockType.Paragraph; } catch (e) { return false; @@ -119,6 +123,7 @@ export function Heading() { const toParagraph = useCallback(() => { try { const [node] = getBlockEntry(editor); + if (!node) return; CustomEditor.turnToBlock(editor, node.blockId as string, BlockType.Paragraph, {}); } catch (e) { @@ -126,26 +131,6 @@ export function Heading() { } }, [editor]); - const getActiveButton = useCallback(() => { - if (isParagraph()) { - return ; - } - - if (isActivated(1)) { - return ; - } - - if (isActivated(2)) { - return ; - } - - if (isActivated(3)) { - return ; - } - - return ; - }, [isActivated, isParagraph]); - const { getButtonProps, selectedIndex } = useKeyboardNavigation({ itemCount: 4, isOpen: open, diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/Href.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/Href.tsx index 9617b0a40..cd925a5a9 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/Href.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/Href.tsx @@ -1,14 +1,18 @@ +import { useCallback, useEffect, useMemo, useState, MouseEvent } from 'react'; +import { useTranslation } from 'react-i18next'; +import { ReactEditor, useSlate } from 'slate-react'; + import { YjsEditor } from '@/application/slate-yjs'; import { CustomEditor } from '@/application/slate-yjs/command'; import { EditorMarkFormat } from '@/application/slate-yjs/types'; +import { ReactComponent as LinkSvg } from '@/assets/icons/link.svg'; +import HrefPopover from '@/components/editor/components/leaf/href/HrefPopover'; import { useSelectionToolbarContext } from '@/components/editor/components/toolbar/selection-toolbar/SelectionToolbar.hooks'; import { createHotkey, getModifier, HOT_KEY_NAME } from '@/utils/hotkeys'; -import React, { useCallback, useEffect, useMemo } from 'react'; + import ActionButton from './ActionButton'; -import { useTranslation } from 'react-i18next'; -import { ReactEditor, useSlate } from 'slate-react'; -import { ReactComponent as LinkSvg } from '@/assets/icons/link.svg'; -import HrefPopover from '@/components/editor/components/leaf/href/HrefPopover'; + + export function Href() { const { t } = useTranslation(); @@ -17,13 +21,13 @@ export function Href() { const editor = useSlate() as YjsEditor; const { visible } = useSelectionToolbarContext(); - const [state, setState] = React.useState({ + const [state, setState] = useState({ isActivated: false, hasFormulaActivated: false, hasMentionActivated: false, }); - const [open, setOpen] = React.useState(false); + const [open, setOpen] = useState(false); const { isActivated, hasFormulaActivated, hasMentionActivated } = state; const getState = useCallback(() => { @@ -44,7 +48,7 @@ export function Href() { }, [visible, getState, editor.selection]); const onClick = useCallback( - (e: React.MouseEvent) => { + (e: MouseEvent) => { e.stopPropagation(); e.preventDefault(); setOpen(true); diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/InlineCode.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/InlineCode.tsx index b5f08e2c8..d8edc9d8e 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/InlineCode.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/InlineCode.tsx @@ -1,11 +1,13 @@ +import { useCallback, useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useSlateStatic } from 'slate-react'; + import { CustomEditor } from '@/application/slate-yjs/command'; import { EditorMarkFormat } from '@/application/slate-yjs/types'; +import { ReactComponent as CodeSvg } from '@/assets/icons/inline_code.svg'; import ActionButton from '@/components/editor/components/toolbar/selection-toolbar/actions/ActionButton'; import { createHotKeyLabel, HOT_KEY_NAME } from '@/utils/hotkeys'; -import React, { useCallback, useMemo } from 'react'; -import { useTranslation } from 'react-i18next'; -import { useSlateStatic } from 'slate-react'; -import { ReactComponent as CodeSvg } from '@/assets/icons/inline_code.svg'; + export function InlineCode() { const { t } = useTranslation(); diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/Italic.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/Italic.tsx index cbad734e1..532401a7b 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/Italic.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/Italic.tsx @@ -1,11 +1,13 @@ +import { useCallback, useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useSlateStatic } from 'slate-react'; + import { CustomEditor } from '@/application/slate-yjs/command'; import { EditorMarkFormat } from '@/application/slate-yjs/types'; +import { ReactComponent as ItalicSvg } from '@/assets/icons/italic.svg'; import ActionButton from '@/components/editor/components/toolbar/selection-toolbar/actions/ActionButton'; import { createHotKeyLabel, HOT_KEY_NAME } from '@/utils/hotkeys'; -import React, { useCallback, useMemo } from 'react'; -import { useTranslation } from 'react-i18next'; -import { useSlateStatic } from 'slate-react'; -import { ReactComponent as ItalicSvg } from '@/assets/icons/italic.svg'; + export function Italic() { const { t } = useTranslation(); diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/MoreOptions.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/MoreOptions.tsx index d30246e1b..561735af9 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/MoreOptions.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/MoreOptions.tsx @@ -1,17 +1,22 @@ -import React from 'react'; +import { Button } from '@mui/material'; +import { PopoverProps } from '@mui/material/Popover'; import { useTranslation } from 'react-i18next'; +import { Editor, Text, Transforms } from 'slate'; import { useSlate } from 'slate-react'; + +import { CustomEditor } from '@/application/slate-yjs/command'; +import { EditorMarkFormat } from '@/application/slate-yjs/types'; +import { ReactComponent as FormulaSvg } from '@/assets/icons/formula.svg'; import { ReactComponent as MoreIcon } from '@/assets/icons/more.svg'; import { ReactComponent as StrikeThroughSvg } from '@/assets/icons/strikethrough.svg'; -import { ReactComponent as FormulaSvg } from '@/assets/icons/formula.svg'; import Popover from '@/components/_shared/popover/Popover'; -import ActionButton from './ActionButton'; -import { Button } from '@mui/material'; + import { useKeyboardNavigation } from '../hooks/useKeyboardNavigation'; -import { CustomEditor } from '@/application/slate-yjs/command'; -import { EditorMarkFormat } from '@/application/slate-yjs/types'; -import { Transforms, Text, Editor } from 'slate'; -import { PopoverProps } from '@mui/material/Popover'; + +import ActionButton from './ActionButton'; +import { useRef, useState } from 'react'; + + const options = [ { @@ -30,13 +35,17 @@ const options = [ labelKey: 'document.plugins.createInlineMathEquation', onClick: (editor: any, setOpen: (v: boolean) => void) => { const selection = editor.selection; + if (!selection) return; const isActivated = CustomEditor.isMarkActive(editor, EditorMarkFormat.Formula); + if (!isActivated) { const text = editor.string(selection); + editor.delete(); editor.insertText('$'); const newSelection = editor.selection; + if (!newSelection) return; Transforms.select(editor, { anchor: { @@ -54,15 +63,18 @@ const options = [ at: selection, match: (n: any) => !Editor.isEditor(n) && Text.isText(n) && (n as any).formula !== undefined, }); + if (!entry) return; const [node, path] = entry; - const formula = (node as any).formula; + const formula = (node).formula; + if (!formula) return; editor.select(path); CustomEditor.removeMark(editor, EditorMarkFormat.Formula); editor.delete(); editor.insertText(formula); } + setOpen(false); }, }, @@ -86,8 +98,8 @@ const popoverProps: Partial = { }; export default function MoreOptions() { - const [open, setOpen] = React.useState(false); - const ref = React.useRef(null); + const [open, setOpen] = useState(false); + const ref = useRef(null); const { t } = useTranslation(); const editor = useSlate(); diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/NumberedList.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/NumberedList.tsx index 6fc11ebb5..ac5648f76 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/NumberedList.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/NumberedList.tsx @@ -1,13 +1,17 @@ +import { useCallback } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useSlateStatic } from 'slate-react'; + import { YjsEditor } from '@/application/slate-yjs'; import { CustomEditor } from '@/application/slate-yjs/command'; import { getBlockEntry } from '@/application/slate-yjs/utils/editor'; import { BlockType } from '@/application/types'; -import React, { useCallback } from 'react'; -import ActionButton from './ActionButton'; -import { useTranslation } from 'react-i18next'; -import { useSlateStatic } from 'slate-react'; import { ReactComponent as NumberedListSvg } from '@/assets/icons/numbered_list.svg'; +import ActionButton from './ActionButton'; + + + export function NumberedList() { const { t } = useTranslation(); const editor = useSlateStatic() as YjsEditor; diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/Paragraph.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/Paragraph.tsx index d1c20a876..2d8e90205 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/Paragraph.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/Paragraph.tsx @@ -1,11 +1,13 @@ +import { useCallback } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useSlateStatic } from 'slate-react'; + import { YjsEditor } from '@/application/slate-yjs'; import { CustomEditor } from '@/application/slate-yjs/command'; import { getBlockEntry } from '@/application/slate-yjs/utils/editor'; import { BlockType } from '@/application/types'; import { ReactComponent as ParagraphSvg } from '@/assets/icons/text.svg'; -import { useCallback } from 'react'; -import { useTranslation } from 'react-i18next'; -import { useSlateStatic } from 'slate-react'; + import ActionButton from './ActionButton'; export function Paragraph() { diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/Quote.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/Quote.tsx index 8b383c9be..ff04d9c7d 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/Quote.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/Quote.tsx @@ -1,13 +1,17 @@ +import { useCallback } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useSlateStatic } from 'slate-react'; + import { YjsEditor } from '@/application/slate-yjs'; import { CustomEditor } from '@/application/slate-yjs/command'; import { getBlockEntry } from '@/application/slate-yjs/utils/editor'; import { BlockType } from '@/application/types'; -import React, { useCallback } from 'react'; -import ActionButton from './ActionButton'; -import { useTranslation } from 'react-i18next'; -import { useSlateStatic } from 'slate-react'; import { ReactComponent as QuoteSvg } from '@/assets/icons/quote.svg'; +import ActionButton from './ActionButton'; + + + export function Quote() { const { t } = useTranslation(); const editor = useSlateStatic() as YjsEditor; diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/StrikeThrough.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/StrikeThrough.tsx index f7d8aa6e4..3eea2ecab 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/StrikeThrough.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/StrikeThrough.tsx @@ -1,11 +1,13 @@ +import { useCallback, useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useSlateStatic } from 'slate-react'; + import { CustomEditor } from '@/application/slate-yjs/command'; import { EditorMarkFormat } from '@/application/slate-yjs/types'; +import { ReactComponent as StrikeThroughSvg } from '@/assets/icons/strikethrough.svg'; import ActionButton from '@/components/editor/components/toolbar/selection-toolbar/actions/ActionButton'; import { createHotKeyLabel, HOT_KEY_NAME } from '@/utils/hotkeys'; -import React, { useCallback, useMemo } from 'react'; -import { useTranslation } from 'react-i18next'; -import { useSlateStatic } from 'slate-react'; -import { ReactComponent as StrikeThroughSvg } from '@/assets/icons/strikethrough.svg'; + export function StrikeThrough() { const { t } = useTranslation(); diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/TurnInto.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/TurnInto.tsx index 323cf89f7..a5cf67a55 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/TurnInto.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/TurnInto.tsx @@ -1,33 +1,35 @@ -import React, { useRef, useState } from 'react'; -import { Button, Menu, MenuItem, PopoverProps, Typography } from '@mui/material'; -import ActionButton from './ActionButton'; -import { ReactComponent as BulletedListSvg } from '@/assets/icons/bulleted_list.svg'; -import { ReactComponent as NumberedListSvg } from '@/assets/icons/numbered_list.svg'; -import { ReactComponent as QuoteSvg } from '@/assets/icons/quote.svg'; -import { ReactComponent as TurnIntoSvg } from '@/assets/icons/turn_into.svg'; +import { Menu, MenuItem, Typography } from '@mui/material'; +import { FC, SVGProps, useRef, useState } from 'react'; +import { useTranslation } from 'react-i18next'; import { useSlateStatic } from 'slate-react'; -import { CustomEditor } from '@/application/slate-yjs/command'; -import { ReactComponent as DownArrow } from '@/assets/icons/triangle_down.svg'; + import { YjsEditor } from '@/application/slate-yjs'; -import { useTranslation } from 'react-i18next'; +import { CustomEditor } from '@/application/slate-yjs/command'; import { getBlockEntry } from '@/application/slate-yjs/utils/editor'; +import type { HeadingBlockData } from '@/application/types'; import { BlockType } from '@/application/types'; -import { useSelectionToolbarContext } from '../SelectionToolbar.hooks'; -import { useKeyboardNavigation } from '../hooks/useKeyboardNavigation'; -import { ReactComponent as ParagraphSvg } from '@/assets/icons/text.svg'; +import { ReactComponent as BulletedListSvg } from '@/assets/icons/bulleted_list.svg'; import { ReactComponent as Heading1 } from '@/assets/icons/h1.svg'; import { ReactComponent as Heading2 } from '@/assets/icons/h2.svg'; import { ReactComponent as Heading3 } from '@/assets/icons/h3.svg'; -import { ReactComponent as ToggleListIcon } from '@/assets/icons/toggle_list.svg'; +import { ReactComponent as NumberedListSvg } from '@/assets/icons/numbered_list.svg'; +import { ReactComponent as QuoteSvg } from '@/assets/icons/quote.svg'; +import { ReactComponent as ParagraphSvg } from '@/assets/icons/text.svg'; +import { ReactComponent as TickIcon } from '@/assets/icons/tick.svg'; import { ReactComponent as ToggleHeading1Icon } from '@/assets/icons/toggle_h1.svg'; import { ReactComponent as ToggleHeading2Icon } from '@/assets/icons/toggle_h2.svg'; import { ReactComponent as ToggleHeading3Icon } from '@/assets/icons/toggle_h3.svg'; -import type { HeadingBlockData } from '@/application/types'; -import { ReactComponent as TickIcon } from '@/assets/icons/tick.svg'; +import { ReactComponent as ToggleListIcon } from '@/assets/icons/toggle_list.svg'; +import { ReactComponent as DownArrow } from '@/assets/icons/triangle_down.svg'; + +import { useKeyboardNavigation } from '../hooks/useKeyboardNavigation'; +import { useSelectionToolbarContext } from '../SelectionToolbar.hooks'; + +import ActionButton from './ActionButton'; type BlockOption = { type: 'paragraph' | 'heading1' | 'heading2' | 'heading3' | 'quote' | 'bulleted' | 'numbered' | 'toggleHeading1' | 'toggleHeading2' | 'toggleHeading3' | 'toggle'; - icon: React.FC>; + icon: FC>; label: string; blockType: BlockType; data?: any; @@ -125,7 +127,6 @@ function TurnInfo() { const [selectedType, setSelectedType] = useState(null); const ref = useRef(null); const editor = useSlateStatic() as YjsEditor; - const selectedText = CustomEditor.getSelectionContent(editor)?.trim() || ''; const { t } = useTranslation(); const { forceShow } = useSelectionToolbarContext(); const open = Boolean(anchorEl); @@ -138,8 +139,10 @@ function TurnInfo() { let currentType: string | null = null; let currentLevel: number | null = null; let currentGroup: BlockOption['group'] | null = null; + try { const [node] = getBlockEntry(editor); + if (node.type === BlockType.Paragraph) { currentType = 'paragraph'; currentGroup = 'text'; @@ -172,17 +175,20 @@ function TurnInfo() { if (currentType === 'heading' && currentLevel) { return t(`document.slashMenu.name.heading${currentLevel}`, { defaultValue: `Heading ${currentLevel}` }); } + if (currentType === 'quote') return t('toolbar.quote', { returnObjects: false, defaultValue: 'Quote' }); if (currentType === 'bulleted') return t('toolbar.bulletList', { returnObjects: false, defaultValue: 'Bulleted List' }); if (currentType === 'numbered') return t('toolbar.numberedList', { returnObjects: false, defaultValue: 'Numbered List' }); const option = blockOptions.find(opt => opt.type === selectedType); + return option ? t(option.label, { defaultValue: option.label }) : 'Text'; }; const handleBlockChange = (option: BlockOption) => { try { const [node] = getBlockEntry(editor); + if (!node) return; if (node.type === option.blockType && @@ -191,6 +197,7 @@ function TurnInfo() { } else { CustomEditor.turnToBlock(editor, node.blockId as string, option.blockType, option.data || {}); } + setSelectedType(option.type); handleClose(); } catch (e) { handleClose(); } @@ -209,6 +216,7 @@ function TurnInfo() { const getTurnIntoOptions = () => { const suggestionOptions = getSuggestionOptions(); + // Filter out options that are already in suggestions return blockOptions.filter(option => !suggestionOptions.some(suggestion => suggestion.type === option.type) @@ -223,6 +231,7 @@ function TurnInfo() { isOpen: open, onSelect: (index) => { const options = [...suggestionOptions, ...turnIntoOptions]; + handleBlockChange(options[index]); }, onClose: handleClose, @@ -235,6 +244,7 @@ function TurnInfo() { // heading1/2/3 匹配 level return option.data && option.data.level === currentLevel; } + if (option.type === 'bulleted' && currentType === 'bulleted') return true; if (option.type === 'numbered' && currentType === 'numbered') return true; if (option.type === 'toggle' && currentType === 'toggle') return true; @@ -250,7 +260,7 @@ function TurnInfo() { onClick={e => { e.preventDefault(); e.stopPropagation(); - setAnchorEl(e.currentTarget); + setAnchorEl(e.currentTarget as HTMLElement); forceShow(true); }} tooltip={getDisplayText()} diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/Underline.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/Underline.tsx index 318e16420..ebdb2c83e 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/Underline.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/Underline.tsx @@ -1,11 +1,13 @@ +import { useCallback, useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useSlateStatic } from 'slate-react'; + import { CustomEditor } from '@/application/slate-yjs/command'; import { EditorMarkFormat } from '@/application/slate-yjs/types'; +import { ReactComponent as UnderlineSvg } from '@/assets/icons/underline.svg'; import ActionButton from '@/components/editor/components/toolbar/selection-toolbar/actions/ActionButton'; import { createHotKeyLabel, HOT_KEY_NAME } from '@/utils/hotkeys'; -import React, { useCallback, useMemo } from 'react'; -import { useTranslation } from 'react-i18next'; -import { useSlateStatic } from 'slate-react'; -import { ReactComponent as UnderlineSvg } from '@/assets/icons/underline.svg'; + export function Underline() { const { t } = useTranslation(); diff --git a/src/components/editor/components/toolbar/selection-toolbar/hooks/useKeyboardNavigation.ts b/src/components/editor/components/toolbar/selection-toolbar/hooks/useKeyboardNavigation.ts index f7b538d87..ac628482a 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/hooks/useKeyboardNavigation.ts +++ b/src/components/editor/components/toolbar/selection-toolbar/hooks/useKeyboardNavigation.ts @@ -1,6 +1,7 @@ -import { HOT_KEY_NAME, createHotkey } from '@/utils/hotkeys'; import { useCallback, useEffect, useRef, useState } from 'react'; +import { createHotkey,HOT_KEY_NAME } from '@/utils/hotkeys'; + interface UseKeyboardNavigationProps { itemCount: number; onSelect: (index: number) => void; diff --git a/src/components/main/AppTheme.tsx b/src/components/main/AppTheme.tsx index 83c95c497..c5add2830 100644 --- a/src/components/main/AppTheme.tsx +++ b/src/components/main/AppTheme.tsx @@ -1,12 +1,12 @@ -import { ThemeModeContext, useAppThemeMode } from '@/components/main/useAppThemeMode'; -import React, { useMemo } from 'react'; import createTheme from '@mui/material/styles/createTheme'; import ThemeProvider from '@mui/material/styles/ThemeProvider'; -import { i18nInstance } from '@/i18n/config'; - +import { ReactNode, useMemo } from 'react'; import { I18nextProvider } from 'react-i18next'; -function AppTheme({ children }: { children: React.ReactNode }) { +import { ThemeModeContext, useAppThemeMode } from '@/components/main/useAppThemeMode'; +import { i18nInstance } from '@/i18n/config'; + +function AppTheme({ children }: { children: ReactNode }) { const { isDark, setIsDark } = useAppThemeMode(); const theme = useMemo( From adbc847fc702f4126dc34406b4c3fba11e5a4b0c Mon Sep 17 00:00:00 2001 From: lumixraku Date: Fri, 16 May 2025 15:32:23 +0800 Subject: [PATCH 25/43] fix: revert eslint config --- .eslintrc.cjs | 57 +++---------------- .../selection-toolbar/actions/TurnInto.tsx | 3 +- 2 files changed, 9 insertions(+), 51 deletions(-) diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 63e94f1bf..2319a6f61 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -5,12 +5,7 @@ module.exports = { es6: true, node: true, }, - extends: [ - 'eslint:recommended', - 'plugin:@typescript-eslint/recommended', - 'plugin:import/recommended', - 'plugin:import/typescript' - ], + extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended'], parser: '@typescript-eslint/parser', parserOptions: { project: 'tsconfig.json', @@ -18,49 +13,8 @@ module.exports = { tsconfigRootDir: __dirname, extraFileExtensions: ['.json'], }, - plugins: ['@typescript-eslint', 'react-hooks', 'import'], - settings: { - 'import/resolver': { - typescript: { - alwaysTryTypes: true, - project: 'tsconfig.json', - }, - }, - }, + plugins: ['@typescript-eslint', 'react-hooks'], rules: { - // Import rules - 'import/order': [ - 'error', - { - 'groups': ['builtin', 'external', 'internal', 'parent', 'sibling', 'index', 'type'], - 'pathGroups': [ - { - 'pattern': '@/**', - 'group': 'internal', - 'position': 'before' - } - ], - 'newlines-between': 'always', - 'alphabetize': { 'order': 'asc', 'caseInsensitive': true } - } - ], - 'import/no-unresolved': 'error', - 'import/no-duplicates': 'error', - 'import/no-named-as-default': 'off', - 'import/no-named-as-default-member': 'off', - - // TypeScript rules - '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/ban-types': ['error', { - 'types': { - 'Object': false, - 'object': false, - 'Function': false, - }, - 'extendDefaults': true - }], - - // Existing rules 'react-hooks/rules-of-hooks': 'error', 'react-hooks/exhaustive-deps': 'error', '@typescript-eslint/adjacent-overload-signatures': 'error', @@ -80,6 +34,7 @@ module.exports = { eqeqeq: ['error', 'always'], 'no-cond-assign': 'error', 'no-duplicate-case': 'error', + 'no-duplicate-imports': 'error', 'no-empty': [ 'error', { @@ -111,7 +66,11 @@ module.exports = { { blankLine: 'any', prev: 'import', next: 'import' }, { blankLine: 'always', prev: 'block-like', next: '*' }, { blankLine: 'always', prev: 'block', next: '*' }, + ], + '@typescript-eslint/no-explicit-any': 'off', + 'import/no-named-as-default': 'off', + 'import/no-named-as-default-member': 'off', }, ignorePatterns: ['src/**/*.test.ts', '**/__tests__/**/*.json', 'package.json', '__mocks__/*.ts'], -}; \ No newline at end of file +}; diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/TurnInto.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/TurnInto.tsx index a5cf67a55..990ac7cd2 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/TurnInto.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/TurnInto.tsx @@ -6,8 +6,7 @@ import { useSlateStatic } from 'slate-react'; import { YjsEditor } from '@/application/slate-yjs'; import { CustomEditor } from '@/application/slate-yjs/command'; import { getBlockEntry } from '@/application/slate-yjs/utils/editor'; -import type { HeadingBlockData } from '@/application/types'; -import { BlockType } from '@/application/types'; +import { type HeadingBlockData, BlockType } from '@/application/types'; import { ReactComponent as BulletedListSvg } from '@/assets/icons/bulleted_list.svg'; import { ReactComponent as Heading1 } from '@/assets/icons/h1.svg'; import { ReactComponent as Heading2 } from '@/assets/icons/h2.svg'; From 842f1f8557f43615e371295e526b2638e2e4cf6c Mon Sep 17 00:00:00 2001 From: lumixraku Date: Fri, 16 May 2025 15:39:19 +0800 Subject: [PATCH 26/43] fix: no start server in cypress component test --- .github/workflows/web_coverage.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/web_coverage.yaml b/.github/workflows/web_coverage.yaml index bee3ecc23..015b47caa 100644 --- a/.github/workflows/web_coverage.yaml +++ b/.github/workflows/web_coverage.yaml @@ -44,7 +44,7 @@ jobs: with: component: true build: pnpm run build - start: pnpm run start + # start: pnpm run start browser: chrome env: COVERAGE: "true" From c6e627921d7b3f063c4e60479667d16963728d67 Mon Sep 17 00:00:00 2001 From: lumixraku Date: Sat, 17 May 2025 21:54:56 +0800 Subject: [PATCH 27/43] fix: href dark mode --- src/components/_shared/popover/Popover.tsx | 6 +++--- src/components/editor/components/leaf/href/HrefPopover.tsx | 6 +++--- .../components/toolbar/selection-toolbar/ToolbarActions.tsx | 6 +++++- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/components/_shared/popover/Popover.tsx b/src/components/_shared/popover/Popover.tsx index bc14c2d43..10908ec27 100644 --- a/src/components/_shared/popover/Popover.tsx +++ b/src/components/_shared/popover/Popover.tsx @@ -1,6 +1,6 @@ import { PopoverOrigin } from '@mui/material/Popover/Popover'; import isEqual from 'lodash-es/isEqual'; -import React, { useCallback, useEffect, useState } from 'react'; +import { useCallback, useEffect, useState } from 'react'; import { Popover as PopoverComponent, PopoverProps as PopoverComponentProps } from '@mui/material'; const defaultProps: Partial = { @@ -33,7 +33,7 @@ const DEFAULT_ORIGINS: Origins = { }, }; -export function calculateOptimalOrigins ( +export function calculateOptimalOrigins( position: Position, popoverWidth: number, popoverHeight: number, @@ -103,7 +103,7 @@ export function calculateOptimalOrigins ( }; } -export function Popover ({ +export function Popover({ children, transformOrigin = DEFAULT_ORIGINS.transformOrigin, anchorOrigin = DEFAULT_ORIGINS.anchorOrigin, diff --git a/src/components/editor/components/leaf/href/HrefPopover.tsx b/src/components/editor/components/leaf/href/HrefPopover.tsx index de5080741..f14967cbf 100644 --- a/src/components/editor/components/leaf/href/HrefPopover.tsx +++ b/src/components/editor/components/leaf/href/HrefPopover.tsx @@ -153,7 +153,7 @@ function HrefPopover({ setUrlValid(true); }} - size={'small'} fullWidth={true} placeholder={t('toolbar.addLink')} /> + size={'small'} fullWidth={true} placeholder={t('toolbar.addLink')} className="text-text-primary" /> {urlValid ? null : (
{t('editor.incorrectLink')} @@ -253,7 +253,7 @@ function HrefPopover({ } }} size={'small'} - fullWidth={true} placeholder={t('toolbar.addLink')} /> + fullWidth={true} placeholder={t('toolbar.addLink')} className="text-text-primary" />
); } diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/AIAssistant.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/AIAssistant.tsx index eed192259..6f2dcf14b 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/AIAssistant.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/AIAssistant.tsx @@ -1,4 +1,4 @@ -import { AIAssistantType,AIWriterMenu, useAIWriter } from '@appflowyinc/ai-chat'; +import { AIAssistantType, AIWriterMenu, useAIWriter } from '@appflowyinc/ai-chat'; import { useCallback, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { ReactEditor, useSlate } from 'slate-react'; @@ -97,7 +97,7 @@ function AIAssistant() { >
- +
diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/Heading.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/Heading.tsx index cc18f6666..a734471d8 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/Heading.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/Heading.tsx @@ -166,7 +166,7 @@ export function Heading() { >
- +
{toolbarVisible && ( @@ -214,7 +214,7 @@ export function Heading() { {String(t(opt.labelKey as any))} {opt.isActive(isParagraph, isActivated) && ( - + )} diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/TurnInto.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/TurnInto.tsx index 990ac7cd2..05e23c632 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/TurnInto.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/TurnInto.tsx @@ -276,7 +276,7 @@ function TurnInfo() { > {getDisplayText()} - +
Date: Sun, 18 May 2025 18:57:22 +0800 Subject: [PATCH 29/43] chore: better align code --- .../components/blocks/heading/Heading.tsx | 2 +- .../selection-toolbar/actions/Align.tsx | 182 ++++++------------ 2 files changed, 64 insertions(+), 120 deletions(-) diff --git a/src/components/editor/components/blocks/heading/Heading.tsx b/src/components/editor/components/blocks/heading/Heading.tsx index 2fe32308b..4001504d4 100644 --- a/src/components/editor/components/blocks/heading/Heading.tsx +++ b/src/components/editor/components/blocks/heading/Heading.tsx @@ -1,6 +1,6 @@ import { HEADER_HEIGHT } from '@/application/constants'; import { EditorElementProps, HeadingNode } from '@/components/editor/editor.type'; -import React, { forwardRef, memo } from 'react'; +import { forwardRef, memo } from 'react'; export const Heading = memo( forwardRef>(({ node, children, ...attributes }, ref) => { diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/Align.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/Align.tsx index 18a69765f..9db45d74d 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/Align.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/Align.tsx @@ -4,6 +4,7 @@ import { useCallback, useEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Element } from 'slate'; import { useSlateStatic } from 'slate-react'; +import React from 'react'; import { YjsEditor } from '@/application/slate-yjs'; import { CustomEditor } from '@/application/slate-yjs/command'; @@ -35,11 +36,30 @@ const popoverProps: Partial = { }, }; +const alignOptions = [ + { + icon: , + labelKey: 'toolbar.alignLeft', + type: AlignType.Left, + }, + { + icon: , + labelKey: 'toolbar.alignCenter', + type: AlignType.Center, + }, + { + icon: , + labelKey: 'toolbar.alignRight', + type: AlignType.Right, + }, +]; + export function Align({ blockId, enabled = true }: { blockId?: string; enabled?: boolean }) { const [open, setOpen] = useState(false); const ref = useRef(null); const { t } = useTranslation(); const editor = useSlateStatic() as YjsEditor; + const { rePosition } = useSelectionToolbarContext(); const getNode = useCallback(() => { let node: Element; @@ -56,7 +76,6 @@ export function Align({ blockId, enabled = true }: { blockId?: string; enabled?: const getAlign = useCallback(() => { try { const node = getNode(); - return (node.data as BlockData).align; } catch (e) { return; @@ -73,32 +92,19 @@ export function Align({ blockId, enabled = true }: { blockId?: string; enabled?: const activeIcon = useCallback(() => { const align = getAlign(); - - switch (align) { - case AlignType.Left: - return ; - case 'center': - return ; - case 'right': - return ; - default: - return ; - } + const option = alignOptions.find(opt => opt.type === align) || alignOptions[0]; + return React.cloneElement(option.icon, { + className: `h-5 w-5 ${align ? 'text-fill-default' : ''}` + }); }, [getAlign]); - const { rePosition } = useSelectionToolbarContext(); - const toggleAlign = useCallback( (align: AlignType) => { return () => { try { const node = getNode(); - - CustomEditor.setBlockData(editor, node.blockId as string, { - align, - }); + CustomEditor.setBlockData(editor, node.blockId as string, { align }); handleClose(); - rePosition(); } catch (e) { return; @@ -109,12 +115,10 @@ export function Align({ blockId, enabled = true }: { blockId?: string; enabled?: ); const { getButtonProps, selectedIndex } = useKeyboardNavigation({ - itemCount: 3, + itemCount: alignOptions.length, isOpen: open, onSelect: (index) => { - const align = [AlignType.Left, AlignType.Center, AlignType.Right][index]; - - toggleAlign(align)(); + toggleAlign(alignOptions[index].type)(); }, onClose: handleClose }); @@ -144,107 +148,47 @@ export function Align({ blockId, enabled = true }: { blockId?: string; enabled?: disableAutoFocus={true} disableEnforceFocus={true} disableRestoreFocus={true} - onClose={() => { - setOpen(false); - }} + onClose={handleClose} open={open && enabled} anchorEl={ref.current} {...popoverProps} >
- - - + {alignOptions.map((option, index) => ( + + ))}
From 06fd01ec59d1b50c12c7dfbe8731659260a27af3 Mon Sep 17 00:00:00 2001 From: lumixraku Date: Sun, 18 May 2025 22:03:33 +0800 Subject: [PATCH 30/43] chore: better translation key --- src/@types/translations/en.json | 16 ++++++++----- src/@types/translations/es-VE.json | 14 +++++++++-- src/@types/translations/eu-ES.json | 2 +- src/@types/translations/fr-CA.json | 14 +++++++++-- src/@types/translations/fr-FR.json | 14 +++++++++-- src/@types/translations/ja-JP.json | 14 +++++++++-- src/@types/translations/ko-KR.json | 15 ++++++++++-- src/@types/translations/zh-CN.json | 14 +++++++++-- src/@types/translations/zh-TW.json | 14 +++++++++-- .../selection-toolbar/actions/TurnInto.tsx | 24 +++++++++---------- 10 files changed, 108 insertions(+), 33 deletions(-) diff --git a/src/@types/translations/en.json b/src/@types/translations/en.json index 554ff1bdb..b13d03d52 100644 --- a/src/@types/translations/en.json +++ b/src/@types/translations/en.json @@ -299,12 +299,16 @@ "color": "Color", "addLink": "Add Link", "link": "Link", - "suggestion": "Suggestion", - "turninto": "Turn into", - "paragraph": "Paragraph", - "heading1": "Heading 1", - "heading2": "Heading 2", - "heading3": "Heading 3" + "h1": "Heading 1", + "h2": "Heading 2", + "h3": "Heading 3", + "alignLeft": "Align left", + "alignRight": "Align right", + "alignCenter": "Align center", + "moreOptions": "More options", + "font": "Font", + "suggestions": "Suggestions", + "turnInto": "Turn into" }, "tooltip": { "lightMode": "Switch to Light mode", diff --git a/src/@types/translations/es-VE.json b/src/@types/translations/es-VE.json index 375f42cce..c40169ce3 100644 --- a/src/@types/translations/es-VE.json +++ b/src/@types/translations/es-VE.json @@ -223,7 +223,17 @@ "highlight": "Resaltado", "color": "Color", "addLink": "Añadir enlace", - "link": "Enlace" + "link": "Enlace", + "h1": "Encabezado 1", + "h2": "Encabezado 2", + "h3": "Encabezado 3", + "alignLeft": "Alinear a la izquierda", + "alignRight": "Alinear a la derecha", + "alignCenter": "Alinear al centro", + "moreOptions": "Más opciones", + "font": "Fuente", + "suggestions": "Sugerencias", + "turnInto": "Convertir en" }, "tooltip": { "lightMode": "Cambiar a modo Claro", @@ -1547,4 +1557,4 @@ "betaTooltip": "Actualmente solo admitimos la búsqueda de páginas.", "fromTrashHint": "De la papelera" } -} +} \ No newline at end of file diff --git a/src/@types/translations/eu-ES.json b/src/@types/translations/eu-ES.json index be987e7a5..fe153883c 100644 --- a/src/@types/translations/eu-ES.json +++ b/src/@types/translations/eu-ES.json @@ -600,4 +600,4 @@ "deleteContentTitle": "Ziur {pageType} ezabatu nahi duzula?", "deleteContentCaption": "{pageType} hau ezabatzen baduzu, zaborrontzitik leheneratu dezakezu." } -} +} \ No newline at end of file diff --git a/src/@types/translations/fr-CA.json b/src/@types/translations/fr-CA.json index 405427c6b..3e9e8751e 100644 --- a/src/@types/translations/fr-CA.json +++ b/src/@types/translations/fr-CA.json @@ -171,7 +171,17 @@ "highlight": "Surligner", "color": "Couleur", "addLink": "Ajouter un lien", - "link": "Lien" + "link": "Lien", + "h1": "Titre 1", + "h2": "Titre 2", + "h3": "Titre 3", + "alignLeft": "Aligner à gauche", + "alignRight": "Aligner à droite", + "alignCenter": "Aligner le centre", + "moreOptions": "Plus d'options", + "font": "Police", + "suggestions": "Suggestions", + "turnInto": "Devenir" }, "tooltip": { "lightMode": "Passer en mode clair", @@ -1256,4 +1266,4 @@ "userIcon": "Icône utilisateur" }, "noLogFiles": "Il n'y a pas de log" -} +} \ No newline at end of file diff --git a/src/@types/translations/fr-FR.json b/src/@types/translations/fr-FR.json index 169d87119..30597d0dd 100644 --- a/src/@types/translations/fr-FR.json +++ b/src/@types/translations/fr-FR.json @@ -292,7 +292,17 @@ "highlight": "Surligner", "color": "Couleur", "addLink": "Ajouter un lien", - "link": "Lien" + "link": "Lien", + "h1": "Titre 1", + "h2": "Titre 2", + "h3": "Titre 3", + "alignLeft": "Aligner à gauche", + "alignRight": "Aligner à droite", + "alignCenter": "Aligner le centre", + "moreOptions": "Plus d'options", + "font": "Police", + "suggestions": "Suggestions", + "turnInto": "Devenir" }, "tooltip": { "lightMode": "Passer en mode clair", @@ -2945,4 +2955,4 @@ "permissionDenied": "Aucune autorisation d'ouvrir ce fichier", "unknownError": "Échec de l'ouverture du fichier" } -} +} \ No newline at end of file diff --git a/src/@types/translations/ja-JP.json b/src/@types/translations/ja-JP.json index 3491ea9cf..1afb7c9ed 100644 --- a/src/@types/translations/ja-JP.json +++ b/src/@types/translations/ja-JP.json @@ -252,7 +252,17 @@ "highlight": "ハイライト", "color": "カラー", "addLink": "リンクを追加", - "link": "リンク" + "link": "リンク", + "h1": "見出し1", + "h2": "見出し2", + "h3": "見出し3", + "alignLeft": "テキストを左揃え", + "alignCenter": "テキストを中央揃え", + "alignRight": "テキストを右揃え", + "moreOptions": "More options", + "font": "フォント", + "suggestions": "提案", + "turnInto": "変換" }, "tooltip": { "lightMode": "ライトモードに切り替える", @@ -2546,4 +2556,4 @@ "zoomIn": "ズームイン", "zoomOut": "ズームアウト" } -} +} \ No newline at end of file diff --git a/src/@types/translations/ko-KR.json b/src/@types/translations/ko-KR.json index 1e161022b..3685a3ca6 100644 --- a/src/@types/translations/ko-KR.json +++ b/src/@types/translations/ko-KR.json @@ -297,7 +297,18 @@ "highlight": "강조", "color": "색상", "addLink": "링크 추가", - "link": "링크" + "link": "링크", + "h1": "헤딩 1", + "h2": "헤딩 2", + "h3": "헤딩 3", + "alignLeft": "왼쪽 정렬", + "alignRight": "오른쪽 정렬", + "alignCenter": "가운데 정렬", + "textAlign": "텍스트 정렬", + "moreOptions": "더 많은 옵션", + "font": "글꼴", + "suggestions": "제안", + "turnInto": "변환" }, "tooltip": { "lightMode": "라이트 모드로 전환", @@ -3072,4 +3083,4 @@ "embedVideoLinkPlaceholder": "여기에 비디오 링크를 붙여넣으세요.", "videoSupported": "지원되는 서비스: YouTube, Vimeo 등.", "copiedVideoLink": "비디오 원본 링크가 클립보드에 복사되었습니다" -} +} \ No newline at end of file diff --git a/src/@types/translations/zh-CN.json b/src/@types/translations/zh-CN.json index 29ba0e50c..e44d119b1 100644 --- a/src/@types/translations/zh-CN.json +++ b/src/@types/translations/zh-CN.json @@ -277,7 +277,17 @@ "highlight": "高亮", "color": "颜色", "addLink": "添加链接", - "link": "关联" + "link": "关联", + "h1": "一级标题", + "h2": "二级标题", + "h3": "三级标题", + "alignLeft": "文本居左对齐", + "alignCenter": "文本居中对齐", + "alignRight": "文本居右对齐", + "moreOptions": "更多选项", + "font": "字体", + "suggestions": "建议", + "turnInto": "变成" }, "tooltip": { "lightMode": "切换到亮色模式", @@ -1889,4 +1899,4 @@ "yesterday": "昨天", "today": "今天" } -} +} \ No newline at end of file diff --git a/src/@types/translations/zh-TW.json b/src/@types/translations/zh-TW.json index 13aeb1225..c9cf467a7 100644 --- a/src/@types/translations/zh-TW.json +++ b/src/@types/translations/zh-TW.json @@ -248,7 +248,17 @@ "highlight": "醒目提示", "color": "顏色", "addLink": "新增連結", - "link": "連結" + "link": "連結", + "h1": "標題 1", + "h2": "標題 2", + "h3": "標題 3", + "alignLeft": "左對齊", + "alignRight": "右對齊", + "alignCenter": "中對齊", + "moreOptions": "更多選項", + "font": "字型", + "suggestions": "建議", + "turnInto": "變成" }, "tooltip": { "lightMode": "切換至亮色模式", @@ -1545,4 +1555,4 @@ "betaLabel": "BETA", "betaTooltip": "目前我們只支援搜尋頁面" } -} +} \ No newline at end of file diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/TurnInto.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/TurnInto.tsx index 05e23c632..66acb6600 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/TurnInto.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/TurnInto.tsx @@ -46,7 +46,7 @@ const blockOptions: BlockOption[] = [ { type: 'heading1', icon: Heading1, - label: 'document.slashMenu.name.heading1', + label: 'toolbar.h1', blockType: BlockType.HeadingBlock, data: { level: 1 }, group: 'text', @@ -54,7 +54,7 @@ const blockOptions: BlockOption[] = [ { type: 'heading2', icon: Heading2, - label: 'document.slashMenu.name.heading2', + label: 'toolbar.h2', blockType: BlockType.HeadingBlock, data: { level: 2 }, group: 'text', @@ -62,7 +62,7 @@ const blockOptions: BlockOption[] = [ { type: 'heading3', icon: Heading3, - label: 'document.slashMenu.name.heading3', + label: 'toolbar.h3', blockType: BlockType.HeadingBlock, data: { level: 3 }, group: 'text', @@ -70,28 +70,28 @@ const blockOptions: BlockOption[] = [ { type: 'bulleted', icon: BulletedListSvg, - label: 'toolbar.bulletList', + label: 'editor.bulletedListShortForm', blockType: BlockType.BulletedListBlock, group: 'list', }, { type: 'numbered', icon: NumberedListSvg, - label: 'editor.numberedList', + label: 'editor.numberedListShortForm', blockType: BlockType.NumberedListBlock, group: 'list', }, { type: 'toggle', icon: ToggleListIcon, - label: 'document.slashMenu.name.toggleList', + label: 'editor.toggleListShortForm', blockType: BlockType.ToggleListBlock, group: 'toggle', }, { type: 'toggleHeading1', icon: ToggleHeading1Icon, - label: 'document.slashMenu.name.toggleHeading1', + label: 'editor.toggleHeading1ShortForm', blockType: BlockType.ToggleListBlock, data: { level: 1 }, group: 'toggle', @@ -99,7 +99,7 @@ const blockOptions: BlockOption[] = [ { type: 'toggleHeading2', icon: ToggleHeading2Icon, - label: 'document.slashMenu.name.toggleHeading2', + label: 'editor.toggleHeading2ShortForm', blockType: BlockType.ToggleListBlock, data: { level: 2 }, group: 'toggle', @@ -107,7 +107,7 @@ const blockOptions: BlockOption[] = [ { type: 'toggleHeading3', icon: ToggleHeading3Icon, - label: 'document.slashMenu.name.toggleHeading3', + label: 'editor.toggleHeading3ShortForm', blockType: BlockType.ToggleListBlock, data: { level: 3 }, group: 'toggle', @@ -115,7 +115,7 @@ const blockOptions: BlockOption[] = [ { type: 'quote', icon: QuoteSvg, - label: 'toolbar.quote', + label: 'editor.quote', blockType: BlockType.QuoteBlock, group: 'other', }, @@ -305,7 +305,7 @@ function TurnInfo() { userSelect: 'none', }} > - {t('toolbar.suggestion', { defaultValue: 'Suggestion' })} + {t('toolbar.suggestions', { defaultValue: 'Suggestions' })} {suggestionOptions.map((option, index) => ( - {t('document.plugins.optionAction.turnInto', { defaultValue: 'Turn into' })} + {t('toolbar.turnInto', { defaultValue: 'Turn into' })} {turnIntoOptions.map((option, index) => ( Date: Mon, 19 May 2025 10:57:03 +0800 Subject: [PATCH 31/43] chore: better code --- .eslintrc.cjs | 4 --- .../selection-toolbar/SelectionToolbar.tsx | 2 +- .../selection-toolbar/actions/Align.tsx | 25 +++++++++++++------ .../actions/BulletedList.tsx | 2 -- .../selection-toolbar/actions/Color.tsx | 2 -- .../selection-toolbar/actions/Heading.tsx | 20 +++++++++++---- .../selection-toolbar/actions/MoreOptions.tsx | 17 ++++++------- .../actions/NumberedList.tsx | 2 -- .../selection-toolbar/actions/Quote.tsx | 2 -- .../selection-toolbar/actions/TurnInto.tsx | 20 +++++++++------ src/components/ui/popover.tsx | 8 +++--- 11 files changed, 57 insertions(+), 47 deletions(-) diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 2319a6f61..b383d8a61 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -66,11 +66,7 @@ module.exports = { { blankLine: 'any', prev: 'import', next: 'import' }, { blankLine: 'always', prev: 'block-like', next: '*' }, { blankLine: 'always', prev: 'block', next: '*' }, - ], - '@typescript-eslint/no-explicit-any': 'off', - 'import/no-named-as-default': 'off', - 'import/no-named-as-default-member': 'off', }, ignorePatterns: ['src/**/*.test.ts', '**/__tests__/**/*.json', 'package.json', '__mocks__/*.ts'], }; diff --git a/src/components/editor/components/toolbar/selection-toolbar/SelectionToolbar.tsx b/src/components/editor/components/toolbar/selection-toolbar/SelectionToolbar.tsx index abdb66099..9c94fbe7f 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/SelectionToolbar.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/SelectionToolbar.tsx @@ -46,7 +46,7 @@ export function SelectionToolbar() {
{ // prevent toolbar from taking focus away from editor diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/Align.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/Align.tsx index 9db45d74d..e18f0679b 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/Align.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/Align.tsx @@ -1,10 +1,9 @@ import Button from '@mui/material/Button'; import { PopoverProps } from '@mui/material/Popover'; -import { useCallback, useEffect, useRef, useState } from 'react'; +import { cloneElement, useCallback, useEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Element } from 'slate'; import { useSlateStatic } from 'slate-react'; -import React from 'react'; import { YjsEditor } from '@/application/slate-yjs'; import { CustomEditor } from '@/application/slate-yjs/command'; @@ -36,20 +35,29 @@ const popoverProps: Partial = { }, }; +// Define allowed translation keys for align options +const alignLabelKeys = [ + 'toolbar.alignLeft', + 'toolbar.alignCenter', + 'toolbar.alignRight', +] as const; + +type AlignLabelKey = typeof alignLabelKeys[number]; + const alignOptions = [ { icon: , - labelKey: 'toolbar.alignLeft', + labelKey: 'toolbar.alignLeft' as AlignLabelKey, type: AlignType.Left, }, { icon: , - labelKey: 'toolbar.alignCenter', + labelKey: 'toolbar.alignCenter' as AlignLabelKey, type: AlignType.Center, }, { icon: , - labelKey: 'toolbar.alignRight', + labelKey: 'toolbar.alignRight' as AlignLabelKey, type: AlignType.Right, }, ]; @@ -76,6 +84,7 @@ export function Align({ blockId, enabled = true }: { blockId?: string; enabled?: const getAlign = useCallback(() => { try { const node = getNode(); + return (node.data as BlockData).align; } catch (e) { return; @@ -93,7 +102,8 @@ export function Align({ blockId, enabled = true }: { blockId?: string; enabled?: const activeIcon = useCallback(() => { const align = getAlign(); const option = alignOptions.find(opt => opt.type === align) || alignOptions[0]; - return React.cloneElement(option.icon, { + + return cloneElement(option.icon, { className: `h-5 w-5 ${align ? 'text-fill-default' : ''}` }); }, [getAlign]); @@ -103,6 +113,7 @@ export function Align({ blockId, enabled = true }: { blockId?: string; enabled?: return () => { try { const node = getNode(); + CustomEditor.setBlockData(editor, node.blockId as string, { align }); handleClose(); rePosition(); @@ -186,7 +197,7 @@ export function Align({ blockId, enabled = true }: { blockId?: string; enabled?: }) }} > - {t(option.labelKey as any)} + {t(option.labelKey)} ))}
diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/BulletedList.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/BulletedList.tsx index 90565e020..4e23899c8 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/BulletedList.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/BulletedList.tsx @@ -10,8 +10,6 @@ import { ReactComponent as BulletedListSvg } from '@/assets/icons/bulleted_list. import ActionButton from './ActionButton'; - - export function BulletedList() { const { t } = useTranslation(); const editor = useSlateStatic() as YjsEditor; diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/Color.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/Color.tsx index f08e516a9..e884e0c70 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/Color.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/Color.tsx @@ -14,8 +14,6 @@ import { renderColor } from '@/utils/color'; import ActionButton from './ActionButton'; - - function Color() { const { t } = useTranslation(); const { visible: toolbarVisible } = useSelectionToolbarContext(); diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/Heading.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/Heading.tsx index a734471d8..f5939e0ad 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/Heading.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/Heading.tsx @@ -39,28 +39,38 @@ const popoverProps: Partial = { }, }; +// Define allowed translation keys for heading options +const headingLabelKeys = [ + 'editor.text', + 'document.slashMenu.name.heading1', + 'document.slashMenu.name.heading2', + 'document.slashMenu.name.heading3', +] as const; + +type HeadingLabelKey = typeof headingLabelKeys[number]; + const headingOptions = [ { icon: , - labelKey: 'editor.text', + labelKey: 'editor.text' as HeadingLabelKey, isActive: (isParagraph: () => boolean, _isActivated: (level: number) => boolean) => isParagraph(), onClick: (toParagraph: () => void, _toHeading: (level: number) => () => void, setOpen: (v: boolean) => void) => () => { toParagraph(); setOpen(false); }, }, { icon: , - labelKey: 'document.slashMenu.name.heading1', + labelKey: 'document.slashMenu.name.heading1' as HeadingLabelKey, isActive: (_isParagraph: () => boolean, isActivated: (level: number) => boolean) => isActivated(1), onClick: (_toParagraph: () => void, toHeading: (level: number) => () => void, setOpen: (v: boolean) => void) => () => { toHeading(1)(); setOpen(false); }, }, { icon: , - labelKey: 'document.slashMenu.name.heading2', + labelKey: 'document.slashMenu.name.heading2' as HeadingLabelKey, isActive: (_isParagraph: () => boolean, isActivated: (level: number) => boolean) => isActivated(2), onClick: (_toParagraph: () => void, toHeading: (level: number) => () => void, setOpen: (v: boolean) => void) => () => { toHeading(2)(); setOpen(false); }, }, { icon: , - labelKey: 'document.slashMenu.name.heading3', + labelKey: 'document.slashMenu.name.heading3' as HeadingLabelKey, isActive: (_isParagraph: () => boolean, isActivated: (level: number) => boolean) => isActivated(3), onClick: (_toParagraph: () => void, toHeading: (level: number) => () => void, setOpen: (v: boolean) => void) => () => { toHeading(3)(); setOpen(false); }, }, @@ -211,7 +221,7 @@ export function Heading() { }) }} > - {String(t(opt.labelKey as any))} + {t(opt.labelKey)} {opt.isActive(isParagraph, isActivated) && ( diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/MoreOptions.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/MoreOptions.tsx index 561735af9..a0d51ef57 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/MoreOptions.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/MoreOptions.tsx @@ -12,17 +12,14 @@ import { ReactComponent as StrikeThroughSvg } from '@/assets/icons/strikethrough import Popover from '@/components/_shared/popover/Popover'; import { useKeyboardNavigation } from '../hooks/useKeyboardNavigation'; - import ActionButton from './ActionButton'; import { useRef, useState } from 'react'; - - const options = [ { icon: , - labelKey: 'editor.strikethrough', - onClick: (editor: any, setOpen: (v: boolean) => void) => { + labelKey: 'editor.strikethrough' as const, + onClick: (editor: Editor, setOpen: (v: boolean) => void) => { CustomEditor.toggleMark(editor, { key: EditorMarkFormat.StrikeThrough, value: true, @@ -32,8 +29,8 @@ const options = [ }, { icon: , - labelKey: 'document.plugins.createInlineMathEquation', - onClick: (editor: any, setOpen: (v: boolean) => void) => { + labelKey: 'document.plugins.createInlineMathEquation' as const, + onClick: (editor: Editor, setOpen: (v: boolean) => void) => { const selection = editor.selection; if (!selection) return; @@ -61,12 +58,12 @@ const options = [ } else { const [entry] = editor.nodes({ at: selection, - match: (n: any) => !Editor.isEditor(n) && Text.isText(n) && (n as any).formula !== undefined, + match: (n) => !Editor.isEditor(n) && Text.isText(n) && n.formula !== undefined, }); if (!entry) return; const [node, path] = entry; - const formula = (node).formula; + const { formula } = node as Text; if (!formula) return; editor.select(path); @@ -162,7 +159,7 @@ export default function MoreOptions() { }) }} > - {t(opt.labelKey as any)} + {t(opt.labelKey)} ))}
diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/NumberedList.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/NumberedList.tsx index ac5648f76..2d1258f22 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/NumberedList.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/NumberedList.tsx @@ -10,8 +10,6 @@ import { ReactComponent as NumberedListSvg } from '@/assets/icons/numbered_list. import ActionButton from './ActionButton'; - - export function NumberedList() { const { t } = useTranslation(); const editor = useSlateStatic() as YjsEditor; diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/Quote.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/Quote.tsx index ff04d9c7d..c07e8d24d 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/Quote.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/Quote.tsx @@ -10,8 +10,6 @@ import { ReactComponent as QuoteSvg } from '@/assets/icons/quote.svg'; import ActionButton from './ActionButton'; - - export function Quote() { const { t } = useTranslation(); const editor = useSlateStatic() as YjsEditor; diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/TurnInto.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/TurnInto.tsx index 66acb6600..2258cea4f 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/TurnInto.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/TurnInto.tsx @@ -6,7 +6,7 @@ import { useSlateStatic } from 'slate-react'; import { YjsEditor } from '@/application/slate-yjs'; import { CustomEditor } from '@/application/slate-yjs/command'; import { getBlockEntry } from '@/application/slate-yjs/utils/editor'; -import { type HeadingBlockData, BlockType } from '@/application/types'; +import { type HeadingBlockData, BlockType, BlockData } from '@/application/types'; import { ReactComponent as BulletedListSvg } from '@/assets/icons/bulleted_list.svg'; import { ReactComponent as Heading1 } from '@/assets/icons/h1.svg'; import { ReactComponent as Heading2 } from '@/assets/icons/h2.svg'; @@ -31,7 +31,7 @@ type BlockOption = { icon: FC>; label: string; blockType: BlockType; - data?: any; + data?: BlockData | HeadingBlockData; group: 'text' | 'list' | 'toggle' | 'other'; }; @@ -121,6 +121,10 @@ const blockOptions: BlockOption[] = [ }, ]; +function isHeadingBlockData(data: BlockOption['data']): data is HeadingBlockData { + return !!data && typeof (data as HeadingBlockData).level === 'number'; +} + function TurnInfo() { const [anchorEl, setAnchorEl] = useState(null); const [selectedType, setSelectedType] = useState(null); @@ -191,7 +195,7 @@ function TurnInfo() { if (!node) return; if (node.type === option.blockType && - (!option.data || (node.type === BlockType.HeadingBlock && (node.data as HeadingBlockData).level === option.data.level))) { + (!option.data || (node.type === BlockType.HeadingBlock && isHeadingBlockData(option.data) && (node.data as HeadingBlockData).level === option.data.level))) { CustomEditor.turnToBlock(editor, node.blockId as string, BlockType.Paragraph, {}); } else { CustomEditor.turnToBlock(editor, node.blockId as string, option.blockType, option.data || {}); @@ -240,14 +244,14 @@ function TurnInfo() { if (!currentType) return false; if (option.type === 'paragraph' && currentType === 'paragraph') return true; if (option.type.startsWith('heading') && currentType === 'heading') { - // heading1/2/3 匹配 level - return option.data && option.data.level === currentLevel; + // heading1/2/3 --- level + return isHeadingBlockData(option.data) && option.data.level === currentLevel; } if (option.type === 'bulleted' && currentType === 'bulleted') return true; if (option.type === 'numbered' && currentType === 'numbered') return true; if (option.type === 'toggle' && currentType === 'toggle') return true; - if (option.type.startsWith('toggleHeading') && currentType === 'toggle' && option.data && option.data.level === currentLevel) return true; + if (option.type.startsWith('toggleHeading') && currentType === 'toggle' && isHeadingBlockData(option.data) && option.data.level === currentLevel) return true; if (option.type === 'quote' && currentType === 'quote') return true; return false; } @@ -310,7 +314,7 @@ function TurnInfo() { {suggestionOptions.map((option, index) => ( getButtonProps(index).ref?.(el as any)} + ref={el => getButtonProps(index).ref?.(el as unknown as HTMLButtonElement)} selected={selectedIndex === index} className="text--text-primary" sx={{ @@ -360,7 +364,7 @@ function TurnInfo() { {turnIntoOptions.map((option, index) => ( getButtonProps(index + suggestionOptions.length).ref?.(el as any)} + ref={el => getButtonProps(index + suggestionOptions.length).ref?.(el as unknown as HTMLButtonElement)} selected={selectedIndex === index + suggestionOptions.length} className="text--text-primary" sx={{ diff --git a/src/components/ui/popover.tsx b/src/components/ui/popover.tsx index 30e92a2ab..210827304 100644 --- a/src/components/ui/popover.tsx +++ b/src/components/ui/popover.tsx @@ -3,15 +3,15 @@ import * as PopoverPrimitive from '@radix-ui/react-popover'; import { cn } from '@/lib/utils'; -function Popover ({ ...props }: React.ComponentProps) { +function Popover({ ...props }: React.ComponentProps) { return ; } -function PopoverTrigger ({ ...props }: React.ComponentProps) { +function PopoverTrigger({ ...props }: React.ComponentProps) { return ; } -function PopoverContent ({ +function PopoverContent({ className, align = 'center', sideOffset = 4, @@ -33,7 +33,7 @@ function PopoverContent ({ ); } -function PopoverAnchor ({ ...props }: React.ComponentProps) { +function PopoverAnchor({ ...props }: React.ComponentProps) { return ; } From d3dc0b8bb178438637c616fc3a775930876c06b7 Mon Sep 17 00:00:00 2001 From: lumixraku Date: Mon, 19 May 2025 15:53:41 +0800 Subject: [PATCH 32/43] fix: Separator --- .../selection-toolbar/ToolbarActions.tsx | 44 +++++++------------ src/components/ui/separator.tsx | 4 +- 2 files changed, 18 insertions(+), 30 deletions(-) diff --git a/src/components/editor/components/toolbar/selection-toolbar/ToolbarActions.tsx b/src/components/editor/components/toolbar/selection-toolbar/ToolbarActions.tsx index 3fdb48204..cf4e18cbf 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/ToolbarActions.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/ToolbarActions.tsx @@ -1,4 +1,4 @@ -import { Divider } from '@mui/material'; +import { Separator } from '@/components/ui/separator'; import { useMemo } from 'react'; import { Editor, Element, Path } from 'slate'; import { useSlate } from 'slate-react'; @@ -23,8 +23,6 @@ import { import MoreOptions from './actions/MoreOptions'; - - function ToolbarActions() { const editor = useSlate() as YjsEditor; const selection = editor.selection; @@ -75,11 +73,9 @@ function ToolbarActions() { > {!isCodeBlock && <> - +
+ +
} { !isAcrossBlock && !isCodeBlock && <> @@ -89,37 +85,29 @@ function ToolbarActions() { - +
+ +
} {!isCodeBlock && } { !isAcrossBlock && !isCodeBlock && <> - +
+ +
- +
+ +
} {!isCodeBlock && } {!isCodeBlock && <> - +
+ +
}
diff --git a/src/components/ui/separator.tsx b/src/components/ui/separator.tsx index ffbc05360..ace291958 100644 --- a/src/components/ui/separator.tsx +++ b/src/components/ui/separator.tsx @@ -3,7 +3,7 @@ import * as SeparatorPrimitive from '@radix-ui/react-separator'; import { cn } from '@/lib/utils'; -function Separator ({ +function Separator({ className, orientation = 'horizontal', decorative = true, @@ -15,7 +15,7 @@ function Separator ({ decorative={decorative} orientation={orientation} className={cn( - 'bg-border-primary shrink-0 data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px', + 'shrink-0 bg-border-primary data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px', className, )} {...props} From ece7a9ba25842f2fdf56fd9b67f7587ff08e22ec Mon Sep 17 00:00:00 2001 From: lumixraku Date: Mon, 19 May 2025 16:17:48 +0800 Subject: [PATCH 33/43] fix: headings use new popover --- .../selection-toolbar/actions/Heading.tsx | 152 ++++++++---------- 1 file changed, 65 insertions(+), 87 deletions(-) diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/Heading.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/Heading.tsx index f5939e0ad..f6617e0bf 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/Heading.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/Heading.tsx @@ -1,5 +1,4 @@ import Button from '@mui/material/Button'; -import { PopoverProps } from '@mui/material/Popover'; import { useCallback, useEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useSlateStatic } from 'slate-react'; @@ -15,30 +14,13 @@ import { ReactComponent as ParagraphSvg } from '@/assets/icons/text.svg'; import { ReactComponent as TextFormatSvg } from '@/assets/icons/text_format.svg'; import { ReactComponent as TickIcon } from '@/assets/icons/tick.svg'; import { ReactComponent as DownArrow } from '@/assets/icons/triangle_down.svg'; -import { Popover } from '@/components/_shared/popover'; +import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'; import { useSelectionToolbarContext } from '@/components/editor/components/toolbar/selection-toolbar/SelectionToolbar.hooks'; import { useKeyboardNavigation } from '../hooks/useKeyboardNavigation'; import ActionButton from './ActionButton'; -const popoverProps: Partial = { - anchorOrigin: { - vertical: 'bottom', - horizontal: 'center', - }, - transformOrigin: { - vertical: 'top', - horizontal: 'center', - }, - slotProps: { - paper: { - className: 'bg-[var(--surface-primary)] rounded-[8px]', - style: { marginTop: '6px' } - }, - }, -}; - // Define allowed translation keys for heading options const headingLabelKeys = [ 'editor.text', @@ -79,7 +61,7 @@ const headingOptions = [ export function Heading() { const { t } = useTranslation(); const editor = useSlateStatic() as YjsEditor; - const { visible: toolbarVisible } = useSelectionToolbarContext(); + const { visible: toolbarVisible, forceShow } = useSelectionToolbarContext(); const [open, setOpen] = useState(false); const ref = useRef(null); @@ -164,74 +146,70 @@ export function Heading() { return (
- - { - e.preventDefault(); - e.stopPropagation(); - setOpen(true); - }} - tooltip={t('editor.text')} - > -
- - -
-
- {toolbarVisible && ( - { - setOpen(false); - }} - open={open} - anchorEl={ref.current} - {...popoverProps} - > -
- {headingOptions.map((opt, idx) => ( - - ))} + + +
+ { + e.preventDefault(); + e.stopPropagation(); + setOpen(true); + forceShow(true); + }} + tooltip={t('editor.text')} + > +
+ + +
+
-
- )} + + {toolbarVisible && ( + +
+ {headingOptions.map((opt, idx) => ( + + ))} +
+
+ )} +
); } From 90371532f0fc14d9cd6f9ce355d93d868556eda3 Mon Sep 17 00:00:00 2001 From: lumixraku Date: Mon, 19 May 2025 16:21:47 +0800 Subject: [PATCH 34/43] fix: use new popover for more options --- .../selection-toolbar/actions/MoreOptions.tsx | 68 +++++++------------ 1 file changed, 24 insertions(+), 44 deletions(-) diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/MoreOptions.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/MoreOptions.tsx index a0d51ef57..be9faa064 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/MoreOptions.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/MoreOptions.tsx @@ -1,5 +1,4 @@ import { Button } from '@mui/material'; -import { PopoverProps } from '@mui/material/Popover'; import { useTranslation } from 'react-i18next'; import { Editor, Text, Transforms } from 'slate'; import { useSlate } from 'slate-react'; @@ -9,7 +8,8 @@ import { EditorMarkFormat } from '@/application/slate-yjs/types'; import { ReactComponent as FormulaSvg } from '@/assets/icons/formula.svg'; import { ReactComponent as MoreIcon } from '@/assets/icons/more.svg'; import { ReactComponent as StrikeThroughSvg } from '@/assets/icons/strikethrough.svg'; -import Popover from '@/components/_shared/popover/Popover'; +import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'; +import { useSelectionToolbarContext } from '@/components/editor/components/toolbar/selection-toolbar/SelectionToolbar.hooks'; import { useKeyboardNavigation } from '../hooks/useKeyboardNavigation'; import ActionButton from './ActionButton'; @@ -77,28 +77,12 @@ const options = [ }, ]; -const popoverProps: Partial = { - anchorOrigin: { - vertical: 'bottom', - horizontal: 'center', - }, - transformOrigin: { - vertical: 'top', - horizontal: 'center', - }, - slotProps: { - paper: { - className: 'bg-[var(--surface-primary)] rounded-[8px]', - style: { marginTop: '6px' } - }, - }, -}; - export default function MoreOptions() { const [open, setOpen] = useState(false); const ref = useRef(null); const { t } = useTranslation(); const editor = useSlate(); + const { forceShow } = useSelectionToolbarContext(); const { getButtonProps, selectedIndex } = useKeyboardNavigation({ itemCount: options.length, @@ -108,28 +92,25 @@ export default function MoreOptions() { }); return ( - <> - { - e.preventDefault(); - e.stopPropagation(); - setOpen(true); - }} - tooltip={t('toolbar.moreOptions', { defaultValue: 'More options' })} - > - - - setOpen(false)} - open={open} - anchorEl={ref.current} - {...popoverProps} - > -
+ + +
+ { + e.preventDefault(); + e.stopPropagation(); + setOpen(true); + forceShow(true); + }} + tooltip={t('toolbar.moreOptions', { defaultValue: 'More options' })} + > + + +
+
+ +
{options.map((opt, idx) => ( ))}
-
- + + ); } \ No newline at end of file From 2ecf002352e58e626618d25a8cf0933c5c9cf299 Mon Sep 17 00:00:00 2001 From: lumixraku Date: Mon, 19 May 2025 16:49:51 +0800 Subject: [PATCH 35/43] feat: popover menu button --- .../selection-toolbar/actions/Heading.tsx | 41 +++++++--------- .../selection-toolbar/actions/MoreOptions.tsx | 47 +++++++++---------- 2 files changed, 39 insertions(+), 49 deletions(-) diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/Heading.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/Heading.tsx index f6617e0bf..d37e17033 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/Heading.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/Heading.tsx @@ -1,4 +1,3 @@ -import Button from '@mui/material/Button'; import { useCallback, useEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useSlateStatic } from 'slate-react'; @@ -14,6 +13,7 @@ import { ReactComponent as ParagraphSvg } from '@/assets/icons/text.svg'; import { ReactComponent as TextFormatSvg } from '@/assets/icons/text_format.svg'; import { ReactComponent as TickIcon } from '@/assets/icons/tick.svg'; import { ReactComponent as DownArrow } from '@/assets/icons/triangle_down.svg'; +import { Button } from '@/components/ui/button'; import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'; import { useSelectionToolbarContext } from '@/components/editor/components/toolbar/selection-toolbar/SelectionToolbar.hooks'; @@ -167,37 +167,27 @@ export function Heading() {
{toolbarVisible && ( - +
{headingOptions.map((opt, idx) => (
- +
{options.map((opt, idx) => ( ))}
From f973550d7395ea80396d1dcaf2421d7050fae718 Mon Sep 17 00:00:00 2001 From: lumixraku Date: Mon, 19 May 2025 17:20:51 +0800 Subject: [PATCH 36/43] feat: menu button for headings & more options --- .../selection-toolbar/actions/Heading.tsx | 35 ++++------------ .../selection-toolbar/actions/MenuButton.tsx | 41 +++++++++++++++++++ .../selection-toolbar/actions/MoreOptions.tsx | 33 ++++----------- 3 files changed, 58 insertions(+), 51 deletions(-) create mode 100644 src/components/editor/components/toolbar/selection-toolbar/actions/MenuButton.tsx diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/Heading.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/Heading.tsx index d37e17033..5d82327c3 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/Heading.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/Heading.tsx @@ -11,15 +11,14 @@ import { ReactComponent as Heading2 } from '@/assets/icons/h2.svg'; import { ReactComponent as Heading3 } from '@/assets/icons/h3.svg'; import { ReactComponent as ParagraphSvg } from '@/assets/icons/text.svg'; import { ReactComponent as TextFormatSvg } from '@/assets/icons/text_format.svg'; -import { ReactComponent as TickIcon } from '@/assets/icons/tick.svg'; import { ReactComponent as DownArrow } from '@/assets/icons/triangle_down.svg'; -import { Button } from '@/components/ui/button'; import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'; import { useSelectionToolbarContext } from '@/components/editor/components/toolbar/selection-toolbar/SelectionToolbar.hooks'; import { useKeyboardNavigation } from '../hooks/useKeyboardNavigation'; import ActionButton from './ActionButton'; +import { MenuButton } from './MenuButton'; // Define allowed translation keys for heading options const headingLabelKeys = [ @@ -170,31 +169,15 @@ export function Heading() {
{headingOptions.map((opt, idx) => ( - + icon={opt.icon} + label={t(opt.labelKey)} + isActive={opt.isActive(isParagraph, isActivated)} + onClick={() => opt.onClick(toParagraph, toHeading, setOpen)} + selected={selectedIndex === idx} + buttonProps={getButtonProps(idx)} + /> ))}
diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/MenuButton.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/MenuButton.tsx new file mode 100644 index 000000000..8cd0104f0 --- /dev/null +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/MenuButton.tsx @@ -0,0 +1,41 @@ +import { ReactNode } from 'react'; +import { Button } from '@/components/ui/button'; +import { ReactComponent as TickIcon } from '@/assets/icons/tick.svg'; + +interface MenuButtonProps { + icon: ReactNode; + label: ReactNode; + isActive?: boolean; + onClick: () => void; + selected?: boolean; + buttonProps?: Record; +} + +export function MenuButton({ icon, label, isActive, onClick, selected, buttonProps }: MenuButtonProps) { + return ( + + ); +} \ No newline at end of file diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/MoreOptions.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/MoreOptions.tsx index b4a7a110e..823dda8b8 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/MoreOptions.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/MoreOptions.tsx @@ -7,14 +7,13 @@ import { EditorMarkFormat } from '@/application/slate-yjs/types'; import { ReactComponent as FormulaSvg } from '@/assets/icons/formula.svg'; import { ReactComponent as MoreIcon } from '@/assets/icons/more.svg'; import { ReactComponent as StrikeThroughSvg } from '@/assets/icons/strikethrough.svg'; -import { ReactComponent as TickIcon } from '@/assets/icons/tick.svg'; -import { Button } from '@/components/ui/button'; import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'; import { useSelectionToolbarContext } from '@/components/editor/components/toolbar/selection-toolbar/SelectionToolbar.hooks'; import { useKeyboardNavigation } from '../hooks/useKeyboardNavigation'; import ActionButton from './ActionButton'; import { useRef, useState } from 'react'; +import { MenuButton } from './MenuButton'; const options = [ { @@ -115,31 +114,15 @@ export default function MoreOptions() {
{options.map((opt, idx) => ( - + selected={selectedIndex === idx} + buttonProps={getButtonProps(idx)} + /> ))}
From 1748f5433536d3e2c0d0447e8f92e42fd80cccfc Mon Sep 17 00:00:00 2001 From: lumixraku Date: Mon, 19 May 2025 17:32:48 +0800 Subject: [PATCH 37/43] chore: better code for turninto --- .../selection-toolbar/actions/TurnInto.tsx | 281 +++++++----------- 1 file changed, 101 insertions(+), 180 deletions(-) diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/TurnInto.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/TurnInto.tsx index 2258cea4f..c889507e7 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/TurnInto.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/TurnInto.tsx @@ -1,4 +1,3 @@ -import { Menu, MenuItem, Typography } from '@mui/material'; import { FC, SVGProps, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useSlateStatic } from 'slate-react'; @@ -14,17 +13,17 @@ import { ReactComponent as Heading3 } from '@/assets/icons/h3.svg'; import { ReactComponent as NumberedListSvg } from '@/assets/icons/numbered_list.svg'; import { ReactComponent as QuoteSvg } from '@/assets/icons/quote.svg'; import { ReactComponent as ParagraphSvg } from '@/assets/icons/text.svg'; -import { ReactComponent as TickIcon } from '@/assets/icons/tick.svg'; import { ReactComponent as ToggleHeading1Icon } from '@/assets/icons/toggle_h1.svg'; import { ReactComponent as ToggleHeading2Icon } from '@/assets/icons/toggle_h2.svg'; import { ReactComponent as ToggleHeading3Icon } from '@/assets/icons/toggle_h3.svg'; import { ReactComponent as ToggleListIcon } from '@/assets/icons/toggle_list.svg'; import { ReactComponent as DownArrow } from '@/assets/icons/triangle_down.svg'; +import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'; +import { useSelectionToolbarContext } from '@/components/editor/components/toolbar/selection-toolbar/SelectionToolbar.hooks'; import { useKeyboardNavigation } from '../hooks/useKeyboardNavigation'; -import { useSelectionToolbarContext } from '../SelectionToolbar.hooks'; - import ActionButton from './ActionButton'; +import { MenuButton } from './MenuButton'; type BlockOption = { type: 'paragraph' | 'heading1' | 'heading2' | 'heading3' | 'quote' | 'bulleted' | 'numbered' | 'toggleHeading1' | 'toggleHeading2' | 'toggleHeading3' | 'toggle'; @@ -126,17 +125,11 @@ function isHeadingBlockData(data: BlockOption['data']): data is HeadingBlockData } function TurnInfo() { - const [anchorEl, setAnchorEl] = useState(null); - const [selectedType, setSelectedType] = useState(null); - const ref = useRef(null); + const [open, setOpen] = useState(false); + const ref = useRef(null); const editor = useSlateStatic() as YjsEditor; const { t } = useTranslation(); - const { forceShow } = useSelectionToolbarContext(); - const open = Boolean(anchorEl); - const handleClose = () => { - setAnchorEl(null); - forceShow(false); - }; + const { visible, forceShow } = useSelectionToolbarContext(); // Helper: get current block type and heading level let currentType: string | null = null; @@ -146,30 +139,32 @@ function TurnInfo() { try { const [node] = getBlockEntry(editor); - if (node.type === BlockType.Paragraph) { - currentType = 'paragraph'; - currentGroup = 'text'; - } - else if (node.type === BlockType.HeadingBlock) { - currentType = 'heading'; - currentLevel = (node.data as HeadingBlockData).level; - currentGroup = 'text'; - } - else if (node.type === BlockType.QuoteBlock) { - currentType = 'quote'; - currentGroup = 'other'; - } - else if (node.type === BlockType.BulletedListBlock) { - currentType = 'bulleted'; - currentGroup = 'list'; - } - else if (node.type === BlockType.NumberedListBlock) { - currentType = 'numbered'; - currentGroup = 'list'; - } - else if (node.type === BlockType.ToggleListBlock) { - currentType = 'toggle'; - currentGroup = 'toggle'; + switch (node.type) { + case BlockType.Paragraph: + currentType = 'paragraph'; + currentGroup = 'text'; + break; + case BlockType.HeadingBlock: + currentType = 'heading'; + currentLevel = (node.data as HeadingBlockData).level; + currentGroup = 'text'; + break; + case BlockType.QuoteBlock: + currentType = 'quote'; + currentGroup = 'other'; + break; + case BlockType.BulletedListBlock: + currentType = 'bulleted'; + currentGroup = 'list'; + break; + case BlockType.NumberedListBlock: + currentType = 'numbered'; + currentGroup = 'list'; + break; + case BlockType.ToggleListBlock: + currentType = 'toggle'; + currentGroup = 'toggle'; + break; } } catch (e) { } @@ -183,9 +178,7 @@ function TurnInfo() { if (currentType === 'bulleted') return t('toolbar.bulletList', { returnObjects: false, defaultValue: 'Bulleted List' }); if (currentType === 'numbered') return t('toolbar.numberedList', { returnObjects: false, defaultValue: 'Numbered List' }); - const option = blockOptions.find(opt => opt.type === selectedType); - - return option ? t(option.label, { defaultValue: option.label }) : 'Text'; + return 'Text'; }; const handleBlockChange = (option: BlockOption) => { @@ -200,10 +193,8 @@ function TurnInfo() { } else { CustomEditor.turnToBlock(editor, node.blockId as string, option.blockType, option.data || {}); } - - setSelectedType(option.type); - handleClose(); - } catch (e) { handleClose(); } + setOpen(false); + } catch (e) { setOpen(false); } }; const getSuggestionOptions = () => { @@ -234,17 +225,15 @@ function TurnInfo() { isOpen: open, onSelect: (index) => { const options = [...suggestionOptions, ...turnIntoOptions]; - handleBlockChange(options[index]); }, - onClose: handleClose, + onClose: () => setOpen(false) }); function isOptionActive(option: BlockOption, currentType: string | null, currentLevel: number | null) { if (!currentType) return false; if (option.type === 'paragraph' && currentType === 'paragraph') return true; if (option.type.startsWith('heading') && currentType === 'heading') { - // heading1/2/3 --- level return isHeadingBlockData(option.data) && option.data.level === currentLevel; } @@ -258,143 +247,75 @@ function TurnInfo() { return (
- { - e.preventDefault(); - e.stopPropagation(); - setAnchorEl(e.currentTarget as HTMLElement); - forceShow(true); - }} - tooltip={getDisplayText()} - > -
- - {getDisplayText()} - - -
-
- - {suggestionOptions.length > 0 && ( - <> - + +
+ { + e.preventDefault(); + e.stopPropagation(); + setOpen(true); + forceShow(true); }} + tooltip={getDisplayText()} > - {t('toolbar.suggestions', { defaultValue: 'Suggestions' })} - - {suggestionOptions.map((option, index) => ( - getButtonProps(index).ref?.(el as unknown as HTMLButtonElement)} - selected={selectedIndex === index} - className="text--text-primary" - sx={{ - ...getButtonProps(index).sx, - fontSize: '14px', - fontStyle: 'normal', - fontWeight: 400, - lineHeight: '20px', - '&.Mui-selected, &.Mui-selected:hover': { - backgroundColor: 'var(--fill-list-hover) !important', - color: 'inherit', - }, - '&:hover': { - backgroundColor: 'var(--fill-list-hover)', - }, - }} - onClick={() => handleBlockChange(option)} - > - - - {t(option.label, { defaultValue: option.label })} +
+ + {getDisplayText()} - {isOptionActive(option, currentType, currentLevel) && ( - - - - )} - - ))} - + +
+
+
+
+ {visible && ( + +
+ {suggestionOptions.length > 0 && ( + <> +
+ {t('toolbar.suggestions', { defaultValue: 'Suggestions' })} +
+ {suggestionOptions.map((option, idx) => ( + } + label={t(option.label, { defaultValue: option.label })} + isActive={isOptionActive(option, currentType, currentLevel)} + onClick={() => handleBlockChange(option)} + selected={selectedIndex === idx} + buttonProps={getButtonProps(idx)} + /> + ))} + + )} +
+ {t('toolbar.turnInto', { defaultValue: 'Turn into' })} +
+ {turnIntoOptions.map((option, idx) => ( + } + label={t(option.label, { defaultValue: option.label })} + isActive={isOptionActive(option, currentType, currentLevel)} + onClick={() => handleBlockChange(option)} + selected={selectedIndex === idx + suggestionOptions.length} + buttonProps={getButtonProps(idx + suggestionOptions.length)} + /> + ))} +
+
)} - - {t('toolbar.turnInto', { defaultValue: 'Turn into' })} - - {turnIntoOptions.map((option, index) => ( - getButtonProps(index + suggestionOptions.length).ref?.(el as unknown as HTMLButtonElement)} - selected={selectedIndex === index + suggestionOptions.length} - className="text--text-primary" - sx={{ - ...getButtonProps(index + suggestionOptions.length).sx, - fontSize: '14px', - fontStyle: 'normal', - fontWeight: 400, - lineHeight: '20px', - '&.Mui-selected, &.Mui-selected:hover': { - backgroundColor: 'var(--fill-list-hover) !important', - color: 'inherit', - }, - '&:hover': { - backgroundColor: 'var(--fill-list-hover)', - }, - }} - onClick={() => handleBlockChange(option)} - > - - - {t(option.label, { defaultValue: option.label })} - - {isOptionActive(option, currentType, currentLevel) && ( - - - - )} - - ))} -
+
); } From 71117b46d857a75321bd327b6d9e9e492c4b096e Mon Sep 17 00:00:00 2001 From: lumixraku Date: Mon, 19 May 2025 17:57:42 +0800 Subject: [PATCH 38/43] chore: no use var(-xxx) in classname --- .../editor/components/leaf/href/HrefPopover.tsx | 2 +- .../toolbar/selection-toolbar/SelectionToolbar.tsx | 2 +- .../selection-toolbar/actions/ActionButton.tsx | 4 ++-- .../toolbar/selection-toolbar/actions/Color.tsx | 2 +- .../selection-toolbar/actions/ColorHighlight.tsx | 2 +- .../selection-toolbar/actions/MenuButton.tsx | 14 ++++++-------- tailwind.config.cjs | 1 + 7 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/components/editor/components/leaf/href/HrefPopover.tsx b/src/components/editor/components/leaf/href/HrefPopover.tsx index f14967cbf..dbf7666cb 100644 --- a/src/components/editor/components/leaf/href/HrefPopover.tsx +++ b/src/components/editor/components/leaf/href/HrefPopover.tsx @@ -298,7 +298,7 @@ function HrefPopover({ transformOrigin={defaultOrigin} slotProps={{ paper: { - className: 'p-4 min-w-[360px] bg-[var(--surface-primary)]', + className: 'p-4 min-w-[360px]', ref: paperRef, }, }} diff --git a/src/components/editor/components/toolbar/selection-toolbar/SelectionToolbar.tsx b/src/components/editor/components/toolbar/selection-toolbar/SelectionToolbar.tsx index 9c94fbe7f..4da2402fd 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/SelectionToolbar.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/SelectionToolbar.tsx @@ -46,7 +46,7 @@ export function SelectionToolbar() {
{ // prevent toolbar from taking focus away from editor diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/ActionButton.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/ActionButton.tsx index 5236368f5..327187f1c 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/ActionButton.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/ActionButton.tsx @@ -27,8 +27,8 @@ const ActionButton = forwardRef< {...props} className={cn( className, - 'bg-transparent px-1.5 py-1.5 text-icon-primary hover:bg-[var(--fill-content-hover)]', - active && 'bg-[var(--fill-theme-select)]' + 'bg-transparent px-1.5 py-1.5 text-icon-primary hover:bg-fill-content-hover', + active && 'bg-fill-theme-select' )} >
diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/Color.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/Color.tsx index e884e0c70..efc25be61 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/Color.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/Color.tsx @@ -120,7 +120,7 @@ function Color() { const popoverContent = useMemo(() => { return ( -
+
{t('editor.textColor')}
diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/ColorHighlight.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/ColorHighlight.tsx index eed27fbdc..767499eed 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/ColorHighlight.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/ColorHighlight.tsx @@ -119,7 +119,7 @@ function ColorHighlight() { const popoverContent = useMemo(() => { return ( -
+
{t('editor.backgroundColor')}
diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/MenuButton.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/MenuButton.tsx index 8cd0104f0..d39a1f7d6 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/MenuButton.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/MenuButton.tsx @@ -17,15 +17,13 @@ export function MenuButton({ icon, label, isActive, onClick, selected, buttonPro {...buttonProps} variant="ghost" className={` - h-8 min-h-8 px-[var(--spacing-spacing-m)] + h-8 min-h-8 px-m flex items-center justify-start text-sm font-normal leading-5 - hover:bg-[var(--fill-list-hover)] - focus-visible:outline-none - focus-visible:ring-0 - focus:outline-none - focus:border-0 - ${selected ? 'bg-[var(--fill-list-hover)]' : ''} + hover:bg-fill-list-hover + focus-visible:outline-none focus-visible:ring-0 + focus:outline-none focus:border-0 + ${selected ? 'bg-fill-list-hover' : ''} `} onClick={onClick} > @@ -33,7 +31,7 @@ export function MenuButton({ icon, label, isActive, onClick, selected, buttonPro {label} {isActive && ( - + )} diff --git a/tailwind.config.cjs b/tailwind.config.cjs index f193c37ec..a6d3ec81d 100644 --- a/tailwind.config.cjs +++ b/tailwind.config.cjs @@ -31,6 +31,7 @@ module.exports = { 600: '20px', xs: '4px', sm: '6px', + m: '8px', md: '12px', lg: '16px', xl: '20px', From 083f40f5cceff54834c580f05a85bcaf758fc3bf Mon Sep 17 00:00:00 2001 From: lumixraku Date: Mon, 19 May 2025 18:22:12 +0800 Subject: [PATCH 39/43] fix: align menu --- .../selection-toolbar/actions/Align.tsx | 123 ++++++------------ .../selection-toolbar/actions/MenuButton.tsx | 2 +- 2 files changed, 41 insertions(+), 84 deletions(-) diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/Align.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/Align.tsx index e18f0679b..b9c6b6e61 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/Align.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/Align.tsx @@ -1,5 +1,3 @@ -import Button from '@mui/material/Button'; -import { PopoverProps } from '@mui/material/Popover'; import { cloneElement, useCallback, useEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Element } from 'slate'; @@ -12,28 +10,12 @@ import { AlignType, BlockData } from '@/application/types'; import { ReactComponent as AlignCenterSvg } from '@/assets/icons/align_center.svg'; import { ReactComponent as AlignLeftSvg } from '@/assets/icons/align_left.svg'; import { ReactComponent as AlignRightSvg } from '@/assets/icons/align_right.svg'; -import { Popover } from '@/components/_shared/popover'; +import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'; import { useSelectionToolbarContext } from '@/components/editor/components/toolbar/selection-toolbar/SelectionToolbar.hooks'; import { useKeyboardNavigation } from '../hooks/useKeyboardNavigation'; - import ActionButton from './ActionButton'; - -const popoverProps: Partial = { - anchorOrigin: { - vertical: 'bottom', - horizontal: 'center', - }, - transformOrigin: { - vertical: -8, - horizontal: 'center', - }, - slotProps: { - paper: { - className: 'bg-[var(--surface-primary)] rounded-[6px]', - }, - }, -}; +import { MenuButton } from './MenuButton'; // Define allowed translation keys for align options const alignLabelKeys = [ @@ -67,7 +49,7 @@ export function Align({ blockId, enabled = true }: { blockId?: string; enabled?: const ref = useRef(null); const { t } = useTranslation(); const editor = useSlateStatic() as YjsEditor; - const { rePosition } = useSelectionToolbarContext(); + const { rePosition, forceShow } = useSelectionToolbarContext(); const getNode = useCallback(() => { let node: Element; @@ -93,18 +75,20 @@ export function Align({ blockId, enabled = true }: { blockId?: string; enabled?: const handleClose = useCallback(() => { setOpen(false); - }, []); + forceShow(false); + }, [forceShow]); const handleOpen = useCallback(() => { setOpen(true); - }, []); + forceShow(true); + }, [forceShow]); const activeIcon = useCallback(() => { const align = getAlign(); const option = alignOptions.find(opt => opt.type === align) || alignOptions[0]; return cloneElement(option.icon, { - className: `h-5 w-5 ${align ? 'text-fill-default' : ''}` + className: 'h-5 w-5' }); }, [getAlign]); @@ -142,65 +126,38 @@ export function Align({ blockId, enabled = true }: { blockId?: string; enabled?: return ( <> - { - e.preventDefault(); - e.stopPropagation(); - handleOpen(); - }} - tooltip={t('document.plugins.optionAction.align')} - > - {activeIcon()} - - - -
- {alignOptions.map((option, index) => ( - - ))} -
+ + + { + e.preventDefault(); + e.stopPropagation(); + handleOpen(); + }} + tooltip={t('document.plugins.optionAction.align')} + > + {activeIcon()} + + + +
+ {alignOptions.map((option, index) => ( + { + toggleAlign(option.type)(); + handleClose(); + }} + selected={selectedIndex === index} + buttonProps={getButtonProps(index)} + /> + ))} +
+
); diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/MenuButton.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/MenuButton.tsx index d39a1f7d6..925ac3a49 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/MenuButton.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/MenuButton.tsx @@ -27,7 +27,7 @@ export function MenuButton({ icon, label, isActive, onClick, selected, buttonPro `} onClick={onClick} > - {icon} + {icon} {label} {isActive && ( From 39b561e516f0ba3391bf21c82d17926fad421d4a Mon Sep 17 00:00:00 2001 From: lumixraku Date: Mon, 19 May 2025 21:09:30 +0800 Subject: [PATCH 40/43] chore: better code --- .github/workflows/web_coverage.yaml | 2 +- .../toolbar/selection-toolbar/actions/MenuButton.tsx | 5 +++-- .../toolbar/selection-toolbar/actions/TurnInto.tsx | 2 ++ .../toolbar/selection-toolbar/hooks/useKeyboardNavigation.ts | 4 +--- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/workflows/web_coverage.yaml b/.github/workflows/web_coverage.yaml index 015b47caa..e49bb679a 100644 --- a/.github/workflows/web_coverage.yaml +++ b/.github/workflows/web_coverage.yaml @@ -58,7 +58,7 @@ jobs: - name: Upload coverage to Codecov uses: codecov/codecov-action@v2 with: - token: ${{ secrets.WEB_CODE_COV }} + token: cf9245e0-e136-4e21-b0ee-35755fa0c493 files: coverage/jest/lcov.info, coverage/cypress/lcov.info flags: appflowy_web name: codecov-umbrella diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/MenuButton.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/MenuButton.tsx index 925ac3a49..bd187ef9e 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/MenuButton.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/MenuButton.tsx @@ -1,6 +1,7 @@ import { ReactNode } from 'react'; -import { Button } from '@/components/ui/button'; +import { Button, buttonVariants } from '@/components/ui/button'; import { ReactComponent as TickIcon } from '@/assets/icons/tick.svg'; +import { VariantProps } from 'class-variance-authority'; interface MenuButtonProps { icon: ReactNode; @@ -8,7 +9,7 @@ interface MenuButtonProps { isActive?: boolean; onClick: () => void; selected?: boolean; - buttonProps?: Record; + buttonProps?: React.ComponentProps<'button'> & VariantProps & { asChild?: boolean }; } export function MenuButton({ icon, label, isActive, onClick, selected, buttonProps }: MenuButtonProps) { diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/TurnInto.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/TurnInto.tsx index c889507e7..fdeeb80ac 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/TurnInto.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/TurnInto.tsx @@ -193,6 +193,7 @@ function TurnInfo() { } else { CustomEditor.turnToBlock(editor, node.blockId as string, option.blockType, option.data || {}); } + setOpen(false); } catch (e) { setOpen(false); } }; @@ -225,6 +226,7 @@ function TurnInfo() { isOpen: open, onSelect: (index) => { const options = [...suggestionOptions, ...turnIntoOptions]; + handleBlockChange(options[index]); }, onClose: () => setOpen(false) diff --git a/src/components/editor/components/toolbar/selection-toolbar/hooks/useKeyboardNavigation.ts b/src/components/editor/components/toolbar/selection-toolbar/hooks/useKeyboardNavigation.ts index ac628482a..761efedcc 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/hooks/useKeyboardNavigation.ts +++ b/src/components/editor/components/toolbar/selection-toolbar/hooks/useKeyboardNavigation.ts @@ -1,6 +1,6 @@ import { useCallback, useEffect, useRef, useState } from 'react'; -import { createHotkey,HOT_KEY_NAME } from '@/utils/hotkeys'; +import { createHotkey, HOT_KEY_NAME } from '@/utils/hotkeys'; interface UseKeyboardNavigationProps { itemCount: number; @@ -20,7 +20,6 @@ export function useKeyboardNavigation({ itemCount, onSelect, onClose, isOpen }: } const handleKeyDown = (e: KeyboardEvent) => { - // 只在按下上下键时阻止默认行为 if (createHotkey(HOT_KEY_NAME.UP)(e) || createHotkey(HOT_KEY_NAME.DOWN)(e)) { e.preventDefault(); e.stopPropagation(); @@ -40,7 +39,6 @@ export function useKeyboardNavigation({ itemCount, onSelect, onClose, isOpen }: } }; - // 使用 capture 阶段来确保在事件到达编辑器之前处理 document.addEventListener('keydown', handleKeyDown, true); return () => { document.removeEventListener('keydown', handleKeyDown, true); From 6e64f87ed02d04c38ee20eb6e745e0fcfd9c2611 Mon Sep 17 00:00:00 2001 From: lumixraku Date: Tue, 20 May 2025 11:25:26 +0800 Subject: [PATCH 41/43] fix: no active bg for color button --- .../components/toolbar/selection-toolbar/actions/Color.tsx | 6 ++---- .../toolbar/selection-toolbar/actions/ColorHighlight.tsx | 4 ++-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/Color.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/Color.tsx index efc25be61..e25c4e156 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/Color.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/Color.tsx @@ -18,9 +18,7 @@ function Color() { const { t } = useTranslation(); const { visible: toolbarVisible } = useSelectionToolbarContext(); const editor = useSlateStatic() as YjsEditor; - const isActivated = - CustomEditor.isMarkActive(editor, EditorMarkFormat.BgColor) || - CustomEditor.isMarkActive(editor, EditorMarkFormat.FontColor); + const _isActivated = CustomEditor.isMarkActive(editor, EditorMarkFormat.FontColor); const marks = CustomEditor.getAllMarks(editor); const activeFontColor = marks.find((mark) => mark[EditorMarkFormat.FontColor])?.[EditorMarkFormat.FontColor]; @@ -155,7 +153,7 @@ function Color() { return ( <> - + diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/ColorHighlight.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/ColorHighlight.tsx index 767499eed..16f746d4d 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/ColorHighlight.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/ColorHighlight.tsx @@ -19,7 +19,7 @@ function ColorHighlight() { const { t } = useTranslation(); const { visible: toolbarVisible } = useSelectionToolbarContext(); const editor = useSlateStatic() as YjsEditor; - const isActivated = CustomEditor.isMarkActive(editor, EditorMarkFormat.BgColor); + const _isActivated = CustomEditor.isMarkActive(editor, EditorMarkFormat.BgColor); const marks = CustomEditor.getAllMarks(editor); const activeBgColor = marks.find((mark) => mark[EditorMarkFormat.BgColor])?.[EditorMarkFormat.BgColor]; @@ -157,7 +157,7 @@ function ColorHighlight() { return ( <> - + From 0e87f88b771dfecd77bd04491caa7f367713064e Mon Sep 17 00:00:00 2001 From: lumixraku Date: Tue, 20 May 2025 12:38:52 +0800 Subject: [PATCH 42/43] fix: border size & color --- .../selection-toolbar/SelectionToolbar.tsx | 2 +- .../selection-toolbar/actions/Align.tsx | 4 +-- .../selection-toolbar/actions/Color.tsx | 2 +- .../actions/ColorHighlight.tsx | 34 +++++++++++-------- 4 files changed, 23 insertions(+), 19 deletions(-) diff --git a/src/components/editor/components/toolbar/selection-toolbar/SelectionToolbar.tsx b/src/components/editor/components/toolbar/selection-toolbar/SelectionToolbar.tsx index 4da2402fd..10c244d4d 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/SelectionToolbar.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/SelectionToolbar.tsx @@ -46,7 +46,7 @@ export function SelectionToolbar() {
{ // prevent toolbar from taking focus away from editor diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/Align.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/Align.tsx index b9c6b6e61..8316253ae 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/Align.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/Align.tsx @@ -140,8 +140,8 @@ export function Align({ blockId, enabled = true }: { blockId?: string; enabled?: {activeIcon()} - -
+ +
{alignOptions.map((option, index) => (
handlePickedColor(EditorMarkFormat.BgColor, color.color)} > -
-
+ {activeBgColor === color.color ? ( + <> +
+
+ + ) : ( +
+ )}
); From 12dfeb0e1c49ec52c276d94d53c842f6ec8aae43 Mon Sep 17 00:00:00 2001 From: lumixraku Date: Tue, 20 May 2025 12:51:34 +0800 Subject: [PATCH 43/43] fix: color --- .../components/toolbar/selection-toolbar/actions/Color.tsx | 3 +-- .../toolbar/selection-toolbar/actions/ColorHighlight.tsx | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/Color.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/Color.tsx index 117cdbad1..0f7adc2bb 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/Color.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/Color.tsx @@ -18,7 +18,6 @@ function Color() { const { t } = useTranslation(); const { visible: toolbarVisible } = useSelectionToolbarContext(); const editor = useSlateStatic() as YjsEditor; - const _isActivated = CustomEditor.isMarkActive(editor, EditorMarkFormat.FontColor); const marks = CustomEditor.getAllMarks(editor); const activeFontColor = marks.find((mark) => mark[EditorMarkFormat.FontColor])?.[EditorMarkFormat.FontColor]; @@ -135,7 +134,7 @@ function Color() {
mark[EditorMarkFormat.BgColor])?.[EditorMarkFormat.BgColor]; @@ -143,7 +142,7 @@ function ColorHighlight() { ) : (