From 5f6b2f1a6337dc8883a06cc77fa85b42dff4c781 Mon Sep 17 00:00:00 2001 From: Henry Heino <46334387+personalizedrefrigerator@users.noreply.github.com> Date: Sun, 17 Dec 2023 12:55:54 -0800 Subject: [PATCH] Mobile,Desktop: Fixes #9522: Fix code block borders in headers of Beta Markdown editor (#9523) --- .../editor/CodeMirror/createEditor.test.ts | 5 +- .../CodeMirror/markdown/decoratorExtension.ts | 114 ++++++++++-------- packages/editor/CodeMirror/theme.ts | 70 ++++++----- 3 files changed, 96 insertions(+), 93 deletions(-) diff --git a/packages/editor/CodeMirror/createEditor.test.ts b/packages/editor/CodeMirror/createEditor.test.ts index bc5350b16d..40db1bea94 100644 --- a/packages/editor/CodeMirror/createEditor.test.ts +++ b/packages/editor/CodeMirror/createEditor.test.ts @@ -42,15 +42,12 @@ describe('createEditor', () => { const headerLine = document.body.querySelector('.cm-headerLine')!; expect(headerLine.textContent).toBe(headerLineText); + expect(getComputedStyle(headerLine).fontSize).toBe('1.6em'); // CodeMirror nests the tag that styles the header within .cm-headerLine: //
Testing...
const headerLineContent = document.body.querySelectorAll('.cm-headerLine > span'); expect(headerLineContent.length).toBeGreaterThanOrEqual(1); - for (const part of headerLineContent) { - const style = getComputedStyle(part); - expect(style.fontSize).toBe('1.6em'); - } // Cleanup editor.remove(); diff --git a/packages/editor/CodeMirror/markdown/decoratorExtension.ts b/packages/editor/CodeMirror/markdown/decoratorExtension.ts index 558de6dabe..db54e1a069 100644 --- a/packages/editor/CodeMirror/markdown/decoratorExtension.ts +++ b/packages/editor/CodeMirror/markdown/decoratorExtension.ts @@ -48,8 +48,28 @@ const blockQuoteDecoration = Decoration.line({ attributes: { class: 'cm-blockQuote' }, }); -const headerLineDecoration = Decoration.line({ - attributes: { class: 'cm-headerLine' }, +const header1LineDecoration = Decoration.line({ + attributes: { class: 'cm-h1 cm-headerLine' }, +}); + +const header2LineDecoration = Decoration.line({ + attributes: { class: 'cm-h2 cm-headerLine' }, +}); + +const header3LineDecoration = Decoration.line({ + attributes: { class: 'cm-h3 cm-headerLine' }, +}); + +const header4LineDecoration = Decoration.line({ + attributes: { class: 'cm-h4 cm-headerLine' }, +}); + +const header5LineDecoration = Decoration.line({ + attributes: { class: 'cm-h5 cm-headerLine' }, +}); + +const header6LineDecoration = Decoration.line({ + attributes: { class: 'cm-h6 cm-headerLine' }, }); const tableHeaderDecoration = Decoration.line({ @@ -72,6 +92,37 @@ const taskMarkerDecoration = Decoration.mark({ attributes: { class: 'cm-taskMarker' }, }); +const nodeNameToLineDecoration: Record = { + 'FencedCode': codeBlockDecoration, + 'CodeBlock': codeBlockDecoration, + 'BlockMath': mathBlockDecoration, + 'Blockquote': blockQuoteDecoration, + + 'SetextHeading1': header1LineDecoration, + 'ATXHeading1': header1LineDecoration, + 'SetextHeading2': header2LineDecoration, + 'ATXHeading2': header2LineDecoration, + 'ATXHeading3': header3LineDecoration, + 'ATXHeading4': header4LineDecoration, + 'ATXHeading5': header5LineDecoration, + 'ATXHeading6': header6LineDecoration, + + 'TableHeader': tableHeaderDecoration, + 'TableDelimiter': tableDelimiterDecoration, + 'TableRow': tableBodyDecoration, +}; + +const nodeNameToMarkDecoration: Record = { + 'InlineCode': inlineCodeDecoration, + 'URL': urlDecoration, + 'InlineMath': inlineMathDecoration, + 'HTMLTag': htmlTagNameDecoration, + 'TagName': htmlTagNameDecoration, + 'HorizontalRule': horizontalRuleDecoration, + 'TaskMarker': taskMarkerDecoration, +}; + + type DecorationDescription = { pos: number; length?: number; decoration: Decoration }; // Returns a set of [Decoration]s, associated with block syntax groups that require @@ -116,58 +167,15 @@ const computeDecorations = (view: EditorView) => { const viewFrom = Math.max(from, node.from); const viewTo = Math.min(to, node.to); - switch (node.name) { - case 'FencedCode': - case 'CodeBlock': - addDecorationToLines(viewFrom, viewTo, codeBlockDecoration); + if (nodeNameToLineDecoration.hasOwnProperty(node.name)) { + const decoration = nodeNameToLineDecoration[node.name]; + addDecorationToLines(viewFrom, viewTo, decoration); blockDecorated = true; - break; - case 'BlockMath': - addDecorationToLines(viewFrom, viewTo, mathBlockDecoration); - blockDecorated = true; - break; - case 'Blockquote': - addDecorationToLines(viewFrom, viewTo, blockQuoteDecoration); - blockDecorated = true; - break; - case 'InlineMath': - addDecorationToRange(viewFrom, viewTo, inlineMathDecoration); - break; - case 'InlineCode': - addDecorationToRange(viewFrom, viewTo, inlineCodeDecoration); - break; - case 'URL': - addDecorationToRange(viewFrom, viewTo, urlDecoration); - break; - case 'SetextHeading1': - case 'SetextHeading2': - case 'ATXHeading1': - case 'ATXHeading2': - case 'ATXHeading3': - case 'ATXHeading4': - case 'ATXHeading5': - case 'ATXHeading6': - addDecorationToLines(viewFrom, viewTo, headerLineDecoration); - break; - case 'HTMLTag': - case 'TagName': - addDecorationToRange(viewFrom, viewTo, htmlTagNameDecoration); - break; - case 'TableHeader': - addDecorationToLines(viewFrom, viewTo, tableHeaderDecoration); - break; - case 'TableDelimiter': - addDecorationToLines(viewFrom, viewTo, tableDelimiterDecoration); - break; - case 'TableRow': - addDecorationToLines(viewFrom, viewTo, tableBodyDecoration); - break; - case 'HorizontalRule': - addDecorationToRange(viewFrom, viewTo, horizontalRuleDecoration); - break; - case 'TaskMarker': - addDecorationToRange(viewFrom, viewTo, taskMarkerDecoration); - break; + } + + if (nodeNameToMarkDecoration.hasOwnProperty(node.name)) { + const decoration = nodeNameToMarkDecoration[node.name]; + addDecorationToRange(viewFrom, viewTo, decoration); } // Only block decorations will have differing first and last lines diff --git a/packages/editor/CodeMirror/theme.ts b/packages/editor/CodeMirror/theme.ts index ee9007572b..7a2c73b8bb 100644 --- a/packages/editor/CodeMirror/theme.ts +++ b/packages/editor/CodeMirror/theme.ts @@ -78,6 +78,11 @@ const createTheme = (theme: any): Extension[] => { // be at least this specific. const selectionBackgroundSelector = '&.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground'; + const baseHeadingStyle = { + fontWeight: 'bold', + fontFamily: theme.fontFamily, + }; + const codeMirrorTheme = EditorView.theme({ '&': baseGlobalStyle, @@ -145,7 +150,7 @@ const createTheme = (theme: any): Extension[] => { // span with a larger font-size, the .cm-inlineCode's bounding box won't // be big enough for its content. // As such, we need to style whichever element directly wraps its content. - '& .cm-headerLine > .cm-inlineCode > *, & :not(.cm-headerLine) > .cm-inlineCode': { + '& .cm-inlineCode': { borderWidth: '1px', borderStyle: 'solid', borderColor: isDarkTheme ? 'rgba(200, 200, 200, 0.5)' : 'rgba(100, 100, 100, 0.5)', @@ -166,6 +171,34 @@ const createTheme = (theme: any): Extension[] => { opacity: theme.isDesktop ? 0.6 : 1, }, + // Applying font size changes with CSS rather than the theme below works + // around an issue where the border for code blocks in headings was too + // small. + '& .cm-h1': { + ...baseHeadingStyle, + fontSize: '1.6em', + }, + '& .cm-h2': { + ...baseHeadingStyle, + fontSize: '1.4em', + }, + '& .cm-h3': { + ...baseHeadingStyle, + fontSize: '1.3em', + }, + '& .cm-h4': { + ...baseHeadingStyle, + fontSize: '1.2em', + }, + '& .cm-h5': { + ...baseHeadingStyle, + fontSize: '1.1em', + }, + '& .cm-h6': { + ...baseHeadingStyle, + fontSize: '1.0em', + }, + // Style the search widget. Use ':root' to increase the selector's precedence // (override the existing preset styles). ':root & .cm-panel.cm-search': { @@ -176,11 +209,6 @@ const createTheme = (theme: any): Extension[] => { }, }, { dark: isDarkTheme }); - const baseHeadingStyle = { - fontWeight: 'bold', - fontFamily: theme.fontFamily, - }; - const highlightingStyle = HighlightStyle.define([ { tag: tags.strong, @@ -190,36 +218,6 @@ const createTheme = (theme: any): Extension[] => { tag: tags.emphasis, fontStyle: 'italic', }, - { - ...baseHeadingStyle, - tag: tags.heading1, - fontSize: '1.6em', - }, - { - ...baseHeadingStyle, - tag: tags.heading2, - fontSize: '1.4em', - }, - { - ...baseHeadingStyle, - tag: tags.heading3, - fontSize: '1.3em', - }, - { - ...baseHeadingStyle, - tag: tags.heading4, - fontSize: '1.2em', - }, - { - ...baseHeadingStyle, - tag: tags.heading5, - fontSize: '1.1em', - }, - { - ...baseHeadingStyle, - tag: tags.heading6, - fontSize: '1.0em', - }, { tag: tags.list, fontFamily: theme.fontFamily,