✨(frontend) preserve @ character when esc is pressed after typing it
improves user experience by keeping @ symbol after cancelling mention trigger Signed-off-by: Cyril <c.gromoff@gmail.com>
This commit is contained in:
@@ -11,6 +11,7 @@ and this project adheres to
|
|||||||
- ♿(frontend) improve accessibility:
|
- ♿(frontend) improve accessibility:
|
||||||
- ♿(frontend) improve ARIA in doc grid and editor for a11y #1519
|
- ♿(frontend) improve ARIA in doc grid and editor for a11y #1519
|
||||||
- 🐛(docx) fix image overflow by limiting width to 600px during export #1525
|
- 🐛(docx) fix image overflow by limiting width to 600px during export #1525
|
||||||
|
- 🐛(frontend) preserve @ character when esc is pressed after typing it #1512
|
||||||
|
|
||||||
## [3.9.0] - 2025-11-10
|
## [3.9.0] - 2025-11-10
|
||||||
|
|
||||||
|
|||||||
@@ -806,6 +806,12 @@ test.describe('Doc Editor', () => {
|
|||||||
});
|
});
|
||||||
await expect(interlinkChild1).toBeVisible({ timeout: 10000 });
|
await expect(interlinkChild1).toBeVisible({ timeout: 10000 });
|
||||||
await expect(interlinkChild1.locator('svg').first()).toBeVisible();
|
await expect(interlinkChild1.locator('svg').first()).toBeVisible();
|
||||||
|
|
||||||
|
await page.keyboard.press('@');
|
||||||
|
|
||||||
|
await page.keyboard.press('Escape');
|
||||||
|
|
||||||
|
await expect(editor.getByText('@')).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('it checks multiple big doc scroll to the top', async ({
|
test('it checks multiple big doc scroll to the top', async ({
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import {
|
|||||||
StyleSchema,
|
StyleSchema,
|
||||||
} from '@blocknote/core';
|
} from '@blocknote/core';
|
||||||
import { useBlockNoteEditor } from '@blocknote/react';
|
import { useBlockNoteEditor } from '@blocknote/react';
|
||||||
|
import type { KeyboardEvent } from 'react';
|
||||||
import { useEffect, useRef, useState } from 'react';
|
import { useEffect, useRef, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { css } from 'styled-components';
|
import { css } from 'styled-components';
|
||||||
@@ -99,38 +100,7 @@ export const SearchPage = ({
|
|||||||
}, 100);
|
}, 100);
|
||||||
}, [inputRef]);
|
}, [inputRef]);
|
||||||
|
|
||||||
return (
|
const closeSearch = (insertContent: string) => {
|
||||||
<Box as="span" $position="relative">
|
|
||||||
<Box
|
|
||||||
as="span"
|
|
||||||
className="inline-content"
|
|
||||||
$background={colorsTokens['greyscale-100']}
|
|
||||||
$color="var(--c--theme--colors--greyscale-700)"
|
|
||||||
$direction="row"
|
|
||||||
$radius="3px"
|
|
||||||
$padding="1px"
|
|
||||||
$display="inline-flex"
|
|
||||||
tabIndex={-1} // Ensure the span is focusable
|
|
||||||
>
|
|
||||||
{' '}
|
|
||||||
{trigger}
|
|
||||||
<Box
|
|
||||||
as="input"
|
|
||||||
$padding={{ left: '3px' }}
|
|
||||||
$css={inputStyle}
|
|
||||||
ref={inputRef}
|
|
||||||
$display="inline-flex"
|
|
||||||
onInput={(e) => {
|
|
||||||
const value = (e.target as HTMLInputElement).value;
|
|
||||||
setSearch(value);
|
|
||||||
}}
|
|
||||||
onKeyDown={(e) => {
|
|
||||||
if (
|
|
||||||
(e.key === 'Backspace' && search.length === 0) ||
|
|
||||||
e.key === 'Escape'
|
|
||||||
) {
|
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
updateInlineContent({
|
updateInlineContent({
|
||||||
type: 'interlinkingSearchInline',
|
type: 'interlinkingSearchInline',
|
||||||
props: {
|
props: {
|
||||||
@@ -141,7 +111,17 @@ export const SearchPage = ({
|
|||||||
|
|
||||||
contentRef(null);
|
contentRef(null);
|
||||||
editor.focus();
|
editor.focus();
|
||||||
editor.insertInlineContent(['']);
|
editor.insertInlineContent([insertContent]);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleKeyDown = (e: KeyboardEvent<HTMLInputElement>) => {
|
||||||
|
if (e.key === 'Escape') {
|
||||||
|
e.preventDefault();
|
||||||
|
// Keep the trigger character ('@' or '/') in the editor when closing with Escape
|
||||||
|
closeSearch(trigger);
|
||||||
|
} else if (e.key === 'Backspace' && search.length === 0) {
|
||||||
|
e.preventDefault();
|
||||||
|
closeSearch('');
|
||||||
} else if (e.key === 'ArrowDown' || e.key === 'ArrowUp') {
|
} else if (e.key === 'ArrowDown' || e.key === 'ArrowUp') {
|
||||||
// Allow arrow keys to be handled by the command menu for navigation
|
// Allow arrow keys to be handled by the command menu for navigation
|
||||||
const commandList = e.currentTarget
|
const commandList = e.currentTarget
|
||||||
@@ -167,7 +147,34 @@ export const SearchPage = ({
|
|||||||
selectedItem?.click();
|
selectedItem?.click();
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box as="span" $position="relative">
|
||||||
|
<Box
|
||||||
|
as="span"
|
||||||
|
className="inline-content"
|
||||||
|
$background={colorsTokens['greyscale-100']}
|
||||||
|
$color="var(--c--theme--colors--greyscale-700)"
|
||||||
|
$direction="row"
|
||||||
|
$radius="3px"
|
||||||
|
$padding="1px"
|
||||||
|
$display="inline-flex"
|
||||||
|
tabIndex={-1} // Ensure the span is focusable
|
||||||
|
>
|
||||||
|
{' '}
|
||||||
|
{trigger}
|
||||||
|
<Box
|
||||||
|
as="input"
|
||||||
|
$padding={{ left: '3px' }}
|
||||||
|
$css={inputStyle}
|
||||||
|
ref={inputRef}
|
||||||
|
$display="inline-flex"
|
||||||
|
onInput={(e) => {
|
||||||
|
const value = (e.target as HTMLInputElement).value;
|
||||||
|
setSearch(value);
|
||||||
}}
|
}}
|
||||||
|
onKeyDown={handleKeyDown}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
<Box
|
<Box
|
||||||
@@ -224,6 +231,8 @@ export const SearchPage = ({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
contentRef(null);
|
||||||
|
|
||||||
editor.insertInlineContent([
|
editor.insertInlineContent([
|
||||||
{
|
{
|
||||||
type: 'interlinkingLinkInline',
|
type: 'interlinkingLinkInline',
|
||||||
|
|||||||
Reference in New Issue
Block a user