From 89618c70f450cd2948108c6caeedc32c91bb3ca8 Mon Sep 17 00:00:00 2001 From: Victor Ilyushchenko Date: Thu, 9 Jan 2025 05:35:48 +0300 Subject: [PATCH] Text alignment actions (#7604) Signed-off-by: Victor Ilyushchenko --- common/config/rush/pnpm-lock.yaml | 28 +++++++++---- models/text-editor/src/index.ts | 41 +++++++++++++++++++ packages/text/package.json | 3 +- packages/text/src/kits/server-kit.ts | 6 +++ .../text/src/markup/__tests__/utils.test.ts | 20 ++++++--- plugins/text-editor-resources/package.json | 3 +- .../src/components/TextEditor.svelte | 1 + .../src/components/TextEditorToolbar.svelte | 1 - .../src/components/extension/inlinePopup.ts | 2 +- .../src/kits/editor-kit.ts | 15 +++++++ .../src/markdown/__tests__/textmodel.test.ts | 6 ++- 11 files changed, 105 insertions(+), 21 deletions(-) diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index 983c40ffc4..cb2f0f1f20 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -1235,6 +1235,9 @@ dependencies: '@tiptap/extension-task-list': specifier: ^2.6.6 version: 2.6.6(@tiptap/core@2.6.6) + '@tiptap/extension-text-align': + specifier: ~2.11.0 + version: 2.11.0(@tiptap/core@2.6.6) '@tiptap/extension-typography': specifier: ^2.6.6 version: 2.6.6(@tiptap/core@2.6.6) @@ -1634,9 +1637,6 @@ dependencies: googleapis: specifier: ^122.0.0 version: 122.0.0 - got: - specifier: ^11.8.3 - version: 11.8.6 graphql: specifier: ^16.8.0 version: 16.9.0 @@ -6252,6 +6252,14 @@ packages: '@tiptap/core': 2.6.6(@tiptap/pm@2.6.6) dev: false + /@tiptap/extension-text-align@2.11.0(@tiptap/core@2.6.6): + resolution: {integrity: sha512-VRXBqO17po6ddqhoWLBa2aCX/tqHdzdKPLfjnBy1fF8hjQKbidzjMWhb4CMm31ApvJjKK/DTkM3EnyYS/XDhng==} + peerDependencies: + '@tiptap/core': ^2.7.0 + dependencies: + '@tiptap/core': 2.6.6(@tiptap/pm@2.6.6) + dev: false + /@tiptap/extension-text@2.6.6(@tiptap/core@2.6.6): resolution: {integrity: sha512-e84uILnRzNzcwK1DVQNpXVmBG1Cq3BJipTOIDl1LHifOok7MBjhI/X+/NR0bd3N2t6gmDTWi63+4GuJ5EeDmsg==} peerDependencies: @@ -24760,7 +24768,7 @@ packages: dev: false file:projects/importer.tgz(esbuild@0.24.2)(ts-node@10.9.2): - resolution: {integrity: sha512-2BSUFKldNxsA3oJ/xjVKMJc975re0WnY1T24b0yscLWNGfMchaPaHNLBMIpd3d4rXywTX0qqBNPt+aWkp2XVBA==, tarball: file:projects/importer.tgz} + resolution: {integrity: sha512-nd4QEoFM7LFj37X/9PCtKl2HTaQl3xnpCbJL+FBuYPJhimHzG4KTvb3E5vZ31OZxgAzYBBLZb1KsswqqlXAJ9A==, tarball: file:projects/importer.tgz} id: file:projects/importer.tgz name: '@rush-temp/importer' version: 0.0.0 @@ -27401,7 +27409,7 @@ packages: dev: false file:projects/pod-backup.tgz: - resolution: {integrity: sha512-f8l7TT88HfNQ8lRgFe4lA5Zbzb3nPF+9dBmaOAd1SFLWAnbp959dyN4CxGPWQDu4VeQ50vKe3wg7FxoYpgQRyg==, tarball: file:projects/pod-backup.tgz} + resolution: {integrity: sha512-Ccg90DAJu+vNRMm00Z8W768WT8M6AKzjUFktH9/RBbDGQSF+UYQNm7Ah/cMJnJ0GMOg/dQ//r7Tob7WL4kaBeg==, tarball: file:projects/pod-backup.tgz} name: '@rush-temp/pod-backup' version: 0.0.0 dependencies: @@ -27895,7 +27903,7 @@ packages: dev: false file:projects/pod-ses.tgz: - resolution: {integrity: sha512-Z+IlSWDXbE1r5gAGoFhvdyRXh8qZUlANdrO4jWmT0HuDHWeDibkjK5/YXnqztHDmMbz3QI60RotnYOdJwkqSLA==, tarball: file:projects/pod-ses.tgz} + resolution: {integrity: sha512-P+HVwQR4SO6F4EXl5ReGR6hbng8CKsl4IK6Ww6u7yhNh1x7YGKzmV7UMRjSUc7qsNu4CzOhF7AYatCB602ANjA==, tarball: file:projects/pod-ses.tgz} name: '@rush-temp/pod-ses' version: 0.0.0 dependencies: @@ -32508,7 +32516,7 @@ packages: dev: false file:projects/text-editor-resources.tgz(@types/node@20.11.19)(bufferutil@4.0.8)(esbuild@0.24.2)(highlight.js@11.8.0)(postcss-load-config@4.0.2)(postcss@8.4.35)(ts-node@10.9.2)(utf-8-validate@6.0.4): - resolution: {integrity: sha512-F0E6R31dkSA4Oa+lx7pYoKeHmMI1N0wMDKomkWOhwtChvWo3EZRj9xySEthXFV8au0htnsi+rKeywFOCcGdmGg==, tarball: file:projects/text-editor-resources.tgz} + resolution: {integrity: sha512-7dR/DxZ3NRHi1qbcIIwu94XiC5aklrmIsXSjx/Nd8lsNy4Tyc0+1nB8lM6di7SF89cG/xwk3JokDbL3tlYQKbw==, tarball: file:projects/text-editor-resources.tgz} id: file:projects/text-editor-resources.tgz name: '@rush-temp/text-editor-resources' version: 0.0.0 @@ -32531,6 +32539,7 @@ packages: '@tiptap/extension-table-cell': 2.6.6(@tiptap/core@2.6.6) '@tiptap/extension-table-header': 2.6.6(@tiptap/core@2.6.6) '@tiptap/extension-table-row': 2.6.6(@tiptap/core@2.6.6) + '@tiptap/extension-text-align': 2.11.0(@tiptap/core@2.6.6) '@tiptap/extension-typography': 2.6.6(@tiptap/core@2.6.6) '@tiptap/extension-underline': 2.6.6(@tiptap/core@2.6.6) '@tiptap/pm': 2.6.6 @@ -32675,7 +32684,7 @@ packages: dev: false file:projects/text.tgz(@types/node@20.11.19)(bufferutil@4.0.8)(esbuild@0.24.2)(ts-node@10.9.2)(utf-8-validate@6.0.4): - resolution: {integrity: sha512-1V1MwGEZv1UxhJ4js4mIMG6U87MbA6jlzPZyE6EmtNpYbQ12DfVTp2XpiK3JlZKs95itkJcFCSTQIMsYSrw4yw==, tarball: file:projects/text.tgz} + resolution: {integrity: sha512-zmM6GDzeNuMbMA7iWwhg8hBc43fylNW7cEmk0fSWw9+G9NqD2MLv2dVb5dnAcPAioj9WBdDakWcrLAAHUBJyMw==, tarball: file:projects/text.tgz} id: file:projects/text.tgz name: '@rush-temp/text' version: 0.0.0 @@ -32695,6 +32704,7 @@ packages: '@tiptap/extension-table-row': 2.6.6(@tiptap/core@2.6.6) '@tiptap/extension-task-item': 2.6.6(@tiptap/core@2.6.6)(@tiptap/pm@2.6.6) '@tiptap/extension-task-list': 2.6.6(@tiptap/core@2.6.6) + '@tiptap/extension-text-align': 2.11.0(@tiptap/core@2.6.6) '@tiptap/extension-typography': 2.6.6(@tiptap/core@2.6.6) '@tiptap/extension-underline': 2.6.6(@tiptap/core@2.6.6) '@tiptap/html': 2.6.6(@tiptap/core@2.6.6)(@tiptap/pm@2.6.6) @@ -32893,7 +32903,7 @@ packages: dev: false file:projects/tool.tgz: - resolution: {integrity: sha512-nyx2osnJq6th2Ttrw5B2TIHUc2j8nTnB4tvx0jIXQLjiq/FEUxBluKLb2eEoHntQ5TxCWEC9a69jkO/QnNWfwA==, tarball: file:projects/tool.tgz} + resolution: {integrity: sha512-Cw36G6uROkPkNIJDl7WeHjhHKmbQth6wmjMu4vBQGz+DfuPf9Eg63SFKq29rOssZ4LlJB1d22Y7XnxilCuXK5g==, tarball: file:projects/tool.tgz} name: '@rush-temp/tool' version: 0.0.0 dependencies: diff --git a/models/text-editor/src/index.ts b/models/text-editor/src/index.ts index ed38407599..028616d9c3 100644 --- a/models/text-editor/src/index.ts +++ b/models/text-editor/src/index.ts @@ -147,6 +147,42 @@ function createImageAlignmentAction (builder: Builder, align: 'center' | 'left' }) } +function createTextAlignmentAction (builder: Builder, align: 'center' | 'left' | 'right'): void { + let icon: Asset + let label: IntlString + let index: number + switch (align) { + case 'left': + icon = textEditor.icon.AlignLeft + label = textEditor.string.AlignLeft + index = 5 + break + case 'center': + icon = textEditor.icon.AlignCenter + label = textEditor.string.AlignCenter + index = 10 + break + case 'right': + icon = textEditor.icon.AlignRight + label = textEditor.string.AlignRight + index = 15 + break + } + + builder.createDoc(textEditor.class.TextEditorAction, core.space.Model, { + kind: 'text', + action: { + command: 'setTextAlign', + params: align + }, + visibilityTester: textEditor.function.IsEditable, + icon, + label, + category: 45, + index + }) +} + export function createModel (builder: Builder): void { builder.createModel(TRefInputActionItem, TTextEditorExtensionFactory, TTextEditorAction) @@ -253,6 +289,11 @@ export function createModel (builder: Builder): void { index: 10 }) + // Text align category + createTextAlignmentAction(builder, 'left') + createTextAlignmentAction(builder, 'center') + createTextAlignmentAction(builder, 'right') + // Quote category builder.createDoc(textEditor.class.TextEditorAction, core.space.Model, { action: { diff --git a/packages/text/package.json b/packages/text/package.json index 5343a89b3a..0bf657add2 100644 --- a/packages/text/package.json +++ b/packages/text/package.json @@ -63,7 +63,8 @@ "@tiptap/suggestion": "^2.6.6", "prosemirror-codemark": "^0.4.2", "markdown-it": "^14.0.0", - "fast-equals": "^5.0.1" + "fast-equals": "^5.0.1", + "@tiptap/extension-text-align": "~2.11.0" }, "repository": "https://github.com/hcengineering/platform", "publishConfig": { diff --git a/packages/text/src/kits/server-kit.ts b/packages/text/src/kits/server-kit.ts index 1b01d876f6..b6a7345bec 100644 --- a/packages/text/src/kits/server-kit.ts +++ b/packages/text/src/kits/server-kit.ts @@ -33,6 +33,7 @@ import { DefaultKit, DefaultKitOptions } from './default-kit' import { CodeExtension, codeOptions } from '../marks/code' import { NoteBaseExtension } from '../marks/noteBase' import { CommentNode } from '../nodes/comment' +import TextAlign from '@tiptap/extension-text-align' const headingLevels: Level[] = [1, 2, 3, 4, 5, 6] @@ -86,6 +87,11 @@ export const ServerKit = Extension.create({ ...taskListExtensions, ...fileExtensions, ...imageExtensions, + TextAlign.configure({ + types: ['heading', 'paragraph'], + alignments: ['left', 'center', 'right'], + defaultAlignment: null + }), TodoItemNode, TodoListNode, ReferenceNode, diff --git a/packages/text/src/markup/__tests__/utils.test.ts b/packages/text/src/markup/__tests__/utils.test.ts index d4fec9c7af..fe9ee6f9e5 100644 --- a/packages/text/src/markup/__tests__/utils.test.ts +++ b/packages/text/src/markup/__tests__/utils.test.ts @@ -61,17 +61,19 @@ describe('EmptyMarkup', () => { describe('getMarkup', () => { it('with empty content', async () => { const editor = new Editor({ extensions }) - expect(getMarkup(editor)).toEqual('{"type":"doc","content":[{"type":"paragraph"}]}') + expect(getMarkup(editor)).toEqual('{"type":"doc","content":[{"type":"paragraph","attrs":{"textAlign":null}}]}') }) it('with some content', async () => { const editor = new Editor({ extensions, content: '

hello

' }) expect(getMarkup(editor)).toEqual( - '{"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":"hello"}]}]}' + '{"type":"doc","content":[{"type":"paragraph","attrs":{"textAlign":null},"content":[{"type":"text","text":"hello"}]}]}' ) }) it('with empty paragraphs as content', async () => { const editor = new Editor({ extensions, content: '

