Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions rollup.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,46 @@ import { promisify } from "util"
/* global Buffer */
const brotliPromise = promisify(brotliCompress)

// Interim workaround for facebook/lexical#2495. On Safari, a collapsed caret in
// RTL text yields a degenerate, out-of-bounds bounding rect, so Lexical's
// scrollIntoViewIfNeeded scrolls the window up on every space/backspace and the
// editor jumps off-screen. A caret that lives inside the editor cannot lie
// entirely above the editor's own top edge; when the rect says it does, the rect
// is unreliable, so skip the scroll. The guard is one-sided on purpose — a rect
// below the editor is the normal "scroll the caret into view" path (including
// auto-scroll inside an overflow:auto editor root) and is left untouched. This
// injects that guard into the bundled
// lexical at build time (the Rails-engine build bundles lexical; the npm build
// externalizes it). Remove once a fixed lexical release is adopted upstream.
function guardLexicalScrollIntoView() {
const PROD_ANCHOR = "if(null===r||null===i)return;let{top:o,bottom:s}=e,l=0,c=0,a=n;"
const PROD_GUARD = "if(null===r||null===i)return;const $lxsr=n.getBoundingClientRect();if(e.bottom<$lxsr.top)return;let{top:o,bottom:s}=e,l=0,c=0,a=n;"
Comment thread
jeremy marked this conversation as resolved.
const DEV_ANCHOR = " if (doc === null || defaultView === null) {\n return;\n }\n let {\n top: currentTop,\n bottom: currentBottom\n } = selectionRect;"
const DEV_GUARD = " if (doc === null || defaultView === null) {\n return;\n }\n // Injected by Lexxy — skip scroll on a bogus above-the-editor caret rect (Safari RTL). facebook/lexical#2495.\n const rootRect = rootElement.getBoundingClientRect();\n if (selectionRect.bottom < rootRect.top) {\n return;\n }\n let {\n top: currentTop,\n bottom: currentBottom\n } = selectionRect;"

let applied = 0
return {
name: "guard-lexical-scroll-into-view",
buildStart() {
applied = 0
},
transform(code, id) {
if (!/[\\/]node_modules[\\/]lexical[\\/]/.test(id)) return null
let out = code
if (out.includes(PROD_ANCHOR)) out = out.replace(PROD_ANCHOR, PROD_GUARD)
if (out.includes(DEV_ANCHOR)) out = out.replace(DEV_ANCHOR, DEV_GUARD)
if (out === code) return null
applied++
return { code: out, map: null }
},
buildEnd() {
if (applied === 0) {
this.error("guard-lexical-scroll-into-view: lexical's scrollIntoViewIfNeeded anchor was not found. The workaround for facebook/lexical#2495 did not apply — re-verify it against the current lexical version.")
}
}
}
Comment thread
jeremy marked this conversation as resolved.
}

export default [
{
input: "./src/index.js",
Expand All @@ -29,6 +69,7 @@ export default [
"@rails/activestorage"
],
plugins: [
guardLexicalScrollIntoView(),
nodeResolve(),
commonjs(),
// Inject Prism for prismjs language components that expect a global Prism
Expand Down
Loading