fix: respect rtl direction in editor blocks

This commit is contained in:
lucaronin
2026-05-17 19:46:18 +02:00
parent 41992506f1
commit 9d331ffda8
3 changed files with 59 additions and 11 deletions

View File

@@ -39,6 +39,11 @@ import { useFilenameAutolinkGuard } from './useFilenameAutolinkGuard'
import './Editor.css'
import './EditorTheme.css'
const RICH_EDITOR_BIDI_DOM_ATTRIBUTES = {
blockContent: { dir: 'auto' },
inlineContent: { dir: 'auto' },
}
interface Tab {
entry: VaultEntry
content: string
@@ -206,6 +211,7 @@ function useEditorSetup({
const editor = useCreateBlockNote({
schema,
domAttributes: RICH_EDITOR_BIDI_DOM_ATTRIBUTES,
uploadFile: (file: File) => uploadImageFile(file, vaultPathRef.current),
_tiptapOptions: { injectNonce: RUNTIME_STYLE_NONCE },
extensions: [

View File

@@ -21,13 +21,15 @@
.editor-content-width--wide .editor__blocknote-container .bn-editor {
max-width: none;
margin-left: 0;
margin-right: 0;
padding-left: 0;
padding-right: 0;
margin-inline: 0;
padding-inline: 0;
}
/* Let each rich-text block resolve its own base direction for mixed LTR/RTL notes. */
.editor__blocknote-container .bn-block-content {
text-align: start;
}
.editor__blocknote-container .bn-inline-content {
unicode-bidi: plaintext;
text-align: start;
@@ -65,7 +67,7 @@
/* The side menu is positioned via transform — we shift it further left */
}
.editor__blocknote-container .bn-block-content {
margin-left: 8px;
margin-inline-start: 8px;
}
.editor__blocknote-container .bn-editor ::selection {
@@ -154,7 +156,7 @@
/* BlockNote renders bullet as ::before on [data-content-type="bulletListItem"]
with content: "•", display: flex, width: 24px */
.editor__blocknote-container [data-content-type="bulletListItem"] {
padding-left: var(--lists-padding-left);
padding-inline-start: var(--lists-padding-left);
margin-bottom: var(--lists-item-spacing);
gap: var(--lists-bullet-gap) !important;
}
@@ -172,17 +174,17 @@
/* Nested list indentation */
.editor__blocknote-container .bn-block-group .bn-block-group {
margin-left: var(--lists-indent-size);
margin-inline-start: var(--lists-indent-size);
}
/* Hide the vertical nesting lines — cleaner without them */
.editor__blocknote-container .bn-block-group .bn-block-group .bn-block-outer::before {
border-left: none !important;
border-inline-start: none !important;
}
/* --- Numbered lists --- */
.editor__blocknote-container [data-content-type="numberedListItem"] {
padding-left: var(--lists-padding-left);
padding-inline-start: var(--lists-padding-left);
margin-bottom: var(--lists-item-spacing);
gap: var(--lists-bullet-gap) !important;
}
@@ -537,8 +539,8 @@
/* --- Blockquote --- */
.editor__blocknote-container [data-content-type="blockquote"],
.editor__blocknote-container blockquote {
border-left: var(--blockquote-border-left-width) solid var(--blockquote-border-left-color);
padding-left: var(--blockquote-padding-left);
border-inline-start: var(--blockquote-border-left-width) solid var(--blockquote-border-left-color);
padding-inline-start: var(--blockquote-padding-left);
margin-top: var(--blockquote-margin-vertical);
margin-bottom: var(--blockquote-margin-vertical);
color: var(--blockquote-color);
@@ -559,6 +561,7 @@
.editor__blocknote-container [data-content-type="table"] td {
padding: var(--table-cell-padding-vertical) var(--table-cell-padding-horizontal);
border-color: var(--table-border-color);
text-align: start;
}
/* --- Horizontal rule --- */

View File

@@ -7,6 +7,9 @@ import { executeCommand, openCommandPalette } from './helpers'
const RTL_TITLE = 'RTL Mixed Direction'
const RTL_PARAGRAPH = 'مرحبا بالعالم'
const MIXED_PARAGRAPH = 'English then مرحبا'
const RTL_LIST_ITEM = 'רשימת בדיקה'
const RTL_QUOTE = 'ציטוט חשוב'
const RTL_TABLE_CELL = 'תא בטבלה'
let tempVaultDir: string
@@ -26,6 +29,14 @@ test.beforeEach(async ({ page }, testInfo) => {
'',
MIXED_PARAGRAPH,
'',
`- ${RTL_LIST_ITEM}`,
'',
`> ${RTL_QUOTE}`,
'',
'| Field | Value |',
'| --- | --- |',
`| ${RTL_TABLE_CELL} | 42 |`,
'',
].join('\n'),
)
await openFixtureVault(page, tempVaultDir)
@@ -62,3 +73,31 @@ test('rich and raw editors resolve text direction per line for Arabic and mixed
await expect(rawLines.filter({ hasText: RTL_PARAGRAPH })).toHaveCSS('unicode-bidi', 'plaintext')
await expect(rawLines.filter({ hasText: RTL_PARAGRAPH })).toHaveCSS('text-align', 'start')
})
test('rich editor uses logical spacing for RTL block elements', async ({ page }) => {
await openNote(page, RTL_TITLE)
const rtlListItem = page.locator('[data-content-type="bulletListItem"]', { hasText: RTL_LIST_ITEM }).first()
await expect(rtlListItem).toBeVisible({ timeout: 5_000 })
await expect(rtlListItem).toHaveAttribute('dir', 'auto')
await expect(rtlListItem).toHaveCSS('direction', 'rtl')
await expect.poll(async () => rtlListItem.evaluate((element) => {
const style = getComputedStyle(element)
return Number.parseFloat(style.paddingRight) > Number.parseFloat(style.paddingLeft)
})).toBe(true)
const rtlQuote = page.locator('.bn-block-content', { hasText: RTL_QUOTE }).first()
await expect(rtlQuote).toBeVisible({ timeout: 5_000 })
await expect(rtlQuote).toHaveAttribute('dir', 'auto')
await expect(rtlQuote).toHaveCSS('direction', 'rtl')
const rtlQuoteElement = page.locator('blockquote', { hasText: RTL_QUOTE }).first()
await expect(rtlQuoteElement).toBeVisible({ timeout: 5_000 })
await expect.poll(async () => rtlQuoteElement.evaluate((element) => {
const style = getComputedStyle(element)
return Number.parseFloat(style.borderRightWidth) > Number.parseFloat(style.borderLeftWidth)
})).toBe(true)
const rtlTableCell = page.locator('td', { hasText: RTL_TABLE_CELL }).first()
await expect(rtlTableCell).toBeVisible({ timeout: 5_000 })
await expect(rtlTableCell).toHaveCSS('text-align', 'start')
})