' }) - expect(getMarkup(editor)).toEqual('{"type":"doc","content":[{"type":"paragraph"},{"type":"paragraph"}]}') + expect(getMarkup(editor)).toEqual( + '{"type":"doc","content":[{"type":"paragraph","attrs":{"textAlign":null}},{"type":"paragraph","attrs":{"textAlign":null}}]}' + ) }) }) @@ -250,13 +252,15 @@ describe('pmNodeToMarkup', () => { const schema = getSchema(extensions) const node = schema.node('paragraph', {}, [schema.text('Hello, world!')]) - expect(pmNodeToMarkup(node)).toEqual('{"type":"paragraph","content":[{"type":"text","text":"Hello, world!"}]}') + expect(pmNodeToMarkup(node)).toEqual( + '{"type":"paragraph","attrs":{"textAlign":null},"content":[{"type":"text","text":"Hello, world!"}]}' + ) }) }) describe('markupToPmNode', () => { it('converts markup to ProseMirrorNode', () => { - const markup = '{"type":"paragraph","content":[{"type":"text","text":"Hello, world!"}]}' + const markup = '{"type":"paragraph","attrs":{"textAlign":null},"content":[{"type":"text","text":"Hello, world!"}]}' const node = markupToPmNode(markup) expect(node.type.name).toEqual('paragraph') @@ -306,7 +310,11 @@ describe('pmNodeToJSON', () => { const schema = getSchema(extensions) const node = schema.node('paragraph', {}, [schema.text('Hello, world!')]) - const json = nodeParagraph(nodeText('Hello, world!')) + const json: MarkupNode = { + type: MarkupNodeType.paragraph, + attrs: { textAlign: null as any }, + content: [nodeText('Hello, world!')] + } expect(pmNodeToJSON(node)).toEqual(json) }) }) diff --git a/plugins/text-editor-resources/package.json b/plugins/text-editor-resources/package.json index f4c7251291..422114b1ea 100644 --- a/plugins/text-editor-resources/package.json +++ b/plugins/text-editor-resources/package.json @@ -87,6 +87,7 @@ "mermaid": "~11.4.1", "@hcengineering/theme": "^0.6.5", "tippy.js": "~6.3.7", - "@hcengineering/chunter": "^0.6.20" + "@hcengineering/chunter": "^0.6.20", + "@tiptap/extension-text-align": "~2.11.0" } } diff --git a/plugins/text-editor-resources/src/components/TextEditor.svelte b/plugins/text-editor-resources/src/components/TextEditor.svelte index 7229f4bdda..61df678044 100644 --- a/plugins/text-editor-resources/src/components/TextEditor.svelte +++ b/plugins/text-editor-resources/src/components/TextEditor.svelte @@ -175,6 +175,7 @@ } : false, drawingBoard: false, + textAlign: false, submit: supportSubmit ? { submit } : false, toolbar: { element: textToolbarElement, diff --git a/plugins/text-editor-resources/src/components/TextEditorToolbar.svelte b/plugins/text-editor-resources/src/components/TextEditorToolbar.svelte index 97158ff4e7..f77559630c 100644 --- a/plugins/text-editor-resources/src/components/TextEditorToolbar.svelte +++ b/plugins/text-editor-resources/src/components/TextEditorToolbar.svelte @@ -60,7 +60,6 @@ const { command } = action.action if ((editor.commands as any)[command] === undefined) { - console.error(`Command ${command} not found`) continue } } diff --git a/plugins/text-editor-resources/src/components/extension/inlinePopup.ts b/plugins/text-editor-resources/src/components/extension/inlinePopup.ts index f88756b7b3..6a229eafba 100644 --- a/plugins/text-editor-resources/src/components/extension/inlinePopup.ts +++ b/plugins/text-editor-resources/src/components/extension/inlinePopup.ts @@ -8,7 +8,7 @@ export const InlinePopupExtension: Extension = BubbleMenu.ext pluginKey: 'inline-popup', element: null, tippyOptions: { - maxWidth: '38rem', + maxWidth: '46rem', zIndex: 500, appendTo: () => document.body } diff --git a/plugins/text-editor-resources/src/kits/editor-kit.ts b/plugins/text-editor-resources/src/kits/editor-kit.ts index c894d913bb..dba920cda3 100644 --- a/plugins/text-editor-resources/src/kits/editor-kit.ts +++ b/plugins/text-editor-resources/src/kits/editor-kit.ts @@ -38,6 +38,7 @@ import { DefaultKit, type DefaultKitOptions } from './default-kit' import { MermaidExtension, type MermaidOptions, mermaidOptions } from '../components/extension/mermaid' import { DrawingBoardExtension, type DrawingBoardOptions } from '../components/extension/drawingBoard' import { type IndendOptions, IndentExtension, indentExtensionOptions } from '../components/extension/indent' +import TextAlign, { type TextAlignOptions } from '@tiptap/extension-text-align' export interface EditorKitOptions extends DefaultKitOptions { history?: false @@ -55,6 +56,7 @@ export interface EditorKitOptions extends DefaultKitOptions { drawingBoard?: DrawingBoardOptions | false mermaid?: MermaidOptions | false indent?: IndendOptions | false + textAlign?: TextAlignOptions | false mode?: 'full' | 'compact' note?: NoteOptions | false submit?: SubmitOptions | false @@ -269,6 +271,19 @@ async function buildEditorKit (): Promise> { ]) } + if (this.options.textAlign !== false) { + staticKitExtensions.push([ + 870, + TextAlign.configure( + this.options.textAlign ?? { + types: ['heading', 'paragraph'], + alignments: ['left', 'center', 'right'], + defaultAlignment: null + } + ) + ]) + } + if (this.options.toolbar !== false) { staticKitExtensions.push([ 900, diff --git a/services/github/pod-github/src/markdown/__tests__/textmodel.test.ts b/services/github/pod-github/src/markdown/__tests__/textmodel.test.ts index cc55c420e6..4f9b2e2107 100644 --- a/services/github/pod-github/src/markdown/__tests__/textmodel.test.ts +++ b/services/github/pod-github/src/markdown/__tests__/textmodel.test.ts @@ -660,6 +660,7 @@ A list of closed updated issues` content: [ { type: MarkupNodeType.paragraph, + attrs: { textAlign: null }, content: [ { type: MarkupNodeType.text, @@ -689,7 +690,7 @@ A list of closed updated issues` content: [ { type: MarkupNodeType.heading, - attrs: { level: 1 }, + attrs: { level: 1, textAlign: null }, content: [ { type: MarkupNodeType.text, @@ -699,6 +700,7 @@ A list of closed updated issues` }, { type: MarkupNodeType.paragraph, + attrs: { textAlign: null }, content: [ { text: '\n', @@ -708,7 +710,7 @@ A list of closed updated issues` }, { type: MarkupNodeType.heading, - attrs: { level: 2 }, + attrs: { level: 2, textAlign: null }, content: [ { type: MarkupNodeType.text,