fix: respect rtl direction in editor blocks
This commit is contained in:
@@ -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: [
|
||||
|
||||
@@ -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 --- */
|
||||
|
||||
@@ -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')
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user