Text alignment actions (#7604)

Signed-off-by: Victor Ilyushchenko <alt13ri@gmail.com>
This commit is contained in:
Victor Ilyushchenko 2025-01-09 05:35:48 +03:00 committed by GitHub
parent 9c78dc6ad3
commit 89618c70f4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 105 additions and 21 deletions

View File

@ -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:

View File

@ -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: {

View File

@ -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": {

View File

@ -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<ServerKitOptions>({
...taskListExtensions,
...fileExtensions,
...imageExtensions,
TextAlign.configure({
types: ['heading', 'paragraph'],
alignments: ['left', 'center', 'right'],
defaultAlignment: null
}),
TodoItemNode,
TodoListNode,
ReferenceNode,

View File

@ -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: '<p>hello</p>' })
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: '<p></p><p></p>' })
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)
})
})

View File

@ -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"
}
}

View File

@ -175,6 +175,7 @@
}
: false,
drawingBoard: false,
textAlign: false,
submit: supportSubmit ? { submit } : false,
toolbar: {
element: textToolbarElement,

View File

@ -60,7 +60,6 @@
const { command } = action.action
if ((editor.commands as any)[command] === undefined) {
console.error(`Command ${command} not found`)
continue
}
}

View File

@ -8,7 +8,7 @@ export const InlinePopupExtension: Extension<BubbleMenuOptions> = BubbleMenu.ext
pluginKey: 'inline-popup',
element: null,
tippyOptions: {
maxWidth: '38rem',
maxWidth: '46rem',
zIndex: 500,
appendTo: () => document.body
}

View File

@ -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<Extension<EditorKitOptions, any>> {
])
}
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,

View File

@ -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,