Skip to content
Merged
Show file tree
Hide file tree
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
34 changes: 27 additions & 7 deletions packages/web-pkg/src/editor/composables/useEditorActions.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { computed, markRaw, ref, unref } from 'vue'
import type { Component } from 'vue'
import { getMarksBetween } from '@tiptap/core'
import type { Editor, Range } from '@tiptap/core'
import { useGettext } from 'vue3-gettext'
import { storeToRefs } from 'pinia'
Expand Down Expand Up @@ -182,13 +183,32 @@ export function useEditorActions(state: TextEditorState) {
title: $gettext('Font size'),
icon: 'font-size-2',
showInSlashCommands: false,
childActions: ['12px', '14px', '16px', '18px', '20px', '24px', '28px', '32px'].map((size) => ({
id: `font-size-${size}`,
title: size,
icon: 'font-size-2',
toolbarAction: (editor) => editor.chain().focus().setFontSize(size).run(),
isActive: (editor) => editor.getAttributes('textStyle').fontSize === size
}))
childActions: [
{
id: 'font-size-default',
title: $gettext('Default'),
icon: 'font-size-2',
toolbarAction: (editor) => editor.chain().focus().unsetFontSize().run(),
isActive: (editor) => {
const { from, to, empty } = editor.state.selection
if (empty || from === to) {
return !editor.getAttributes('textStyle').fontSize
}

// For mixed font sizes in a range selection, no option should be active, including "Default".
return !getMarksBetween(from, to, editor.state.doc).some(
({ mark }) => mark.type.name === 'textStyle' && !!mark.attrs.fontSize
)
}
},
...['12px', '14px', '16px', '18px', '20px', '24px', '28px', '32px'].map((size) => ({
id: `font-size-${size}`,
title: size,
icon: 'font-size-2',
toolbarAction: (editor: Editor) => editor.chain().focus().setFontSize(size).run(),
isActive: (editor: Editor) => editor.isActive('textStyle', { fontSize: size })
}))
]
})

const alignLeft = (): EditorAction => ({
Expand Down
38 changes: 7 additions & 31 deletions packages/web-pkg/src/editor/styles/text-editor.css
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@
border: none;
margin: 0 auto;
padding: 0;
font-size: calc(15px * var(--text-editor-zoom-factor));
font-size: 15px;
line-height: 1.6;
zoom: var(--text-editor-zoom-factor);
}

.text-editor-content .ProseMirror:focus,
Expand Down Expand Up @@ -66,31 +67,6 @@
margin: 0 0 8px;
}

.text-editor-content .ProseMirror h1,
.text-editor-content .ProseMirror h2,
.text-editor-content .ProseMirror h3,
.text-editor-content .ProseMirror h4 {
margin: 0 0 8px;
line-height: 1.25;
font-weight: 600;
}

.text-editor-content .ProseMirror h1 {
font-size: calc(30px * var(--text-editor-zoom-factor));
}

.text-editor-content .ProseMirror h2 {
font-size: calc(24px * var(--text-editor-zoom-factor));
}

.text-editor-content .ProseMirror h3 {
font-size: calc(20px * var(--text-editor-zoom-factor));
}

.text-editor-content .ProseMirror h4 {
font-size: calc(17px * var(--text-editor-zoom-factor));
}

.text-editor-content .ProseMirror a {
color: var(--oc-role-primary);
text-decoration: underline;
Expand Down Expand Up @@ -134,13 +110,13 @@
}

.text-editor-content textarea {
font-size: calc(15px * var(--text-editor-zoom-factor));
line-height: 1.6;
font-size: 15px;
zoom: var(--text-editor-zoom-factor);
}

