Heading levels (#10078)

Larger h1 to enable three levels of headings.

<img width="508" alt="Screenshot 2024-05-24 at 07 38 31" src="https://github.com/enso-org/enso/assets/1047859/53e77040-30c2-4ed0-bfb5-81d4a703a565">

Fixes #10051.

# Important Notes
Refactored Lexical styling.
This commit is contained in:
Kaz Wesley 2024-05-24 14:27:18 -07:00 committed by GitHub
parent ac5fbbcd17
commit ca53b69dfb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 87 additions and 49 deletions

View File

@ -22,7 +22,7 @@ const textSync: LexicalPlugin = {
},
}
useLexical(contentElement, 'PlainTextEditor', [plainText, textSync])
useLexical(contentElement, 'PlainTextEditor', {}, [plainText, textSync])
</script>
<template>

View File

@ -1,5 +1,5 @@
<script setup lang="ts">
import { useLexical, type LexicalPlugin } from '@/components/lexical'
import { lexicalTheme, useLexical, type LexicalPlugin } from '@/components/lexical'
import FloatingSelectionMenu from '@/components/lexical/FloatingSelectionMenu.vue'
import LexicalContent from '@/components/lexical/LexicalContent.vue'
import SelectionFormattingToolbar from '@/components/lexical/SelectionFormattingToolbar.vue'
@ -18,7 +18,7 @@ import {
import { HeadingNode, QuoteNode, registerRichText } from '@lexical/rich-text'
import { TableCellNode, TableNode, TableRowNode } from '@lexical/table'
import { syncRef } from '@vueuse/core'
import { shallowRef, type ComponentInstance } from 'vue'
import { shallowRef, useCssModule, type ComponentInstance } from 'vue'
const markdown = defineModel<string>({ required: true })
@ -56,7 +56,8 @@ const markdownSyncPlugin: LexicalPlugin = {
},
}
const { editor } = useLexical(contentElement, 'MarkdownEditor', [
const theme = lexicalTheme(useCssModule('lexicalTheme'))
const { editor } = useLexical(contentElement, 'MarkdownEditor', theme, [
listPlugin,
markdownPlugin,
markdownSyncPlugin,
@ -77,43 +78,6 @@ const formatting = useFormatting(editor)
.fullHeight {
height: 100%;
}
.LexicalContent :deep(h1) {
font-weight: 700;
font-size: 16px;
line-height: 1.75;
}
.LexicalContent :deep(h2, h3, h4, h5, h6) {
font-size: 14px;
line-height: 2;
}
.LexicalContent :deep(p + p) {
margin-bottom: 4px;
}
.LexicalContent :deep(ol) {
list-style-type: decimal;
list-style-position: outside;
padding-left: 1.6em;
}
.LexicalContent :deep(ul) {
list-style-type: disc;
list-style-position: outside;
padding-left: 1.6em;
}
.LexicalContent :deep(strong) {
font-weight: bold;
}
.LexicalContent :deep(.lexical-strikethrough) {
text-decoration: line-through;
}
.LexicalContent :deep(.lexical-italic) {
font-style: italic;
}
</style>
<style module="lexicalTheme" src="@/components/lexical/theme.css" />

View File

@ -1,11 +1,13 @@
import { unrefElement, type MaybeElement } from '@vueuse/core'
import {
createEditor,
type EditorThemeClasses,
type KlassConstructor,
type LexicalEditor,
type LexicalNode,
type LexicalNodeReplacement,
} from 'lexical'
import { assertDefined } from 'shared/util/assert'
import { markRaw, onMounted, type Ref } from 'vue'
type NodeDefinition = KlassConstructor<typeof LexicalNode> | LexicalNodeReplacement
@ -15,9 +17,34 @@ export interface LexicalPlugin {
register: (editor: LexicalEditor) => void
}
export function lexicalTheme(theme: Record<string, string>): EditorThemeClasses {
interface EditorThemeShape extends Record<string, EditorThemeShape | string> {}
const editorClasses: EditorThemeShape = {}
for (const [classPath, className] of Object.entries(theme)) {
const path = classPath.split('_')
const leaf = path.pop()
// `split` will always return at least one value
assertDefined(leaf)
let obj = editorClasses
for (const section of path) {
const nextObj = (obj[section] ??= {})
if (typeof nextObj === 'string') {
console.warn(
`Lexical theme contained path '${classPath}', but path component '${section}' is a leaf.`,
)
continue
}
obj = nextObj
}
obj[leaf] = className
}
return editorClasses
}
export function useLexical(
contentElement: Ref<MaybeElement>,
namespace: string,
theme: EditorThemeClasses,
plugins: LexicalPlugin[],
) {
const nodes = new Set<NodeDefinition>()
@ -27,12 +54,7 @@ export function useLexical(
createEditor({
editable: true,
namespace,
theme: {
text: {
strikethrough: 'lexical-strikethrough',
italic: 'lexical-italic',
},
},
theme,
nodes: [...nodes],
onError: console.error,
}),

View File

@ -0,0 +1,52 @@
/*
Lexical theme. Class names are derived from the `LexicalThemeClasses` type from `lexical`, with the hierarchy flattened
using `_` to separate levels. See the `lexicalTheme` function in `lexical/formatting.ts`.
*/
.heading_h1 {
font-weight: 700;
font-size: 20px;
line-height: 1.75;
}
.heading_h2 {
font-weight: 700;
font-size: 16px;
line-height: 1.75;
}
.heading_h3,
.heading_h4,
.heading_h5,
.heading_h6 {
font-size: 14px;
line-height: 2;
}
.text_strikethrough {
text-decoration: line-through;
}
.text_italic {
font-style: italic;
}
.text_quote {
margin-left: 0.2em;
border-left: 0.3em solid #ccc;
padding-left: 1.6em;
}
.text_bold {
font-weight: bold;
}
.paragraph {
margin-bottom: 0.5em;
}
.list_ol {
list-style-type: decimal;
list-style-position: outside;
padding-left: 1.6em;
}
.list_ul {
list-style-type: disc;
list-style-position: outside;
padding-left: 1.6em;
}