mirror of
https://github.com/ueberdosis/tiptap.git
synced 2024-11-27 14:59:27 +08:00
chore: reformat all files
This commit is contained in:
parent
6be1f7b15d
commit
eee0e834f0
@ -1,5 +1,5 @@
|
||||
---
|
||||
"@tiptap/extension-placeholder": major
|
||||
'@tiptap/extension-placeholder': major
|
||||
---
|
||||
|
||||
Officially remove the `considerAnyAsEmpty` which has not been used since version 2.5
|
||||
|
@ -1,5 +1,5 @@
|
||||
---
|
||||
"@tiptap/extension-list-keymap": patch
|
||||
'@tiptap/extension-list-keymap': patch
|
||||
---
|
||||
|
||||
Fix backspace behavior when selection is not collapsed
|
||||
|
@ -1,5 +1,5 @@
|
||||
---
|
||||
"@tiptap/core": patch
|
||||
'@tiptap/core': patch
|
||||
---
|
||||
|
||||
preserve existing node attributes when running setNode
|
||||
|
@ -1,59 +1,59 @@
|
||||
---
|
||||
"@tiptap/extension-collaboration-cursor": major
|
||||
"@tiptap/extension-code-block-lowlight": major
|
||||
"@tiptap/extension-character-count": major
|
||||
"@tiptap/extension-horizontal-rule": major
|
||||
"@tiptap/extension-collaboration": major
|
||||
"@tiptap/extension-floating-menu": major
|
||||
"@tiptap/extension-ordered-list": major
|
||||
"@tiptap/extension-table-header": major
|
||||
"@tiptap/extension-bubble-menu": major
|
||||
"@tiptap/extension-bullet-list": major
|
||||
"@tiptap/extension-font-family": major
|
||||
"@tiptap/extension-list-keymap": major
|
||||
"@tiptap/extension-placeholder": major
|
||||
"@tiptap/extension-superscript": major
|
||||
"@tiptap/extension-blockquote": major
|
||||
"@tiptap/extension-code-block": major
|
||||
"@tiptap/extension-dropcursor": major
|
||||
"@tiptap/extension-hard-break": major
|
||||
"@tiptap/extension-table-cell": major
|
||||
"@tiptap/extension-text-align": major
|
||||
"@tiptap/extension-text-style": major
|
||||
"@tiptap/extension-typography": major
|
||||
"@tiptap/extension-gapcursor": major
|
||||
"@tiptap/extension-highlight": major
|
||||
"@tiptap/extension-list-item": major
|
||||
"@tiptap/extension-paragraph": major
|
||||
"@tiptap/extension-subscript": major
|
||||
"@tiptap/extension-table-row": major
|
||||
"@tiptap/extension-task-item": major
|
||||
"@tiptap/extension-task-list": major
|
||||
"@tiptap/extension-underline": major
|
||||
"@tiptap/extension-document": major
|
||||
"@tiptap/extension-heading": major
|
||||
"@tiptap/extension-history": major
|
||||
"@tiptap/extension-mention": major
|
||||
"@tiptap/extension-youtube": major
|
||||
"@tiptap/extension-italic": major
|
||||
"@tiptap/extension-strike": major
|
||||
"@tiptap/extension-color": major
|
||||
"@tiptap/extension-focus": major
|
||||
"@tiptap/extension-image": major
|
||||
"@tiptap/extension-table": major
|
||||
"@tiptap/extension-bold": major
|
||||
"@tiptap/extension-code": major
|
||||
"@tiptap/extension-link": major
|
||||
"@tiptap/extension-text": major
|
||||
"@tiptap/starter-kit": major
|
||||
"@tiptap/suggestion": major
|
||||
"@tiptap/react": major
|
||||
"@tiptap/vue-2": major
|
||||
"@tiptap/vue-3": major
|
||||
"@tiptap/core": major
|
||||
"@tiptap/html": major
|
||||
"@tiptap/pm": major
|
||||
"tiptap-demos": major
|
||||
'@tiptap/extension-collaboration-cursor': major
|
||||
'@tiptap/extension-code-block-lowlight': major
|
||||
'@tiptap/extension-character-count': major
|
||||
'@tiptap/extension-horizontal-rule': major
|
||||
'@tiptap/extension-collaboration': major
|
||||
'@tiptap/extension-floating-menu': major
|
||||
'@tiptap/extension-ordered-list': major
|
||||
'@tiptap/extension-table-header': major
|
||||
'@tiptap/extension-bubble-menu': major
|
||||
'@tiptap/extension-bullet-list': major
|
||||
'@tiptap/extension-font-family': major
|
||||
'@tiptap/extension-list-keymap': major
|
||||
'@tiptap/extension-placeholder': major
|
||||
'@tiptap/extension-superscript': major
|
||||
'@tiptap/extension-blockquote': major
|
||||
'@tiptap/extension-code-block': major
|
||||
'@tiptap/extension-dropcursor': major
|
||||
'@tiptap/extension-hard-break': major
|
||||
'@tiptap/extension-table-cell': major
|
||||
'@tiptap/extension-text-align': major
|
||||
'@tiptap/extension-text-style': major
|
||||
'@tiptap/extension-typography': major
|
||||
'@tiptap/extension-gapcursor': major
|
||||
'@tiptap/extension-highlight': major
|
||||
'@tiptap/extension-list-item': major
|
||||
'@tiptap/extension-paragraph': major
|
||||
'@tiptap/extension-subscript': major
|
||||
'@tiptap/extension-table-row': major
|
||||
'@tiptap/extension-task-item': major
|
||||
'@tiptap/extension-task-list': major
|
||||
'@tiptap/extension-underline': major
|
||||
'@tiptap/extension-document': major
|
||||
'@tiptap/extension-heading': major
|
||||
'@tiptap/extension-history': major
|
||||
'@tiptap/extension-mention': major
|
||||
'@tiptap/extension-youtube': major
|
||||
'@tiptap/extension-italic': major
|
||||
'@tiptap/extension-strike': major
|
||||
'@tiptap/extension-color': major
|
||||
'@tiptap/extension-focus': major
|
||||
'@tiptap/extension-image': major
|
||||
'@tiptap/extension-table': major
|
||||
'@tiptap/extension-bold': major
|
||||
'@tiptap/extension-code': major
|
||||
'@tiptap/extension-link': major
|
||||
'@tiptap/extension-text': major
|
||||
'@tiptap/starter-kit': major
|
||||
'@tiptap/suggestion': major
|
||||
'@tiptap/react': major
|
||||
'@tiptap/vue-2': major
|
||||
'@tiptap/vue-3': major
|
||||
'@tiptap/core': major
|
||||
'@tiptap/html': major
|
||||
'@tiptap/pm': major
|
||||
'tiptap-demos': major
|
||||
---
|
||||
|
||||
We are now building packages with tsup which does not support UMD builds, please repackage if you require UMD builds
|
||||
|
@ -1,11 +1,11 @@
|
||||
---
|
||||
"@tiptap/extension-floating-menu": major
|
||||
"@tiptap/extension-bubble-menu": major
|
||||
"@tiptap/extension-mention": major
|
||||
"@tiptap/suggestion": major
|
||||
"@tiptap/react": major
|
||||
"@tiptap/vue-2": major
|
||||
"@tiptap/vue-3": major
|
||||
'@tiptap/extension-floating-menu': major
|
||||
'@tiptap/extension-bubble-menu': major
|
||||
'@tiptap/extension-mention': major
|
||||
'@tiptap/suggestion': major
|
||||
'@tiptap/react': major
|
||||
'@tiptap/vue-2': major
|
||||
'@tiptap/vue-3': major
|
||||
---
|
||||
|
||||
Removed tippy.js and replaced it with [Floating UI](https://floating-ui.com/) - a newer, more lightweight and customizable floating element library.
|
||||
|
@ -1,5 +1,5 @@
|
||||
---
|
||||
"@tiptap/vue-3": patch
|
||||
'@tiptap/vue-3': patch
|
||||
---
|
||||
|
||||
Fix editor destruction before transition end if editor is nested
|
||||
|
@ -1,5 +1,5 @@
|
||||
---
|
||||
"@tiptap/extension-bubble-menu": patch
|
||||
'@tiptap/extension-bubble-menu': patch
|
||||
---
|
||||
|
||||
Add `element: HTMLElement` to `shouldShow` options within the BubbleMenu options.
|
||||
|
@ -1,5 +1,5 @@
|
||||
---
|
||||
"@tiptap/core": minor
|
||||
'@tiptap/core': minor
|
||||
---
|
||||
|
||||
Previously, only a json representation of the node could be inserted into the editor. This change allows for the insertion of Prosemirror `Node`s and `Fragment`s directly into the editor through the `insertContentAt`, `setContent` and `insertContent` commands.
|
||||
|
@ -1,5 +1,5 @@
|
||||
---
|
||||
"@tiptap/core": patch
|
||||
'@tiptap/core': patch
|
||||
---
|
||||
|
||||
Addresses a bug with `insertContentAt`'s `simulatedPasteRules` option where it could only accept text and not Prosemirror `Node` and `Content`
|
||||
|
@ -1,5 +1,5 @@
|
||||
---
|
||||
"@tiptap/core": major
|
||||
'@tiptap/core': major
|
||||
---
|
||||
|
||||
Fix `getPos` type in `NodeViewRendererProps` to potentially be `undefined`
|
||||
|
@ -1,5 +1,5 @@
|
||||
---
|
||||
"@tiptap/core": patch
|
||||
'@tiptap/core': patch
|
||||
---
|
||||
|
||||
Updates the types of `addOptions` and `addStorage` to have the parent be possibly undefined which is the most accurate typing
|
||||
|
@ -1,5 +1,5 @@
|
||||
---
|
||||
"@tiptap/core": major
|
||||
'@tiptap/core': major
|
||||
---
|
||||
|
||||
`insertContent` and `insertContentAt` commands should not split text nodes like paragraphs into multiple nodes when the inserted content is at the beginning of the text to avoid empty nodes being created
|
||||
|
@ -1,5 +1,5 @@
|
||||
---
|
||||
"@tiptap/core": patch
|
||||
'@tiptap/core': patch
|
||||
---
|
||||
|
||||
feat: add `once` to EventEmitters
|
||||
|
@ -1,5 +1,5 @@
|
||||
---
|
||||
"@tiptap/vue-2": patch
|
||||
'@tiptap/vue-2': patch
|
||||
---
|
||||
|
||||
Pin vue-ts-types to a working version for vue-2
|
||||
|
@ -1,5 +1,5 @@
|
||||
---
|
||||
"@tiptap/react": minor
|
||||
'@tiptap/react': minor
|
||||
---
|
||||
|
||||
Throw an error in development mode if immediatelyRender is not set in SSR mode
|
||||
|
@ -1,5 +1,5 @@
|
||||
---
|
||||
"@tiptap/react": patch
|
||||
'@tiptap/react': patch
|
||||
---
|
||||
|
||||
React 19 is now allowed as a peer dep, we did not have to make any changes for React 19
|
||||
|
@ -1,5 +1,5 @@
|
||||
---
|
||||
"@tiptap/extension-mention": patch
|
||||
'@tiptap/extension-mention': patch
|
||||
---
|
||||
|
||||
add zero-width space to resolve cursor selection issue
|
||||
|
@ -1,6 +1,6 @@
|
||||
---
|
||||
"@tiptap/core": patch
|
||||
"@tiptap/extension-hard-break": patch
|
||||
'@tiptap/core': patch
|
||||
'@tiptap/extension-hard-break': patch
|
||||
---
|
||||
|
||||
Add Node `linebreakReplacement` support and enable on hard-break nodes
|
||||
|
@ -1,5 +1,5 @@
|
||||
---
|
||||
"@tiptap/core": patch
|
||||
'@tiptap/core': patch
|
||||
---
|
||||
|
||||
Improve handling of selections with `updateAttributes`. Should no longer modify parent nodes of the same type.
|
||||
|
@ -1,5 +1,5 @@
|
||||
---
|
||||
"@tiptap/extension-table": patch
|
||||
'@tiptap/extension-table': patch
|
||||
---
|
||||
|
||||
enforce cellMinWidth even on column not resized by the user, fixes #5435
|
||||
|
@ -1,5 +1,5 @@
|
||||
---
|
||||
"@tiptap/starter-kit": major
|
||||
'@tiptap/starter-kit': major
|
||||
---
|
||||
|
||||
We have now added the Link, ListKeymap, and Underline extensions to the starter kit for a smoother onboarding experience
|
||||
|
@ -1,6 +1,6 @@
|
||||
---
|
||||
"@tiptap/extension-link": patch
|
||||
"tiptap-demos": patch
|
||||
'@tiptap/extension-link': patch
|
||||
'tiptap-demos': patch
|
||||
---
|
||||
|
||||
The link extension's `validate` option now applies to both auto-linking and XSS mitigation. While, the new `shouldAutoLink` option is used to disable auto linking on an otherwise valid url.
|
||||
|
@ -1,4 +1,4 @@
|
||||
title: "Community Extension: "
|
||||
title: 'Community Extension: '
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
@ -9,7 +9,7 @@ body:
|
||||
attributes:
|
||||
label: Description
|
||||
description: Please describe how your extension works and what it does.
|
||||
placeholder: "My extension does …"
|
||||
placeholder: 'My extension does …'
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
@ -17,7 +17,7 @@ body:
|
||||
attributes:
|
||||
label: Installation
|
||||
description: Please describe how users can install your extension.
|
||||
placeholder: "npm install …"
|
||||
placeholder: 'npm install …'
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
@ -25,7 +25,7 @@ body:
|
||||
attributes:
|
||||
label: Usage
|
||||
description: Please describe how users can use your extension in their editor.
|
||||
placeholder: "To use my extension you have to …"
|
||||
placeholder: 'To use my extension you have to …'
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
@ -34,11 +34,11 @@ body:
|
||||
label: Type
|
||||
description: Please select the type of this extension.
|
||||
options:
|
||||
- "Node"
|
||||
- "Mark"
|
||||
- "Prosemirror plugin"
|
||||
- "Package or Kit"
|
||||
- "Other"
|
||||
- 'Node'
|
||||
- 'Mark'
|
||||
- 'Prosemirror plugin'
|
||||
- 'Package or Kit'
|
||||
- 'Other'
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
@ -46,6 +46,6 @@ body:
|
||||
attributes:
|
||||
label: Other
|
||||
description: Feel free to add any other information about your extension.
|
||||
placeholder: "I hope you like …"
|
||||
placeholder: 'I hope you like …'
|
||||
validations:
|
||||
required: false
|
||||
|
16
.github/DISCUSSION_TEMPLATE/feature-requests.yml
vendored
16
.github/DISCUSSION_TEMPLATE/feature-requests.yml
vendored
@ -1,6 +1,6 @@
|
||||
title: "Feature Request: "
|
||||
title: 'Feature Request: '
|
||||
labels:
|
||||
- "Type: Feature Request"
|
||||
- 'Type: Feature Request'
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
@ -11,7 +11,7 @@ body:
|
||||
attributes:
|
||||
label: Description
|
||||
description: Please describe the feature you would like to see in Tiptap.
|
||||
placeholder: "I wish there was an extension for …"
|
||||
placeholder: 'I wish there was an extension for …'
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
@ -19,7 +19,7 @@ body:
|
||||
attributes:
|
||||
label: Use Case
|
||||
description: Please describe the use case for this feature.
|
||||
placeholder: "I want to use this feature for …"
|
||||
placeholder: 'I want to use this feature for …'
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
@ -28,9 +28,9 @@ body:
|
||||
label: Type
|
||||
description: Please select the type of this feature.
|
||||
options:
|
||||
- "New extension"
|
||||
- "New feature"
|
||||
- "New Tiptap API"
|
||||
- "Other"
|
||||
- 'New extension'
|
||||
- 'New feature'
|
||||
- 'New Tiptap API'
|
||||
- 'Other'
|
||||
validations:
|
||||
required: true
|
||||
|
22
.github/DISCUSSION_TEMPLATE/showcase.yml
vendored
22
.github/DISCUSSION_TEMPLATE/showcase.yml
vendored
@ -1,4 +1,4 @@
|
||||
title: "Community Extension: "
|
||||
title: 'Community Extension: '
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
@ -9,7 +9,7 @@ body:
|
||||
attributes:
|
||||
label: Description
|
||||
description: Please describe what your project is about
|
||||
placeholder: "My project is about …"
|
||||
placeholder: 'My project is about …'
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
@ -17,7 +17,7 @@ body:
|
||||
attributes:
|
||||
label: URL
|
||||
description: If possible share the URL of your project.
|
||||
placeholder: "https://example.com"
|
||||
placeholder: 'https://example.com'
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
@ -25,7 +25,7 @@ body:
|
||||
attributes:
|
||||
label: About
|
||||
description: Feel free to talk about how you used Tiptap in your project, what you liked about it, what you didn't like about it, and what you would like to see in the future.
|
||||
placeholder: "If used Tiptap to …"
|
||||
placeholder: 'If used Tiptap to …'
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
@ -34,12 +34,12 @@ body:
|
||||
label: Type
|
||||
description: Please select the type of your project.
|
||||
options:
|
||||
- "Chat Application"
|
||||
- "Commenting Application"
|
||||
- "Content Management System"
|
||||
- "Document Editor"
|
||||
- "Document Editor with Collaboration"
|
||||
- "Other"
|
||||
- 'Chat Application'
|
||||
- 'Commenting Application'
|
||||
- 'Content Management System'
|
||||
- 'Document Editor'
|
||||
- 'Document Editor with Collaboration'
|
||||
- 'Other'
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
@ -47,6 +47,6 @@ body:
|
||||
attributes:
|
||||
label: Other
|
||||
description: Feel free to add any other information about your project.
|
||||
placeholder: "I hope you like …"
|
||||
placeholder: 'I hope you like …'
|
||||
validations:
|
||||
required: false
|
||||
|
20
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
20
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@ -1,14 +1,14 @@
|
||||
name: Bug Report
|
||||
title: "[Bug]: "
|
||||
title: '[Bug]: '
|
||||
description: Found a bug in the editor core or one of the extensions? Report it here to help us improve.
|
||||
labels:
|
||||
- "Type: Bug"
|
||||
- "Category: Open Source"
|
||||
- "Status: New"
|
||||
- 'Type: Bug'
|
||||
- 'Category: Open Source'
|
||||
- 'Status: New'
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: "### Please provide details to help us diagnose the bug."
|
||||
value: '### Please provide details to help us diagnose the bug.'
|
||||
- type: input
|
||||
id: packages
|
||||
attributes:
|
||||
@ -30,7 +30,7 @@ body:
|
||||
attributes:
|
||||
label: Bug Description
|
||||
description: Provide a clear and concise description of what the bug is.
|
||||
placeholder: "The issue occurs when..."
|
||||
placeholder: 'The issue occurs when...'
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
@ -59,7 +59,7 @@ body:
|
||||
id: sandbox
|
||||
attributes:
|
||||
label: Code Example URL
|
||||
description: "Link a CodeSandbox, Stackblitz, GitHub repository, or similar to help us reproduce the issue faster."
|
||||
description: 'Link a CodeSandbox, Stackblitz, GitHub repository, or similar to help us reproduce the issue faster.'
|
||||
placeholder: https://codesandbox.io/s/example
|
||||
validations:
|
||||
required: false
|
||||
@ -74,14 +74,14 @@ body:
|
||||
id: context
|
||||
attributes:
|
||||
label: Additional Context (Optional)
|
||||
description: "Add any other context about the problem here, such as screenshots or videos."
|
||||
description: 'Add any other context about the problem here, such as screenshots or videos.'
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Dependency Updates
|
||||
description: "Have you updated your dependencies? This can often resolve issues."
|
||||
description: 'Have you updated your dependencies? This can often resolve issues.'
|
||||
options:
|
||||
- label: Yes, I've updated all my dependencies.
|
||||
required: true
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: "Thank you for helping us improve our open-source projects by reporting this issue!"
|
||||
value: 'Thank you for helping us improve our open-source projects by reporting this issue!'
|
||||
|
24
.github/ISSUE_TEMPLATE/bug_report_pro.yml
vendored
24
.github/ISSUE_TEMPLATE/bug_report_pro.yml
vendored
@ -1,14 +1,14 @@
|
||||
name: Bug Report (Tiptap Pro)
|
||||
title: "[PRO]: "
|
||||
title: '[PRO]: '
|
||||
description: If you've encountered a bug with Tiptap Pro features, please report it here.
|
||||
labels:
|
||||
- "Type: Bug"
|
||||
- "Category: Pro"
|
||||
- "Status: New"
|
||||
- 'Type: Bug'
|
||||
- 'Category: Pro'
|
||||
- 'Status: New'
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: "### Please ensure this issue is for Tiptap Pro features only. Provide as much detail as possible to help us identify the issue quickly."
|
||||
value: '### Please ensure this issue is for Tiptap Pro features only. Provide as much detail as possible to help us identify the issue quickly.'
|
||||
- type: input
|
||||
id: packages
|
||||
attributes:
|
||||
@ -30,7 +30,7 @@ body:
|
||||
attributes:
|
||||
label: Description of the Bug
|
||||
description: Provide a clear and concise description of what the bug is.
|
||||
placeholder: "The issue occurs when..."
|
||||
placeholder: 'The issue occurs when...'
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
@ -48,10 +48,10 @@ body:
|
||||
required: true
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: "### Helpful Code Examples"
|
||||
value: '### Helpful Code Examples'
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: "Providing a CodeSandbox link is crucial for diagnosing issues faster. Below are templates you might use:"
|
||||
value: 'Providing a CodeSandbox link is crucial for diagnosing issues faster. Below are templates you might use:'
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
@ -63,7 +63,7 @@ body:
|
||||
id: sandbox
|
||||
attributes:
|
||||
label: Code Example (Preferred)
|
||||
description: "Provide a link to a CodeSandbox or other code repository to help us reproduce the issue."
|
||||
description: 'Provide a link to a CodeSandbox or other code repository to help us reproduce the issue.'
|
||||
placeholder: https://codesandbox.io/s/example
|
||||
validations:
|
||||
required: false
|
||||
@ -78,14 +78,14 @@ body:
|
||||
id: context
|
||||
attributes:
|
||||
label: Additional Context (Optional)
|
||||
description: "Add any other context about the problem here, like screenshots or videos."
|
||||
description: 'Add any other context about the problem here, like screenshots or videos.'
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Dependency Updates
|
||||
description: "Have you updated your dependencies? It can often resolve issues."
|
||||
description: 'Have you updated your dependencies? It can often resolve issues.'
|
||||
options:
|
||||
- label: Yes, I've updated all my dependencies.
|
||||
required: true
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: "Thank you for contributing to Tiptap Pro by reporting this issue!"
|
||||
value: 'Thank you for contributing to Tiptap Pro by reporting this issue!'
|
||||
|
16
.github/ISSUE_TEMPLATE/documentation.yml
vendored
16
.github/ISSUE_TEMPLATE/documentation.yml
vendored
@ -1,10 +1,10 @@
|
||||
name: Documentation feedback
|
||||
description: Share what we need to explain better.
|
||||
title: "[Documentation]: "
|
||||
title: '[Documentation]: '
|
||||
labels:
|
||||
- "Type: Documentation"
|
||||
- "Category: Open Source"
|
||||
- "Status: New"
|
||||
- 'Type: Documentation'
|
||||
- 'Category: Open Source'
|
||||
- 'Status: New'
|
||||
body:
|
||||
- type: input
|
||||
id: url
|
||||
@ -17,28 +17,28 @@ body:
|
||||
id: part-of-the-documentation
|
||||
attributes:
|
||||
label: What part of the documentation needs improvement?
|
||||
placeholder: "I’ve read the following page of the documentation …"
|
||||
placeholder: 'I’ve read the following page of the documentation …'
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: good-parts
|
||||
attributes:
|
||||
label: What is helpful about that part?
|
||||
placeholder: "I think this part is really good: …"
|
||||
placeholder: 'I think this part is really good: …'
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: bad-parts
|
||||
attributes:
|
||||
label: What is hard to understand, missing or misleading?
|
||||
placeholder: "But you really need to improve …"
|
||||
placeholder: 'But you really need to improve …'
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: context
|
||||
attributes:
|
||||
label: Anything to add? (optional)
|
||||
description: "Add any other context or screenshots here."
|
||||
description: 'Add any other context or screenshots here.'
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
|
2
.github/dependabot.yml
vendored
2
.github/dependabot.yml
vendored
@ -3,7 +3,6 @@
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
|
||||
- package-ecosystem: 'github-actions'
|
||||
directory: '/'
|
||||
open-pull-requests-limit: 10
|
||||
@ -12,4 +11,3 @@ updates:
|
||||
day: 'monday'
|
||||
reviewers:
|
||||
- 'bdbch'
|
||||
|
||||
|
7
.github/pull_request_template.md
vendored
7
.github/pull_request_template.md
vendored
@ -1,19 +1,25 @@
|
||||
## Changes Overview
|
||||
|
||||
<!-- Briefly describe your changes. -->
|
||||
|
||||
## Implementation Approach
|
||||
|
||||
<!-- Describe your approach to implementing these changes. Keep it concise. -->
|
||||
|
||||
## Testing Done
|
||||
|
||||
<!-- Explain how you tested these changes. Link to test scenarios or specs if relevant. -->
|
||||
|
||||
## Verification Steps
|
||||
|
||||
<!-- Describe steps reviewers can take to verify the functionality of your changes. -->
|
||||
|
||||
## Additional Notes
|
||||
|
||||
<!-- Add any other notes or screenshots about the PR here. -->
|
||||
|
||||
## Checklist
|
||||
|
||||
- [ ] I have created a [changeset](https://github.com/changesets/changesets) for this PR if necessary.
|
||||
- [ ] My changes do not break the library.
|
||||
- [ ] I have added tests where applicable.
|
||||
@ -21,4 +27,5 @@
|
||||
- [ ] I have fixed any lint issues.
|
||||
|
||||
## Related Issues
|
||||
|
||||
<!-- Link any related issues here -->
|
||||
|
16
.github/workflows/build.yml
vendored
16
.github/workflows/build.yml
vendored
@ -73,16 +73,16 @@ jobs:
|
||||
matrix:
|
||||
node-version: [20]
|
||||
test-spec:
|
||||
- { name: "Integration", spec: "./tests/cypress/integration/**/*.spec.{js,ts}" }
|
||||
- { name: 'Integration', spec: './tests/cypress/integration/**/*.spec.{js,ts}' }
|
||||
#- { name: "Demos/Commands", spec: "./demos/src/Commands/**/*.spec.{js,ts}" }
|
||||
- { name: "Demos/Examples", spec: "./demos/src/Examples/**/*.spec.{js,ts}" }
|
||||
- { name: "Demos/Experiments", spec: "./demos/src/Experiments/**/*.spec.{js,ts}" }
|
||||
- { name: "Demos/Extensions", spec: "./demos/src/Extensions/**/*.spec.{js,ts}" }
|
||||
- { name: "Demos/GuideContent", spec: "./demos/src/GuideContent/**/*.spec.{js,ts}" }
|
||||
- { name: "Demos/GuideGettingStarted", spec: "./demos/src/GuideGettingStarted/**/*.spec.{js,ts}" }
|
||||
- { name: 'Demos/Examples', spec: './demos/src/Examples/**/*.spec.{js,ts}' }
|
||||
- { name: 'Demos/Experiments', spec: './demos/src/Experiments/**/*.spec.{js,ts}' }
|
||||
- { name: 'Demos/Extensions', spec: './demos/src/Extensions/**/*.spec.{js,ts}' }
|
||||
- { name: 'Demos/GuideContent', spec: './demos/src/GuideContent/**/*.spec.{js,ts}' }
|
||||
- { name: 'Demos/GuideGettingStarted', spec: './demos/src/GuideGettingStarted/**/*.spec.{js,ts}' }
|
||||
#- { name: "Demos/GuideNodeViews", "./demos/src/GuideNodeViews/**/*.spec.{js,ts}" }
|
||||
- { name: "Demos/Marks", spec: "./demos/src/Marks/**/*.spec.{js,ts}" }
|
||||
- { name: "Demos/Nodes", spec: "./demos/src/Nodes/**/*.spec.{js,ts}" }
|
||||
- { name: 'Demos/Marks', spec: './demos/src/Marks/**/*.spec.{js,ts}' }
|
||||
- { name: 'Demos/Nodes', spec: './demos/src/Nodes/**/*.spec.{js,ts}' }
|
||||
#- { name: "Demos/Overview", spec: "./demos/src/Overview/**/*.spec.{js,ts}" }
|
||||
|
||||
steps:
|
||||
|
2
.github/workflows/publish.yml
vendored
2
.github/workflows/publish.yml
vendored
@ -34,7 +34,7 @@ jobs:
|
||||
- name: Setup Node ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
node-version: ${{ matrix.node-version }}
|
||||
registry-url: 'https://registry.npmjs.org/'
|
||||
|
||||
- name: Load cached dependencies
|
||||
|
1301
CHANGELOG.md
1301
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
15
README.md
15
README.md
@ -1,4 +1,5 @@
|
||||
# Tiptap Editor
|
||||
|
||||
The Tiptap Editor is a headless, framework-agnostic rich text editor that's customizable and extendable through extensions. Its headless nature means it comes without a set user interface, offering full design freedom (for a jumpstart, see linked [UI templates](#examples-codesandbox-and-ui-templates) below). Tiptap is based on the highly reliable [ProseMirror](https://github.com/ProseMirror/prosemirror) library.
|
||||
|
||||
Tiptap Editor is complemented by the collaboration open-source backend [Hocuspocus](https://github.com/ueberdosis/hocuspocus). Both the Editor and Hocuspocus form the foundation of the [Tiptap Suite](https://tiptap.dev/).
|
||||
@ -13,12 +14,12 @@ Tiptap Editor is complemented by the collaboration open-source backend [Hocuspoc
|
||||
### How does the Tiptap Editor work?
|
||||
|
||||
- **Headless Framework:** Tiptap does not rely on a user interface. So there is no need for class overrides or code hacks. If you do need an example UI feel free to browse our [UI templates](#examples-codesandbox-and-ui-templates) linked below.
|
||||
- **Framework-agnostic:** The Tiptap Editor is designed to work across different frontend frameworks. This means whether you're using Vue, React, or plain JavaScript, Tiptap integrates without compatibility issues.
|
||||
- **Framework-agnostic:** The Tiptap Editor is designed to work across different frontend frameworks. This means whether you're using Vue, React, or plain JavaScript, Tiptap integrates without compatibility issues.
|
||||
- **Extension based:** Extensions in Tiptap allow for a tailored editing experience, from simple text styling to advanced features like drag-and-drop block editing. You have the option to choose from over 100 extensions available in the [documentation](https://tiptap.dev/docs/editor/extensions) and [community](https://github.com/ueberdosis/awesome-tiptap/#community-extensions) to enhance your editor's functionality.
|
||||
- **Customize your UX:** The editor was built to give you control to define your own [extensions](https://tiptap.dev/docs/editor/guide/custom-extensions) and [nodes](https://tiptap.dev/docs/editor/api/nodes).
|
||||
|
||||
|
||||
### Editor Pro Extensions
|
||||
|
||||
The **Pro Extensions** are a set of advanced functionalities that enhance the capabilities of the Tiptap Editor. They are additional features that can be integrated into the base editor to provide more sophisticated editing options.
|
||||
|
||||
Key functionalities include collaborative editing, which allows multiple users to edit documents simultaneously, drag-and-drop file management for easier handling of documents and media, and unique node ID assignment. Review the docs right [here](https://tiptap.dev/docs/editor/extensions).
|
||||
@ -26,28 +27,35 @@ Key functionalities include collaborative editing, which allows multiple users t
|
||||
Pro Extensions are free with a [Tiptap account](https://cloud.tiptap.dev/pro-extensions). Once signed up, review the guide in your account.
|
||||
|
||||
### Make your editor collaborative
|
||||
|
||||
Interested in collaborative editing? Check out our open-source package [Hocuspocus](https://github.com/ueberdosis/hocuspocus) - a collaboration backend built around the CRDT power of [Yjs](https://github.com/yjs/yjs). Hocuspocus serves as the backbone for the [Tiptap Suite](https://tiptap.dev/).
|
||||
|
||||
## Documentation
|
||||
|
||||
For more detailed information, make sure to check out our [documentation](https://tiptap.dev/docs/editor/installation). If you encounter any problems or have suggestions for our system, please open an issue.
|
||||
|
||||
### Examples, CodeSandbox and UI Templates
|
||||
|
||||
Have a look at the [examples to see Tiptap in action](https://tiptap.dev/examples) or review and fork our codesandboxes.
|
||||
|
||||
- [Basic example of the Tiptap editor.](https://codesandbox.io/p/devbox/editor-9x9dkd?embed=1&file=%2Fsrc%2FApp.js)
|
||||
- [Collaboration ready Tiptap CodeSandbox](https://codesandbox.io/p/devbox/collaboration-4stk94)
|
||||
- React notion-like block editor template: [Demo](https://templates.tiptap.dev/)
|
||||
|
||||
## About Tiptap
|
||||
|
||||
Tiptap is a collection of developer components based on open-source technology, forming the basis of our advanced, paid features. It includes the open-source editor component, collaboration features, Content AI, and Tiptap Cloud. We are developing open-source products that also shape our paid features. We're committed to improving both, ensuring quality and reliability in every update.
|
||||
|
||||
For more details, visit the Tiptap [documentation](https://tiptap.dev/docs/editor/introduction) or [website](https://tiptap.dev/).
|
||||
|
||||
### Community
|
||||
|
||||
For help, discussion about best practices, or any other conversation that would benefit from being searchable:
|
||||
|
||||
[Discuss Tiptap on GitHub](https://github.com/ueberdosis/tiptap/discussions)
|
||||
|
||||
### Sponsors 💖
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td align="center">
|
||||
@ -102,9 +110,11 @@ For help, discussion about best practices, or any other conversation that would
|
||||
[iFixit](https://www.ifixit.com/), [ApostropheCMS](https://apostrophecms.com/), [Novadiscovery](http://www.novadiscovery.com/), [Omics Data Automation](https://www.omicsautomation.com), [Flow Mobile](https://www.flowmobile.app/), [DocIQ](https://www.dociq.io/) and [hundreds of awesome individuals](https://github.com/sponsors/ueberdosis).
|
||||
|
||||
### Contributing
|
||||
|
||||
Feel like adding some magic of your own to Tiptap Editor Core? We welcome contributions! Please see our [CONTRIBUTING](CONTRIBUTING.md) guidelines for how to get started.
|
||||
|
||||
### Contributors
|
||||
|
||||
[Sam Willis](https://github.com/samwillis),
|
||||
[Brian Hung](https://github.com/BrianHung),
|
||||
[Dirk Holtwick](https://github.com/holtwick),
|
||||
@ -118,4 +128,5 @@ Feel like adding some magic of your own to Tiptap Editor Core? We welcome contri
|
||||
[Gregor](https://github.com/gambolputty) and [many more](../../contributors).
|
||||
|
||||
## License
|
||||
|
||||
The MIT License (MIT). Please see [License File](LICENSE.md) for more information.
|
||||
|
@ -1,10 +1,4 @@
|
||||
module.exports = {
|
||||
presets: [
|
||||
'@babel/preset-env',
|
||||
'@babel/preset-react',
|
||||
],
|
||||
plugins: [
|
||||
'@babel/plugin-proposal-nullish-coalescing-operator',
|
||||
'@babel/plugin-proposal-optional-chaining',
|
||||
],
|
||||
presets: ['@babel/preset-env', '@babel/preset-react'],
|
||||
plugins: ['@babel/plugin-proposal-nullish-coalescing-operator', '@babel/plugin-proposal-optional-chaining'],
|
||||
}
|
||||
|
@ -1,10 +1,9 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta http-equiv="refresh" content="0; url=/preview/" />
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
<body></body>
|
||||
</html>
|
||||
|
@ -6,23 +6,18 @@
|
||||
:key="index"
|
||||
@click="setTab(language.name)"
|
||||
class="px-3 py-2 text-sm text-white leading-[125%] font-semibold rounded-[0.625rem] transition-all"
|
||||
:class="[currentTab === language.name
|
||||
? 'opacity-100 bg-[#1C1917]'
|
||||
: 'opacity-50 bg-transparent hover:opacity-100 hover:bg-[#1C1917]'
|
||||
:class="[
|
||||
currentTab === language.name
|
||||
? 'opacity-100 bg-[#1C1917]'
|
||||
: 'opacity-50 bg-transparent hover:opacity-100 hover:bg-[#1C1917]',
|
||||
]"
|
||||
>
|
||||
{{ language.name }}
|
||||
</button>
|
||||
</div>
|
||||
<div class="overflow-hidden">
|
||||
<div
|
||||
class="bg-white"
|
||||
:class="[hidePreview ? 'hidden' : '']"
|
||||
>
|
||||
<demo-frame
|
||||
:src="currentIframeUrl"
|
||||
:key="currentIframeUrl"
|
||||
/>
|
||||
<div class="bg-white" :class="[hidePreview ? 'hidden' : '']">
|
||||
<demo-frame :src="currentIframeUrl" :key="currentIframeUrl" />
|
||||
</div>
|
||||
|
||||
<div class="text-white bg-black" v-if="!hideSource && currentFile">
|
||||
@ -30,9 +25,10 @@
|
||||
<div class="flex flex-auto px-4 border-b-2 border-gray-800">
|
||||
<button
|
||||
class="inline-flex relative mr-4 py-2 pb-[calc(0.3rem + 2px)] mb-[-2px] border-b-2 border-transparent font-mono text-sm whitespace-nowrap"
|
||||
:class="[!showDebug && currentFile.content === file.content
|
||||
? 'text-white border-white font-bold'
|
||||
: 'text-gray-400'
|
||||
:class="[
|
||||
!showDebug && currentFile.content === file.content
|
||||
? 'text-white border-white font-bold'
|
||||
: 'text-gray-400',
|
||||
]"
|
||||
v-for="(file, index) in source"
|
||||
:key="index"
|
||||
@ -44,10 +40,7 @@
|
||||
<button
|
||||
v-if="debugJSON"
|
||||
class="inline-flex relative py-2 pb-[calc(0.3rem + 2px)] mb-[-2px] border-b-2 border-transparent font-mono text-sm ml-auto"
|
||||
:class="[showDebug
|
||||
? 'text-white border-white font-bold'
|
||||
: 'text-gray-400'
|
||||
]"
|
||||
:class="[showDebug ? 'text-white border-white font-bold' : 'text-gray-400']"
|
||||
@click="showDebug = !showDebug"
|
||||
>
|
||||
Inspect
|
||||
@ -67,9 +60,7 @@
|
||||
<a class="flex-shrink min-w-0 overflow-hidden overflow-ellipsis whitespace-nowrap" :href="currentIframeUrl">
|
||||
{{ name }}/{{ currentTab }}
|
||||
</a>
|
||||
<a class="pl-4 whitespace-nowrap" :href="githubUrl" target="_blank">
|
||||
Edit on GitHub →
|
||||
</a>
|
||||
<a class="pl-4 whitespace-nowrap" :href="githubUrl" target="_blank"> Edit on GitHub → </a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -128,9 +119,7 @@ export default {
|
||||
},
|
||||
|
||||
query() {
|
||||
return Object.fromEntries(Object
|
||||
.entries(this.$route.query)
|
||||
.map(([key, value]) => [key, this.fromString(value)]))
|
||||
return Object.fromEntries(Object.entries(this.$route.query).map(([key, value]) => [key, this.fromString(value)]))
|
||||
},
|
||||
|
||||
inline() {
|
||||
@ -220,9 +209,10 @@ export default {
|
||||
|
||||
mounted() {
|
||||
// TODO: load language from url params
|
||||
const intitialTab = localStorage.tab && this.tabs.some(tab => tab.name === localStorage.tab)
|
||||
? localStorage.tab
|
||||
: this.sortedTabs[0]?.name
|
||||
const intitialTab =
|
||||
localStorage.tab && this.tabs.some(tab => tab.name === localStorage.tab)
|
||||
? localStorage.tab
|
||||
: this.sortedTabs[0]?.name
|
||||
|
||||
this.setTab(intitialTab, false)
|
||||
|
||||
|
@ -1,21 +1,16 @@
|
||||
<template>
|
||||
<div class="flex flex-col relative min-h-[5rem]">
|
||||
<div class="absolute top-0 left-0 w-full h-full flex justify-center items-center pointer-events-none" v-if="isLoading">
|
||||
<svg
|
||||
class="animate-spin -ml-1 mr-3 h-5 w-5"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<circle
|
||||
class="opacity-25"
|
||||
cx="12"
|
||||
cy="12"
|
||||
r="10"
|
||||
stroke="currentColor"
|
||||
stroke-width="4"
|
||||
<div
|
||||
class="absolute top-0 left-0 w-full h-full flex justify-center items-center pointer-events-none"
|
||||
v-if="isLoading"
|
||||
>
|
||||
<svg class="animate-spin -ml-1 mr-3 h-5 w-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" />
|
||||
<path
|
||||
class="opacity-75"
|
||||
fill="currentColor"
|
||||
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
||||
/>
|
||||
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" />
|
||||
</svg>
|
||||
</div>
|
||||
<iframe
|
||||
@ -53,6 +48,4 @@ export default {
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
<style></style>
|
||||
|
@ -1,8 +1,8 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Preview</title>
|
||||
</head>
|
||||
<body>
|
||||
|
@ -9,17 +9,16 @@ import { createRouter, createWebHistory } from 'vue-router'
|
||||
import Demo from './Demo.vue'
|
||||
import App from './index.vue'
|
||||
|
||||
const routes = demos
|
||||
.map(({ name, tabs }) => {
|
||||
return {
|
||||
path: `/${name}`,
|
||||
component: Demo,
|
||||
props: {
|
||||
name,
|
||||
tabs,
|
||||
},
|
||||
}
|
||||
})
|
||||
const routes = demos.map(({ name, tabs }) => {
|
||||
return {
|
||||
path: `/${name}`,
|
||||
component: Demo,
|
||||
props: {
|
||||
name,
|
||||
tabs,
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory('preview'),
|
||||
@ -30,25 +29,28 @@ createApp(App)
|
||||
.directive('resize', {
|
||||
beforeMount: (el, { value = {} }) => {
|
||||
el.addEventListener('load', () => {
|
||||
iframeResize({
|
||||
...value,
|
||||
// messageCallback(messageData) {
|
||||
// if (messageData.message.type !== 'resize') {
|
||||
// return
|
||||
// }
|
||||
iframeResize(
|
||||
{
|
||||
...value,
|
||||
// messageCallback(messageData) {
|
||||
// if (messageData.message.type !== 'resize') {
|
||||
// return
|
||||
// }
|
||||
|
||||
// const style = window.getComputedStyle(el.parentElement)
|
||||
// const maxHeight = parseInt(style.getPropertyValue('max-height'), 10)
|
||||
// const style = window.getComputedStyle(el.parentElement)
|
||||
// const maxHeight = parseInt(style.getPropertyValue('max-height'), 10)
|
||||
|
||||
// if (messageData.message.height > maxHeight) {
|
||||
// el.setAttribute('scrolling', 'auto')
|
||||
// } else {
|
||||
// el.setAttribute('scrolling', 'no')
|
||||
// }
|
||||
// if (messageData.message.height > maxHeight) {
|
||||
// el.setAttribute('scrolling', 'auto')
|
||||
// } else {
|
||||
// el.setAttribute('scrolling', 'no')
|
||||
// }
|
||||
|
||||
// el?.iFrameResizer?.resize?.()
|
||||
// },
|
||||
}, el)
|
||||
// el?.iFrameResizer?.resize?.()
|
||||
// },
|
||||
},
|
||||
el,
|
||||
)
|
||||
})
|
||||
},
|
||||
unmounted(el) {
|
||||
|
@ -5,7 +5,6 @@ import { ref } from 'vue'
|
||||
const showDemoList = process.env.NODE_ENV === 'development'
|
||||
|
||||
const searchValue = ref('')
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -16,17 +15,16 @@ const searchValue = ref('')
|
||||
placeholder="Search for a demo..."
|
||||
autofocus
|
||||
v-model="searchValue"
|
||||
>
|
||||
/>
|
||||
<ul v-if="showDemoList || listing">
|
||||
<li
|
||||
class="p-5 border-b-2 border-black"
|
||||
v-for="route in $router.options.routes.filter(route => searchValue === ''? true : route.props.name.toLowerCase().includes(searchValue.toLowerCase()))"
|
||||
v-for="route in $router.options.routes.filter(route =>
|
||||
searchValue === '' ? true : route.props.name.toLowerCase().includes(searchValue.toLowerCase()),
|
||||
)"
|
||||
:key="route.path"
|
||||
>
|
||||
<router-link
|
||||
class="block mb-2 font-medium"
|
||||
:to="route.path"
|
||||
>
|
||||
<router-link class="block mb-2 font-medium" :to="route.path">
|
||||
{{ route.props.name }}
|
||||
</router-link>
|
||||
|
||||
@ -77,9 +75,7 @@ export default {
|
||||
|
||||
computed: {
|
||||
query() {
|
||||
return Object.fromEntries(Object
|
||||
.entries(this.$route.query)
|
||||
.map(([key, value]) => [key, this.fromString(value)]))
|
||||
return Object.fromEntries(Object.entries(this.$route.query).map(([key, value]) => [key, this.fromString(value)]))
|
||||
},
|
||||
|
||||
listing() {
|
||||
|
@ -9,17 +9,7 @@ async function init() {
|
||||
|
||||
highlighter = await shiki.createHighlighter({
|
||||
themes: ['material-theme-darker'],
|
||||
langs: [
|
||||
'html',
|
||||
'js',
|
||||
'jsx',
|
||||
'ts',
|
||||
'tsx',
|
||||
'css',
|
||||
'vue-html',
|
||||
'vue',
|
||||
'scss',
|
||||
],
|
||||
langs: ['html', 'js', 'jsx', 'ts', 'tsx', 'css', 'vue-html', 'vue', 'scss'],
|
||||
})
|
||||
|
||||
return highlighter
|
||||
|
@ -4,92 +4,98 @@
|
||||
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url("https://rsms.me/inter/font-files/Inter-Regular.woff2?v=3.19") format("woff2"),
|
||||
url("https://rsms.me/inter/font-files/Inter-Regular.woff?v=3.19") format("woff");
|
||||
src:
|
||||
url('https://rsms.me/inter/font-files/Inter-Regular.woff2?v=3.19') format('woff2'),
|
||||
url('https://rsms.me/inter/font-files/Inter-Regular.woff?v=3.19') format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: italic;
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url("https://rsms.me/inter/font-files/Inter-Italic.woff2?v=3.19") format("woff2"),
|
||||
url("https://rsms.me/inter/font-files/Inter-Italic.woff?v=3.19") format("woff");
|
||||
src:
|
||||
url('https://rsms.me/inter/font-files/Inter-Italic.woff2?v=3.19') format('woff2'),
|
||||
url('https://rsms.me/inter/font-files/Inter-Italic.woff?v=3.19') format('woff');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
src: url("https://rsms.me/inter/font-files/Inter-Medium.woff2?v=3.19") format("woff2"),
|
||||
url("https://rsms.me/inter/font-files/Inter-Medium.woff?v=3.19") format("woff");
|
||||
src:
|
||||
url('https://rsms.me/inter/font-files/Inter-Medium.woff2?v=3.19') format('woff2'),
|
||||
url('https://rsms.me/inter/font-files/Inter-Medium.woff?v=3.19') format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: italic;
|
||||
font-style: italic;
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
src: url("https://rsms.me/inter/font-files/Inter-MediumItalic.woff2?v=3.19") format("woff2"),
|
||||
url("https://rsms.me/inter/font-files/Inter-MediumItalic.woff?v=3.19") format("woff");
|
||||
src:
|
||||
url('https://rsms.me/inter/font-files/Inter-MediumItalic.woff2?v=3.19') format('woff2'),
|
||||
url('https://rsms.me/inter/font-files/Inter-MediumItalic.woff?v=3.19') format('woff');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-display: swap;
|
||||
src: url("https://rsms.me/inter/font-files/Inter-SemiBold.woff2?v=3.19") format("woff2"),
|
||||
url("https://rsms.me/inter/font-files/Inter-SemiBold.woff?v=3.19") format("woff");
|
||||
src:
|
||||
url('https://rsms.me/inter/font-files/Inter-SemiBold.woff2?v=3.19') format('woff2'),
|
||||
url('https://rsms.me/inter/font-files/Inter-SemiBold.woff?v=3.19') format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: italic;
|
||||
font-style: italic;
|
||||
font-weight: 600;
|
||||
font-display: swap;
|
||||
src: url("https://rsms.me/inter/font-files/Inter-SemiBoldItalic.woff2?v=3.19") format("woff2"),
|
||||
url("https://rsms.me/inter/font-files/Inter-SemiBoldItalic.woff?v=3.19") format("woff");
|
||||
src:
|
||||
url('https://rsms.me/inter/font-files/Inter-SemiBoldItalic.woff2?v=3.19') format('woff2'),
|
||||
url('https://rsms.me/inter/font-files/Inter-SemiBoldItalic.woff?v=3.19') format('woff');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src: url("https://rsms.me/inter/font-files/Inter-Bold.woff2?v=3.19") format("woff2"),
|
||||
url("https://rsms.me/inter/font-files/Inter-Bold.woff?v=3.19") format("woff");
|
||||
src:
|
||||
url('https://rsms.me/inter/font-files/Inter-Bold.woff2?v=3.19') format('woff2'),
|
||||
url('https://rsms.me/inter/font-files/Inter-Bold.woff?v=3.19') format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: italic;
|
||||
font-style: italic;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src: url("https://rsms.me/inter/font-files/Inter-BoldItalic.woff2?v=3.19") format("woff2"),
|
||||
url("https://rsms.me/inter/font-files/Inter-BoldItalic.woff?v=3.19") format("woff");
|
||||
src:
|
||||
url('https://rsms.me/inter/font-files/Inter-BoldItalic.woff2?v=3.19') format('woff2'),
|
||||
url('https://rsms.me/inter/font-files/Inter-BoldItalic.woff?v=3.19') format('woff');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'JetBrains Mono';
|
||||
font-style: normal;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src:
|
||||
url("https://cdn.jsdelivr.net/gh/JetBrains/JetBrainsMono/web/woff2/JetBrainsMono-Regular.woff2") format("woff2"),
|
||||
url("https://cdn.jsdelivr.net/gh/JetBrains/JetBrainsMono/web/woff2/JetBrainsMono-Regular.woff") format("woff"),
|
||||
;
|
||||
url('https://cdn.jsdelivr.net/gh/JetBrains/JetBrainsMono/web/woff2/JetBrainsMono-Regular.woff2') format('woff2'),
|
||||
url('https://cdn.jsdelivr.net/gh/JetBrains/JetBrainsMono/web/woff2/JetBrainsMono-Regular.woff') format('woff');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'JetBrains Mono';
|
||||
font-style: normal;
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src:
|
||||
url("https://cdn.jsdelivr.net/gh/JetBrains/JetBrainsMono/web/woff2/JetBrainsMono-Bold.woff2") format("woff2"),
|
||||
url("https://cdn.jsdelivr.net/gh/JetBrains/JetBrainsMono/web/woff2/JetBrainsMono-Bold.woff") format("woff"),
|
||||
;
|
||||
url('https://cdn.jsdelivr.net/gh/JetBrains/JetBrainsMono/web/woff2/JetBrainsMono-Bold.woff2') format('woff2'),
|
||||
url('https://cdn.jsdelivr.net/gh/JetBrains/JetBrainsMono/web/woff2/JetBrainsMono-Bold.woff') format('woff');
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
|
@ -18,21 +18,18 @@ export default function init(name: string, source: any) {
|
||||
const root = document.getElementById('app')
|
||||
|
||||
if (root) {
|
||||
createRoot(root)
|
||||
.render(React.createElement(module.default))
|
||||
createRoot(root).render(React.createElement(module.default))
|
||||
}
|
||||
debug()
|
||||
})
|
||||
.catch(() => {
|
||||
import(`../src/${demoCategory}/${demoName}/${frameworkName}/index.jsx`)
|
||||
.then(module => {
|
||||
const root = document.getElementById('app')
|
||||
import(`../src/${demoCategory}/${demoName}/${frameworkName}/index.jsx`).then(module => {
|
||||
const root = document.getElementById('app')
|
||||
|
||||
if (root) {
|
||||
createRoot(root)
|
||||
.render(React.createElement(module.default))
|
||||
}
|
||||
debug()
|
||||
})
|
||||
if (root) {
|
||||
createRoot(root).render(React.createElement(module.default))
|
||||
}
|
||||
debug()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
@ -1,22 +1,22 @@
|
||||
/* Base HTML and global element styles*/
|
||||
:root {
|
||||
--white: #FFF;
|
||||
--black: #2E2B29;
|
||||
--black-contrast: #110F0E;
|
||||
--white: #fff;
|
||||
--black: #2e2b29;
|
||||
--black-contrast: #110f0e;
|
||||
--gray-1: rgba(61, 37, 20, 0.05);
|
||||
--gray-2: rgba(61, 37, 20, 0.08);
|
||||
--gray-3: rgba(61, 37, 20, 0.12);
|
||||
--gray-4: rgba(53, 38, 28, 0.30);
|
||||
--gray-5: rgba(28, 25, 23, 0.60);
|
||||
--green: #22C55E;
|
||||
--purple: #6A00F5;
|
||||
--purple-contrast: #5800CC;
|
||||
--gray-4: rgba(53, 38, 28, 0.3);
|
||||
--gray-5: rgba(28, 25, 23, 0.6);
|
||||
--green: #22c55e;
|
||||
--purple: #6a00f5;
|
||||
--purple-contrast: #5800cc;
|
||||
--purple-light: rgba(88, 5, 255, 0.05);
|
||||
--yellow-contrast: #FACC15;
|
||||
--yellow-contrast: #facc15;
|
||||
--yellow: rgba(250, 204, 21, 0.4);
|
||||
--yellow-light: #FFFAE5;
|
||||
--red: #FF5C33;
|
||||
--red-light: #FFEBE5;
|
||||
--yellow-light: #fffae5;
|
||||
--red: #ff5c33;
|
||||
--red-light: #ffebe5;
|
||||
--shadow: 0px 12px 33px 0px rgba(0, 0, 0, 0.06), 0px 3.618px 9.949px 0px rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
@ -27,7 +27,22 @@
|
||||
}
|
||||
|
||||
html {
|
||||
font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
||||
font-family:
|
||||
Inter,
|
||||
ui-sans-serif,
|
||||
system-ui,
|
||||
-apple-system,
|
||||
BlinkMacSystemFont,
|
||||
'Segoe UI',
|
||||
Roboto,
|
||||
'Helvetica Neue',
|
||||
Arial,
|
||||
'Noto Sans',
|
||||
sans-serif,
|
||||
'Apple Color Emoji',
|
||||
'Segoe UI Emoji',
|
||||
'Segoe UI Symbol',
|
||||
'Noto Color Emoji';
|
||||
line-height: 1.5;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
@ -104,7 +119,7 @@ textarea {
|
||||
line-height: 1.15;
|
||||
margin: none;
|
||||
padding: 0.375rem 0.625rem;
|
||||
transition: all 0.2s cubic-bezier(0.65,0.05,0.36,1);
|
||||
transition: all 0.2s cubic-bezier(0.65, 0.05, 0.36, 1);
|
||||
|
||||
&:hover {
|
||||
background-color: var(--gray-3);
|
||||
@ -150,7 +165,7 @@ select:not([disabled]) {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
input[type="text"],
|
||||
input[type='text'],
|
||||
textarea {
|
||||
background-color: unset;
|
||||
border: 1px solid var(--gray-3);
|
||||
@ -166,7 +181,8 @@ textarea {
|
||||
border-color: var(--gray-4);
|
||||
}
|
||||
|
||||
&:focus-visible, &:focus {
|
||||
&:focus-visible,
|
||||
&:focus {
|
||||
border-color: var(--purple);
|
||||
outline: none;
|
||||
}
|
||||
@ -229,7 +245,7 @@ form {
|
||||
background-color: var(--purple-light);
|
||||
|
||||
&::after {
|
||||
content: "";
|
||||
content: '';
|
||||
background-image: url("data:image/svg+xml;utf8,<svg version='1.1' id='Layer_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' width='28px' height='30px' viewBox='0 0 24 30' style='enable-background:new 0 0 50 50;' xml:space='preserve'><rect x='0' y='10' width='6' height='10' fill='%236A00F5' rx='3' ry='3'><animateTransform attributeType='xml' attributeName='transform' type='translate' values='0 0; 0 5; 0 -5; 0 0' begin='0' dur='0.6s' repeatCount='indefinite'/></rect><rect x='10' y='10' width='6' height='10' fill='%236A00F5' rx='3' ry='3'><animateTransform attributeType='xml' attributeName='transform' type='translate' values='0 0; 0 5; 0 -5; 0 0' begin='0.2s' dur='0.6s' repeatCount='indefinite'/></rect><rect x='20' y='10' width='6' height='10' fill='%236A00F5' rx='3' ry='3'><animateTransform attributeType='xml' attributeName='transform' type='translate' values='0 0; 0 5; 0 -5; 0 0' begin='0.4s' dur='0.6s' repeatCount='indefinite'/></rect></svg>");
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
@ -272,13 +288,13 @@ hr {
|
||||
}
|
||||
|
||||
kbd {
|
||||
background-color: var(--gray-2);
|
||||
border: 1px solid var(--gray-2);
|
||||
border-radius: 0.25rem;
|
||||
font-size: 0.6rem;
|
||||
line-height: 1.15;
|
||||
padding: 0.1rem 0.25rem;
|
||||
text-transform: uppercase;
|
||||
background-color: var(--gray-2);
|
||||
border: 1px solid var(--gray-2);
|
||||
border-radius: 0.25rem;
|
||||
font-size: 0.6rem;
|
||||
line-height: 1.15;
|
||||
padding: 0.1rem 0.25rem;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
/* Layout and structure */
|
||||
@ -345,7 +361,7 @@ kbd {
|
||||
line-height: 1.15;
|
||||
min-height: 1.5rem;
|
||||
padding: 0 0.375rem;
|
||||
transition: all 0.2s cubic-bezier(0.65,0.05,0.36,1);
|
||||
transition: all 0.2s cubic-bezier(0.65, 0.05, 0.36, 1);
|
||||
|
||||
&:has(input:checked) {
|
||||
background-color: var(--white);
|
||||
|
@ -10,12 +10,11 @@ export default function init(name: string, source: any) {
|
||||
|
||||
const [demoCategory, demoName, frameworkName] = splitName(name)
|
||||
|
||||
import(`../src/${demoCategory}/${demoName}/${frameworkName}/index.svelte`)
|
||||
.then(Module => {
|
||||
const Component = Module.default
|
||||
import(`../src/${demoCategory}/${demoName}/${frameworkName}/index.svelte`).then(Module => {
|
||||
const Component = Module.default
|
||||
|
||||
new Component({ target: document.querySelector('#app') }) // eslint-disable-line
|
||||
new Component({ target: document.querySelector('#app') }) // eslint-disable-line
|
||||
|
||||
debug()
|
||||
})
|
||||
debug()
|
||||
})
|
||||
}
|
||||
|
@ -12,14 +12,13 @@ export default function init(name: string, source: any) {
|
||||
|
||||
const [demoCategory, demoName, frameworkName] = splitName(name)
|
||||
|
||||
import(`../src/${demoCategory}/${demoName}/${frameworkName}/index.vue`)
|
||||
.then(module => {
|
||||
const app = createApp(module.default)
|
||||
import(`../src/${demoCategory}/${demoName}/${frameworkName}/index.vue`).then(module => {
|
||||
const app = createApp(module.default)
|
||||
|
||||
if (typeof module.configureApp === 'function') {
|
||||
module.configureApp(app)
|
||||
}
|
||||
app.mount('#app')
|
||||
debug()
|
||||
})
|
||||
if (typeof module.configureApp === 'function') {
|
||||
module.configureApp(app)
|
||||
}
|
||||
app.mount('#app')
|
||||
debug()
|
||||
})
|
||||
}
|
||||
|
@ -13,7 +13,13 @@ const MenuBar = ({ editor }) => {
|
||||
}, [editor])
|
||||
|
||||
const onCutToEnd = useCallback(() => {
|
||||
editor.chain().cut({ from: editor.state.selection.$from.pos, to: editor.state.selection.$to.pos }, editor.state.doc.nodeSize - 2).run()
|
||||
editor
|
||||
.chain()
|
||||
.cut(
|
||||
{ from: editor.state.selection.$from.pos, to: editor.state.selection.$to.pos },
|
||||
editor.state.doc.nodeSize - 2,
|
||||
)
|
||||
.run()
|
||||
}, [editor])
|
||||
|
||||
if (!editor) {
|
||||
|
@ -42,13 +42,18 @@ const MenuBar = () => {
|
||||
<button data-test-id="html-content" onClick={() => editor.chain().insertContent(htmlContent).focus().run()}>
|
||||
Insert HTML content
|
||||
</button>
|
||||
<button data-test-id="html-content-spans" onClick={() => editor.chain().insertContent('<p><b>Hello</b> <i>World</i></p>').focus().run()}>
|
||||
<button
|
||||
data-test-id="html-content-spans"
|
||||
onClick={() => editor.chain().insertContent('<p><b>Hello</b> <i>World</i></p>').focus().run()}
|
||||
>
|
||||
Insert HTML with span tags content
|
||||
</button>
|
||||
<button data-test-id="text-content" onClick={() => editor.chain().insertContent(textContent).focus().run()}>
|
||||
Insert text content
|
||||
</button>
|
||||
<button data-test-id="image-content" onClick={insertImage}>Insert image</button>
|
||||
<button data-test-id="image-content" onClick={insertImage}>
|
||||
Insert image
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
@ -72,7 +77,5 @@ const extensions = [
|
||||
const content = ''
|
||||
|
||||
export default () => {
|
||||
return (
|
||||
<EditorProvider slotBefore={<MenuBar />} extensions={extensions} content={content}></EditorProvider>
|
||||
)
|
||||
return <EditorProvider slotBefore={<MenuBar />} extensions={extensions} content={content}></EditorProvider>
|
||||
}
|
||||
|
@ -11,7 +11,10 @@ context('/src/Commands/InsertContent/React/', () => {
|
||||
cy.get('button[data-test-id="html-content"]').click()
|
||||
|
||||
// check if the content html is correct
|
||||
cy.get('.tiptap').should('contain.html', '<h1><a target="_blank" rel="noopener noreferrer nofollow" href="https://tiptap.dev/">Tiptap</a></h1><p><strong>Hello World</strong></p><p>This is a paragraph<br>with a break.</p><p>And this is some additional string content.</p>')
|
||||
cy.get('.tiptap').should(
|
||||
'contain.html',
|
||||
'<h1><a target="_blank" rel="noopener noreferrer nofollow" href="https://tiptap.dev/">Tiptap</a></h1><p><strong>Hello World</strong></p><p>This is a paragraph<br>with a break.</p><p>And this is some additional string content.</p>',
|
||||
)
|
||||
})
|
||||
|
||||
it('should keep spaces inbetween tags in html content', () => {
|
||||
@ -32,7 +35,10 @@ context('/src/Commands/InsertContent/React/', () => {
|
||||
cy.get('button[data-test-id="text-content"]').click()
|
||||
|
||||
// check if the content html is correct
|
||||
cy.get('.tiptap').should('contain.html', 'Hello World\nThis is content with a new line. Is this working?\n\nLets see if multiple new lines are inserted correctly')
|
||||
cy.get('.tiptap').should(
|
||||
'contain.html',
|
||||
'Hello World\nThis is content with a new line. Is this working?\n\nLets see if multiple new lines are inserted correctly',
|
||||
)
|
||||
})
|
||||
|
||||
it('should keep newlines in pre tag', () => {
|
||||
@ -86,7 +92,9 @@ context('/src/Commands/InsertContent/React/', () => {
|
||||
|
||||
it('should remove newlines and tabs when parseOptions.preserveWhitespace=false', () => {
|
||||
cy.get('.tiptap').then(([{ editor }]) => {
|
||||
editor.commands.insertContent('\n<h1>Tiptap</h1><p><strong>Hello\n World</strong>\n</p>\n', { parseOptions: { preserveWhitespace: false } })
|
||||
editor.commands.insertContent('\n<h1>Tiptap</h1><p><strong>Hello\n World</strong>\n</p>\n', {
|
||||
parseOptions: { preserveWhitespace: false },
|
||||
})
|
||||
cy.get('.tiptap').should('contain.html', '<h1>Tiptap</h1><p><strong>Hello World</strong></p>')
|
||||
})
|
||||
})
|
||||
@ -96,7 +104,10 @@ context('/src/Commands/InsertContent/React/', () => {
|
||||
editor.commands.insertContent('<p>HelloWorld</p>')
|
||||
editor.commands.setTextSelection(6)
|
||||
editor.commands.insertContent('<img src="https://example.image/1" alt="This is an example" />')
|
||||
cy.get('.tiptap').should('contain.html', '<p>Hello</p><img src="https://example.image/1" alt="This is an example" contenteditable="false" draggable="true"><p>World</p>')
|
||||
cy.get('.tiptap').should(
|
||||
'contain.html',
|
||||
'<p>Hello</p><img src="https://example.image/1" alt="This is an example" contenteditable="false" draggable="true"><p>World</p>',
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@ -105,7 +116,10 @@ context('/src/Commands/InsertContent/React/', () => {
|
||||
editor.commands.insertContent('<p>HelloWorld</p>')
|
||||
editor.commands.setTextSelection(1)
|
||||
editor.commands.insertContent('<img src="https://example.image/1" alt="This is an example" />')
|
||||
cy.get('.tiptap').should('contain.html', '<img src="https://example.image/1" alt="This is an example" contenteditable="false" draggable="true"><p>HelloWorld</p>')
|
||||
cy.get('.tiptap').should(
|
||||
'contain.html',
|
||||
'<img src="https://example.image/1" alt="This is an example" contenteditable="false" draggable="true"><p>HelloWorld</p>',
|
||||
)
|
||||
})
|
||||
})
|
||||
it('should respect editor.options.parseOptions if defined to be `false`', () => {
|
||||
@ -131,5 +145,4 @@ context('/src/Commands/InsertContent/React/', () => {
|
||||
cy.get('.tiptap').should('contain.html', '<h1>Tiptap</h1><p><strong>Hello World</strong></p>')
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
@ -17,19 +17,11 @@ const MenuBar = () => {
|
||||
<div className="control-group">
|
||||
<div>
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={useInputRules}
|
||||
onChange={() => setUseInputRules(prev => !prev)}
|
||||
/>
|
||||
<input type="checkbox" checked={useInputRules} onChange={() => setUseInputRules(prev => !prev)} />
|
||||
Apply input rules
|
||||
</label>
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={usePasteRules}
|
||||
onChange={() => setUsePasteRules(prev => !prev)}
|
||||
/>
|
||||
<input type="checkbox" checked={usePasteRules} onChange={() => setUsePasteRules(prev => !prev)} />
|
||||
Apply paste rules
|
||||
</label>
|
||||
</div>
|
||||
@ -163,14 +155,10 @@ const MenuBar = () => {
|
||||
)
|
||||
}
|
||||
|
||||
const extensions = [
|
||||
StarterKit,
|
||||
]
|
||||
const extensions = [StarterKit]
|
||||
|
||||
const content = ''
|
||||
|
||||
export default () => {
|
||||
return (
|
||||
<EditorProvider slotBefore={<MenuBar />} extensions={extensions} content={content}></EditorProvider>
|
||||
)
|
||||
return <EditorProvider slotBefore={<MenuBar />} extensions={extensions} content={content}></EditorProvider>
|
||||
}
|
||||
|
@ -25,7 +25,5 @@ const extensions = [
|
||||
const content = ''
|
||||
|
||||
export default () => {
|
||||
return (
|
||||
<EditorProvider extensions={extensions} content={content}></EditorProvider>
|
||||
)
|
||||
return <EditorProvider extensions={extensions} content={content}></EditorProvider>
|
||||
}
|
||||
|
@ -30,7 +30,10 @@ context('/src/Commands/SetContent/React/', () => {
|
||||
|
||||
it('should insert a Prosemirror Fragment as content', () => {
|
||||
cy.get('.tiptap').then(([{ editor }]) => {
|
||||
editor.commands.setContent(editor.schema.node('doc', null, editor.schema.node('paragraph', null, editor.schema.text('Hello World.'))).content)
|
||||
editor.commands.setContent(
|
||||
editor.schema.node('doc', null, editor.schema.node('paragraph', null, editor.schema.text('Hello World.')))
|
||||
.content,
|
||||
)
|
||||
cy.get('.tiptap').should('contain.html', '<p>Hello World.</p>')
|
||||
})
|
||||
})
|
||||
@ -57,8 +60,13 @@ context('/src/Commands/SetContent/React/', () => {
|
||||
|
||||
it('should insert more complex html content', () => {
|
||||
cy.get('.tiptap').then(([{ editor }]) => {
|
||||
editor.commands.setContent('<h1>Welcome to Tiptap</h1><p>This is a paragraph.</p><ul><li><p>List Item A</p></li><li><p>List Item B</p><ul><li><p>Subchild</p></li></ul></li></ul>')
|
||||
cy.get('.tiptap').should('contain.html', '<h1>Welcome to Tiptap</h1><p>This is a paragraph.</p><ul><li><p>List Item A</p></li><li><p>List Item B</p><ul><li><p>Subchild</p></li></ul></li></ul>')
|
||||
editor.commands.setContent(
|
||||
'<h1>Welcome to Tiptap</h1><p>This is a paragraph.</p><ul><li><p>List Item A</p></li><li><p>List Item B</p><ul><li><p>Subchild</p></li></ul></li></ul>',
|
||||
)
|
||||
cy.get('.tiptap').should(
|
||||
'contain.html',
|
||||
'<h1>Welcome to Tiptap</h1><p>This is a paragraph.</p><ul><li><p>List Item A</p></li><li><p>List Item B</p><ul><li><p>Subchild</p></li></ul></li></ul>',
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@ -94,7 +102,10 @@ context('/src/Commands/SetContent/React/', () => {
|
||||
it('should insert mentions', () => {
|
||||
cy.get('.tiptap').then(([{ editor }]) => {
|
||||
editor.commands.setContent('<p><span data-type="mention" data-id="1" data-label="John Doe">@John Doe</span></p>')
|
||||
cy.get('.tiptap').should('contain.html', '<span data-type="mention" data-id="1" data-label="John Doe" contenteditable="false">@John Doe</span>')
|
||||
cy.get('.tiptap').should(
|
||||
'contain.html',
|
||||
'<span data-type="mention" data-id="1" data-label="John Doe" contenteditable="false">@John Doe</span>',
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@ -109,7 +120,9 @@ context('/src/Commands/SetContent/React/', () => {
|
||||
// This exists in insertContentAt as well
|
||||
it('should keep newlines and tabs between html fragments when preserveWhitespace = full', () => {
|
||||
cy.get('.tiptap').then(([{ editor }]) => {
|
||||
editor.commands.setContent('<h1>Tiptap</h1>\n\t<p><strong>Hello World</strong></p>', false, { preserveWhitespace: 'full' })
|
||||
editor.commands.setContent('<h1>Tiptap</h1>\n\t<p><strong>Hello World</strong></p>', false, {
|
||||
preserveWhitespace: 'full',
|
||||
})
|
||||
cy.get('.tiptap').should('contain.html', '<h1>Tiptap</h1><p>\n\t</p><p><strong>Hello World</strong></p>')
|
||||
})
|
||||
})
|
||||
@ -174,7 +187,9 @@ context('/src/Commands/SetContent/React/', () => {
|
||||
|
||||
it('should remove newlines and tabs when parseOptions.preserveWhitespace=false', () => {
|
||||
cy.get('.tiptap').then(([{ editor }]) => {
|
||||
editor.commands.setContent('\n<h1>Tiptap</h1><p><strong>Hello\n World</strong>\n</p>\n', false, { preserveWhitespace: false })
|
||||
editor.commands.setContent('\n<h1>Tiptap</h1><p><strong>Hello\n World</strong>\n</p>\n', false, {
|
||||
preserveWhitespace: false,
|
||||
})
|
||||
cy.get('.tiptap').should('contain.html', '<h1>Tiptap</h1><p><strong>Hello World</strong></p>')
|
||||
})
|
||||
})
|
||||
|
@ -24,8 +24,8 @@
|
||||
}
|
||||
|
||||
pre {
|
||||
background: #0D0D0D;
|
||||
color: #FFF;
|
||||
background: #0d0d0d;
|
||||
color: #fff;
|
||||
font-family: 'JetBrainsMono', monospace;
|
||||
padding: 0.75rem 1rem;
|
||||
border-radius: 0.5rem;
|
||||
@ -45,12 +45,12 @@
|
||||
|
||||
blockquote {
|
||||
padding-left: 1rem;
|
||||
border-left: 2px solid rgba(#0D0D0D, 0.1);
|
||||
border-left: 2px solid rgba(#0d0d0d, 0.1);
|
||||
}
|
||||
|
||||
hr {
|
||||
border: none;
|
||||
border-top: 2px solid rgba(#0D0D0D, 0.1);
|
||||
border-top: 2px solid rgba(#0d0d0d, 0.1);
|
||||
margin: 2rem 0;
|
||||
}
|
||||
}
|
||||
|
@ -78,9 +78,7 @@ const getInitialUser = () => {
|
||||
}
|
||||
}
|
||||
|
||||
const Editor = ({
|
||||
ydoc, provider, room,
|
||||
}) => {
|
||||
const Editor = ({ ydoc, provider, room }) => {
|
||||
const [status, setStatus] = useState('connecting')
|
||||
const [currentUser, setCurrentUser] = useState(getInitialUser)
|
||||
|
||||
@ -187,15 +185,12 @@ const Editor = ({
|
||||
|
||||
<EditorContent editor={editor} className="main-group" />
|
||||
|
||||
<div
|
||||
className="collab-status-group"
|
||||
data-state={status === 'connected' ? 'online' : 'offline'}
|
||||
>
|
||||
<div className="collab-status-group" data-state={status === 'connected' ? 'online' : 'offline'}>
|
||||
<label>
|
||||
{status === 'connected'
|
||||
? `${editor.storage.collaborationCursor.users.length} user${
|
||||
editor.storage.collaborationCursor.users.length === 1 ? '' : 's'
|
||||
} online in ${room}`
|
||||
editor.storage.collaborationCursor.users.length === 1 ? '' : 's'
|
||||
} online in ${room}`
|
||||
: 'offline'}
|
||||
</label>
|
||||
<button style={{ '--color': currentUser.color }} onClick={setName}>
|
||||
|
@ -91,14 +91,14 @@
|
||||
|
||||
/* Highlight specific styles */
|
||||
mark {
|
||||
background-color: #FAF594;
|
||||
background-color: #faf594;
|
||||
border-radius: 0.4rem;
|
||||
box-decoration-break: clone;
|
||||
padding: 0.1rem 0.3rem;
|
||||
}
|
||||
|
||||
/* Task list specific styles */
|
||||
ul[data-type="taskList"] {
|
||||
ul[data-type='taskList'] {
|
||||
list-style: none;
|
||||
margin-left: 0;
|
||||
padding: 0;
|
||||
@ -118,11 +118,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
input[type="checkbox"] {
|
||||
input[type='checkbox'] {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
ul[data-type="taskList"] {
|
||||
ul[data-type='taskList'] {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
@ -205,11 +205,11 @@ body {
|
||||
flex-direction: row;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 400;
|
||||
gap: 1rem;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
justify-content: space-between;
|
||||
padding: 0.375rem 0.5rem 0.375rem 1rem;
|
||||
position: sticky;
|
||||
width: 100%;
|
||||
width: 100%;
|
||||
z-index: 100;
|
||||
|
||||
button {
|
||||
@ -230,13 +230,13 @@ body {
|
||||
&::before {
|
||||
background-color: var(--color);
|
||||
border-radius: 0.375rem;
|
||||
content: "";
|
||||
content: '';
|
||||
height: 100%;
|
||||
left: 0;
|
||||
opacity: 0.5;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
transition: all 0.2s cubic-bezier(0.65,0.05,0.36,1);
|
||||
transition: all 0.2s cubic-bezier(0.65, 0.05, 0.36, 1);
|
||||
width: 100%;
|
||||
z-index: -1;
|
||||
}
|
||||
@ -256,13 +256,13 @@ body {
|
||||
|
||||
&::before {
|
||||
border-radius: 50%;
|
||||
content: " ";
|
||||
content: ' ';
|
||||
height: 0.35rem;
|
||||
width: 0.35rem;
|
||||
}
|
||||
}
|
||||
|
||||
&[data-state="online"] {
|
||||
&[data-state='online'] {
|
||||
label {
|
||||
&::before {
|
||||
background-color: var(--green);
|
||||
@ -270,7 +270,7 @@ body {
|
||||
}
|
||||
}
|
||||
|
||||
&[data-state="offline"] {
|
||||
&[data-state='offline'] {
|
||||
label {
|
||||
&::before {
|
||||
background-color: var(--red);
|
||||
|
@ -6,10 +6,7 @@ import Collaboration from '@tiptap/extension-collaboration'
|
||||
import CollaborationCursor from '@tiptap/extension-collaboration-cursor'
|
||||
import { EditorContent, useEditor } from '@tiptap/react'
|
||||
import StarterKit from '@tiptap/starter-kit'
|
||||
import React, {
|
||||
useCallback, useEffect,
|
||||
useState,
|
||||
} from 'react'
|
||||
import React, { useCallback, useEffect, useState } from 'react'
|
||||
import * as Y from 'yjs'
|
||||
|
||||
const room = 'room-1'
|
||||
@ -55,10 +52,12 @@ const websocketProvider = new TiptapCollabProvider({
|
||||
})
|
||||
|
||||
const getInitialUser = () => {
|
||||
return JSON.parse(localStorage.getItem('currentUser')) || {
|
||||
name: getRandomName(),
|
||||
color: getRandomColor(),
|
||||
}
|
||||
return (
|
||||
JSON.parse(localStorage.getItem('currentUser')) || {
|
||||
name: getRandomName(),
|
||||
color: getRandomColor(),
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
export default () => {
|
||||
|
@ -27,7 +27,7 @@
|
||||
background: #0d0d0d;
|
||||
border-radius: 0.5rem;
|
||||
color: #fff;
|
||||
font-family: "JetBrainsMono", monospace;
|
||||
font-family: 'JetBrainsMono', monospace;
|
||||
padding: 0.75rem 1rem;
|
||||
|
||||
code {
|
||||
@ -62,7 +62,7 @@
|
||||
margin: 2rem 0;
|
||||
}
|
||||
|
||||
ul[data-type="taskList"] {
|
||||
ul[data-type='taskList'] {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
|
||||
@ -127,7 +127,7 @@
|
||||
&::before {
|
||||
background: rgba(#0d0d0d, 0.5);
|
||||
border-radius: 50%;
|
||||
content: " ";
|
||||
content: ' ';
|
||||
display: inline-block;
|
||||
flex: 0 0 auto;
|
||||
height: 0.5rem;
|
||||
|
@ -44,15 +44,13 @@ export default () => {
|
||||
|
||||
// empty
|
||||
if (url === '') {
|
||||
editor.chain().focus().extendMarkRange('link').unsetLink()
|
||||
.run()
|
||||
editor.chain().focus().extendMarkRange('link').unsetLink().run()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// update link
|
||||
editor.chain().focus().extendMarkRange('link').setLink({ href: url })
|
||||
.run()
|
||||
editor.chain().focus().extendMarkRange('link').setLink({ href: url }).run()
|
||||
}, [editor])
|
||||
|
||||
if (!editor) {
|
||||
@ -63,11 +61,7 @@ export default () => {
|
||||
<>
|
||||
<div className="control-group">
|
||||
<div className="button-group">
|
||||
<button
|
||||
onClick={setLink}
|
||||
className={editorState.isLink ? 'is-active' : ''}
|
||||
data-testid="setLink"
|
||||
>
|
||||
<button onClick={setLink} className={editorState.isLink ? 'is-active' : ''} data-testid="setLink">
|
||||
Set link
|
||||
</button>
|
||||
<button
|
||||
|
@ -42,61 +42,39 @@ context('/src/Examples/AutolinkValidation/React/', () => {
|
||||
cy.get('.tiptap').type('https://tiptap.dev {home}')
|
||||
cy.get('.tiptap').should('have.text', 'https://tiptap.dev ')
|
||||
cy.get('[data-testid=unsetLink]').click()
|
||||
cy.get('.tiptap')
|
||||
.find('a')
|
||||
.should('have.length', 0)
|
||||
cy.get('.tiptap').find('a').should('have.length', 0)
|
||||
cy.get('.tiptap').type('{end}http://www.example.com/ ')
|
||||
cy.get('.tiptap')
|
||||
.find('a')
|
||||
.should('have.length', 1)
|
||||
.should('have.attr', 'href', 'http://www.example.com/')
|
||||
cy.get('.tiptap').find('a').should('have.length', 1).should('have.attr', 'href', 'http://www.example.com/')
|
||||
})
|
||||
|
||||
it('should not relink unset links after hitting next paragraph', () => {
|
||||
cy.get('.tiptap').type('https://tiptap.dev {home}')
|
||||
cy.get('.tiptap').should('have.text', 'https://tiptap.dev ')
|
||||
cy.get('[data-testid=unsetLink]').click()
|
||||
cy.get('.tiptap')
|
||||
.find('a')
|
||||
.should('have.length', 0)
|
||||
cy.get('.tiptap').find('a').should('have.length', 0)
|
||||
cy.get('.tiptap').type('{end}typing other text should prevent the link from relinking when hitting enter{enter}')
|
||||
cy.get('.tiptap')
|
||||
.find('a')
|
||||
.should('have.length', 0)
|
||||
cy.get('.tiptap').find('a').should('have.length', 0)
|
||||
})
|
||||
|
||||
it('should not relink unset links after modifying', () => {
|
||||
cy.get('.tiptap').type('https://tiptap.dev {home}')
|
||||
cy.get('.tiptap').should('have.text', 'https://tiptap.dev ')
|
||||
cy.get('[data-testid=unsetLink]').click()
|
||||
cy.get('.tiptap')
|
||||
.find('a')
|
||||
.should('have.length', 0)
|
||||
cy.get('.tiptap')
|
||||
.type('{home}')
|
||||
.type('{rightArrow}'.repeat('https://'.length))
|
||||
.type('blah')
|
||||
cy.get('.tiptap').find('a').should('have.length', 0)
|
||||
cy.get('.tiptap').type('{home}').type('{rightArrow}'.repeat('https://'.length)).type('blah')
|
||||
cy.get('.tiptap').should('have.text', 'https://blahtiptap.dev ')
|
||||
cy.get('.tiptap')
|
||||
.find('a')
|
||||
.should('have.length', 0)
|
||||
cy.get('.tiptap').find('a').should('have.length', 0)
|
||||
})
|
||||
|
||||
it('should autolink after hitting enter (new paragraph)', () => {
|
||||
cy.get('.tiptap').type('https://tiptap.dev{enter}')
|
||||
cy.get('.tiptap').should('have.text', 'https://tiptap.dev')
|
||||
cy.get('.tiptap')
|
||||
.find('a')
|
||||
.should('have.length', 1)
|
||||
.should('have.attr', 'href', 'https://tiptap.dev')
|
||||
cy.get('.tiptap').find('a').should('have.length', 1).should('have.attr', 'href', 'https://tiptap.dev')
|
||||
})
|
||||
|
||||
it('should autolink after hitting shift-enter (hardbreak)', () => {
|
||||
cy.get('.tiptap').type('https://tiptap.dev{shift+enter}')
|
||||
cy.get('.tiptap').should('have.text', 'https://tiptap.dev')
|
||||
cy.get('.tiptap')
|
||||
.find('a')
|
||||
.should('have.length', 1)
|
||||
.should('have.attr', 'href', 'https://tiptap.dev')
|
||||
cy.get('.tiptap').find('a').should('have.length', 1).should('have.attr', 'href', 'https://tiptap.dev')
|
||||
})
|
||||
})
|
||||
|
@ -70,8 +70,8 @@ export default {
|
||||
}
|
||||
|
||||
pre {
|
||||
background: #0D0D0D;
|
||||
color: #FFF;
|
||||
background: #0d0d0d;
|
||||
color: #fff;
|
||||
font-family: 'JetBrainsMono', monospace;
|
||||
padding: 0.75rem 1rem;
|
||||
border-radius: 0.5rem;
|
||||
@ -95,7 +95,7 @@ export default {
|
||||
|
||||
blockquote {
|
||||
padding-left: 1rem;
|
||||
border-left: 2px solid rgba(#0D0D0D, 0.1);
|
||||
border-left: 2px solid rgba(#0d0d0d, 0.1);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -38,12 +38,8 @@ const MenuBar = ({ editor }) => {
|
||||
>
|
||||
Code
|
||||
</button>
|
||||
<button onClick={() => editor.chain().focus().unsetAllMarks().run()}>
|
||||
Clear marks
|
||||
</button>
|
||||
<button onClick={() => editor.chain().focus().clearNodes().run()}>
|
||||
Clear nodes
|
||||
</button>
|
||||
<button onClick={() => editor.chain().focus().unsetAllMarks().run()}>Clear marks</button>
|
||||
<button onClick={() => editor.chain().focus().clearNodes().run()}>Clear nodes</button>
|
||||
<button
|
||||
onClick={() => editor.chain().focus().setParagraph().run()}
|
||||
className={editor.isActive('paragraph') ? 'is-active' : ''}
|
||||
@ -110,18 +106,10 @@ const MenuBar = ({ editor }) => {
|
||||
>
|
||||
Blockquote
|
||||
</button>
|
||||
<button onClick={() => editor.chain().focus().setHorizontalRule().run()}>
|
||||
Horizontal rule
|
||||
</button>
|
||||
<button onClick={() => editor.chain().focus().setHardBreak().run()}>
|
||||
Hard break
|
||||
</button>
|
||||
<button onClick={() => editor.chain().focus().undo().run()}>
|
||||
Undo
|
||||
</button>
|
||||
<button onClick={() => editor.chain().focus().redo().run()}>
|
||||
Redo
|
||||
</button>
|
||||
<button onClick={() => editor.chain().focus().setHorizontalRule().run()}>Horizontal rule</button>
|
||||
<button onClick={() => editor.chain().focus().setHardBreak().run()}>Hard break</button>
|
||||
<button onClick={() => editor.chain().focus().undo().run()}>Undo</button>
|
||||
<button onClick={() => editor.chain().focus().redo().run()}>Redo</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
@ -129,9 +117,7 @@ const MenuBar = ({ editor }) => {
|
||||
|
||||
export default () => {
|
||||
const editor = useEditor({
|
||||
extensions: [
|
||||
StarterKit,
|
||||
],
|
||||
extensions: [StarterKit],
|
||||
content,
|
||||
editorProps: {
|
||||
attributes: {
|
||||
|
@ -5,66 +5,93 @@
|
||||
<button @click="editor.chain().focus().toggleBold().run()" :class="{ 'is-active': editor.isActive('bold') }">
|
||||
Bold
|
||||
</button>
|
||||
<button @click="editor.chain().focus().toggleItalic().run()" :class="{ 'is-active': editor.isActive('italic') }">
|
||||
<button
|
||||
@click="editor.chain().focus().toggleItalic().run()"
|
||||
:class="{ 'is-active': editor.isActive('italic') }"
|
||||
>
|
||||
Italic
|
||||
</button>
|
||||
<button @click="editor.chain().focus().toggleStrike().run()" :class="{ 'is-active': editor.isActive('strike') }">
|
||||
<button
|
||||
@click="editor.chain().focus().toggleStrike().run()"
|
||||
:class="{ 'is-active': editor.isActive('strike') }"
|
||||
>
|
||||
Strike
|
||||
</button>
|
||||
<button @click="editor.chain().focus().toggleCode().run()" :class="{ 'is-active': editor.isActive('code') }">
|
||||
Code
|
||||
</button>
|
||||
<button @click="editor.chain().focus().unsetAllMarks().run()">
|
||||
Clear marks
|
||||
</button>
|
||||
<button @click="editor.chain().focus().clearNodes().run()">
|
||||
Clear nodes
|
||||
</button>
|
||||
<button @click="editor.chain().focus().setParagraph().run()" :class="{ 'is-active': editor.isActive('paragraph') }">
|
||||
<button @click="editor.chain().focus().unsetAllMarks().run()">Clear marks</button>
|
||||
<button @click="editor.chain().focus().clearNodes().run()">Clear nodes</button>
|
||||
<button
|
||||
@click="editor.chain().focus().setParagraph().run()"
|
||||
:class="{ 'is-active': editor.isActive('paragraph') }"
|
||||
>
|
||||
Paragraph
|
||||
</button>
|
||||
<button @click="editor.chain().focus().toggleHeading({ level: 1 }).run()" :class="{ 'is-active': editor.isActive('heading', { level: 1 }) }">
|
||||
<button
|
||||
@click="editor.chain().focus().toggleHeading({ level: 1 }).run()"
|
||||
:class="{ 'is-active': editor.isActive('heading', { level: 1 }) }"
|
||||
>
|
||||
H1
|
||||
</button>
|
||||
<button @click="editor.chain().focus().toggleHeading({ level: 2 }).run()" :class="{ 'is-active': editor.isActive('heading', { level: 2 }) }">
|
||||
<button
|
||||
@click="editor.chain().focus().toggleHeading({ level: 2 }).run()"
|
||||
:class="{ 'is-active': editor.isActive('heading', { level: 2 }) }"
|
||||
>
|
||||
H2
|
||||
</button>
|
||||
<button @click="editor.chain().focus().toggleHeading({ level: 3 }).run()" :class="{ 'is-active': editor.isActive('heading', { level: 3 }) }">
|
||||
<button
|
||||
@click="editor.chain().focus().toggleHeading({ level: 3 }).run()"
|
||||
:class="{ 'is-active': editor.isActive('heading', { level: 3 }) }"
|
||||
>
|
||||
H3
|
||||
</button>
|
||||
<button @click="editor.chain().focus().toggleHeading({ level: 4 }).run()" :class="{ 'is-active': editor.isActive('heading', { level: 4 }) }">
|
||||
<button
|
||||
@click="editor.chain().focus().toggleHeading({ level: 4 }).run()"
|
||||
:class="{ 'is-active': editor.isActive('heading', { level: 4 }) }"
|
||||
>
|
||||
H4
|
||||
</button>
|
||||
<button @click="editor.chain().focus().toggleHeading({ level: 5 }).run()" :class="{ 'is-active': editor.isActive('heading', { level: 5 }) }">
|
||||
<button
|
||||
@click="editor.chain().focus().toggleHeading({ level: 5 }).run()"
|
||||
:class="{ 'is-active': editor.isActive('heading', { level: 5 }) }"
|
||||
>
|
||||
H5
|
||||
</button>
|
||||
<button @click="editor.chain().focus().toggleHeading({ level: 6 }).run()" :class="{ 'is-active': editor.isActive('heading', { level: 6 }) }">
|
||||
<button
|
||||
@click="editor.chain().focus().toggleHeading({ level: 6 }).run()"
|
||||
:class="{ 'is-active': editor.isActive('heading', { level: 6 }) }"
|
||||
>
|
||||
H6
|
||||
</button>
|
||||
<button @click="editor.chain().focus().toggleBulletList().run()" :class="{ 'is-active': editor.isActive('bulletList') }">
|
||||
<button
|
||||
@click="editor.chain().focus().toggleBulletList().run()"
|
||||
:class="{ 'is-active': editor.isActive('bulletList') }"
|
||||
>
|
||||
Bullet list
|
||||
</button>
|
||||
<button @click="editor.chain().focus().toggleOrderedList().run()" :class="{ 'is-active': editor.isActive('orderedList') }">
|
||||
<button
|
||||
@click="editor.chain().focus().toggleOrderedList().run()"
|
||||
:class="{ 'is-active': editor.isActive('orderedList') }"
|
||||
>
|
||||
Ordered list
|
||||
</button>
|
||||
<button @click="editor.chain().focus().toggleCodeBlock().run()" :class="{ 'is-active': editor.isActive('codeBlock') }">
|
||||
<button
|
||||
@click="editor.chain().focus().toggleCodeBlock().run()"
|
||||
:class="{ 'is-active': editor.isActive('codeBlock') }"
|
||||
>
|
||||
Code block
|
||||
</button>
|
||||
<button @click="editor.chain().focus().toggleBlockquote().run()" :class="{ 'is-active': editor.isActive('blockquote') }">
|
||||
<button
|
||||
@click="editor.chain().focus().toggleBlockquote().run()"
|
||||
:class="{ 'is-active': editor.isActive('blockquote') }"
|
||||
>
|
||||
Blockquote
|
||||
</button>
|
||||
<button @click="editor.chain().focus().setHorizontalRule().run()">
|
||||
Horizontal rule
|
||||
</button>
|
||||
<button @click="editor.chain().focus().setHardBreak().run()">
|
||||
Hard break
|
||||
</button>
|
||||
<button @click="editor.chain().focus().undo().run()">
|
||||
Undo
|
||||
</button>
|
||||
<button @click="editor.chain().focus().redo().run()">
|
||||
Redo
|
||||
</button>
|
||||
<button @click="editor.chain().focus().setHorizontalRule().run()">Horizontal rule</button>
|
||||
<button @click="editor.chain().focus().setHardBreak().run()">Hard break</button>
|
||||
<button @click="editor.chain().focus().undo().run()">Undo</button>
|
||||
<button @click="editor.chain().focus().redo().run()">Redo</button>
|
||||
</div>
|
||||
</div>
|
||||
<editor-content :editor="editor" />
|
||||
@ -90,9 +117,7 @@ export default {
|
||||
|
||||
mounted() {
|
||||
this.editor = new Editor({
|
||||
extensions: [
|
||||
StarterKit,
|
||||
],
|
||||
extensions: [StarterKit],
|
||||
content,
|
||||
editorProps: {
|
||||
attributes: {
|
||||
|
@ -37,12 +37,8 @@ const MenuBar = ({ editor }) => {
|
||||
>
|
||||
Code
|
||||
</button>
|
||||
<button onClick={() => editor.chain().focus().unsetAllMarks().run()}>
|
||||
Clear marks
|
||||
</button>
|
||||
<button onClick={() => editor.chain().focus().clearNodes().run()}>
|
||||
Clear nodes
|
||||
</button>
|
||||
<button onClick={() => editor.chain().focus().unsetAllMarks().run()}>Clear marks</button>
|
||||
<button onClick={() => editor.chain().focus().clearNodes().run()}>Clear nodes</button>
|
||||
<button
|
||||
onClick={() => editor.chain().focus().setParagraph().run()}
|
||||
className={editor.isActive('paragraph') ? 'is-active' : ''}
|
||||
@ -109,27 +105,17 @@ const MenuBar = ({ editor }) => {
|
||||
>
|
||||
Blockquote
|
||||
</button>
|
||||
<button onClick={() => editor.chain().focus().setHorizontalRule().run()}>
|
||||
Horizontal rule
|
||||
</button>
|
||||
<button onClick={() => editor.chain().focus().setHardBreak().run()}>
|
||||
Hard break
|
||||
</button>
|
||||
<button onClick={() => editor.chain().focus().undo().run()}>
|
||||
Undo
|
||||
</button>
|
||||
<button onClick={() => editor.chain().focus().redo().run()}>
|
||||
Redo
|
||||
</button>
|
||||
<button onClick={() => editor.chain().focus().setHorizontalRule().run()}>Horizontal rule</button>
|
||||
<button onClick={() => editor.chain().focus().setHardBreak().run()}>Hard break</button>
|
||||
<button onClick={() => editor.chain().focus().undo().run()}>Undo</button>
|
||||
<button onClick={() => editor.chain().focus().redo().run()}>Redo</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default () => {
|
||||
const editor = useEditor({
|
||||
extensions: [
|
||||
StarterKit,
|
||||
],
|
||||
extensions: [StarterKit],
|
||||
content: `
|
||||
<h2>
|
||||
Hi there,
|
||||
|
@ -12,57 +12,75 @@
|
||||
<button @click="editor.chain().focus().toggleCode().run()" :class="{ 'is-active': editor.isActive('code') }">
|
||||
Code
|
||||
</button>
|
||||
<button @click="editor.chain().focus().unsetAllMarks().run()">
|
||||
Clear marks
|
||||
</button>
|
||||
<button @click="editor.chain().focus().clearNodes().run()">
|
||||
Clear nodes
|
||||
</button>
|
||||
<button @click="editor.chain().focus().unsetAllMarks().run()">Clear marks</button>
|
||||
<button @click="editor.chain().focus().clearNodes().run()">Clear nodes</button>
|
||||
<button @click="editor.chain().focus().setParagraph().run()" :class="{ 'is-active': editor.isActive('paragraph') }">
|
||||
Paragraph
|
||||
</button>
|
||||
<button @click="editor.chain().focus().toggleHeading({ level: 1 }).run()" :class="{ 'is-active': editor.isActive('heading', { level: 1 }) }">
|
||||
<button
|
||||
@click="editor.chain().focus().toggleHeading({ level: 1 }).run()"
|
||||
:class="{ 'is-active': editor.isActive('heading', { level: 1 }) }"
|
||||
>
|
||||
H1
|
||||
</button>
|
||||
<button @click="editor.chain().focus().toggleHeading({ level: 2 }).run()" :class="{ 'is-active': editor.isActive('heading', { level: 2 }) }">
|
||||
<button
|
||||
@click="editor.chain().focus().toggleHeading({ level: 2 }).run()"
|
||||
:class="{ 'is-active': editor.isActive('heading', { level: 2 }) }"
|
||||
>
|
||||
H2
|
||||
</button>
|
||||
<button @click="editor.chain().focus().toggleHeading({ level: 3 }).run()" :class="{ 'is-active': editor.isActive('heading', { level: 3 }) }">
|
||||
<button
|
||||
@click="editor.chain().focus().toggleHeading({ level: 3 }).run()"
|
||||
:class="{ 'is-active': editor.isActive('heading', { level: 3 }) }"
|
||||
>
|
||||
H3
|
||||
</button>
|
||||
<button @click="editor.chain().focus().toggleHeading({ level: 4 }).run()" :class="{ 'is-active': editor.isActive('heading', { level: 4 }) }">
|
||||
<button
|
||||
@click="editor.chain().focus().toggleHeading({ level: 4 }).run()"
|
||||
:class="{ 'is-active': editor.isActive('heading', { level: 4 }) }"
|
||||
>
|
||||
H4
|
||||
</button>
|
||||
<button @click="editor.chain().focus().toggleHeading({ level: 5 }).run()" :class="{ 'is-active': editor.isActive('heading', { level: 5 }) }">
|
||||
<button
|
||||
@click="editor.chain().focus().toggleHeading({ level: 5 }).run()"
|
||||
:class="{ 'is-active': editor.isActive('heading', { level: 5 }) }"
|
||||
>
|
||||
H5
|
||||
</button>
|
||||
<button @click="editor.chain().focus().toggleHeading({ level: 6 }).run()" :class="{ 'is-active': editor.isActive('heading', { level: 6 }) }">
|
||||
<button
|
||||
@click="editor.chain().focus().toggleHeading({ level: 6 }).run()"
|
||||
:class="{ 'is-active': editor.isActive('heading', { level: 6 }) }"
|
||||
>
|
||||
H6
|
||||
</button>
|
||||
<button @click="editor.chain().focus().toggleBulletList().run()" :class="{ 'is-active': editor.isActive('bulletList') }">
|
||||
<button
|
||||
@click="editor.chain().focus().toggleBulletList().run()"
|
||||
:class="{ 'is-active': editor.isActive('bulletList') }"
|
||||
>
|
||||
Bullet list
|
||||
</button>
|
||||
<button @click="editor.chain().focus().toggleOrderedList().run()" :class="{ 'is-active': editor.isActive('orderedList') }">
|
||||
<button
|
||||
@click="editor.chain().focus().toggleOrderedList().run()"
|
||||
:class="{ 'is-active': editor.isActive('orderedList') }"
|
||||
>
|
||||
Ordered list
|
||||
</button>
|
||||
<button @click="editor.chain().focus().toggleCodeBlock().run()" :class="{ 'is-active': editor.isActive('codeBlock') }">
|
||||
<button
|
||||
@click="editor.chain().focus().toggleCodeBlock().run()"
|
||||
:class="{ 'is-active': editor.isActive('codeBlock') }"
|
||||
>
|
||||
Code block
|
||||
</button>
|
||||
<button @click="editor.chain().focus().toggleBlockquote().run()" :class="{ 'is-active': editor.isActive('blockquote') }">
|
||||
<button
|
||||
@click="editor.chain().focus().toggleBlockquote().run()"
|
||||
:class="{ 'is-active': editor.isActive('blockquote') }"
|
||||
>
|
||||
Blockquote
|
||||
</button>
|
||||
<button @click="editor.chain().focus().setHorizontalRule().run()">
|
||||
Horizontal rule
|
||||
</button>
|
||||
<button @click="editor.chain().focus().setHardBreak().run()">
|
||||
Hard break
|
||||
</button>
|
||||
<button @click="editor.chain().focus().undo().run()">
|
||||
Undo
|
||||
</button>
|
||||
<button @click="editor.chain().focus().redo().run()">
|
||||
Redo
|
||||
</button>
|
||||
<button @click="editor.chain().focus().setHorizontalRule().run()">Horizontal rule</button>
|
||||
<button @click="editor.chain().focus().setHardBreak().run()">Hard break</button>
|
||||
<button @click="editor.chain().focus().undo().run()">Undo</button>
|
||||
<button @click="editor.chain().focus().redo().run()">Redo</button>
|
||||
</div>
|
||||
<editor-content :editor="editor" />
|
||||
</template>
|
||||
@ -87,9 +105,7 @@ export default {
|
||||
|
||||
mounted() {
|
||||
this.editor = new Editor({
|
||||
extensions: [
|
||||
StarterKit,
|
||||
],
|
||||
extensions: [StarterKit],
|
||||
content: `
|
||||
<h1 class="test">
|
||||
This is a red headline
|
||||
|
@ -3,15 +3,21 @@ import './CodeBlockComponent.scss'
|
||||
import { NodeViewContent, NodeViewWrapper } from '@tiptap/react'
|
||||
import React from 'react'
|
||||
|
||||
export default ({ node: { attrs: { language: defaultLanguage } }, updateAttributes, extension }) => (
|
||||
export default ({
|
||||
node: {
|
||||
attrs: { language: defaultLanguage },
|
||||
},
|
||||
updateAttributes,
|
||||
extension,
|
||||
}) => (
|
||||
<NodeViewWrapper className="code-block">
|
||||
<select contentEditable={false} defaultValue={defaultLanguage} onChange={event => updateAttributes({ language: event.target.value })}>
|
||||
<option value="null">
|
||||
auto
|
||||
</option>
|
||||
<option disabled>
|
||||
—
|
||||
</option>
|
||||
<select
|
||||
contentEditable={false}
|
||||
defaultValue={defaultLanguage}
|
||||
onChange={event => updateAttributes({ language: event.target.value })}
|
||||
>
|
||||
<option value="null">auto</option>
|
||||
<option disabled>—</option>
|
||||
{extension.options.lowlight.listLanguages().map((lang, index) => (
|
||||
<option key={index} value={lang}>
|
||||
{lang}
|
||||
|
@ -33,7 +33,10 @@ const MenuBar = ({ editor }) => {
|
||||
return (
|
||||
<div className="control-group">
|
||||
<div className="button-group">
|
||||
<button onClick={() => editor.chain().focus().toggleCodeBlock().run()} className={editor.isActive('codeBlock') ? 'is-active' : ''}>
|
||||
<button
|
||||
onClick={() => editor.chain().focus().toggleCodeBlock().run()}
|
||||
className={editor.isActive('codeBlock') ? 'is-active' : ''}
|
||||
>
|
||||
Toggle code block
|
||||
</button>
|
||||
</div>
|
||||
@ -47,13 +50,11 @@ export default () => {
|
||||
Document,
|
||||
Paragraph,
|
||||
Text,
|
||||
CodeBlockLowlight
|
||||
.extend({
|
||||
addNodeView() {
|
||||
return ReactNodeViewRenderer(CodeBlockComponent)
|
||||
},
|
||||
})
|
||||
.configure({ lowlight }),
|
||||
CodeBlockLowlight.extend({
|
||||
addNodeView() {
|
||||
return ReactNodeViewRenderer(CodeBlockComponent)
|
||||
},
|
||||
}).configure({ lowlight }),
|
||||
],
|
||||
content: `
|
||||
<p>
|
||||
|
@ -1,12 +1,8 @@
|
||||
<template>
|
||||
<node-view-wrapper class="code-block">
|
||||
<select contenteditable="false" v-model="selectedLanguage">
|
||||
<option :value="null">
|
||||
auto
|
||||
</option>
|
||||
<option disabled>
|
||||
—
|
||||
</option>
|
||||
<option :value="null">auto</option>
|
||||
<option disabled>—</option>
|
||||
<option v-for="(language, index) in languages" :value="language" :key="index">
|
||||
{{ language }}
|
||||
</option>
|
||||
|
@ -2,7 +2,10 @@
|
||||
<div v-if="editor" class="container">
|
||||
<div class="control-group">
|
||||
<div class="button-">
|
||||
<button @click="editor.chain().focus().toggleCodeBlock().run()" :class="{ 'is-active': editor.isActive('codeBlock') }">
|
||||
<button
|
||||
@click="editor.chain().focus().toggleCodeBlock().run()"
|
||||
:class="{ 'is-active': editor.isActive('codeBlock') }"
|
||||
>
|
||||
Toggle code block
|
||||
</button>
|
||||
</div>
|
||||
@ -52,13 +55,11 @@ export default {
|
||||
Document,
|
||||
Paragraph,
|
||||
Text,
|
||||
CodeBlockLowlight
|
||||
.extend({
|
||||
addNodeView() {
|
||||
return VueNodeViewRenderer(CodeBlockComponent)
|
||||
},
|
||||
})
|
||||
.configure({ lowlight }),
|
||||
CodeBlockLowlight.extend({
|
||||
addNodeView() {
|
||||
return VueNodeViewRenderer(CodeBlockComponent)
|
||||
},
|
||||
}).configure({ lowlight }),
|
||||
],
|
||||
content: `
|
||||
<p>
|
||||
|
@ -106,8 +106,7 @@ export default ({ editor }) => {
|
||||
{
|
||||
icon: 'format-clear',
|
||||
title: 'Clear format',
|
||||
action: () => editor.chain().focus().clearNodes().unsetAllMarks()
|
||||
.run(),
|
||||
action: () => editor.chain().focus().clearNodes().unsetAllMarks().run(),
|
||||
},
|
||||
{
|
||||
type: 'divider',
|
||||
|
@ -3,14 +3,8 @@ import './MenuItem.scss'
|
||||
import React from 'react'
|
||||
import remixiconUrl from 'remixicon/fonts/remixicon.symbol.svg'
|
||||
|
||||
export default ({
|
||||
icon, title, action, isActive = null,
|
||||
}) => (
|
||||
<button
|
||||
className={`menu-item${isActive && isActive() ? ' is-active' : ''}`}
|
||||
onClick={action}
|
||||
title={title}
|
||||
>
|
||||
export default ({ icon, title, action, isActive = null }) => (
|
||||
<button className={`menu-item${isActive && isActive() ? ' is-active' : ''}`} onClick={action} title={title}>
|
||||
<svg className="remix">
|
||||
<use xlinkHref={`${remixiconUrl}#ri-${icon}`} />
|
||||
</svg>
|
||||
|
@ -9,10 +9,7 @@ import TaskItem from '@tiptap/extension-task-item'
|
||||
import TaskList from '@tiptap/extension-task-list'
|
||||
import { EditorContent, useEditor } from '@tiptap/react'
|
||||
import StarterKit from '@tiptap/starter-kit'
|
||||
import React, {
|
||||
useCallback, useEffect,
|
||||
useState,
|
||||
} from 'react'
|
||||
import React, { useCallback, useEffect, useState } from 'react'
|
||||
import * as Y from 'yjs'
|
||||
|
||||
import { variables } from '../../../variables.js'
|
||||
@ -67,10 +64,12 @@ const websocketProvider = new TiptapCollabProvider({
|
||||
})
|
||||
|
||||
const getInitialUser = () => {
|
||||
return JSON.parse(localStorage.getItem('currentUser')) || {
|
||||
name: getRandomName(),
|
||||
color: getRandomColor(),
|
||||
}
|
||||
return (
|
||||
JSON.parse(localStorage.getItem('currentUser')) || {
|
||||
name: getRandomName(),
|
||||
color: getRandomColor(),
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
export default () => {
|
||||
|
@ -27,7 +27,7 @@
|
||||
background: #0d0d0d;
|
||||
border-radius: 0.5rem;
|
||||
color: #fff;
|
||||
font-family: "JetBrainsMono", monospace;
|
||||
font-family: 'JetBrainsMono', monospace;
|
||||
padding: 0.75rem 1rem;
|
||||
|
||||
code {
|
||||
@ -62,7 +62,7 @@
|
||||
margin: 2rem 0;
|
||||
}
|
||||
|
||||
ul[data-type="taskList"] {
|
||||
ul[data-type='taskList'] {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
|
||||
@ -135,7 +135,7 @@
|
||||
&::before {
|
||||
background: rgba(#0d0d0d, 0.5);
|
||||
border-radius: 50%;
|
||||
content: " ";
|
||||
content: ' ';
|
||||
display: inline-block;
|
||||
flex: 0 0 auto;
|
||||
height: 0.5rem;
|
||||
|
@ -125,11 +125,7 @@ export default {
|
||||
{
|
||||
icon: 'format-clear',
|
||||
title: 'Clear format',
|
||||
action: () => this.editor.chain()
|
||||
.focus()
|
||||
.clearNodes()
|
||||
.unsetAllMarks()
|
||||
.run(),
|
||||
action: () => this.editor.chain().focus().clearNodes().unsetAllMarks().run(),
|
||||
},
|
||||
{
|
||||
type: 'divider',
|
||||
|
@ -1,10 +1,5 @@
|
||||
<template>
|
||||
<button
|
||||
class="menu-item"
|
||||
:class="{ 'is-active': isActive ? isActive(): null }"
|
||||
@click="action"
|
||||
:title="title"
|
||||
>
|
||||
<button class="menu-item" :class="{ 'is-active': isActive ? isActive() : null }" @click="action" :title="title">
|
||||
<svg class="remix">
|
||||
<use :xlink:href="`${remixiconUrl}#ri-${icon}`" />
|
||||
</svg>
|
||||
|
@ -5,11 +5,12 @@
|
||||
<div class="editor__footer">
|
||||
<div :class="`editor__status editor__status--${status}`">
|
||||
<template v-if="status === 'connected'">
|
||||
{{ editor.storage.collaborationCursor.users.length }} user{{ editor.storage.collaborationCursor.users.length === 1 ? '' : 's' }} online in {{ room }}
|
||||
</template>
|
||||
<template v-else>
|
||||
offline
|
||||
{{ editor.storage.collaborationCursor.users.length }} user{{
|
||||
editor.storage.collaborationCursor.users.length === 1 ? '' : 's'
|
||||
}}
|
||||
online in {{ room }}
|
||||
</template>
|
||||
<template v-else> offline </template>
|
||||
</div>
|
||||
<div class="editor__name">
|
||||
<button @click="setName">
|
||||
@ -103,9 +104,7 @@ export default {
|
||||
|
||||
methods: {
|
||||
setName() {
|
||||
const name = (window.prompt('Name') || '')
|
||||
.trim()
|
||||
.substring(0, 32)
|
||||
const name = (window.prompt('Name') || '').trim().substring(0, 32)
|
||||
|
||||
if (name) {
|
||||
return this.updateCurrentUser({
|
||||
@ -122,20 +121,36 @@ export default {
|
||||
},
|
||||
|
||||
getRandomColor() {
|
||||
return getRandomElement([
|
||||
'#958DF1',
|
||||
'#F98181',
|
||||
'#FBBC88',
|
||||
'#FAF594',
|
||||
'#70CFF8',
|
||||
'#94FADB',
|
||||
'#B9F18D',
|
||||
])
|
||||
return getRandomElement(['#958DF1', '#F98181', '#FBBC88', '#FAF594', '#70CFF8', '#94FADB', '#B9F18D'])
|
||||
},
|
||||
|
||||
getRandomName() {
|
||||
return getRandomElement([
|
||||
'Lea Thompson', 'Cyndi Lauper', 'Tom Cruise', 'Madonna', 'Jerry Hall', 'Joan Collins', 'Winona Ryder', 'Christina Applegate', 'Alyssa Milano', 'Molly Ringwald', 'Ally Sheedy', 'Debbie Harry', 'Olivia Newton-John', 'Elton John', 'Michael J. Fox', 'Axl Rose', 'Emilio Estevez', 'Ralph Macchio', 'Rob Lowe', 'Jennifer Grey', 'Mickey Rourke', 'John Cusack', 'Matthew Broderick', 'Justine Bateman', 'Lisa Bonet',
|
||||
'Lea Thompson',
|
||||
'Cyndi Lauper',
|
||||
'Tom Cruise',
|
||||
'Madonna',
|
||||
'Jerry Hall',
|
||||
'Joan Collins',
|
||||
'Winona Ryder',
|
||||
'Christina Applegate',
|
||||
'Alyssa Milano',
|
||||
'Molly Ringwald',
|
||||
'Ally Sheedy',
|
||||
'Debbie Harry',
|
||||
'Olivia Newton-John',
|
||||
'Elton John',
|
||||
'Michael J. Fox',
|
||||
'Axl Rose',
|
||||
'Emilio Estevez',
|
||||
'Ralph Macchio',
|
||||
'Rob Lowe',
|
||||
'Jennifer Grey',
|
||||
'Mickey Rourke',
|
||||
'John Cusack',
|
||||
'Matthew Broderick',
|
||||
'Justine Bateman',
|
||||
'Lisa Bonet',
|
||||
])
|
||||
},
|
||||
},
|
||||
@ -149,10 +164,10 @@ export default {
|
||||
|
||||
<style lang="scss">
|
||||
.editor {
|
||||
background-color: #FFF;
|
||||
border: 3px solid #0D0D0D;
|
||||
background-color: #fff;
|
||||
border: 3px solid #0d0d0d;
|
||||
border-radius: 0.75rem;
|
||||
color: #0D0D0D;
|
||||
color: #0d0d0d;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-height: 26rem;
|
||||
@ -179,8 +194,8 @@ export default {
|
||||
|
||||
&__footer {
|
||||
align-items: center;
|
||||
border-top: 3px solid #0D0D0D;
|
||||
color: #0D0D0D;
|
||||
border-top: 3px solid #0d0d0d;
|
||||
color: #0d0d0d;
|
||||
display: flex;
|
||||
flex: 0 0 auto;
|
||||
flex-wrap: wrap;
|
||||
@ -198,7 +213,7 @@ export default {
|
||||
display: flex;
|
||||
|
||||
&::before {
|
||||
background: rgba(#0D0D0D, 0.5);
|
||||
background: rgba(#0d0d0d, 0.5);
|
||||
border-radius: 50%;
|
||||
content: ' ';
|
||||
display: inline-block;
|
||||
@ -213,7 +228,7 @@ export default {
|
||||
}
|
||||
|
||||
&--connected::before {
|
||||
background: #B9F18D;
|
||||
background: #b9f18d;
|
||||
}
|
||||
}
|
||||
|
||||
@ -222,15 +237,15 @@ export default {
|
||||
background: none;
|
||||
border: none;
|
||||
border-radius: 0.4rem;
|
||||
color: #0D0D0D;
|
||||
color: #0d0d0d;
|
||||
font: inherit;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
padding: 0.25rem 0.5rem;
|
||||
|
||||
&:hover {
|
||||
background-color: #0D0D0D;
|
||||
color: #FFF;
|
||||
background-color: #0d0d0d;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -238,8 +253,8 @@ export default {
|
||||
|
||||
/* Give a remote user a caret */
|
||||
.collaboration-cursor__caret {
|
||||
border-left: 1px solid #0D0D0D;
|
||||
border-right: 1px solid #0D0D0D;
|
||||
border-left: 1px solid #0d0d0d;
|
||||
border-right: 1px solid #0d0d0d;
|
||||
margin-left: -1px;
|
||||
margin-right: -1px;
|
||||
pointer-events: none;
|
||||
@ -250,7 +265,7 @@ export default {
|
||||
/* Render the username above the caret */
|
||||
.collaboration-cursor__label {
|
||||
border-radius: 3px 3px 3px 0;
|
||||
color: #0D0D0D;
|
||||
color: #0d0d0d;
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
@ -289,9 +304,9 @@ export default {
|
||||
}
|
||||
|
||||
pre {
|
||||
background: #0D0D0D;
|
||||
background: #0d0d0d;
|
||||
border-radius: 0.5rem;
|
||||
color: #FFF;
|
||||
color: #fff;
|
||||
font-family: 'JetBrainsMono', monospace;
|
||||
padding: 0.75rem 1rem;
|
||||
|
||||
@ -304,7 +319,7 @@ export default {
|
||||
}
|
||||
|
||||
mark {
|
||||
background-color: #FAF594;
|
||||
background-color: #faf594;
|
||||
}
|
||||
|
||||
img {
|
||||
@ -317,17 +332,17 @@ export default {
|
||||
}
|
||||
|
||||
blockquote {
|
||||
border-left: 2px solid rgba(#0D0D0D, 0.1);
|
||||
border-left: 2px solid rgba(#0d0d0d, 0.1);
|
||||
padding-left: 1rem;
|
||||
}
|
||||
|
||||
hr {
|
||||
border: none;
|
||||
border-top: 2px solid rgba(#0D0D0D, 0.1);
|
||||
border-top: 2px solid rgba(#0d0d0d, 0.1);
|
||||
margin: 2rem 0;
|
||||
}
|
||||
|
||||
ul[data-type="taskList"] {
|
||||
ul[data-type='taskList'] {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
|
||||
|
@ -1,11 +1,6 @@
|
||||
import './MentionList.scss'
|
||||
|
||||
import React, {
|
||||
forwardRef,
|
||||
useEffect,
|
||||
useImperativeHandle,
|
||||
useState,
|
||||
} from 'react'
|
||||
import React, { forwardRef, useEffect, useImperativeHandle, useState } from 'react'
|
||||
|
||||
export const MentionList = forwardRef((props, ref) => {
|
||||
const [selectedIndex, setSelectedIndex] = useState(0)
|
||||
@ -19,7 +14,7 @@ export const MentionList = forwardRef((props, ref) => {
|
||||
}
|
||||
|
||||
const upHandler = () => {
|
||||
setSelectedIndex(((selectedIndex + props.items.length) - 1) % props.items.length)
|
||||
setSelectedIndex((selectedIndex + props.items.length - 1) % props.items.length)
|
||||
}
|
||||
|
||||
const downHandler = () => {
|
||||
@ -55,8 +50,8 @@ export const MentionList = forwardRef((props, ref) => {
|
||||
|
||||
return (
|
||||
<div className="dropdown-menu">
|
||||
{props.items.length
|
||||
? props.items.map((item, index) => (
|
||||
{props.items.length ? (
|
||||
props.items.map((item, index) => (
|
||||
<button
|
||||
className={index === selectedIndex ? 'is-selected' : ''}
|
||||
key={index}
|
||||
@ -65,8 +60,9 @@ export const MentionList = forwardRef((props, ref) => {
|
||||
{item}
|
||||
</button>
|
||||
))
|
||||
: <div className="item">No result</div>
|
||||
}
|
||||
) : (
|
||||
<div className="item">No result</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
})
|
||||
|
@ -44,26 +44,17 @@ export default () => {
|
||||
},
|
||||
})
|
||||
|
||||
const percentage = editor
|
||||
? Math.round((100 / limit) * characterCount)
|
||||
: 0
|
||||
const percentage = editor ? Math.round((100 / limit) * characterCount) : 0
|
||||
|
||||
return (
|
||||
<>
|
||||
<EditorContent editor={editor} />
|
||||
{editor
|
||||
&& <div className={`character-count ${editor.storage.characterCount.characters() === limit ? 'character-count--warning' : ''}`}>
|
||||
<svg
|
||||
height="20"
|
||||
width="20"
|
||||
viewBox="0 0 20 20"
|
||||
>
|
||||
<circle
|
||||
r="10"
|
||||
cx="10"
|
||||
cy="10"
|
||||
fill="#e9ecef"
|
||||
/>
|
||||
{editor && (
|
||||
<div
|
||||
className={`character-count ${editor.storage.characterCount.characters() === limit ? 'character-count--warning' : ''}`}
|
||||
>
|
||||
<svg height="20" width="20" viewBox="0 0 20 20">
|
||||
<circle r="10" cx="10" cy="10" fill="#e9ecef" />
|
||||
<circle
|
||||
r="5"
|
||||
cx="10"
|
||||
@ -74,17 +65,11 @@ export default () => {
|
||||
strokeDasharray={`calc(${percentage} * 31.4 / 100) 31.4`}
|
||||
transform="rotate(-90) translate(-20)"
|
||||
/>
|
||||
<circle
|
||||
r="6"
|
||||
cx="10"
|
||||
cy="10"
|
||||
fill="white"
|
||||
/>
|
||||
<circle r="6" cx="10" cy="10" fill="white" />
|
||||
</svg>
|
||||
|
||||
{editor.storage.characterCount.characters()} / {limit} characters
|
||||
</div>
|
||||
}
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
@ -23,15 +23,16 @@ context('/src/Examples/Community/React/', () => {
|
||||
cy.get('.dropdown-menu').should('be.visible')
|
||||
|
||||
// select the first user
|
||||
cy.get('.dropdown-menu button').first().then($el => {
|
||||
const name = $el.text()
|
||||
cy.get('.dropdown-menu button')
|
||||
.first()
|
||||
.then($el => {
|
||||
const name = $el.text()
|
||||
|
||||
$el.click()
|
||||
|
||||
// check if the user is mentioned
|
||||
cy.get('.tiptap').should('have.text', `@${name} `)
|
||||
cy.get('.character-count').should('contain', '2 / 280 characters')
|
||||
})
|
||||
$el.click()
|
||||
|
||||
// check if the user is mentioned
|
||||
cy.get('.tiptap').should('have.text', `@${name} `)
|
||||
cy.get('.character-count').should('contain', '2 / 280 characters')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -19,7 +19,7 @@
|
||||
color: var(--gray-5);
|
||||
display: flex;
|
||||
font-size: 0.75rem;
|
||||
gap: .5rem;
|
||||
gap: 0.5rem;
|
||||
margin: 1.5rem;
|
||||
|
||||
svg {
|
||||
|
@ -1,8 +1,4 @@
|
||||
import {
|
||||
computePosition,
|
||||
flip,
|
||||
shift,
|
||||
} from '@floating-ui/dom'
|
||||
import { computePosition, flip, shift } from '@floating-ui/dom'
|
||||
import { posToDOMRect, ReactRenderer } from '@tiptap/react'
|
||||
|
||||
import { MentionList } from './MentionList.jsx'
|
||||
@ -27,8 +23,34 @@ const updatePosition = (editor, element) => {
|
||||
export default {
|
||||
items: ({ query }) => {
|
||||
return [
|
||||
'Lea Thompson', 'Cyndi Lauper', 'Tom Cruise', 'Madonna', 'Jerry Hall', 'Joan Collins', 'Winona Ryder', 'Christina Applegate', 'Alyssa Milano', 'Molly Ringwald', 'Ally Sheedy', 'Debbie Harry', 'Olivia Newton-John', 'Elton John', 'Michael J. Fox', 'Axl Rose', 'Emilio Estevez', 'Ralph Macchio', 'Rob Lowe', 'Jennifer Grey', 'Mickey Rourke', 'John Cusack', 'Matthew Broderick', 'Justine Bateman', 'Lisa Bonet',
|
||||
].filter(item => item.toLowerCase().startsWith(query.toLowerCase())).slice(0, 5)
|
||||
'Lea Thompson',
|
||||
'Cyndi Lauper',
|
||||
'Tom Cruise',
|
||||
'Madonna',
|
||||
'Jerry Hall',
|
||||
'Joan Collins',
|
||||
'Winona Ryder',
|
||||
'Christina Applegate',
|
||||
'Alyssa Milano',
|
||||
'Molly Ringwald',
|
||||
'Ally Sheedy',
|
||||
'Debbie Harry',
|
||||
'Olivia Newton-John',
|
||||
'Elton John',
|
||||
'Michael J. Fox',
|
||||
'Axl Rose',
|
||||
'Emilio Estevez',
|
||||
'Ralph Macchio',
|
||||
'Rob Lowe',
|
||||
'Jennifer Grey',
|
||||
'Mickey Rourke',
|
||||
'John Cusack',
|
||||
'Matthew Broderick',
|
||||
'Justine Bateman',
|
||||
'Lisa Bonet',
|
||||
]
|
||||
.filter(item => item.toLowerCase().startsWith(query.toLowerCase()))
|
||||
.slice(0, 5)
|
||||
},
|
||||
|
||||
render: () => {
|
||||
@ -36,7 +58,6 @@ export default {
|
||||
|
||||
return {
|
||||
onStart: props => {
|
||||
|
||||
if (!props.clientRect) {
|
||||
return
|
||||
}
|
||||
|
@ -10,9 +10,7 @@
|
||||
{{ item }}
|
||||
</button>
|
||||
</template>
|
||||
<div class="item" v-else>
|
||||
No result
|
||||
</div>
|
||||
<div class="item" v-else>No result</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -63,7 +61,7 @@ export default {
|
||||
},
|
||||
|
||||
upHandler() {
|
||||
this.selectedIndex = ((this.selectedIndex + this.items.length) - 1) % this.items.length
|
||||
this.selectedIndex = (this.selectedIndex + this.items.length - 1) % this.items.length
|
||||
},
|
||||
|
||||
downHandler() {
|
||||
|
@ -23,15 +23,16 @@ context('/src/Examples/Community/Vue/', () => {
|
||||
cy.get('.dropdown-menu').should('be.visible')
|
||||
|
||||
// select the first user
|
||||
cy.get('.dropdown-menu button').first().then($el => {
|
||||
const name = $el.text()
|
||||
cy.get('.dropdown-menu button')
|
||||
.first()
|
||||
.then($el => {
|
||||
const name = $el.text()
|
||||
|
||||
$el.click()
|
||||
|
||||
// check if the user is mentioned
|
||||
cy.get('.tiptap').should('have.text', `@${name} `)
|
||||
cy.get('.character-count').should('contain', '2 / 280 characters')
|
||||
})
|
||||
$el.click()
|
||||
|
||||
// check if the user is mentioned
|
||||
cy.get('.tiptap').should('have.text', `@${name} `)
|
||||
cy.get('.character-count').should('contain', '2 / 280 characters')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -1,18 +1,15 @@
|
||||
<template>
|
||||
<editor-content :editor="editor" />
|
||||
|
||||
<div v-if="editor" :class="{'character-count': true, 'character-count--warning': editor.storage.characterCount.characters() === limit}">
|
||||
<svg
|
||||
height="20"
|
||||
width="20"
|
||||
viewBox="0 0 20 20"
|
||||
>
|
||||
<circle
|
||||
r="10"
|
||||
cx="10"
|
||||
cy="10"
|
||||
fill="#e9ecef"
|
||||
/>
|
||||
<div
|
||||
v-if="editor"
|
||||
:class="{
|
||||
'character-count': true,
|
||||
'character-count--warning': editor.storage.characterCount.characters() === limit,
|
||||
}"
|
||||
>
|
||||
<svg height="20" width="20" viewBox="0 0 20 20">
|
||||
<circle r="10" cx="10" cy="10" fill="#e9ecef" />
|
||||
<circle
|
||||
r="5"
|
||||
cx="10"
|
||||
@ -23,12 +20,7 @@
|
||||
:stroke-dasharray="`calc(${percentage} * 31.4 / 100) 31.4`"
|
||||
transform="rotate(-90) translate(-20)"
|
||||
/>
|
||||
<circle
|
||||
r="6"
|
||||
cx="10"
|
||||
cy="10"
|
||||
fill="white"
|
||||
/>
|
||||
<circle r="6" cx="10" cy="10" fill="white" />
|
||||
</svg>
|
||||
|
||||
{{ editor.storage.characterCount.characters() }} / {{ limit }} characters
|
||||
@ -115,7 +107,7 @@ export default {
|
||||
color: var(--gray-5);
|
||||
display: flex;
|
||||
font-size: 0.75rem;
|
||||
gap: .5rem;
|
||||
gap: 0.5rem;
|
||||
margin: 1.5rem;
|
||||
|
||||
svg {
|
||||
|
@ -1,8 +1,4 @@
|
||||
import {
|
||||
computePosition,
|
||||
flip,
|
||||
shift,
|
||||
} from '@floating-ui/dom'
|
||||
import { computePosition, flip, shift } from '@floating-ui/dom'
|
||||
import { posToDOMRect, VueRenderer } from '@tiptap/vue-3'
|
||||
|
||||
import MentionList from './MentionList.vue'
|
||||
@ -27,8 +23,34 @@ const updatePosition = (editor, element) => {
|
||||
export default {
|
||||
items: ({ query }) => {
|
||||
return [
|
||||
'Lea Thompson', 'Cyndi Lauper', 'Tom Cruise', 'Madonna', 'Jerry Hall', 'Joan Collins', 'Winona Ryder', 'Christina Applegate', 'Alyssa Milano', 'Molly Ringwald', 'Ally Sheedy', 'Debbie Harry', 'Olivia Newton-John', 'Elton John', 'Michael J. Fox', 'Axl Rose', 'Emilio Estevez', 'Ralph Macchio', 'Rob Lowe', 'Jennifer Grey', 'Mickey Rourke', 'John Cusack', 'Matthew Broderick', 'Justine Bateman', 'Lisa Bonet',
|
||||
].filter(item => item.toLowerCase().startsWith(query.toLowerCase())).slice(0, 5)
|
||||
'Lea Thompson',
|
||||
'Cyndi Lauper',
|
||||
'Tom Cruise',
|
||||
'Madonna',
|
||||
'Jerry Hall',
|
||||
'Joan Collins',
|
||||
'Winona Ryder',
|
||||
'Christina Applegate',
|
||||
'Alyssa Milano',
|
||||
'Molly Ringwald',
|
||||
'Ally Sheedy',
|
||||
'Debbie Harry',
|
||||
'Olivia Newton-John',
|
||||
'Elton John',
|
||||
'Michael J. Fox',
|
||||
'Axl Rose',
|
||||
'Emilio Estevez',
|
||||
'Ralph Macchio',
|
||||
'Rob Lowe',
|
||||
'Jennifer Grey',
|
||||
'Mickey Rourke',
|
||||
'John Cusack',
|
||||
'Matthew Broderick',
|
||||
'Justine Bateman',
|
||||
'Lisa Bonet',
|
||||
]
|
||||
.filter(item => item.toLowerCase().startsWith(query.toLowerCase()))
|
||||
.slice(0, 5)
|
||||
},
|
||||
|
||||
render: () => {
|
||||
|
@ -37,7 +37,5 @@ export default () => {
|
||||
`,
|
||||
})
|
||||
|
||||
return (
|
||||
<EditorContent editor={editor} />
|
||||
)
|
||||
return <EditorContent editor={editor} />
|
||||
}
|
||||
|
@ -12,12 +12,16 @@ context('/src/Examples/CustomDocument/React/', () => {
|
||||
|
||||
it('should have a headline and a paragraph', () => {
|
||||
cy.get('.tiptap h1').should('exist').should('have.text', 'It’ll always have a heading …')
|
||||
cy.get('.tiptap p').should('exist').should('have.text', '… if you pass a custom document. That’s the beauty of having full control over the schema.')
|
||||
cy.get('.tiptap p')
|
||||
.should('exist')
|
||||
.should('have.text', '… if you pass a custom document. That’s the beauty of having full control over the schema.')
|
||||
})
|
||||
|
||||
it('should have a tooltip for a paragraph on a new line', () => {
|
||||
cy.get('.tiptap').type('{enter}')
|
||||
cy.get('.tiptap p[data-placeholder]').should('exist').should('have.attr', 'data-placeholder', 'Can you add some further context?')
|
||||
cy.get('.tiptap p[data-placeholder]')
|
||||
.should('exist')
|
||||
.should('have.attr', 'data-placeholder', 'Can you add some further context?')
|
||||
})
|
||||
|
||||
it('should have a headline after clearing the document', () => {
|
||||
@ -31,16 +35,12 @@ context('/src/Examples/CustomDocument/React/', () => {
|
||||
|
||||
it('should have a headline after clearing the document & enter paragraph automatically after adding a headline', () => {
|
||||
cy.get('.tiptap').type('{selectall}{backspace}Hello world{enter}')
|
||||
cy.get('.tiptap h1')
|
||||
.should('exist')
|
||||
.should('have.text', 'Hello world')
|
||||
cy.get('.tiptap h1').should('exist').should('have.text', 'Hello world')
|
||||
cy.get('.tiptap p[data-placeholder]')
|
||||
.should('exist')
|
||||
.should('have.attr', 'data-placeholder', 'Can you add some further context?')
|
||||
|
||||
cy.get('.tiptap').type('This is a paragraph for this test document')
|
||||
cy.get('.tiptap p')
|
||||
.should('exist')
|
||||
.should('have.text', 'This is a paragraph for this test document')
|
||||
cy.get('.tiptap p').should('exist').should('have.text', 'This is a paragraph for this test document')
|
||||
})
|
||||
})
|
||||
|
@ -12,12 +12,16 @@ context('/src/Examples/CustomDocument/Vue/', () => {
|
||||
|
||||
it('should have a headline and a paragraph', () => {
|
||||
cy.get('.tiptap h1').should('exist').should('have.text', 'It’ll always have a heading …')
|
||||
cy.get('.tiptap p').should('exist').should('have.text', '… if you pass a custom document. That’s the beauty of having full control over the schema.')
|
||||
cy.get('.tiptap p')
|
||||
.should('exist')
|
||||
.should('have.text', '… if you pass a custom document. That’s the beauty of having full control over the schema.')
|
||||
})
|
||||
|
||||
it('should have a tooltip for a paragraph on a new line', () => {
|
||||
cy.get('.tiptap').type('{enter}')
|
||||
cy.get('.tiptap p[data-placeholder]').should('exist').should('have.attr', 'data-placeholder', 'Can you add some further context?')
|
||||
cy.get('.tiptap p[data-placeholder]')
|
||||
.should('exist')
|
||||
.should('have.attr', 'data-placeholder', 'Can you add some further context?')
|
||||
})
|
||||
|
||||
it('should have a headline after clearing the document', () => {
|
||||
@ -33,16 +37,12 @@ context('/src/Examples/CustomDocument/Vue/', () => {
|
||||
it('should have a headline after clearing the document & enter paragraph automatically after adding a headline', () => {
|
||||
cy.get('.tiptap').type('{selectall}{backspace}Hello world{enter}')
|
||||
cy.wait(100)
|
||||
cy.get('.tiptap h1')
|
||||
.should('exist')
|
||||
.should('have.text', 'Hello world')
|
||||
cy.get('.tiptap h1').should('exist').should('have.text', 'Hello world')
|
||||
cy.get('.tiptap p[data-placeholder]')
|
||||
.should('exist')
|
||||
.should('have.attr', 'data-placeholder', 'Can you add some further context?')
|
||||
|
||||
cy.get('.tiptap').type('This is a paragraph for this test document')
|
||||
cy.get('.tiptap p')
|
||||
.should('exist')
|
||||
.should('have.text', 'This is a paragraph for this test document')
|
||||
cy.get('.tiptap p').should('exist').should('have.text', 'This is a paragraph for this test document')
|
||||
})
|
||||
})
|
||||
|
@ -1,16 +1,19 @@
|
||||
import { Paragraph as BaseParagraph } from '@tiptap/extension-paragraph'
|
||||
import {
|
||||
NodeViewContent,
|
||||
NodeViewWrapper,
|
||||
ReactNodeViewRenderer,
|
||||
} from '@tiptap/react'
|
||||
import { NodeViewContent, NodeViewWrapper, ReactNodeViewRenderer } from '@tiptap/react'
|
||||
|
||||
const ParagraphComponent = ({ node }) => {
|
||||
return (
|
||||
<NodeViewWrapper style={{ position: 'relative' }}>
|
||||
<span contentEditable={false} className="label" style={{
|
||||
position: 'absolute', right: '100%', fontSize: '10px', color: '#999',
|
||||
}}>
|
||||
<span
|
||||
contentEditable={false}
|
||||
className="label"
|
||||
style={{
|
||||
position: 'absolute',
|
||||
right: '100%',
|
||||
fontSize: '10px',
|
||||
color: '#999',
|
||||
}}
|
||||
>
|
||||
{node.textContent.length}
|
||||
</span>
|
||||
<NodeViewContent as="p" />
|
||||
|
@ -21,7 +21,5 @@ export default () => {
|
||||
`,
|
||||
})
|
||||
|
||||
return (
|
||||
<EditorContent editor={editor} />
|
||||
)
|
||||
return <EditorContent editor={editor} />
|
||||
}
|
||||
|
@ -11,7 +11,9 @@ context('/src/Examples/CustomParagraph/React/', () => {
|
||||
})
|
||||
|
||||
it('should have a paragraph and text length', () => {
|
||||
cy.get('.tiptap p').should('exist').should('have.text', 'Each line shows the number of characters in the paragraph.')
|
||||
cy.get('.tiptap p')
|
||||
.should('exist')
|
||||
.should('have.text', 'Each line shows the number of characters in the paragraph.')
|
||||
cy.get('.tiptap .label').should('exist').should('have.text', '58')
|
||||
})
|
||||
|
||||
|
@ -11,7 +11,9 @@ context('/src/Examples/CustomParagraph/React/', () => {
|
||||
})
|
||||
|
||||
it('should have a paragraph and text length', () => {
|
||||
cy.get('.tiptap p').should('exist').should('have.text', 'Each line shows the number of characters in the paragraph.')
|
||||
cy.get('.tiptap p')
|
||||
.should('exist')
|
||||
.should('have.text', 'Each line shows the number of characters in the paragraph.')
|
||||
cy.get('.tiptap .label').should('exist').should('have.text', '58')
|
||||
})
|
||||
|
||||
|
@ -15,19 +15,14 @@ const MenuBar = () => {
|
||||
selector: ctx => {
|
||||
return {
|
||||
isBold: ctx.editor.isActive('bold'),
|
||||
canBold: ctx.editor.can().chain().focus().toggleBold()
|
||||
.run(),
|
||||
canBold: ctx.editor.can().chain().focus().toggleBold().run(),
|
||||
isItalic: ctx.editor.isActive('italic'),
|
||||
canItalic: ctx.editor.can().chain().focus().toggleItalic()
|
||||
.run(),
|
||||
canItalic: ctx.editor.can().chain().focus().toggleItalic().run(),
|
||||
isStrike: ctx.editor.isActive('strike'),
|
||||
canStrike: ctx.editor.can().chain().focus().toggleStrike()
|
||||
.run(),
|
||||
canStrike: ctx.editor.can().chain().focus().toggleStrike().run(),
|
||||
isCode: ctx.editor.isActive('code'),
|
||||
canCode: ctx.editor.can().chain().focus().toggleCode()
|
||||
.run(),
|
||||
canClearMarks: ctx.editor.can().chain().focus().unsetAllMarks()
|
||||
.run(),
|
||||
canCode: ctx.editor.can().chain().focus().toggleCode().run(),
|
||||
canClearMarks: ctx.editor.can().chain().focus().unsetAllMarks().run(),
|
||||
isParagraph: ctx.editor.isActive('paragraph'),
|
||||
isHeading1: ctx.editor.isActive('heading', { level: 1 }),
|
||||
isHeading2: ctx.editor.isActive('heading', { level: 2 }),
|
||||
@ -39,10 +34,8 @@ const MenuBar = () => {
|
||||
isOrderedList: ctx.editor.isActive('orderedList'),
|
||||
isCodeBlock: ctx.editor.isActive('codeBlock'),
|
||||
isBlockquote: ctx.editor.isActive('blockquote'),
|
||||
canUndo: ctx.editor.can().chain().focus().undo()
|
||||
.run(),
|
||||
canRedo: ctx.editor.can().chain().focus().redo()
|
||||
.run(),
|
||||
canUndo: ctx.editor.can().chain().focus().undo().run(),
|
||||
canRedo: ctx.editor.can().chain().focus().redo().run(),
|
||||
isPurple: editor.isActive('textStyle', { color: '#958DF1' }),
|
||||
}
|
||||
},
|
||||
@ -83,12 +76,8 @@ const MenuBar = () => {
|
||||
>
|
||||
Code
|
||||
</button>
|
||||
<button onClick={() => editor.chain().focus().unsetAllMarks().run()}>
|
||||
Clear marks
|
||||
</button>
|
||||
<button onClick={() => editor.chain().focus().clearNodes().run()}>
|
||||
Clear nodes
|
||||
</button>
|
||||
<button onClick={() => editor.chain().focus().unsetAllMarks().run()}>Clear marks</button>
|
||||
<button onClick={() => editor.chain().focus().clearNodes().run()}>Clear nodes</button>
|
||||
<button
|
||||
onClick={() => editor.chain().focus().setParagraph().run()}
|
||||
className={editorState.isParagraph ? 'is-active' : ''}
|
||||
@ -155,22 +144,12 @@ const MenuBar = () => {
|
||||
>
|
||||
Blockquote
|
||||
</button>
|
||||
<button onClick={() => editor.chain().focus().setHorizontalRule().run()}>
|
||||
Horizontal rule
|
||||
</button>
|
||||
<button onClick={() => editor.chain().focus().setHardBreak().run()}>
|
||||
Hard break
|
||||
</button>
|
||||
<button
|
||||
onClick={() => editor.chain().focus().undo().run()}
|
||||
disabled={!editorState.canUndo}
|
||||
>
|
||||
<button onClick={() => editor.chain().focus().setHorizontalRule().run()}>Horizontal rule</button>
|
||||
<button onClick={() => editor.chain().focus().setHardBreak().run()}>Hard break</button>
|
||||
<button onClick={() => editor.chain().focus().undo().run()} disabled={!editorState.canUndo}>
|
||||
Undo
|
||||
</button>
|
||||
<button
|
||||
onClick={() => editor.chain().focus().redo().run()}
|
||||
disabled={!editorState.canRedo}
|
||||
>
|
||||
<button onClick={() => editor.chain().focus().redo().run()} disabled={!editorState.canRedo}>
|
||||
Redo
|
||||
</button>
|
||||
<button
|
||||
@ -231,7 +210,5 @@ const content = `
|
||||
`
|
||||
|
||||
export default () => {
|
||||
return (
|
||||
<EditorProvider slotBefore={<MenuBar />} extensions={extensions} content={content}></EditorProvider>
|
||||
)
|
||||
return <EditorProvider slotBefore={<MenuBar />} extensions={extensions} content={content}></EditorProvider>
|
||||
}
|
||||
|
@ -2,70 +2,114 @@
|
||||
<div v-if="editor" class="container">
|
||||
<div class="control-group">
|
||||
<div class="button-group">
|
||||
<button @click="editor.chain().focus().toggleBold().run()" :disabled="!editor.can().chain().focus().toggleBold().run()" :class="{ 'is-active': editor.isActive('bold') }">
|
||||
<button
|
||||
@click="editor.chain().focus().toggleBold().run()"
|
||||
:disabled="!editor.can().chain().focus().toggleBold().run()"
|
||||
:class="{ 'is-active': editor.isActive('bold') }"
|
||||
>
|
||||
Bold
|
||||
</button>
|
||||
<button @click="editor.chain().focus().toggleItalic().run()" :disabled="!editor.can().chain().focus().toggleItalic().run()" :class="{ 'is-active': editor.isActive('italic') }">
|
||||
<button
|
||||
@click="editor.chain().focus().toggleItalic().run()"
|
||||
:disabled="!editor.can().chain().focus().toggleItalic().run()"
|
||||
:class="{ 'is-active': editor.isActive('italic') }"
|
||||
>
|
||||
Italic
|
||||
</button>
|
||||
<button @click="editor.chain().focus().toggleStrike().run()" :disabled="!editor.can().chain().focus().toggleStrike().run()" :class="{ 'is-active': editor.isActive('strike') }">
|
||||
<button
|
||||
@click="editor.chain().focus().toggleStrike().run()"
|
||||
:disabled="!editor.can().chain().focus().toggleStrike().run()"
|
||||
:class="{ 'is-active': editor.isActive('strike') }"
|
||||
>
|
||||
Strike
|
||||
</button>
|
||||
<button @click="editor.chain().focus().toggleCode().run()" :disabled="!editor.can().chain().focus().toggleCode().run()" :class="{ 'is-active': editor.isActive('code') }">
|
||||
<button
|
||||
@click="editor.chain().focus().toggleCode().run()"
|
||||
:disabled="!editor.can().chain().focus().toggleCode().run()"
|
||||
:class="{ 'is-active': editor.isActive('code') }"
|
||||
>
|
||||
Code
|
||||
</button>
|
||||
<button @click="editor.chain().focus().unsetAllMarks().run()">
|
||||
Clear marks
|
||||
</button>
|
||||
<button @click="editor.chain().focus().clearNodes().run()">
|
||||
Clear nodes
|
||||
</button>
|
||||
<button @click="editor.chain().focus().setParagraph().run()" :class="{ 'is-active': editor.isActive('paragraph') }">
|
||||
<button @click="editor.chain().focus().unsetAllMarks().run()">Clear marks</button>
|
||||
<button @click="editor.chain().focus().clearNodes().run()">Clear nodes</button>
|
||||
<button
|
||||
@click="editor.chain().focus().setParagraph().run()"
|
||||
:class="{ 'is-active': editor.isActive('paragraph') }"
|
||||
>
|
||||
Paragraph
|
||||
</button>
|
||||
<button @click="editor.chain().focus().toggleHeading({ level: 1 }).run()" :class="{ 'is-active': editor.isActive('heading', { level: 1 }) }">
|
||||
<button
|
||||
@click="editor.chain().focus().toggleHeading({ level: 1 }).run()"
|
||||
:class="{ 'is-active': editor.isActive('heading', { level: 1 }) }"
|
||||
>
|
||||
H1
|
||||
</button>
|
||||
<button @click="editor.chain().focus().toggleHeading({ level: 2 }).run()" :class="{ 'is-active': editor.isActive('heading', { level: 2 }) }">
|
||||
<button
|
||||
@click="editor.chain().focus().toggleHeading({ level: 2 }).run()"
|
||||
:class="{ 'is-active': editor.isActive('heading', { level: 2 }) }"
|
||||
>
|
||||
H2
|
||||
</button>
|
||||
<button @click="editor.chain().focus().toggleHeading({ level: 3 }).run()" :class="{ 'is-active': editor.isActive('heading', { level: 3 }) }">
|
||||
<button
|
||||
@click="editor.chain().focus().toggleHeading({ level: 3 }).run()"
|
||||
:class="{ 'is-active': editor.isActive('heading', { level: 3 }) }"
|
||||
>
|
||||
H3
|
||||
</button>
|
||||
<button @click="editor.chain().focus().toggleHeading({ level: 4 }).run()" :class="{ 'is-active': editor.isActive('heading', { level: 4 }) }">
|
||||
<button
|
||||
@click="editor.chain().focus().toggleHeading({ level: 4 }).run()"
|
||||
:class="{ 'is-active': editor.isActive('heading', { level: 4 }) }"
|
||||
>
|
||||
H4
|
||||
</button>
|
||||
<button @click="editor.chain().focus().toggleHeading({ level: 5 }).run()" :class="{ 'is-active': editor.isActive('heading', { level: 5 }) }">
|
||||
<button
|
||||
@click="editor.chain().focus().toggleHeading({ level: 5 }).run()"
|
||||
:class="{ 'is-active': editor.isActive('heading', { level: 5 }) }"
|
||||
>
|
||||
H5
|
||||
</button>
|
||||
<button @click="editor.chain().focus().toggleHeading({ level: 6 }).run()" :class="{ 'is-active': editor.isActive('heading', { level: 6 }) }">
|
||||
<button
|
||||
@click="editor.chain().focus().toggleHeading({ level: 6 }).run()"
|
||||
:class="{ 'is-active': editor.isActive('heading', { level: 6 }) }"
|
||||
>
|
||||
H6
|
||||
</button>
|
||||
<button @click="editor.chain().focus().toggleBulletList().run()" :class="{ 'is-active': editor.isActive('bulletList') }">
|
||||
<button
|
||||
@click="editor.chain().focus().toggleBulletList().run()"
|
||||
:class="{ 'is-active': editor.isActive('bulletList') }"
|
||||
>
|
||||
Bullet list
|
||||
</button>
|
||||
<button @click="editor.chain().focus().toggleOrderedList().run()" :class="{ 'is-active': editor.isActive('orderedList') }">
|
||||
<button
|
||||
@click="editor.chain().focus().toggleOrderedList().run()"
|
||||
:class="{ 'is-active': editor.isActive('orderedList') }"
|
||||
>
|
||||
Ordered list
|
||||
</button>
|
||||
<button @click="editor.chain().focus().toggleCodeBlock().run()" :class="{ 'is-active': editor.isActive('codeBlock') }">
|
||||
<button
|
||||
@click="editor.chain().focus().toggleCodeBlock().run()"
|
||||
:class="{ 'is-active': editor.isActive('codeBlock') }"
|
||||
>
|
||||
Code block
|
||||
</button>
|
||||
<button @click="editor.chain().focus().toggleBlockquote().run()" :class="{ 'is-active': editor.isActive('blockquote') }">
|
||||
<button
|
||||
@click="editor.chain().focus().toggleBlockquote().run()"
|
||||
:class="{ 'is-active': editor.isActive('blockquote') }"
|
||||
>
|
||||
Blockquote
|
||||
</button>
|
||||
<button @click="editor.chain().focus().setHorizontalRule().run()">
|
||||
Horizontal rule
|
||||
</button>
|
||||
<button @click="editor.chain().focus().setHardBreak().run()">
|
||||
Hard break
|
||||
</button>
|
||||
<button @click="editor.chain().focus().setHorizontalRule().run()">Horizontal rule</button>
|
||||
<button @click="editor.chain().focus().setHardBreak().run()">Hard break</button>
|
||||
<button @click="editor.chain().focus().undo().run()" :disabled="!editor.can().chain().focus().undo().run()">
|
||||
Undo
|
||||
</button>
|
||||
<button @click="editor.chain().focus().redo().run()" :disabled="!editor.can().chain().focus().redo().run()">
|
||||
Redo
|
||||
</button>
|
||||
<button @click="editor.chain().focus().setColor('#958DF1').run()" :class="{ 'is-active': editor.isActive('textStyle', { color: '#958DF1' }) }">
|
||||
<button
|
||||
@click="editor.chain().focus().setColor('#958DF1').run()"
|
||||
:class="{ 'is-active': editor.isActive('textStyle', { color: '#958DF1' }) }"
|
||||
>
|
||||
Purple
|
||||
</button>
|
||||
</div>
|
||||
|
@ -2,16 +2,9 @@
|
||||
<node-view-wrapper class="draw">
|
||||
<div class="control-group">
|
||||
<div class="button-group">
|
||||
<input type="color" v-model="color">
|
||||
<input
|
||||
type="number"
|
||||
min="1"
|
||||
max="10"
|
||||
v-model="size"
|
||||
>
|
||||
<button @click="clear">
|
||||
Clear
|
||||
</button>
|
||||
<input type="color" v-model="color" />
|
||||
<input type="number" min="1" max="10" v-model="size" />
|
||||
<button @click="clear">Clear</button>
|
||||
</div>
|
||||
<svg viewBox="0 0 500 250" ref="canvas">
|
||||
<template v-for="item in node.attrs.lines">
|
||||
@ -49,15 +42,7 @@ export default {
|
||||
|
||||
data() {
|
||||
return {
|
||||
color: getRandomElement([
|
||||
'#A975FF',
|
||||
'#FB5151',
|
||||
'#FD9170',
|
||||
'#FFCB6B',
|
||||
'#68CEF8',
|
||||
'#80CBC4',
|
||||
'#9DEF8F',
|
||||
]),
|
||||
color: getRandomElement(['#A975FF', '#FB5151', '#FD9170', '#FFCB6B', '#68CEF8', '#80CBC4', '#9DEF8F']),
|
||||
size: Math.ceil(Math.random() * Math.floor(10)),
|
||||
svg: null,
|
||||
path: null,
|
||||
@ -78,9 +63,7 @@ export default {
|
||||
.attr('stroke', this.color)
|
||||
.attr('stroke-width', this.size)
|
||||
|
||||
const moveEvent = event.type === 'mousedown'
|
||||
? 'mousemove'
|
||||
: 'touchmove'
|
||||
const moveEvent = event.type === 'mousedown' ? 'mousemove' : 'touchmove'
|
||||
|
||||
this.svg.on(moveEvent, this.onMove)
|
||||
},
|
||||
|
@ -14,40 +14,76 @@ const MenuBar = ({ editor }) => {
|
||||
return (
|
||||
<div className="control-group">
|
||||
<div className="button-group">
|
||||
<button onClick={() => editor.chain().focus().toggleHeading({ level: 1 }).run()} className={editor.isActive('heading', { level: 1 }) ? 'is-active' : ''}>
|
||||
<button
|
||||
onClick={() => editor.chain().focus().toggleHeading({ level: 1 }).run()}
|
||||
className={editor.isActive('heading', { level: 1 }) ? 'is-active' : ''}
|
||||
>
|
||||
H1
|
||||
</button>
|
||||
<button onClick={() => editor.chain().focus().toggleHeading({ level: 2 }).run()} className={editor.isActive('heading', { level: 2 }) ? 'is-active' : ''}>
|
||||
<button
|
||||
onClick={() => editor.chain().focus().toggleHeading({ level: 2 }).run()}
|
||||
className={editor.isActive('heading', { level: 2 }) ? 'is-active' : ''}
|
||||
>
|
||||
H2
|
||||
</button>
|
||||
<button onClick={() => editor.chain().focus().toggleHeading({ level: 3 }).run()} className={editor.isActive('heading', { level: 3 }) ? 'is-active' : ''}>
|
||||
<button
|
||||
onClick={() => editor.chain().focus().toggleHeading({ level: 3 }).run()}
|
||||
className={editor.isActive('heading', { level: 3 }) ? 'is-active' : ''}
|
||||
>
|
||||
H3
|
||||
</button>
|
||||
<button onClick={() => editor.chain().focus().setParagraph().run()} className={editor.isActive('paragraph') ? 'is-active' : ''}>
|
||||
<button
|
||||
onClick={() => editor.chain().focus().setParagraph().run()}
|
||||
className={editor.isActive('paragraph') ? 'is-active' : ''}
|
||||
>
|
||||
Paragraph
|
||||
</button>
|
||||
<button onClick={() => editor.chain().focus().toggleBold().run()} className={editor.isActive('bold') ? 'is-active' : ''}>
|
||||
<button
|
||||
onClick={() => editor.chain().focus().toggleBold().run()}
|
||||
className={editor.isActive('bold') ? 'is-active' : ''}
|
||||
>
|
||||
Bold
|
||||
</button>
|
||||
<button onClick={() => editor.chain().focus().toggleItalic().run()} className={editor.isActive('italic') ? 'is-active' : ''}>
|
||||
<button
|
||||
onClick={() => editor.chain().focus().toggleItalic().run()}
|
||||
className={editor.isActive('italic') ? 'is-active' : ''}
|
||||
>
|
||||
Italic
|
||||
</button>
|
||||
<button onClick={() => editor.chain().focus().toggleStrike().run()} className={editor.isActive('strike') ? 'is-active' : ''}>
|
||||
<button
|
||||
onClick={() => editor.chain().focus().toggleStrike().run()}
|
||||
className={editor.isActive('strike') ? 'is-active' : ''}
|
||||
>
|
||||
Strike
|
||||
</button>
|
||||
<button onClick={() => editor.chain().focus().toggleHighlight().run()} className={editor.isActive('highlight') ? 'is-active' : ''}>
|
||||
<button
|
||||
onClick={() => editor.chain().focus().toggleHighlight().run()}
|
||||
className={editor.isActive('highlight') ? 'is-active' : ''}
|
||||
>
|
||||
Highlight
|
||||
</button>
|
||||
<button onClick={() => editor.chain().focus().setTextAlign('left').run()} className={editor.isActive({ textAlign: 'left' }) ? 'is-active' : ''}>
|
||||
<button
|
||||
onClick={() => editor.chain().focus().setTextAlign('left').run()}
|
||||
className={editor.isActive({ textAlign: 'left' }) ? 'is-active' : ''}
|
||||
>
|
||||
Left
|
||||
</button>
|
||||
<button onClick={() => editor.chain().focus().setTextAlign('center').run()} className={editor.isActive({ textAlign: 'center' }) ? 'is-active' : ''}>
|
||||
<button
|
||||
onClick={() => editor.chain().focus().setTextAlign('center').run()}
|
||||
className={editor.isActive({ textAlign: 'center' }) ? 'is-active' : ''}
|
||||
>
|
||||
Center
|
||||
</button>
|
||||
<button onClick={() => editor.chain().focus().setTextAlign('right').run()} className={editor.isActive({ textAlign: 'right' }) ? 'is-active' : ''}>
|
||||
<button
|
||||
onClick={() => editor.chain().focus().setTextAlign('right').run()}
|
||||
className={editor.isActive({ textAlign: 'right' }) ? 'is-active' : ''}
|
||||
>
|
||||
Right
|
||||
</button>
|
||||
<button onClick={() => editor.chain().focus().setTextAlign('justify').run()} className={editor.isActive({ textAlign: 'justify' }) ? 'is-active' : ''}>
|
||||
<button
|
||||
onClick={() => editor.chain().focus().setTextAlign('justify').run()}
|
||||
className={editor.isActive({ textAlign: 'justify' }) ? 'is-active' : ''}
|
||||
>
|
||||
Justify
|
||||
</button>
|
||||
</div>
|
||||
|
@ -7,9 +7,7 @@ context('/src/Examples/Formatting/React/', () => {
|
||||
cy.get('.tiptap').type('{selectall}{backspace}')
|
||||
})
|
||||
|
||||
const marks = [
|
||||
{ label: 'Highlight', mark: 'mark' },
|
||||
]
|
||||
const marks = [{ label: 'Highlight', mark: 'mark' }]
|
||||
|
||||
marks.forEach(m => {
|
||||
it(`sets ${m.label}`, () => {
|
||||
|
@ -78,7 +78,7 @@
|
||||
}
|
||||
|
||||
mark {
|
||||
background-color: #FAF594;
|
||||
background-color: #faf594;
|
||||
border-radius: 0.4rem;
|
||||
box-decoration-break: clone;
|
||||
padding: 0.1rem 0.3rem;
|
||||
|
@ -7,9 +7,7 @@ context('/src/Examples/Formatting/Vue/', () => {
|
||||
cy.get('.tiptap').type('{selectall}{backspace}')
|
||||
})
|
||||
|
||||
const marks = [
|
||||
{ label: 'Highlight', mark: 'mark' },
|
||||
]
|
||||
const marks = [{ label: 'Highlight', mark: 'mark' }]
|
||||
|
||||
marks.forEach(m => {
|
||||
it(`sets ${m.label}`, () => {
|
||||
|
@ -2,40 +2,73 @@
|
||||
<div v-if="editor" class="container">
|
||||
<div class="control-group">
|
||||
<div class="button-group">
|
||||
<button @click="editor.chain().focus().toggleHeading({ level: 1 }).run()" :class="{ 'is-active': editor.isActive('heading', { level: 1 }) }">
|
||||
<button
|
||||
@click="editor.chain().focus().toggleHeading({ level: 1 }).run()"
|
||||
:class="{ 'is-active': editor.isActive('heading', { level: 1 }) }"
|
||||
>
|
||||
H1
|
||||
</button>
|
||||
<button @click="editor.chain().focus().toggleHeading({ level: 2 }).run()" :class="{ 'is-active': editor.isActive('heading', { level: 2 }) }">
|
||||
<button
|
||||
@click="editor.chain().focus().toggleHeading({ level: 2 }).run()"
|
||||
:class="{ 'is-active': editor.isActive('heading', { level: 2 }) }"
|
||||
>
|
||||
H2
|
||||
</button>
|
||||
<button @click="editor.chain().focus().toggleHeading({ level: 3 }).run()" :class="{ 'is-active': editor.isActive('heading', { level: 3 }) }">
|
||||
<button
|
||||
@click="editor.chain().focus().toggleHeading({ level: 3 }).run()"
|
||||
:class="{ 'is-active': editor.isActive('heading', { level: 3 }) }"
|
||||
>
|
||||
H3
|
||||
</button>
|
||||
<button @click="editor.chain().focus().setParagraph().run()" :class="{ 'is-active': editor.isActive('paragraph') }">
|
||||
<button
|
||||
@click="editor.chain().focus().setParagraph().run()"
|
||||
:class="{ 'is-active': editor.isActive('paragraph') }"
|
||||
>
|
||||
Paragraph
|
||||
</button>
|
||||
<button @click="editor.chain().focus().toggleBold().run()" :class="{ 'is-active': editor.isActive('bold') }">
|
||||
Bold
|
||||
</button>
|
||||
<button @click="editor.chain().focus().toggleItalic().run()" :class="{ 'is-active': editor.isActive('italic') }">
|
||||
<button
|
||||
@click="editor.chain().focus().toggleItalic().run()"
|
||||
:class="{ 'is-active': editor.isActive('italic') }"
|
||||
>
|
||||
Italic
|
||||
</button>
|
||||
<button @click="editor.chain().focus().toggleStrike().run()" :class="{ 'is-active': editor.isActive('strike') }">
|
||||
<button
|
||||
@click="editor.chain().focus().toggleStrike().run()"
|
||||
:class="{ 'is-active': editor.isActive('strike') }"
|
||||
>
|
||||
Strike
|
||||
</button>
|
||||
<button @click="editor.chain().focus().toggleHighlight().run()" :class="{ 'is-active': editor.isActive('highlight') }">
|
||||
<button
|
||||
@click="editor.chain().focus().toggleHighlight().run()"
|
||||
:class="{ 'is-active': editor.isActive('highlight') }"
|
||||
>
|
||||
Highlight
|
||||
</button>
|
||||
<button @click="editor.chain().focus().setTextAlign('left').run()" :class="{ 'is-active': editor.isActive({ textAlign: 'left' }) }">
|
||||
<button
|
||||
@click="editor.chain().focus().setTextAlign('left').run()"
|
||||
:class="{ 'is-active': editor.isActive({ textAlign: 'left' }) }"
|
||||
>
|
||||
Left
|
||||
</button>
|
||||
<button @click="editor.chain().focus().setTextAlign('center').run()" :class="{ 'is-active': editor.isActive({ textAlign: 'center' }) }">
|
||||
<button
|
||||
@click="editor.chain().focus().setTextAlign('center').run()"
|
||||
:class="{ 'is-active': editor.isActive({ textAlign: 'center' }) }"
|
||||
>
|
||||
Center
|
||||
</button>
|
||||
<button @click="editor.chain().focus().setTextAlign('right').run()" :class="{ 'is-active': editor.isActive({ textAlign: 'right' }) }">
|
||||
<button
|
||||
@click="editor.chain().focus().setTextAlign('right').run()"
|
||||
:class="{ 'is-active': editor.isActive({ textAlign: 'right' }) }"
|
||||
>
|
||||
Right
|
||||
</button>
|
||||
<button @click="editor.chain().focus().setTextAlign('justify').run()" :class="{ 'is-active': editor.isActive({ textAlign: 'justify' }) }">
|
||||
<button
|
||||
@click="editor.chain().focus().setTextAlign('justify').run()"
|
||||
:class="{ 'is-active': editor.isActive({ textAlign: 'justify' }) }"
|
||||
>
|
||||
Justify
|
||||
</button>
|
||||
</div>
|
||||
@ -186,7 +219,7 @@ export default {
|
||||
}
|
||||
|
||||
mark {
|
||||
background-color: #FAF594;
|
||||
background-color: #faf594;
|
||||
border-radius: 0.4rem;
|
||||
box-decoration-break: clone;
|
||||
padding: 0.1rem 0.3rem;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user