/* Codeblock styles */
.text-editor-content .ProseMirror pre {
font-size: calc(14px * var(--text-editor-zoom-factor));
font-size: 14px;
background: var(--oc-role-surface-container-high);
padding: 8px 12px;
border-radius: 6px;
Expand All @@ -150,7 +126,7 @@

/* Inline code styles */
.text-editor-content .ProseMirror :not(pre) > code {
font-size: calc(13px * var(--text-editor-zoom-factor));
font-size: 13px;
padding: 2px 4px;
border-radius: 6px;
background: var(--oc-role-surface-container-high);
Expand All @@ -169,7 +145,7 @@
border-radius: 6px;
background: var(--oc-role-surface-container-high);
color: var(--oc-role-on-surface-variant);
font-size: calc(13px * var(--text-editor-zoom-factor));
font-size: 13px;
}

.text-editor-content .ProseMirror .text-editor-frontmatter pre {
Expand Down
1 change: 1 addition & 0 deletions packages/web-pkg/tests/unit/editor/composables/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ export function createMockEditor(options: MockEditorOptions = {}) {
'insertTable',
'setFontFamily',
'setFontSize',
'unsetFontSize',
'setLineHeight',
'setColor',
'unsetColor',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest'
import { ref } from 'vue'
import type { Range } from '@tiptap/core'
import { Editor } from '@tiptap/vue-3'
import type { Node as ProseMirrorNode } from '@tiptap/pm/model'
import StarterKit from '@tiptap/starter-kit'
import { mock } from 'vitest-mock-extended'
import { createMockEditor } from './helpers'
Expand Down Expand Up @@ -208,7 +209,7 @@ describe('useEditorActions', () => {
}

const dropdownActions = [
{ name: 'fontSize', setMethod: 'setFontSize', attrKey: 'fontSize', valueIsTitle: true },
{ name: 'fontSize', setMethod: 'setFontSize', attrKey: 'fontSize', valueIsTitle: false },
{ name: 'lineHeight', setMethod: 'setLineHeight', attrKey: 'lineHeight', valueIsTitle: true },
{ name: 'textColor', setMethod: 'setColor', attrKey: 'color', valueIsTitle: false },
{
Expand Down Expand Up @@ -245,7 +246,9 @@ describe('useEditorActions', () => {
? actions[name]().childActions!.find(
({ id }) => id !== 'background-color-transparent'
)!
: actions[name]().childActions![0]
: name === 'fontSize'
? actions[name]().childActions!.find(({ id }) => id !== 'font-size-default')!
: actions[name]().childActions![0]
childAction.toolbarAction!(editor)
expect(editor._chain[setMethod]).toHaveBeenCalled()
})
Expand Down Expand Up @@ -290,6 +293,57 @@ describe('useEditorActions', () => {
)
).toBe(false)
})

it('fontSize has a default entry that resets font size', () => {
const editor = createMockEditor()
const action = actions.fontSize()
const defaultAction = action.childActions!.find(({ id }) => id === 'font-size-default')

expect(defaultAction).toBeDefined()
defaultAction!.toolbarAction!(editor)
expect(editor._chain.unsetFontSize).toHaveBeenCalled()
expect(defaultAction!.isActive!(createMockEditor({ attributes: { textStyle: {} } }))).toBe(
true
)
expect(
defaultAction!.isActive!(
createMockEditor({ attributes: { textStyle: { fontSize: '16px' } } })
)
).toBe(false)
})

it('fontSize option isActive delegates to editor.isActive', () => {
const option = actions.fontSize().childActions!.find(({ id }) => id === 'font-size-16px')
const editor = createMockEditor({
isActive: (type, attrs) => type === 'textStyle' && attrs?.fontSize === '16px'
})

expect(option).toBeDefined()
expect(option!.isActive!(editor)).toBe(true)
expect(editor.isActive).toHaveBeenCalledWith('textStyle', { fontSize: '16px' })
})

it('fontSize has no active option for mixed selection', () => {
const editor = createMockEditor({ attributes: { textStyle: {} }, isActive: () => false })
const action = actions.fontSize()

Object.assign(editor.state.selection, { from: 1, to: 6, empty: false })
Object.assign(editor.state.doc, {
nodesBetween: (_from: number, _to: number, callback: (node: ProseMirrorNode) => void) => {
callback({ isText: true, nodeSize: 1, marks: [] } as unknown as ProseMirrorNode)
callback({
isText: true,
nodeSize: 1,
marks: [{ type: { name: 'textStyle' }, attrs: { fontSize: '16px' } }]
} as unknown as ProseMirrorNode)
}
})

const activeStates = action.childActions!.map(
(childAction) => childAction.isActive?.(editor) ?? false
)
expect(activeStates).toEqual(Array(action.childActions!.length).fill(false))
})
})

describe('heading', () => {
Expand Down