mirror of
https://github.com/ueberdosis/tiptap.git
synced 2024-11-23 19:19:03 +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
|
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
|
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
|
preserve existing node attributes when running setNode
|
||||||
|
@ -1,59 +1,59 @@
|
|||||||
---
|
---
|
||||||
"@tiptap/extension-collaboration-cursor": major
|
'@tiptap/extension-collaboration-cursor': major
|
||||||
"@tiptap/extension-code-block-lowlight": major
|
'@tiptap/extension-code-block-lowlight': major
|
||||||
"@tiptap/extension-character-count": major
|
'@tiptap/extension-character-count': major
|
||||||
"@tiptap/extension-horizontal-rule": major
|
'@tiptap/extension-horizontal-rule': major
|
||||||
"@tiptap/extension-collaboration": major
|
'@tiptap/extension-collaboration': major
|
||||||
"@tiptap/extension-floating-menu": major
|
'@tiptap/extension-floating-menu': major
|
||||||
"@tiptap/extension-ordered-list": major
|
'@tiptap/extension-ordered-list': major
|
||||||
"@tiptap/extension-table-header": major
|
'@tiptap/extension-table-header': major
|
||||||
"@tiptap/extension-bubble-menu": major
|
'@tiptap/extension-bubble-menu': major
|
||||||
"@tiptap/extension-bullet-list": major
|
'@tiptap/extension-bullet-list': major
|
||||||
"@tiptap/extension-font-family": major
|
'@tiptap/extension-font-family': major
|
||||||
"@tiptap/extension-list-keymap": major
|
'@tiptap/extension-list-keymap': major
|
||||||
"@tiptap/extension-placeholder": major
|
'@tiptap/extension-placeholder': major
|
||||||
"@tiptap/extension-superscript": major
|
'@tiptap/extension-superscript': major
|
||||||
"@tiptap/extension-blockquote": major
|
'@tiptap/extension-blockquote': major
|
||||||
"@tiptap/extension-code-block": major
|
'@tiptap/extension-code-block': major
|
||||||
"@tiptap/extension-dropcursor": major
|
'@tiptap/extension-dropcursor': major
|
||||||
"@tiptap/extension-hard-break": major
|
'@tiptap/extension-hard-break': major
|
||||||
"@tiptap/extension-table-cell": major
|
'@tiptap/extension-table-cell': major
|
||||||
"@tiptap/extension-text-align": major
|
'@tiptap/extension-text-align': major
|
||||||
"@tiptap/extension-text-style": major
|
'@tiptap/extension-text-style': major
|
||||||
"@tiptap/extension-typography": major
|
'@tiptap/extension-typography': major
|
||||||
"@tiptap/extension-gapcursor": major
|
'@tiptap/extension-gapcursor': major
|
||||||
"@tiptap/extension-highlight": major
|
'@tiptap/extension-highlight': major
|
||||||
"@tiptap/extension-list-item": major
|
'@tiptap/extension-list-item': major
|
||||||
"@tiptap/extension-paragraph": major
|
'@tiptap/extension-paragraph': major
|
||||||
"@tiptap/extension-subscript": major
|
'@tiptap/extension-subscript': major
|
||||||
"@tiptap/extension-table-row": major
|
'@tiptap/extension-table-row': major
|
||||||
"@tiptap/extension-task-item": major
|
'@tiptap/extension-task-item': major
|
||||||
"@tiptap/extension-task-list": major
|
'@tiptap/extension-task-list': major
|
||||||
"@tiptap/extension-underline": major
|
'@tiptap/extension-underline': major
|
||||||
"@tiptap/extension-document": major
|
'@tiptap/extension-document': major
|
||||||
"@tiptap/extension-heading": major
|
'@tiptap/extension-heading': major
|
||||||
"@tiptap/extension-history": major
|
'@tiptap/extension-history': major
|
||||||
"@tiptap/extension-mention": major
|
'@tiptap/extension-mention': major
|
||||||
"@tiptap/extension-youtube": major
|
'@tiptap/extension-youtube': major
|
||||||
"@tiptap/extension-italic": major
|
'@tiptap/extension-italic': major
|
||||||
"@tiptap/extension-strike": major
|
'@tiptap/extension-strike': major
|
||||||
"@tiptap/extension-color": major
|
'@tiptap/extension-color': major
|
||||||
"@tiptap/extension-focus": major
|
'@tiptap/extension-focus': major
|
||||||
"@tiptap/extension-image": major
|
'@tiptap/extension-image': major
|
||||||
"@tiptap/extension-table": major
|
'@tiptap/extension-table': major
|
||||||
"@tiptap/extension-bold": major
|
'@tiptap/extension-bold': major
|
||||||
"@tiptap/extension-code": major
|
'@tiptap/extension-code': major
|
||||||
"@tiptap/extension-link": major
|
'@tiptap/extension-link': major
|
||||||
"@tiptap/extension-text": major
|
'@tiptap/extension-text': major
|
||||||
"@tiptap/starter-kit": major
|
'@tiptap/starter-kit': major
|
||||||
"@tiptap/suggestion": major
|
'@tiptap/suggestion': major
|
||||||
"@tiptap/react": major
|
'@tiptap/react': major
|
||||||
"@tiptap/vue-2": major
|
'@tiptap/vue-2': major
|
||||||
"@tiptap/vue-3": major
|
'@tiptap/vue-3': major
|
||||||
"@tiptap/core": major
|
'@tiptap/core': major
|
||||||
"@tiptap/html": major
|
'@tiptap/html': major
|
||||||
"@tiptap/pm": major
|
'@tiptap/pm': major
|
||||||
"tiptap-demos": major
|
'tiptap-demos': major
|
||||||
---
|
---
|
||||||
|
|
||||||
We are now building packages with tsup which does not support UMD builds, please repackage if you require UMD builds
|
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-floating-menu': major
|
||||||
"@tiptap/extension-bubble-menu": major
|
'@tiptap/extension-bubble-menu': major
|
||||||
"@tiptap/extension-mention": major
|
'@tiptap/extension-mention': major
|
||||||
"@tiptap/suggestion": major
|
'@tiptap/suggestion': major
|
||||||
"@tiptap/react": major
|
'@tiptap/react': major
|
||||||
"@tiptap/vue-2": major
|
'@tiptap/vue-2': major
|
||||||
"@tiptap/vue-3": 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.
|
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
|
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.
|
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.
|
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`
|
Addresses a bug with `insertContentAt`'s `simulatedPasteRules` option where it could only accept text and not Prosemirror `Node` and `Content`
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
---
|
---
|
||||||
"@tiptap/core": major
|
'@tiptap/core': major
|
||||||
---
|
---
|
||||||
|
|
||||||
Fix `getPos` type in `NodeViewRendererProps` to potentially be `undefined`
|
Fix `getPos` type in `NodeViewRendererProps` to potentially be `undefined`
|
||||||
|
|
||||||
Breaking change: Types may flag uses of getPos where an `undefined` possibility isn't handled.
|
Breaking change: Types may flag uses of getPos where an `undefined` possibility isn't handled.
|
||||||
Why this change was made: To ensure the type reflects the real functionality of this function.
|
Why this change was made: To ensure the type reflects the real functionality of this function.
|
||||||
How to update: Ensure that the return value of `getPos` exists before making use of the value.
|
How to update: Ensure that the return value of `getPos` exists before making use of the value.
|
||||||
|
@ -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
|
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
|
`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
|
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
|
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
|
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
|
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
|
add zero-width space to resolve cursor selection issue
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
"@tiptap/core": patch
|
'@tiptap/core': patch
|
||||||
"@tiptap/extension-hard-break": patch
|
'@tiptap/extension-hard-break': patch
|
||||||
---
|
---
|
||||||
|
|
||||||
Add Node `linebreakReplacement` support and enable on hard-break nodes
|
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.
|
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
|
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
|
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/extension-link': patch
|
||||||
"tiptap-demos": 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.
|
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:
|
body:
|
||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
@ -9,7 +9,7 @@ body:
|
|||||||
attributes:
|
attributes:
|
||||||
label: Description
|
label: Description
|
||||||
description: Please describe how your extension works and what it does.
|
description: Please describe how your extension works and what it does.
|
||||||
placeholder: "My extension does …"
|
placeholder: 'My extension does …'
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
@ -17,7 +17,7 @@ body:
|
|||||||
attributes:
|
attributes:
|
||||||
label: Installation
|
label: Installation
|
||||||
description: Please describe how users can install your extension.
|
description: Please describe how users can install your extension.
|
||||||
placeholder: "npm install …"
|
placeholder: 'npm install …'
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
@ -25,7 +25,7 @@ body:
|
|||||||
attributes:
|
attributes:
|
||||||
label: Usage
|
label: Usage
|
||||||
description: Please describe how users can use your extension in their editor.
|
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:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: dropdown
|
- type: dropdown
|
||||||
@ -34,11 +34,11 @@ body:
|
|||||||
label: Type
|
label: Type
|
||||||
description: Please select the type of this extension.
|
description: Please select the type of this extension.
|
||||||
options:
|
options:
|
||||||
- "Node"
|
- 'Node'
|
||||||
- "Mark"
|
- 'Mark'
|
||||||
- "Prosemirror plugin"
|
- 'Prosemirror plugin'
|
||||||
- "Package or Kit"
|
- 'Package or Kit'
|
||||||
- "Other"
|
- 'Other'
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
@ -46,6 +46,6 @@ body:
|
|||||||
attributes:
|
attributes:
|
||||||
label: Other
|
label: Other
|
||||||
description: Feel free to add any other information about your extension.
|
description: Feel free to add any other information about your extension.
|
||||||
placeholder: "I hope you like …"
|
placeholder: 'I hope you like …'
|
||||||
validations:
|
validations:
|
||||||
required: false
|
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:
|
labels:
|
||||||
- "Type: Feature Request"
|
- 'Type: Feature Request'
|
||||||
body:
|
body:
|
||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
@ -11,7 +11,7 @@ body:
|
|||||||
attributes:
|
attributes:
|
||||||
label: Description
|
label: Description
|
||||||
description: Please describe the feature you would like to see in Tiptap.
|
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:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
@ -19,7 +19,7 @@ body:
|
|||||||
attributes:
|
attributes:
|
||||||
label: Use Case
|
label: Use Case
|
||||||
description: Please describe the use case for this feature.
|
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:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: dropdown
|
- type: dropdown
|
||||||
@ -28,9 +28,9 @@ body:
|
|||||||
label: Type
|
label: Type
|
||||||
description: Please select the type of this feature.
|
description: Please select the type of this feature.
|
||||||
options:
|
options:
|
||||||
- "New extension"
|
- 'New extension'
|
||||||
- "New feature"
|
- 'New feature'
|
||||||
- "New Tiptap API"
|
- 'New Tiptap API'
|
||||||
- "Other"
|
- 'Other'
|
||||||
validations:
|
validations:
|
||||||
required: true
|
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:
|
body:
|
||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
@ -9,7 +9,7 @@ body:
|
|||||||
attributes:
|
attributes:
|
||||||
label: Description
|
label: Description
|
||||||
description: Please describe what your project is about
|
description: Please describe what your project is about
|
||||||
placeholder: "My project is about …"
|
placeholder: 'My project is about …'
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: input
|
- type: input
|
||||||
@ -17,7 +17,7 @@ body:
|
|||||||
attributes:
|
attributes:
|
||||||
label: URL
|
label: URL
|
||||||
description: If possible share the URL of your project.
|
description: If possible share the URL of your project.
|
||||||
placeholder: "https://example.com"
|
placeholder: 'https://example.com'
|
||||||
validations:
|
validations:
|
||||||
required: false
|
required: false
|
||||||
- type: textarea
|
- type: textarea
|
||||||
@ -25,7 +25,7 @@ body:
|
|||||||
attributes:
|
attributes:
|
||||||
label: About
|
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.
|
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:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: dropdown
|
- type: dropdown
|
||||||
@ -34,12 +34,12 @@ body:
|
|||||||
label: Type
|
label: Type
|
||||||
description: Please select the type of your project.
|
description: Please select the type of your project.
|
||||||
options:
|
options:
|
||||||
- "Chat Application"
|
- 'Chat Application'
|
||||||
- "Commenting Application"
|
- 'Commenting Application'
|
||||||
- "Content Management System"
|
- 'Content Management System'
|
||||||
- "Document Editor"
|
- 'Document Editor'
|
||||||
- "Document Editor with Collaboration"
|
- 'Document Editor with Collaboration'
|
||||||
- "Other"
|
- 'Other'
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
@ -47,6 +47,6 @@ body:
|
|||||||
attributes:
|
attributes:
|
||||||
label: Other
|
label: Other
|
||||||
description: Feel free to add any other information about your project.
|
description: Feel free to add any other information about your project.
|
||||||
placeholder: "I hope you like …"
|
placeholder: 'I hope you like …'
|
||||||
validations:
|
validations:
|
||||||
required: false
|
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
|
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.
|
description: Found a bug in the editor core or one of the extensions? Report it here to help us improve.
|
||||||
labels:
|
labels:
|
||||||
- "Type: Bug"
|
- 'Type: Bug'
|
||||||
- "Category: Open Source"
|
- 'Category: Open Source'
|
||||||
- "Status: New"
|
- 'Status: New'
|
||||||
body:
|
body:
|
||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: "### Please provide details to help us diagnose the bug."
|
value: '### Please provide details to help us diagnose the bug.'
|
||||||
- type: input
|
- type: input
|
||||||
id: packages
|
id: packages
|
||||||
attributes:
|
attributes:
|
||||||
@ -30,7 +30,7 @@ body:
|
|||||||
attributes:
|
attributes:
|
||||||
label: Bug Description
|
label: Bug Description
|
||||||
description: Provide a clear and concise description of what the bug is.
|
description: Provide a clear and concise description of what the bug is.
|
||||||
placeholder: "The issue occurs when..."
|
placeholder: 'The issue occurs when...'
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: dropdown
|
- type: dropdown
|
||||||
@ -59,7 +59,7 @@ body:
|
|||||||
id: sandbox
|
id: sandbox
|
||||||
attributes:
|
attributes:
|
||||||
label: Code Example URL
|
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
|
placeholder: https://codesandbox.io/s/example
|
||||||
validations:
|
validations:
|
||||||
required: false
|
required: false
|
||||||
@ -74,14 +74,14 @@ body:
|
|||||||
id: context
|
id: context
|
||||||
attributes:
|
attributes:
|
||||||
label: Additional Context (Optional)
|
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
|
- type: checkboxes
|
||||||
attributes:
|
attributes:
|
||||||
label: Dependency Updates
|
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:
|
options:
|
||||||
- label: Yes, I've updated all my dependencies.
|
- label: Yes, I've updated all my dependencies.
|
||||||
required: true
|
required: true
|
||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
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)
|
name: Bug Report (Tiptap Pro)
|
||||||
title: "[PRO]: "
|
title: '[PRO]: '
|
||||||
description: If you've encountered a bug with Tiptap Pro features, please report it here.
|
description: If you've encountered a bug with Tiptap Pro features, please report it here.
|
||||||
labels:
|
labels:
|
||||||
- "Type: Bug"
|
- 'Type: Bug'
|
||||||
- "Category: Pro"
|
- 'Category: Pro'
|
||||||
- "Status: New"
|
- 'Status: New'
|
||||||
body:
|
body:
|
||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
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
|
- type: input
|
||||||
id: packages
|
id: packages
|
||||||
attributes:
|
attributes:
|
||||||
@ -30,7 +30,7 @@ body:
|
|||||||
attributes:
|
attributes:
|
||||||
label: Description of the Bug
|
label: Description of the Bug
|
||||||
description: Provide a clear and concise description of what the bug is.
|
description: Provide a clear and concise description of what the bug is.
|
||||||
placeholder: "The issue occurs when..."
|
placeholder: 'The issue occurs when...'
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: dropdown
|
- type: dropdown
|
||||||
@ -48,10 +48,10 @@ body:
|
|||||||
required: true
|
required: true
|
||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: "### Helpful Code Examples"
|
value: '### Helpful Code Examples'
|
||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
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
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: |
|
value: |
|
||||||
@ -63,7 +63,7 @@ body:
|
|||||||
id: sandbox
|
id: sandbox
|
||||||
attributes:
|
attributes:
|
||||||
label: Code Example (Preferred)
|
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
|
placeholder: https://codesandbox.io/s/example
|
||||||
validations:
|
validations:
|
||||||
required: false
|
required: false
|
||||||
@ -78,14 +78,14 @@ body:
|
|||||||
id: context
|
id: context
|
||||||
attributes:
|
attributes:
|
||||||
label: Additional Context (Optional)
|
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
|
- type: checkboxes
|
||||||
attributes:
|
attributes:
|
||||||
label: Dependency Updates
|
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:
|
options:
|
||||||
- label: Yes, I've updated all my dependencies.
|
- label: Yes, I've updated all my dependencies.
|
||||||
required: true
|
required: true
|
||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
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
|
name: Documentation feedback
|
||||||
description: Share what we need to explain better.
|
description: Share what we need to explain better.
|
||||||
title: "[Documentation]: "
|
title: '[Documentation]: '
|
||||||
labels:
|
labels:
|
||||||
- "Type: Documentation"
|
- 'Type: Documentation'
|
||||||
- "Category: Open Source"
|
- 'Category: Open Source'
|
||||||
- "Status: New"
|
- 'Status: New'
|
||||||
body:
|
body:
|
||||||
- type: input
|
- type: input
|
||||||
id: url
|
id: url
|
||||||
@ -17,28 +17,28 @@ body:
|
|||||||
id: part-of-the-documentation
|
id: part-of-the-documentation
|
||||||
attributes:
|
attributes:
|
||||||
label: What part of the documentation needs improvement?
|
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:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: good-parts
|
id: good-parts
|
||||||
attributes:
|
attributes:
|
||||||
label: What is helpful about that part?
|
label: What is helpful about that part?
|
||||||
placeholder: "I think this part is really good: …"
|
placeholder: 'I think this part is really good: …'
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: bad-parts
|
id: bad-parts
|
||||||
attributes:
|
attributes:
|
||||||
label: What is hard to understand, missing or misleading?
|
label: What is hard to understand, missing or misleading?
|
||||||
placeholder: "But you really need to improve …"
|
placeholder: 'But you really need to improve …'
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: context
|
id: context
|
||||||
attributes:
|
attributes:
|
||||||
label: Anything to add? (optional)
|
label: Anything to add? (optional)
|
||||||
description: "Add any other context or screenshots here."
|
description: 'Add any other context or screenshots here.'
|
||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: |
|
value: |
|
||||||
|
2
.github/dependabot.yml
vendored
2
.github/dependabot.yml
vendored
@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
version: 2
|
version: 2
|
||||||
updates:
|
updates:
|
||||||
|
|
||||||
- package-ecosystem: 'github-actions'
|
- package-ecosystem: 'github-actions'
|
||||||
directory: '/'
|
directory: '/'
|
||||||
open-pull-requests-limit: 10
|
open-pull-requests-limit: 10
|
||||||
@ -12,4 +11,3 @@ updates:
|
|||||||
day: 'monday'
|
day: 'monday'
|
||||||
reviewers:
|
reviewers:
|
||||||
- 'bdbch'
|
- 'bdbch'
|
||||||
|
|
||||||
|
7
.github/pull_request_template.md
vendored
7
.github/pull_request_template.md
vendored
@ -1,19 +1,25 @@
|
|||||||
## Changes Overview
|
## Changes Overview
|
||||||
|
|
||||||
<!-- Briefly describe your changes. -->
|
<!-- Briefly describe your changes. -->
|
||||||
|
|
||||||
## Implementation Approach
|
## Implementation Approach
|
||||||
|
|
||||||
<!-- Describe your approach to implementing these changes. Keep it concise. -->
|
<!-- Describe your approach to implementing these changes. Keep it concise. -->
|
||||||
|
|
||||||
## Testing Done
|
## Testing Done
|
||||||
|
|
||||||
<!-- Explain how you tested these changes. Link to test scenarios or specs if relevant. -->
|
<!-- Explain how you tested these changes. Link to test scenarios or specs if relevant. -->
|
||||||
|
|
||||||
## Verification Steps
|
## Verification Steps
|
||||||
|
|
||||||
<!-- Describe steps reviewers can take to verify the functionality of your changes. -->
|
<!-- Describe steps reviewers can take to verify the functionality of your changes. -->
|
||||||
|
|
||||||
## Additional Notes
|
## Additional Notes
|
||||||
|
|
||||||
<!-- Add any other notes or screenshots about the PR here. -->
|
<!-- Add any other notes or screenshots about the PR here. -->
|
||||||
|
|
||||||
## Checklist
|
## Checklist
|
||||||
|
|
||||||
- [ ] I have created a [changeset](https://github.com/changesets/changesets) for this PR if necessary.
|
- [ ] I have created a [changeset](https://github.com/changesets/changesets) for this PR if necessary.
|
||||||
- [ ] My changes do not break the library.
|
- [ ] My changes do not break the library.
|
||||||
- [ ] I have added tests where applicable.
|
- [ ] I have added tests where applicable.
|
||||||
@ -21,4 +27,5 @@
|
|||||||
- [ ] I have fixed any lint issues.
|
- [ ] I have fixed any lint issues.
|
||||||
|
|
||||||
## Related Issues
|
## Related Issues
|
||||||
|
|
||||||
<!-- Link any related issues here -->
|
<!-- Link any related issues here -->
|
||||||
|
16
.github/workflows/build.yml
vendored
16
.github/workflows/build.yml
vendored
@ -73,16 +73,16 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
node-version: [20]
|
node-version: [20]
|
||||||
test-spec:
|
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/Commands", spec: "./demos/src/Commands/**/*.spec.{js,ts}" }
|
||||||
- { name: "Demos/Examples", spec: "./demos/src/Examples/**/*.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/Experiments', spec: './demos/src/Experiments/**/*.spec.{js,ts}' }
|
||||||
- { name: "Demos/Extensions", spec: "./demos/src/Extensions/**/*.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/GuideContent', spec: './demos/src/GuideContent/**/*.spec.{js,ts}' }
|
||||||
- { name: "Demos/GuideGettingStarted", spec: "./demos/src/GuideGettingStarted/**/*.spec.{js,ts}" }
|
- { name: 'Demos/GuideGettingStarted', spec: './demos/src/GuideGettingStarted/**/*.spec.{js,ts}' }
|
||||||
#- { name: "Demos/GuideNodeViews", "./demos/src/GuideNodeViews/**/*.spec.{js,ts}" }
|
#- { name: "Demos/GuideNodeViews", "./demos/src/GuideNodeViews/**/*.spec.{js,ts}" }
|
||||||
- { name: "Demos/Marks", spec: "./demos/src/Marks/**/*.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/Nodes', spec: './demos/src/Nodes/**/*.spec.{js,ts}' }
|
||||||
#- { name: "Demos/Overview", spec: "./demos/src/Overview/**/*.spec.{js,ts}" }
|
#- { name: "Demos/Overview", spec: "./demos/src/Overview/**/*.spec.{js,ts}" }
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
2
.github/workflows/publish.yml
vendored
2
.github/workflows/publish.yml
vendored
@ -34,7 +34,7 @@ jobs:
|
|||||||
- name: Setup Node ${{ matrix.node-version }}
|
- name: Setup Node ${{ matrix.node-version }}
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
registry-url: 'https://registry.npmjs.org/'
|
registry-url: 'https://registry.npmjs.org/'
|
||||||
|
|
||||||
- name: Load cached dependencies
|
- 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
|
# 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.
|
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/).
|
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?
|
### 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.
|
- **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.
|
- **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).
|
- **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
|
### 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.
|
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).
|
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.
|
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
|
### 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/).
|
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
|
## 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.
|
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
|
### 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.
|
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)
|
- [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)
|
- [Collaboration ready Tiptap CodeSandbox](https://codesandbox.io/p/devbox/collaboration-4stk94)
|
||||||
- React notion-like block editor template: [Demo](https://templates.tiptap.dev/)
|
- React notion-like block editor template: [Demo](https://templates.tiptap.dev/)
|
||||||
|
|
||||||
## About Tiptap
|
## 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.
|
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/).
|
For more details, visit the Tiptap [documentation](https://tiptap.dev/docs/editor/introduction) or [website](https://tiptap.dev/).
|
||||||
|
|
||||||
### Community
|
### Community
|
||||||
|
|
||||||
For help, discussion about best practices, or any other conversation that would benefit from being searchable:
|
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)
|
[Discuss Tiptap on GitHub](https://github.com/ueberdosis/tiptap/discussions)
|
||||||
|
|
||||||
### Sponsors 💖
|
### Sponsors 💖
|
||||||
|
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<td align="center">
|
<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).
|
[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
|
### 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.
|
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
|
### Contributors
|
||||||
|
|
||||||
[Sam Willis](https://github.com/samwillis),
|
[Sam Willis](https://github.com/samwillis),
|
||||||
[Brian Hung](https://github.com/BrianHung),
|
[Brian Hung](https://github.com/BrianHung),
|
||||||
[Dirk Holtwick](https://github.com/holtwick),
|
[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).
|
[Gregor](https://github.com/gambolputty) and [many more](../../contributors).
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
The MIT License (MIT). Please see [License File](LICENSE.md) for more information.
|
The MIT License (MIT). Please see [License File](LICENSE.md) for more information.
|
||||||
|
@ -1,10 +1,4 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
presets: [
|
presets: ['@babel/preset-env', '@babel/preset-react'],
|
||||||
'@babel/preset-env',
|
plugins: ['@babel/plugin-proposal-nullish-coalescing-operator', '@babel/plugin-proposal-optional-chaining'],
|
||||||
'@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">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8"/>
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<meta http-equiv="refresh" content="0; url=/preview/" />
|
<meta http-equiv="refresh" content="0; url=/preview/" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body></body>
|
||||||
</body>
|
|
||||||
</html>
|
</html>
|
||||||
|
@ -6,23 +6,18 @@
|
|||||||
:key="index"
|
:key="index"
|
||||||
@click="setTab(language.name)"
|
@click="setTab(language.name)"
|
||||||
class="px-3 py-2 text-sm text-white leading-[125%] font-semibold rounded-[0.625rem] transition-all"
|
class="px-3 py-2 text-sm text-white leading-[125%] font-semibold rounded-[0.625rem] transition-all"
|
||||||
:class="[currentTab === language.name
|
:class="[
|
||||||
? 'opacity-100 bg-[#1C1917]'
|
currentTab === language.name
|
||||||
: 'opacity-50 bg-transparent hover:opacity-100 hover:bg-[#1C1917]'
|
? 'opacity-100 bg-[#1C1917]'
|
||||||
|
: 'opacity-50 bg-transparent hover:opacity-100 hover:bg-[#1C1917]',
|
||||||
]"
|
]"
|
||||||
>
|
>
|
||||||
{{ language.name }}
|
{{ language.name }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="overflow-hidden">
|
<div class="overflow-hidden">
|
||||||
<div
|
<div class="bg-white" :class="[hidePreview ? 'hidden' : '']">
|
||||||
class="bg-white"
|
<demo-frame :src="currentIframeUrl" :key="currentIframeUrl" />
|
||||||
:class="[hidePreview ? 'hidden' : '']"
|
|
||||||
>
|
|
||||||
<demo-frame
|
|
||||||
:src="currentIframeUrl"
|
|
||||||
:key="currentIframeUrl"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="text-white bg-black" v-if="!hideSource && currentFile">
|
<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">
|
<div class="flex flex-auto px-4 border-b-2 border-gray-800">
|
||||||
<button
|
<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="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
|
:class="[
|
||||||
? 'text-white border-white font-bold'
|
!showDebug && currentFile.content === file.content
|
||||||
: 'text-gray-400'
|
? 'text-white border-white font-bold'
|
||||||
|
: 'text-gray-400',
|
||||||
]"
|
]"
|
||||||
v-for="(file, index) in source"
|
v-for="(file, index) in source"
|
||||||
:key="index"
|
:key="index"
|
||||||
@ -44,10 +40,7 @@
|
|||||||
<button
|
<button
|
||||||
v-if="debugJSON"
|
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="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
|
:class="[showDebug ? 'text-white border-white font-bold' : 'text-gray-400']"
|
||||||
? 'text-white border-white font-bold'
|
|
||||||
: 'text-gray-400'
|
|
||||||
]"
|
|
||||||
@click="showDebug = !showDebug"
|
@click="showDebug = !showDebug"
|
||||||
>
|
>
|
||||||
Inspect
|
Inspect
|
||||||
@ -67,9 +60,7 @@
|
|||||||
<a class="flex-shrink min-w-0 overflow-hidden overflow-ellipsis whitespace-nowrap" :href="currentIframeUrl">
|
<a class="flex-shrink min-w-0 overflow-hidden overflow-ellipsis whitespace-nowrap" :href="currentIframeUrl">
|
||||||
{{ name }}/{{ currentTab }}
|
{{ name }}/{{ currentTab }}
|
||||||
</a>
|
</a>
|
||||||
<a class="pl-4 whitespace-nowrap" :href="githubUrl" target="_blank">
|
<a class="pl-4 whitespace-nowrap" :href="githubUrl" target="_blank"> Edit on GitHub → </a>
|
||||||
Edit on GitHub →
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -128,9 +119,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
query() {
|
query() {
|
||||||
return Object.fromEntries(Object
|
return Object.fromEntries(Object.entries(this.$route.query).map(([key, value]) => [key, this.fromString(value)]))
|
||||||
.entries(this.$route.query)
|
|
||||||
.map(([key, value]) => [key, this.fromString(value)]))
|
|
||||||
},
|
},
|
||||||
|
|
||||||
inline() {
|
inline() {
|
||||||
@ -220,9 +209,10 @@ export default {
|
|||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
// TODO: load language from url params
|
// TODO: load language from url params
|
||||||
const intitialTab = localStorage.tab && this.tabs.some(tab => tab.name === localStorage.tab)
|
const intitialTab =
|
||||||
? localStorage.tab
|
localStorage.tab && this.tabs.some(tab => tab.name === localStorage.tab)
|
||||||
: this.sortedTabs[0]?.name
|
? localStorage.tab
|
||||||
|
: this.sortedTabs[0]?.name
|
||||||
|
|
||||||
this.setTab(intitialTab, false)
|
this.setTab(intitialTab, false)
|
||||||
|
|
||||||
|
@ -1,21 +1,16 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col relative min-h-[5rem]">
|
<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">
|
<div
|
||||||
<svg
|
class="absolute top-0 left-0 w-full h-full flex justify-center items-center pointer-events-none"
|
||||||
class="animate-spin -ml-1 mr-3 h-5 w-5"
|
v-if="isLoading"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
>
|
||||||
fill="none"
|
<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">
|
||||||
viewBox="0 0 24 24"
|
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" />
|
||||||
>
|
<path
|
||||||
<circle
|
class="opacity-75"
|
||||||
class="opacity-25"
|
fill="currentColor"
|
||||||
cx="12"
|
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"
|
||||||
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" />
|
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<iframe
|
<iframe
|
||||||
@ -53,6 +48,4 @@ export default {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
|
|
||||||
</style>
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<!DOCTYPE html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8"/>
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Preview</title>
|
<title>Preview</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
@ -9,17 +9,16 @@ import { createRouter, createWebHistory } from 'vue-router'
|
|||||||
import Demo from './Demo.vue'
|
import Demo from './Demo.vue'
|
||||||
import App from './index.vue'
|
import App from './index.vue'
|
||||||
|
|
||||||
const routes = demos
|
const routes = demos.map(({ name, tabs }) => {
|
||||||
.map(({ name, tabs }) => {
|
return {
|
||||||
return {
|
path: `/${name}`,
|
||||||
path: `/${name}`,
|
component: Demo,
|
||||||
component: Demo,
|
props: {
|
||||||
props: {
|
name,
|
||||||
name,
|
tabs,
|
||||||
tabs,
|
},
|
||||||
},
|
}
|
||||||
}
|
})
|
||||||
})
|
|
||||||
|
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
history: createWebHistory('preview'),
|
history: createWebHistory('preview'),
|
||||||
@ -30,25 +29,28 @@ createApp(App)
|
|||||||
.directive('resize', {
|
.directive('resize', {
|
||||||
beforeMount: (el, { value = {} }) => {
|
beforeMount: (el, { value = {} }) => {
|
||||||
el.addEventListener('load', () => {
|
el.addEventListener('load', () => {
|
||||||
iframeResize({
|
iframeResize(
|
||||||
...value,
|
{
|
||||||
// messageCallback(messageData) {
|
...value,
|
||||||
// if (messageData.message.type !== 'resize') {
|
// messageCallback(messageData) {
|
||||||
// return
|
// if (messageData.message.type !== 'resize') {
|
||||||
// }
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
// const style = window.getComputedStyle(el.parentElement)
|
// const style = window.getComputedStyle(el.parentElement)
|
||||||
// const maxHeight = parseInt(style.getPropertyValue('max-height'), 10)
|
// const maxHeight = parseInt(style.getPropertyValue('max-height'), 10)
|
||||||
|
|
||||||
// if (messageData.message.height > maxHeight) {
|
// if (messageData.message.height > maxHeight) {
|
||||||
// el.setAttribute('scrolling', 'auto')
|
// el.setAttribute('scrolling', 'auto')
|
||||||
// } else {
|
// } else {
|
||||||
// el.setAttribute('scrolling', 'no')
|
// el.setAttribute('scrolling', 'no')
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// el?.iFrameResizer?.resize?.()
|
// el?.iFrameResizer?.resize?.()
|
||||||
// },
|
// },
|
||||||
}, el)
|
},
|
||||||
|
el,
|
||||||
|
)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
unmounted(el) {
|
unmounted(el) {
|
||||||
|
@ -5,7 +5,6 @@ import { ref } from 'vue'
|
|||||||
const showDemoList = process.env.NODE_ENV === 'development'
|
const showDemoList = process.env.NODE_ENV === 'development'
|
||||||
|
|
||||||
const searchValue = ref('')
|
const searchValue = ref('')
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@ -16,17 +15,16 @@ const searchValue = ref('')
|
|||||||
placeholder="Search for a demo..."
|
placeholder="Search for a demo..."
|
||||||
autofocus
|
autofocus
|
||||||
v-model="searchValue"
|
v-model="searchValue"
|
||||||
>
|
/>
|
||||||
<ul v-if="showDemoList || listing">
|
<ul v-if="showDemoList || listing">
|
||||||
<li
|
<li
|
||||||
class="p-5 border-b-2 border-black"
|
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"
|
:key="route.path"
|
||||||
>
|
>
|
||||||
<router-link
|
<router-link class="block mb-2 font-medium" :to="route.path">
|
||||||
class="block mb-2 font-medium"
|
|
||||||
:to="route.path"
|
|
||||||
>
|
|
||||||
{{ route.props.name }}
|
{{ route.props.name }}
|
||||||
</router-link>
|
</router-link>
|
||||||
|
|
||||||
@ -77,9 +75,7 @@ export default {
|
|||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
query() {
|
query() {
|
||||||
return Object.fromEntries(Object
|
return Object.fromEntries(Object.entries(this.$route.query).map(([key, value]) => [key, this.fromString(value)]))
|
||||||
.entries(this.$route.query)
|
|
||||||
.map(([key, value]) => [key, this.fromString(value)]))
|
|
||||||
},
|
},
|
||||||
|
|
||||||
listing() {
|
listing() {
|
||||||
|
@ -9,17 +9,7 @@ async function init() {
|
|||||||
|
|
||||||
highlighter = await shiki.createHighlighter({
|
highlighter = await shiki.createHighlighter({
|
||||||
themes: ['material-theme-darker'],
|
themes: ['material-theme-darker'],
|
||||||
langs: [
|
langs: ['html', 'js', 'jsx', 'ts', 'tsx', 'css', 'vue-html', 'vue', 'scss'],
|
||||||
'html',
|
|
||||||
'js',
|
|
||||||
'jsx',
|
|
||||||
'ts',
|
|
||||||
'tsx',
|
|
||||||
'css',
|
|
||||||
'vue-html',
|
|
||||||
'vue',
|
|
||||||
'scss',
|
|
||||||
],
|
|
||||||
})
|
})
|
||||||
|
|
||||||
return highlighter
|
return highlighter
|
||||||
|
@ -4,92 +4,98 @@
|
|||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Inter';
|
font-family: 'Inter';
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
src: url("https://rsms.me/inter/font-files/Inter-Regular.woff2?v=3.19") format("woff2"),
|
src:
|
||||||
url("https://rsms.me/inter/font-files/Inter-Regular.woff?v=3.19") format("woff");
|
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-face {
|
||||||
font-family: 'Inter';
|
font-family: 'Inter';
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
src: url("https://rsms.me/inter/font-files/Inter-Italic.woff2?v=3.19") format("woff2"),
|
src:
|
||||||
url("https://rsms.me/inter/font-files/Inter-Italic.woff?v=3.19") format("woff");
|
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-face {
|
||||||
font-family: 'Inter';
|
font-family: 'Inter';
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
src: url("https://rsms.me/inter/font-files/Inter-Medium.woff2?v=3.19") format("woff2"),
|
src:
|
||||||
url("https://rsms.me/inter/font-files/Inter-Medium.woff?v=3.19") format("woff");
|
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-face {
|
||||||
font-family: 'Inter';
|
font-family: 'Inter';
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
src: url("https://rsms.me/inter/font-files/Inter-MediumItalic.woff2?v=3.19") format("woff2"),
|
src:
|
||||||
url("https://rsms.me/inter/font-files/Inter-MediumItalic.woff?v=3.19") format("woff");
|
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-face {
|
||||||
font-family: 'Inter';
|
font-family: 'Inter';
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
src: url("https://rsms.me/inter/font-files/Inter-SemiBold.woff2?v=3.19") format("woff2"),
|
src:
|
||||||
url("https://rsms.me/inter/font-files/Inter-SemiBold.woff?v=3.19") format("woff");
|
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-face {
|
||||||
font-family: 'Inter';
|
font-family: 'Inter';
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
src: url("https://rsms.me/inter/font-files/Inter-SemiBoldItalic.woff2?v=3.19") format("woff2"),
|
src:
|
||||||
url("https://rsms.me/inter/font-files/Inter-SemiBoldItalic.woff?v=3.19") format("woff");
|
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-face {
|
||||||
font-family: 'Inter';
|
font-family: 'Inter';
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
src: url("https://rsms.me/inter/font-files/Inter-Bold.woff2?v=3.19") format("woff2"),
|
src:
|
||||||
url("https://rsms.me/inter/font-files/Inter-Bold.woff?v=3.19") format("woff");
|
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-face {
|
||||||
font-family: 'Inter';
|
font-family: 'Inter';
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
src: url("https://rsms.me/inter/font-files/Inter-BoldItalic.woff2?v=3.19") format("woff2"),
|
src:
|
||||||
url("https://rsms.me/inter/font-files/Inter-BoldItalic.woff?v=3.19") format("woff");
|
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-face {
|
||||||
font-family: 'JetBrains Mono';
|
font-family: 'JetBrains Mono';
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
src:
|
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.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.woff') format('woff');
|
||||||
;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'JetBrains Mono';
|
font-family: 'JetBrains Mono';
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
src:
|
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.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.woff') format('woff');
|
||||||
;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
::-webkit-scrollbar {
|
::-webkit-scrollbar {
|
||||||
|
@ -18,21 +18,18 @@ export default function init(name: string, source: any) {
|
|||||||
const root = document.getElementById('app')
|
const root = document.getElementById('app')
|
||||||
|
|
||||||
if (root) {
|
if (root) {
|
||||||
createRoot(root)
|
createRoot(root).render(React.createElement(module.default))
|
||||||
.render(React.createElement(module.default))
|
|
||||||
}
|
}
|
||||||
debug()
|
debug()
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
import(`../src/${demoCategory}/${demoName}/${frameworkName}/index.jsx`)
|
import(`../src/${demoCategory}/${demoName}/${frameworkName}/index.jsx`).then(module => {
|
||||||
.then(module => {
|
const root = document.getElementById('app')
|
||||||
const root = document.getElementById('app')
|
|
||||||
|
|
||||||
if (root) {
|
if (root) {
|
||||||
createRoot(root)
|
createRoot(root).render(React.createElement(module.default))
|
||||||
.render(React.createElement(module.default))
|
}
|
||||||
}
|
debug()
|
||||||
debug()
|
})
|
||||||
})
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,22 +1,22 @@
|
|||||||
/* Base HTML and global element styles*/
|
/* Base HTML and global element styles*/
|
||||||
:root {
|
:root {
|
||||||
--white: #FFF;
|
--white: #fff;
|
||||||
--black: #2E2B29;
|
--black: #2e2b29;
|
||||||
--black-contrast: #110F0E;
|
--black-contrast: #110f0e;
|
||||||
--gray-1: rgba(61, 37, 20, 0.05);
|
--gray-1: rgba(61, 37, 20, 0.05);
|
||||||
--gray-2: rgba(61, 37, 20, 0.08);
|
--gray-2: rgba(61, 37, 20, 0.08);
|
||||||
--gray-3: rgba(61, 37, 20, 0.12);
|
--gray-3: rgba(61, 37, 20, 0.12);
|
||||||
--gray-4: rgba(53, 38, 28, 0.30);
|
--gray-4: rgba(53, 38, 28, 0.3);
|
||||||
--gray-5: rgba(28, 25, 23, 0.60);
|
--gray-5: rgba(28, 25, 23, 0.6);
|
||||||
--green: #22C55E;
|
--green: #22c55e;
|
||||||
--purple: #6A00F5;
|
--purple: #6a00f5;
|
||||||
--purple-contrast: #5800CC;
|
--purple-contrast: #5800cc;
|
||||||
--purple-light: rgba(88, 5, 255, 0.05);
|
--purple-light: rgba(88, 5, 255, 0.05);
|
||||||
--yellow-contrast: #FACC15;
|
--yellow-contrast: #facc15;
|
||||||
--yellow: rgba(250, 204, 21, 0.4);
|
--yellow: rgba(250, 204, 21, 0.4);
|
||||||
--yellow-light: #FFFAE5;
|
--yellow-light: #fffae5;
|
||||||
--red: #FF5C33;
|
--red: #ff5c33;
|
||||||
--red-light: #FFEBE5;
|
--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);
|
--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 {
|
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;
|
line-height: 1.5;
|
||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
@ -104,7 +119,7 @@ textarea {
|
|||||||
line-height: 1.15;
|
line-height: 1.15;
|
||||||
margin: none;
|
margin: none;
|
||||||
padding: 0.375rem 0.625rem;
|
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 {
|
&:hover {
|
||||||
background-color: var(--gray-3);
|
background-color: var(--gray-3);
|
||||||
@ -150,7 +165,7 @@ select:not([disabled]) {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type="text"],
|
input[type='text'],
|
||||||
textarea {
|
textarea {
|
||||||
background-color: unset;
|
background-color: unset;
|
||||||
border: 1px solid var(--gray-3);
|
border: 1px solid var(--gray-3);
|
||||||
@ -166,7 +181,8 @@ textarea {
|
|||||||
border-color: var(--gray-4);
|
border-color: var(--gray-4);
|
||||||
}
|
}
|
||||||
|
|
||||||
&:focus-visible, &:focus {
|
&:focus-visible,
|
||||||
|
&:focus {
|
||||||
border-color: var(--purple);
|
border-color: var(--purple);
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
@ -229,7 +245,7 @@ form {
|
|||||||
background-color: var(--purple-light);
|
background-color: var(--purple-light);
|
||||||
|
|
||||||
&::after {
|
&::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-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-size: cover;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
@ -272,13 +288,13 @@ hr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
kbd {
|
kbd {
|
||||||
background-color: var(--gray-2);
|
background-color: var(--gray-2);
|
||||||
border: 1px solid var(--gray-2);
|
border: 1px solid var(--gray-2);
|
||||||
border-radius: 0.25rem;
|
border-radius: 0.25rem;
|
||||||
font-size: 0.6rem;
|
font-size: 0.6rem;
|
||||||
line-height: 1.15;
|
line-height: 1.15;
|
||||||
padding: 0.1rem 0.25rem;
|
padding: 0.1rem 0.25rem;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Layout and structure */
|
/* Layout and structure */
|
||||||
@ -345,7 +361,7 @@ kbd {
|
|||||||
line-height: 1.15;
|
line-height: 1.15;
|
||||||
min-height: 1.5rem;
|
min-height: 1.5rem;
|
||||||
padding: 0 0.375rem;
|
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) {
|
&:has(input:checked) {
|
||||||
background-color: var(--white);
|
background-color: var(--white);
|
||||||
|
@ -10,12 +10,11 @@ export default function init(name: string, source: any) {
|
|||||||
|
|
||||||
const [demoCategory, demoName, frameworkName] = splitName(name)
|
const [demoCategory, demoName, frameworkName] = splitName(name)
|
||||||
|
|
||||||
import(`../src/${demoCategory}/${demoName}/${frameworkName}/index.svelte`)
|
import(`../src/${demoCategory}/${demoName}/${frameworkName}/index.svelte`).then(Module => {
|
||||||
.then(Module => {
|
const Component = Module.default
|
||||||
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)
|
const [demoCategory, demoName, frameworkName] = splitName(name)
|
||||||
|
|
||||||
import(`../src/${demoCategory}/${demoName}/${frameworkName}/index.vue`)
|
import(`../src/${demoCategory}/${demoName}/${frameworkName}/index.vue`).then(module => {
|
||||||
.then(module => {
|
const app = createApp(module.default)
|
||||||
const app = createApp(module.default)
|
|
||||||
|
|
||||||
if (typeof module.configureApp === 'function') {
|
if (typeof module.configureApp === 'function') {
|
||||||
module.configureApp(app)
|
module.configureApp(app)
|
||||||
}
|
}
|
||||||
app.mount('#app')
|
app.mount('#app')
|
||||||
debug()
|
debug()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,13 @@ const MenuBar = ({ editor }) => {
|
|||||||
}, [editor])
|
}, [editor])
|
||||||
|
|
||||||
const onCutToEnd = useCallback(() => {
|
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])
|
}, [editor])
|
||||||
|
|
||||||
if (!editor) {
|
if (!editor) {
|
||||||
|
@ -5,11 +5,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* List styles */
|
/* List styles */
|
||||||
ul,
|
ul,
|
||||||
ol {
|
ol {
|
||||||
padding: 0 1rem;
|
padding: 0 1rem;
|
||||||
margin: 1.25rem 1rem 1.25rem 0.4rem;
|
margin: 1.25rem 1rem 1.25rem 0.4rem;
|
||||||
|
|
||||||
li p {
|
li p {
|
||||||
margin-top: 0.25em;
|
margin-top: 0.25em;
|
||||||
margin-bottom: 0.25em;
|
margin-bottom: 0.25em;
|
||||||
@ -17,39 +17,39 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Heading styles */
|
/* Heading styles */
|
||||||
h1,
|
h1,
|
||||||
h2,
|
h2,
|
||||||
h3,
|
h3,
|
||||||
h4,
|
h4,
|
||||||
h5,
|
h5,
|
||||||
h6 {
|
h6 {
|
||||||
line-height: 1.1;
|
line-height: 1.1;
|
||||||
margin-top: 2.5rem;
|
margin-top: 2.5rem;
|
||||||
text-wrap: pretty;
|
text-wrap: pretty;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1,
|
h1,
|
||||||
h2 {
|
h2 {
|
||||||
margin-top: 3.5rem;
|
margin-top: 3.5rem;
|
||||||
margin-bottom: 1.5rem;
|
margin-bottom: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
font-size: 1.4rem;
|
font-size: 1.4rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
h2 {
|
h2 {
|
||||||
font-size: 1.2rem;
|
font-size: 1.2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
h3 {
|
h3 {
|
||||||
font-size: 1.1rem;
|
font-size: 1.1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
h4,
|
h4,
|
||||||
h5,
|
h5,
|
||||||
h6 {
|
h6 {
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Code and preformatted text styles */
|
/* Code and preformatted text styles */
|
||||||
|
@ -42,13 +42,18 @@ const MenuBar = () => {
|
|||||||
<button data-test-id="html-content" onClick={() => editor.chain().insertContent(htmlContent).focus().run()}>
|
<button data-test-id="html-content" onClick={() => editor.chain().insertContent(htmlContent).focus().run()}>
|
||||||
Insert HTML content
|
Insert HTML content
|
||||||
</button>
|
</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
|
Insert HTML with span tags content
|
||||||
</button>
|
</button>
|
||||||
<button data-test-id="text-content" onClick={() => editor.chain().insertContent(textContent).focus().run()}>
|
<button data-test-id="text-content" onClick={() => editor.chain().insertContent(textContent).focus().run()}>
|
||||||
Insert text content
|
Insert text content
|
||||||
</button>
|
</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>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
@ -72,7 +77,5 @@ const extensions = [
|
|||||||
const content = ''
|
const content = ''
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
return (
|
return <EditorProvider slotBefore={<MenuBar />} extensions={extensions} content={content}></EditorProvider>
|
||||||
<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()
|
cy.get('button[data-test-id="html-content"]').click()
|
||||||
|
|
||||||
// check if the content html is correct
|
// 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', () => {
|
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()
|
cy.get('button[data-test-id="text-content"]').click()
|
||||||
|
|
||||||
// check if the content html is correct
|
// 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', () => {
|
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', () => {
|
it('should remove newlines and tabs when parseOptions.preserveWhitespace=false', () => {
|
||||||
cy.get('.tiptap').then(([{ editor }]) => {
|
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>')
|
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.insertContent('<p>HelloWorld</p>')
|
||||||
editor.commands.setTextSelection(6)
|
editor.commands.setTextSelection(6)
|
||||||
editor.commands.insertContent('<img src="https://example.image/1" alt="This is an example" />')
|
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.insertContent('<p>HelloWorld</p>')
|
||||||
editor.commands.setTextSelection(1)
|
editor.commands.setTextSelection(1)
|
||||||
editor.commands.insertContent('<img src="https://example.image/1" alt="This is an example" />')
|
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`', () => {
|
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>')
|
cy.get('.tiptap').should('contain.html', '<h1>Tiptap</h1><p><strong>Hello World</strong></p>')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
})
|
})
|
||||||
|
@ -5,11 +5,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* List styles */
|
/* List styles */
|
||||||
ul,
|
ul,
|
||||||
ol {
|
ol {
|
||||||
padding: 0 1rem;
|
padding: 0 1rem;
|
||||||
margin: 1.25rem 1rem 1.25rem 0.4rem;
|
margin: 1.25rem 1rem 1.25rem 0.4rem;
|
||||||
|
|
||||||
li p {
|
li p {
|
||||||
margin-top: 0.25em;
|
margin-top: 0.25em;
|
||||||
margin-bottom: 0.25em;
|
margin-bottom: 0.25em;
|
||||||
@ -17,39 +17,39 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Heading styles */
|
/* Heading styles */
|
||||||
h1,
|
h1,
|
||||||
h2,
|
h2,
|
||||||
h3,
|
h3,
|
||||||
h4,
|
h4,
|
||||||
h5,
|
h5,
|
||||||
h6 {
|
h6 {
|
||||||
line-height: 1.1;
|
line-height: 1.1;
|
||||||
margin-top: 2.5rem;
|
margin-top: 2.5rem;
|
||||||
text-wrap: pretty;
|
text-wrap: pretty;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1,
|
h1,
|
||||||
h2 {
|
h2 {
|
||||||
margin-top: 3.5rem;
|
margin-top: 3.5rem;
|
||||||
margin-bottom: 1.5rem;
|
margin-bottom: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
font-size: 1.4rem;
|
font-size: 1.4rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
h2 {
|
h2 {
|
||||||
font-size: 1.2rem;
|
font-size: 1.2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
h3 {
|
h3 {
|
||||||
font-size: 1.1rem;
|
font-size: 1.1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
h4,
|
h4,
|
||||||
h5,
|
h5,
|
||||||
h6 {
|
h6 {
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Code and preformatted text styles */
|
/* Code and preformatted text styles */
|
||||||
|
@ -17,19 +17,11 @@ const MenuBar = () => {
|
|||||||
<div className="control-group">
|
<div className="control-group">
|
||||||
<div>
|
<div>
|
||||||
<label>
|
<label>
|
||||||
<input
|
<input type="checkbox" checked={useInputRules} onChange={() => setUseInputRules(prev => !prev)} />
|
||||||
type="checkbox"
|
|
||||||
checked={useInputRules}
|
|
||||||
onChange={() => setUseInputRules(prev => !prev)}
|
|
||||||
/>
|
|
||||||
Apply input rules
|
Apply input rules
|
||||||
</label>
|
</label>
|
||||||
<label>
|
<label>
|
||||||
<input
|
<input type="checkbox" checked={usePasteRules} onChange={() => setUsePasteRules(prev => !prev)} />
|
||||||
type="checkbox"
|
|
||||||
checked={usePasteRules}
|
|
||||||
onChange={() => setUsePasteRules(prev => !prev)}
|
|
||||||
/>
|
|
||||||
Apply paste rules
|
Apply paste rules
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
@ -163,14 +155,10 @@ const MenuBar = () => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const extensions = [
|
const extensions = [StarterKit]
|
||||||
StarterKit,
|
|
||||||
]
|
|
||||||
|
|
||||||
const content = ''
|
const content = ''
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
return (
|
return <EditorProvider slotBefore={<MenuBar />} extensions={extensions} content={content}></EditorProvider>
|
||||||
<EditorProvider slotBefore={<MenuBar />} extensions={extensions} content={content}></EditorProvider>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
@ -5,11 +5,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* List styles */
|
/* List styles */
|
||||||
ul,
|
ul,
|
||||||
ol {
|
ol {
|
||||||
padding: 0 1rem;
|
padding: 0 1rem;
|
||||||
margin: 1.25rem 1rem 1.25rem 0.4rem;
|
margin: 1.25rem 1rem 1.25rem 0.4rem;
|
||||||
|
|
||||||
li p {
|
li p {
|
||||||
margin-top: 0.25em;
|
margin-top: 0.25em;
|
||||||
margin-bottom: 0.25em;
|
margin-bottom: 0.25em;
|
||||||
@ -17,39 +17,39 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Heading styles */
|
/* Heading styles */
|
||||||
h1,
|
h1,
|
||||||
h2,
|
h2,
|
||||||
h3,
|
h3,
|
||||||
h4,
|
h4,
|
||||||
h5,
|
h5,
|
||||||
h6 {
|
h6 {
|
||||||
line-height: 1.1;
|
line-height: 1.1;
|
||||||
margin-top: 2.5rem;
|
margin-top: 2.5rem;
|
||||||
text-wrap: pretty;
|
text-wrap: pretty;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1,
|
h1,
|
||||||
h2 {
|
h2 {
|
||||||
margin-top: 3.5rem;
|
margin-top: 3.5rem;
|
||||||
margin-bottom: 1.5rem;
|
margin-bottom: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
font-size: 1.4rem;
|
font-size: 1.4rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
h2 {
|
h2 {
|
||||||
font-size: 1.2rem;
|
font-size: 1.2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
h3 {
|
h3 {
|
||||||
font-size: 1.1rem;
|
font-size: 1.1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
h4,
|
h4,
|
||||||
h5,
|
h5,
|
||||||
h6 {
|
h6 {
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Code and preformatted text styles */
|
/* Code and preformatted text styles */
|
||||||
|
@ -25,7 +25,5 @@ const extensions = [
|
|||||||
const content = ''
|
const content = ''
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
return (
|
return <EditorProvider extensions={extensions} content={content}></EditorProvider>
|
||||||
<EditorProvider extensions={extensions} content={content}></EditorProvider>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,10 @@ context('/src/Commands/SetContent/React/', () => {
|
|||||||
|
|
||||||
it('should insert a Prosemirror Fragment as content', () => {
|
it('should insert a Prosemirror Fragment as content', () => {
|
||||||
cy.get('.tiptap').then(([{ editor }]) => {
|
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>')
|
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', () => {
|
it('should insert more complex html content', () => {
|
||||||
cy.get('.tiptap').then(([{ editor }]) => {
|
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>')
|
editor.commands.setContent(
|
||||||
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>')
|
'<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', () => {
|
it('should insert mentions', () => {
|
||||||
cy.get('.tiptap').then(([{ editor }]) => {
|
cy.get('.tiptap').then(([{ editor }]) => {
|
||||||
editor.commands.setContent('<p><span data-type="mention" data-id="1" data-label="John Doe">@John Doe</span></p>')
|
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
|
// This exists in insertContentAt as well
|
||||||
it('should keep newlines and tabs between html fragments when preserveWhitespace = full', () => {
|
it('should keep newlines and tabs between html fragments when preserveWhitespace = full', () => {
|
||||||
cy.get('.tiptap').then(([{ editor }]) => {
|
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>')
|
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', () => {
|
it('should remove newlines and tabs when parseOptions.preserveWhitespace=false', () => {
|
||||||
cy.get('.tiptap').then(([{ editor }]) => {
|
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>')
|
cy.get('.tiptap').should('contain.html', '<h1>Tiptap</h1><p><strong>Hello World</strong></p>')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -24,8 +24,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
pre {
|
pre {
|
||||||
background: #0D0D0D;
|
background: #0d0d0d;
|
||||||
color: #FFF;
|
color: #fff;
|
||||||
font-family: 'JetBrainsMono', monospace;
|
font-family: 'JetBrainsMono', monospace;
|
||||||
padding: 0.75rem 1rem;
|
padding: 0.75rem 1rem;
|
||||||
border-radius: 0.5rem;
|
border-radius: 0.5rem;
|
||||||
@ -45,12 +45,12 @@
|
|||||||
|
|
||||||
blockquote {
|
blockquote {
|
||||||
padding-left: 1rem;
|
padding-left: 1rem;
|
||||||
border-left: 2px solid rgba(#0D0D0D, 0.1);
|
border-left: 2px solid rgba(#0d0d0d, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
hr {
|
hr {
|
||||||
border: none;
|
border: none;
|
||||||
border-top: 2px solid rgba(#0D0D0D, 0.1);
|
border-top: 2px solid rgba(#0d0d0d, 0.1);
|
||||||
margin: 2rem 0;
|
margin: 2rem 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -78,9 +78,7 @@ const getInitialUser = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const Editor = ({
|
const Editor = ({ ydoc, provider, room }) => {
|
||||||
ydoc, provider, room,
|
|
||||||
}) => {
|
|
||||||
const [status, setStatus] = useState('connecting')
|
const [status, setStatus] = useState('connecting')
|
||||||
const [currentUser, setCurrentUser] = useState(getInitialUser)
|
const [currentUser, setCurrentUser] = useState(getInitialUser)
|
||||||
|
|
||||||
@ -187,15 +185,12 @@ const Editor = ({
|
|||||||
|
|
||||||
<EditorContent editor={editor} className="main-group" />
|
<EditorContent editor={editor} className="main-group" />
|
||||||
|
|
||||||
<div
|
<div className="collab-status-group" data-state={status === 'connected' ? 'online' : 'offline'}>
|
||||||
className="collab-status-group"
|
|
||||||
data-state={status === 'connected' ? 'online' : 'offline'}
|
|
||||||
>
|
|
||||||
<label>
|
<label>
|
||||||
{status === 'connected'
|
{status === 'connected'
|
||||||
? `${editor.storage.collaborationCursor.users.length} user${
|
? `${editor.storage.collaborationCursor.users.length} user${
|
||||||
editor.storage.collaborationCursor.users.length === 1 ? '' : 's'
|
editor.storage.collaborationCursor.users.length === 1 ? '' : 's'
|
||||||
} online in ${room}`
|
} online in ${room}`
|
||||||
: 'offline'}
|
: 'offline'}
|
||||||
</label>
|
</label>
|
||||||
<button style={{ '--color': currentUser.color }} onClick={setName}>
|
<button style={{ '--color': currentUser.color }} onClick={setName}>
|
||||||
|
@ -5,11 +5,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* List styles */
|
/* List styles */
|
||||||
ul,
|
ul,
|
||||||
ol {
|
ol {
|
||||||
padding: 0 1rem;
|
padding: 0 1rem;
|
||||||
margin: 1.25rem 1rem 1.25rem 0.4rem;
|
margin: 1.25rem 1rem 1.25rem 0.4rem;
|
||||||
|
|
||||||
li p {
|
li p {
|
||||||
margin-top: 0.25em;
|
margin-top: 0.25em;
|
||||||
margin-bottom: 0.25em;
|
margin-bottom: 0.25em;
|
||||||
@ -17,39 +17,39 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Heading styles */
|
/* Heading styles */
|
||||||
h1,
|
h1,
|
||||||
h2,
|
h2,
|
||||||
h3,
|
h3,
|
||||||
h4,
|
h4,
|
||||||
h5,
|
h5,
|
||||||
h6 {
|
h6 {
|
||||||
line-height: 1.1;
|
line-height: 1.1;
|
||||||
margin-top: 2.5rem;
|
margin-top: 2.5rem;
|
||||||
text-wrap: pretty;
|
text-wrap: pretty;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1,
|
h1,
|
||||||
h2 {
|
h2 {
|
||||||
margin-top: 3.5rem;
|
margin-top: 3.5rem;
|
||||||
margin-bottom: 1.5rem;
|
margin-bottom: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
font-size: 1.4rem;
|
font-size: 1.4rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
h2 {
|
h2 {
|
||||||
font-size: 1.2rem;
|
font-size: 1.2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
h3 {
|
h3 {
|
||||||
font-size: 1.1rem;
|
font-size: 1.1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
h4,
|
h4,
|
||||||
h5,
|
h5,
|
||||||
h6 {
|
h6 {
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Code and preformatted text styles */
|
/* Code and preformatted text styles */
|
||||||
@ -91,14 +91,14 @@
|
|||||||
|
|
||||||
/* Highlight specific styles */
|
/* Highlight specific styles */
|
||||||
mark {
|
mark {
|
||||||
background-color: #FAF594;
|
background-color: #faf594;
|
||||||
border-radius: 0.4rem;
|
border-radius: 0.4rem;
|
||||||
box-decoration-break: clone;
|
box-decoration-break: clone;
|
||||||
padding: 0.1rem 0.3rem;
|
padding: 0.1rem 0.3rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Task list specific styles */
|
/* Task list specific styles */
|
||||||
ul[data-type="taskList"] {
|
ul[data-type='taskList'] {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
@ -118,11 +118,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type="checkbox"] {
|
input[type='checkbox'] {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
ul[data-type="taskList"] {
|
ul[data-type='taskList'] {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -170,7 +170,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Column-half */
|
/* Column-half */
|
||||||
body {
|
body {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -205,11 +205,11 @@ body {
|
|||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
font-size: 0.75rem;
|
font-size: 0.75rem;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
padding: 0.375rem 0.5rem 0.375rem 1rem;
|
padding: 0.375rem 0.5rem 0.375rem 1rem;
|
||||||
position: sticky;
|
position: sticky;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
|
|
||||||
button {
|
button {
|
||||||
@ -230,13 +230,13 @@ body {
|
|||||||
&::before {
|
&::before {
|
||||||
background-color: var(--color);
|
background-color: var(--color);
|
||||||
border-radius: 0.375rem;
|
border-radius: 0.375rem;
|
||||||
content: "";
|
content: '';
|
||||||
height: 100%;
|
height: 100%;
|
||||||
left: 0;
|
left: 0;
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
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%;
|
width: 100%;
|
||||||
z-index: -1;
|
z-index: -1;
|
||||||
}
|
}
|
||||||
@ -256,13 +256,13 @@ body {
|
|||||||
|
|
||||||
&::before {
|
&::before {
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
content: " ";
|
content: ' ';
|
||||||
height: 0.35rem;
|
height: 0.35rem;
|
||||||
width: 0.35rem;
|
width: 0.35rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&[data-state="online"] {
|
&[data-state='online'] {
|
||||||
label {
|
label {
|
||||||
&::before {
|
&::before {
|
||||||
background-color: var(--green);
|
background-color: var(--green);
|
||||||
@ -270,7 +270,7 @@ body {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&[data-state="offline"] {
|
&[data-state='offline'] {
|
||||||
label {
|
label {
|
||||||
&::before {
|
&::before {
|
||||||
background-color: var(--red);
|
background-color: var(--red);
|
||||||
|
@ -6,10 +6,7 @@ import Collaboration from '@tiptap/extension-collaboration'
|
|||||||
import CollaborationCursor from '@tiptap/extension-collaboration-cursor'
|
import CollaborationCursor from '@tiptap/extension-collaboration-cursor'
|
||||||
import { EditorContent, useEditor } from '@tiptap/react'
|
import { EditorContent, useEditor } from '@tiptap/react'
|
||||||
import StarterKit from '@tiptap/starter-kit'
|
import StarterKit from '@tiptap/starter-kit'
|
||||||
import React, {
|
import React, { useCallback, useEffect, useState } from 'react'
|
||||||
useCallback, useEffect,
|
|
||||||
useState,
|
|
||||||
} from 'react'
|
|
||||||
import * as Y from 'yjs'
|
import * as Y from 'yjs'
|
||||||
|
|
||||||
const room = 'room-1'
|
const room = 'room-1'
|
||||||
@ -55,10 +52,12 @@ const websocketProvider = new TiptapCollabProvider({
|
|||||||
})
|
})
|
||||||
|
|
||||||
const getInitialUser = () => {
|
const getInitialUser = () => {
|
||||||
return JSON.parse(localStorage.getItem('currentUser')) || {
|
return (
|
||||||
name: getRandomName(),
|
JSON.parse(localStorage.getItem('currentUser')) || {
|
||||||
color: getRandomColor(),
|
name: getRandomName(),
|
||||||
}
|
color: getRandomColor(),
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
|
@ -27,9 +27,9 @@
|
|||||||
background: #0d0d0d;
|
background: #0d0d0d;
|
||||||
border-radius: 0.5rem;
|
border-radius: 0.5rem;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
font-family: "JetBrainsMono", monospace;
|
font-family: 'JetBrainsMono', monospace;
|
||||||
padding: 0.75rem 1rem;
|
padding: 0.75rem 1rem;
|
||||||
|
|
||||||
code {
|
code {
|
||||||
background: none;
|
background: none;
|
||||||
color: inherit;
|
color: inherit;
|
||||||
@ -62,7 +62,7 @@
|
|||||||
margin: 2rem 0;
|
margin: 2rem 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
ul[data-type="taskList"] {
|
ul[data-type='taskList'] {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
||||||
@ -127,7 +127,7 @@
|
|||||||
&::before {
|
&::before {
|
||||||
background: rgba(#0d0d0d, 0.5);
|
background: rgba(#0d0d0d, 0.5);
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
content: " ";
|
content: ' ';
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
height: 0.5rem;
|
height: 0.5rem;
|
||||||
|
@ -44,15 +44,13 @@ export default () => {
|
|||||||
|
|
||||||
// empty
|
// empty
|
||||||
if (url === '') {
|
if (url === '') {
|
||||||
editor.chain().focus().extendMarkRange('link').unsetLink()
|
editor.chain().focus().extendMarkRange('link').unsetLink().run()
|
||||||
.run()
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// update link
|
// update link
|
||||||
editor.chain().focus().extendMarkRange('link').setLink({ href: url })
|
editor.chain().focus().extendMarkRange('link').setLink({ href: url }).run()
|
||||||
.run()
|
|
||||||
}, [editor])
|
}, [editor])
|
||||||
|
|
||||||
if (!editor) {
|
if (!editor) {
|
||||||
@ -63,11 +61,7 @@ export default () => {
|
|||||||
<>
|
<>
|
||||||
<div className="control-group">
|
<div className="control-group">
|
||||||
<div className="button-group">
|
<div className="button-group">
|
||||||
<button
|
<button onClick={setLink} className={editorState.isLink ? 'is-active' : ''} data-testid="setLink">
|
||||||
onClick={setLink}
|
|
||||||
className={editorState.isLink ? 'is-active' : ''}
|
|
||||||
data-testid="setLink"
|
|
||||||
>
|
|
||||||
Set link
|
Set link
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
|
@ -42,61 +42,39 @@ context('/src/Examples/AutolinkValidation/React/', () => {
|
|||||||
cy.get('.tiptap').type('https://tiptap.dev {home}')
|
cy.get('.tiptap').type('https://tiptap.dev {home}')
|
||||||
cy.get('.tiptap').should('have.text', 'https://tiptap.dev ')
|
cy.get('.tiptap').should('have.text', 'https://tiptap.dev ')
|
||||||
cy.get('[data-testid=unsetLink]').click()
|
cy.get('[data-testid=unsetLink]').click()
|
||||||
cy.get('.tiptap')
|
cy.get('.tiptap').find('a').should('have.length', 0)
|
||||||
.find('a')
|
|
||||||
.should('have.length', 0)
|
|
||||||
cy.get('.tiptap').type('{end}http://www.example.com/ ')
|
cy.get('.tiptap').type('{end}http://www.example.com/ ')
|
||||||
cy.get('.tiptap')
|
cy.get('.tiptap').find('a').should('have.length', 1).should('have.attr', 'href', 'http://www.example.com/')
|
||||||
.find('a')
|
|
||||||
.should('have.length', 1)
|
|
||||||
.should('have.attr', 'href', 'http://www.example.com/')
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should not relink unset links after hitting next paragraph', () => {
|
it('should not relink unset links after hitting next paragraph', () => {
|
||||||
cy.get('.tiptap').type('https://tiptap.dev {home}')
|
cy.get('.tiptap').type('https://tiptap.dev {home}')
|
||||||
cy.get('.tiptap').should('have.text', 'https://tiptap.dev ')
|
cy.get('.tiptap').should('have.text', 'https://tiptap.dev ')
|
||||||
cy.get('[data-testid=unsetLink]').click()
|
cy.get('[data-testid=unsetLink]').click()
|
||||||
cy.get('.tiptap')
|
cy.get('.tiptap').find('a').should('have.length', 0)
|
||||||
.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').type('{end}typing other text should prevent the link from relinking when hitting enter{enter}')
|
||||||
cy.get('.tiptap')
|
cy.get('.tiptap').find('a').should('have.length', 0)
|
||||||
.find('a')
|
|
||||||
.should('have.length', 0)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should not relink unset links after modifying', () => {
|
it('should not relink unset links after modifying', () => {
|
||||||
cy.get('.tiptap').type('https://tiptap.dev {home}')
|
cy.get('.tiptap').type('https://tiptap.dev {home}')
|
||||||
cy.get('.tiptap').should('have.text', 'https://tiptap.dev ')
|
cy.get('.tiptap').should('have.text', 'https://tiptap.dev ')
|
||||||
cy.get('[data-testid=unsetLink]').click()
|
cy.get('[data-testid=unsetLink]').click()
|
||||||
cy.get('.tiptap')
|
cy.get('.tiptap').find('a').should('have.length', 0)
|
||||||
.find('a')
|
cy.get('.tiptap').type('{home}').type('{rightArrow}'.repeat('https://'.length)).type('blah')
|
||||||
.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').should('have.text', 'https://blahtiptap.dev ')
|
||||||
cy.get('.tiptap')
|
cy.get('.tiptap').find('a').should('have.length', 0)
|
||||||
.find('a')
|
|
||||||
.should('have.length', 0)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should autolink after hitting enter (new paragraph)', () => {
|
it('should autolink after hitting enter (new paragraph)', () => {
|
||||||
cy.get('.tiptap').type('https://tiptap.dev{enter}')
|
cy.get('.tiptap').type('https://tiptap.dev{enter}')
|
||||||
cy.get('.tiptap').should('have.text', 'https://tiptap.dev')
|
cy.get('.tiptap').should('have.text', 'https://tiptap.dev')
|
||||||
cy.get('.tiptap')
|
cy.get('.tiptap').find('a').should('have.length', 1).should('have.attr', 'href', 'https://tiptap.dev')
|
||||||
.find('a')
|
|
||||||
.should('have.length', 1)
|
|
||||||
.should('have.attr', 'href', 'https://tiptap.dev')
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should autolink after hitting shift-enter (hardbreak)', () => {
|
it('should autolink after hitting shift-enter (hardbreak)', () => {
|
||||||
cy.get('.tiptap').type('https://tiptap.dev{shift+enter}')
|
cy.get('.tiptap').type('https://tiptap.dev{shift+enter}')
|
||||||
cy.get('.tiptap').should('have.text', 'https://tiptap.dev')
|
cy.get('.tiptap').should('have.text', 'https://tiptap.dev')
|
||||||
cy.get('.tiptap')
|
cy.get('.tiptap').find('a').should('have.length', 1).should('have.attr', 'href', 'https://tiptap.dev')
|
||||||
.find('a')
|
|
||||||
.should('have.length', 1)
|
|
||||||
.should('have.attr', 'href', 'https://tiptap.dev')
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -5,11 +5,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* List styles */
|
/* List styles */
|
||||||
ul,
|
ul,
|
||||||
ol {
|
ol {
|
||||||
padding: 0 1rem;
|
padding: 0 1rem;
|
||||||
margin: 1.25rem 1rem 1.25rem 0.4rem;
|
margin: 1.25rem 1rem 1.25rem 0.4rem;
|
||||||
|
|
||||||
li p {
|
li p {
|
||||||
margin-top: 0.25em;
|
margin-top: 0.25em;
|
||||||
margin-bottom: 0.25em;
|
margin-bottom: 0.25em;
|
||||||
@ -17,39 +17,39 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Heading styles */
|
/* Heading styles */
|
||||||
h1,
|
h1,
|
||||||
h2,
|
h2,
|
||||||
h3,
|
h3,
|
||||||
h4,
|
h4,
|
||||||
h5,
|
h5,
|
||||||
h6 {
|
h6 {
|
||||||
line-height: 1.1;
|
line-height: 1.1;
|
||||||
margin-top: 2.5rem;
|
margin-top: 2.5rem;
|
||||||
text-wrap: pretty;
|
text-wrap: pretty;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1,
|
h1,
|
||||||
h2 {
|
h2 {
|
||||||
margin-top: 3.5rem;
|
margin-top: 3.5rem;
|
||||||
margin-bottom: 1.5rem;
|
margin-bottom: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
font-size: 1.4rem;
|
font-size: 1.4rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
h2 {
|
h2 {
|
||||||
font-size: 1.2rem;
|
font-size: 1.2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
h3 {
|
h3 {
|
||||||
font-size: 1.1rem;
|
font-size: 1.1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
h4,
|
h4,
|
||||||
h5,
|
h5,
|
||||||
h6 {
|
h6 {
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Code and preformatted text styles */
|
/* Code and preformatted text styles */
|
||||||
|
@ -70,8 +70,8 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pre {
|
pre {
|
||||||
background: #0D0D0D;
|
background: #0d0d0d;
|
||||||
color: #FFF;
|
color: #fff;
|
||||||
font-family: 'JetBrainsMono', monospace;
|
font-family: 'JetBrainsMono', monospace;
|
||||||
padding: 0.75rem 1rem;
|
padding: 0.75rem 1rem;
|
||||||
border-radius: 0.5rem;
|
border-radius: 0.5rem;
|
||||||
@ -95,7 +95,7 @@ export default {
|
|||||||
|
|
||||||
blockquote {
|
blockquote {
|
||||||
padding-left: 1rem;
|
padding-left: 1rem;
|
||||||
border-left: 2px solid rgba(#0D0D0D, 0.1);
|
border-left: 2px solid rgba(#0d0d0d, 0.1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -38,12 +38,8 @@ const MenuBar = ({ editor }) => {
|
|||||||
>
|
>
|
||||||
Code
|
Code
|
||||||
</button>
|
</button>
|
||||||
<button onClick={() => editor.chain().focus().unsetAllMarks().run()}>
|
<button onClick={() => editor.chain().focus().unsetAllMarks().run()}>Clear marks</button>
|
||||||
Clear marks
|
<button onClick={() => editor.chain().focus().clearNodes().run()}>Clear nodes</button>
|
||||||
</button>
|
|
||||||
<button onClick={() => editor.chain().focus().clearNodes().run()}>
|
|
||||||
Clear nodes
|
|
||||||
</button>
|
|
||||||
<button
|
<button
|
||||||
onClick={() => editor.chain().focus().setParagraph().run()}
|
onClick={() => editor.chain().focus().setParagraph().run()}
|
||||||
className={editor.isActive('paragraph') ? 'is-active' : ''}
|
className={editor.isActive('paragraph') ? 'is-active' : ''}
|
||||||
@ -110,18 +106,10 @@ const MenuBar = ({ editor }) => {
|
|||||||
>
|
>
|
||||||
Blockquote
|
Blockquote
|
||||||
</button>
|
</button>
|
||||||
<button onClick={() => editor.chain().focus().setHorizontalRule().run()}>
|
<button onClick={() => editor.chain().focus().setHorizontalRule().run()}>Horizontal rule</button>
|
||||||
Horizontal rule
|
<button onClick={() => editor.chain().focus().setHardBreak().run()}>Hard break</button>
|
||||||
</button>
|
<button onClick={() => editor.chain().focus().undo().run()}>Undo</button>
|
||||||
<button onClick={() => editor.chain().focus().setHardBreak().run()}>
|
<button onClick={() => editor.chain().focus().redo().run()}>Redo</button>
|
||||||
Hard break
|
|
||||||
</button>
|
|
||||||
<button onClick={() => editor.chain().focus().undo().run()}>
|
|
||||||
Undo
|
|
||||||
</button>
|
|
||||||
<button onClick={() => editor.chain().focus().redo().run()}>
|
|
||||||
Redo
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
@ -129,9 +117,7 @@ const MenuBar = ({ editor }) => {
|
|||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
const editor = useEditor({
|
const editor = useEditor({
|
||||||
extensions: [
|
extensions: [StarterKit],
|
||||||
StarterKit,
|
|
||||||
],
|
|
||||||
content,
|
content,
|
||||||
editorProps: {
|
editorProps: {
|
||||||
attributes: {
|
attributes: {
|
||||||
|
@ -5,11 +5,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* List styles */
|
/* List styles */
|
||||||
ul,
|
ul,
|
||||||
ol {
|
ol {
|
||||||
padding: 0 1rem;
|
padding: 0 1rem;
|
||||||
margin: 1.25rem 1rem 1.25rem 0.4rem;
|
margin: 1.25rem 1rem 1.25rem 0.4rem;
|
||||||
|
|
||||||
li p {
|
li p {
|
||||||
margin-top: 0.25em;
|
margin-top: 0.25em;
|
||||||
margin-bottom: 0.25em;
|
margin-bottom: 0.25em;
|
||||||
@ -17,39 +17,39 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Heading styles */
|
/* Heading styles */
|
||||||
h1,
|
h1,
|
||||||
h2,
|
h2,
|
||||||
h3,
|
h3,
|
||||||
h4,
|
h4,
|
||||||
h5,
|
h5,
|
||||||
h6 {
|
h6 {
|
||||||
line-height: 1.1;
|
line-height: 1.1;
|
||||||
margin-top: 2.5rem;
|
margin-top: 2.5rem;
|
||||||
text-wrap: pretty;
|
text-wrap: pretty;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1,
|
h1,
|
||||||
h2 {
|
h2 {
|
||||||
margin-top: 3.5rem;
|
margin-top: 3.5rem;
|
||||||
margin-bottom: 1.5rem;
|
margin-bottom: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
font-size: 1.4rem;
|
font-size: 1.4rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
h2 {
|
h2 {
|
||||||
font-size: 1.2rem;
|
font-size: 1.2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
h3 {
|
h3 {
|
||||||
font-size: 1.1rem;
|
font-size: 1.1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
h4,
|
h4,
|
||||||
h5,
|
h5,
|
||||||
h6 {
|
h6 {
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Code and preformatted text styles */
|
/* Code and preformatted text styles */
|
||||||
|
@ -5,66 +5,93 @@
|
|||||||
<button @click="editor.chain().focus().toggleBold().run()" :class="{ 'is-active': editor.isActive('bold') }">
|
<button @click="editor.chain().focus().toggleBold().run()" :class="{ 'is-active': editor.isActive('bold') }">
|
||||||
Bold
|
Bold
|
||||||
</button>
|
</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
|
Italic
|
||||||
</button>
|
</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
|
Strike
|
||||||
</button>
|
</button>
|
||||||
<button @click="editor.chain().focus().toggleCode().run()" :class="{ 'is-active': editor.isActive('code') }">
|
<button @click="editor.chain().focus().toggleCode().run()" :class="{ 'is-active': editor.isActive('code') }">
|
||||||
Code
|
Code
|
||||||
</button>
|
</button>
|
||||||
<button @click="editor.chain().focus().unsetAllMarks().run()">
|
<button @click="editor.chain().focus().unsetAllMarks().run()">Clear marks</button>
|
||||||
Clear marks
|
<button @click="editor.chain().focus().clearNodes().run()">Clear nodes</button>
|
||||||
</button>
|
<button
|
||||||
<button @click="editor.chain().focus().clearNodes().run()">
|
@click="editor.chain().focus().setParagraph().run()"
|
||||||
Clear nodes
|
:class="{ 'is-active': editor.isActive('paragraph') }"
|
||||||
</button>
|
>
|
||||||
<button @click="editor.chain().focus().setParagraph().run()" :class="{ 'is-active': editor.isActive('paragraph') }">
|
|
||||||
Paragraph
|
Paragraph
|
||||||
</button>
|
</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
|
H1
|
||||||
</button>
|
</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
|
H2
|
||||||
</button>
|
</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
|
H3
|
||||||
</button>
|
</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
|
H4
|
||||||
</button>
|
</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
|
H5
|
||||||
</button>
|
</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
|
H6
|
||||||
</button>
|
</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
|
Bullet list
|
||||||
</button>
|
</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
|
Ordered list
|
||||||
</button>
|
</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
|
Code block
|
||||||
</button>
|
</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
|
Blockquote
|
||||||
</button>
|
</button>
|
||||||
<button @click="editor.chain().focus().setHorizontalRule().run()">
|
<button @click="editor.chain().focus().setHorizontalRule().run()">Horizontal rule</button>
|
||||||
Horizontal rule
|
<button @click="editor.chain().focus().setHardBreak().run()">Hard break</button>
|
||||||
</button>
|
<button @click="editor.chain().focus().undo().run()">Undo</button>
|
||||||
<button @click="editor.chain().focus().setHardBreak().run()">
|
<button @click="editor.chain().focus().redo().run()">Redo</button>
|
||||||
Hard break
|
|
||||||
</button>
|
|
||||||
<button @click="editor.chain().focus().undo().run()">
|
|
||||||
Undo
|
|
||||||
</button>
|
|
||||||
<button @click="editor.chain().focus().redo().run()">
|
|
||||||
Redo
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<editor-content :editor="editor" />
|
<editor-content :editor="editor" />
|
||||||
@ -90,9 +117,7 @@ export default {
|
|||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
this.editor = new Editor({
|
this.editor = new Editor({
|
||||||
extensions: [
|
extensions: [StarterKit],
|
||||||
StarterKit,
|
|
||||||
],
|
|
||||||
content,
|
content,
|
||||||
editorProps: {
|
editorProps: {
|
||||||
attributes: {
|
attributes: {
|
||||||
|
@ -37,12 +37,8 @@ const MenuBar = ({ editor }) => {
|
|||||||
>
|
>
|
||||||
Code
|
Code
|
||||||
</button>
|
</button>
|
||||||
<button onClick={() => editor.chain().focus().unsetAllMarks().run()}>
|
<button onClick={() => editor.chain().focus().unsetAllMarks().run()}>Clear marks</button>
|
||||||
Clear marks
|
<button onClick={() => editor.chain().focus().clearNodes().run()}>Clear nodes</button>
|
||||||
</button>
|
|
||||||
<button onClick={() => editor.chain().focus().clearNodes().run()}>
|
|
||||||
Clear nodes
|
|
||||||
</button>
|
|
||||||
<button
|
<button
|
||||||
onClick={() => editor.chain().focus().setParagraph().run()}
|
onClick={() => editor.chain().focus().setParagraph().run()}
|
||||||
className={editor.isActive('paragraph') ? 'is-active' : ''}
|
className={editor.isActive('paragraph') ? 'is-active' : ''}
|
||||||
@ -109,27 +105,17 @@ const MenuBar = ({ editor }) => {
|
|||||||
>
|
>
|
||||||
Blockquote
|
Blockquote
|
||||||
</button>
|
</button>
|
||||||
<button onClick={() => editor.chain().focus().setHorizontalRule().run()}>
|
<button onClick={() => editor.chain().focus().setHorizontalRule().run()}>Horizontal rule</button>
|
||||||
Horizontal rule
|
<button onClick={() => editor.chain().focus().setHardBreak().run()}>Hard break</button>
|
||||||
</button>
|
<button onClick={() => editor.chain().focus().undo().run()}>Undo</button>
|
||||||
<button onClick={() => editor.chain().focus().setHardBreak().run()}>
|
<button onClick={() => editor.chain().focus().redo().run()}>Redo</button>
|
||||||
Hard break
|
|
||||||
</button>
|
|
||||||
<button onClick={() => editor.chain().focus().undo().run()}>
|
|
||||||
Undo
|
|
||||||
</button>
|
|
||||||
<button onClick={() => editor.chain().focus().redo().run()}>
|
|
||||||
Redo
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
const editor = useEditor({
|
const editor = useEditor({
|
||||||
extensions: [
|
extensions: [StarterKit],
|
||||||
StarterKit,
|
|
||||||
],
|
|
||||||
content: `
|
content: `
|
||||||
<h2>
|
<h2>
|
||||||
Hi there,
|
Hi there,
|
||||||
|
@ -5,11 +5,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* List styles */
|
/* List styles */
|
||||||
ul,
|
ul,
|
||||||
ol {
|
ol {
|
||||||
padding: 0 1rem;
|
padding: 0 1rem;
|
||||||
margin: 1.25rem 1rem 1.25rem 0.4rem;
|
margin: 1.25rem 1rem 1.25rem 0.4rem;
|
||||||
|
|
||||||
li p {
|
li p {
|
||||||
margin-top: 0.25em;
|
margin-top: 0.25em;
|
||||||
margin-bottom: 0.25em;
|
margin-bottom: 0.25em;
|
||||||
@ -17,39 +17,39 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Heading styles */
|
/* Heading styles */
|
||||||
h1,
|
h1,
|
||||||
h2,
|
h2,
|
||||||
h3,
|
h3,
|
||||||
h4,
|
h4,
|
||||||
h5,
|
h5,
|
||||||
h6 {
|
h6 {
|
||||||
line-height: 1.1;
|
line-height: 1.1;
|
||||||
margin-top: 2.5rem;
|
margin-top: 2.5rem;
|
||||||
text-wrap: pretty;
|
text-wrap: pretty;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1,
|
h1,
|
||||||
h2 {
|
h2 {
|
||||||
margin-top: 3.5rem;
|
margin-top: 3.5rem;
|
||||||
margin-bottom: 1.5rem;
|
margin-bottom: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
font-size: 1.4rem;
|
font-size: 1.4rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
h2 {
|
h2 {
|
||||||
font-size: 1.2rem;
|
font-size: 1.2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
h3 {
|
h3 {
|
||||||
font-size: 1.1rem;
|
font-size: 1.1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
h4,
|
h4,
|
||||||
h5,
|
h5,
|
||||||
h6 {
|
h6 {
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Code and preformatted text styles */
|
/* Code and preformatted text styles */
|
||||||
|
@ -12,57 +12,75 @@
|
|||||||
<button @click="editor.chain().focus().toggleCode().run()" :class="{ 'is-active': editor.isActive('code') }">
|
<button @click="editor.chain().focus().toggleCode().run()" :class="{ 'is-active': editor.isActive('code') }">
|
||||||
Code
|
Code
|
||||||
</button>
|
</button>
|
||||||
<button @click="editor.chain().focus().unsetAllMarks().run()">
|
<button @click="editor.chain().focus().unsetAllMarks().run()">Clear marks</button>
|
||||||
Clear marks
|
<button @click="editor.chain().focus().clearNodes().run()">Clear nodes</button>
|
||||||
</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().setParagraph().run()" :class="{ 'is-active': editor.isActive('paragraph') }">
|
||||||
Paragraph
|
Paragraph
|
||||||
</button>
|
</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
|
H1
|
||||||
</button>
|
</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
|
H2
|
||||||
</button>
|
</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
|
H3
|
||||||
</button>
|
</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
|
H4
|
||||||
</button>
|
</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
|
H5
|
||||||
</button>
|
</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
|
H6
|
||||||
</button>
|
</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
|
Bullet list
|
||||||
</button>
|
</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
|
Ordered list
|
||||||
</button>
|
</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
|
Code block
|
||||||
</button>
|
</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
|
Blockquote
|
||||||
</button>
|
</button>
|
||||||
<button @click="editor.chain().focus().setHorizontalRule().run()">
|
<button @click="editor.chain().focus().setHorizontalRule().run()">Horizontal rule</button>
|
||||||
Horizontal rule
|
<button @click="editor.chain().focus().setHardBreak().run()">Hard break</button>
|
||||||
</button>
|
<button @click="editor.chain().focus().undo().run()">Undo</button>
|
||||||
<button @click="editor.chain().focus().setHardBreak().run()">
|
<button @click="editor.chain().focus().redo().run()">Redo</button>
|
||||||
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" />
|
<editor-content :editor="editor" />
|
||||||
</template>
|
</template>
|
||||||
@ -87,9 +105,7 @@ export default {
|
|||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
this.editor = new Editor({
|
this.editor = new Editor({
|
||||||
extensions: [
|
extensions: [StarterKit],
|
||||||
StarterKit,
|
|
||||||
],
|
|
||||||
content: `
|
content: `
|
||||||
<h1 class="test">
|
<h1 class="test">
|
||||||
This is a red headline
|
This is a red headline
|
||||||
|
@ -3,15 +3,21 @@ import './CodeBlockComponent.scss'
|
|||||||
import { NodeViewContent, NodeViewWrapper } from '@tiptap/react'
|
import { NodeViewContent, NodeViewWrapper } from '@tiptap/react'
|
||||||
import React from '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">
|
<NodeViewWrapper className="code-block">
|
||||||
<select contentEditable={false} defaultValue={defaultLanguage} onChange={event => updateAttributes({ language: event.target.value })}>
|
<select
|
||||||
<option value="null">
|
contentEditable={false}
|
||||||
auto
|
defaultValue={defaultLanguage}
|
||||||
</option>
|
onChange={event => updateAttributes({ language: event.target.value })}
|
||||||
<option disabled>
|
>
|
||||||
—
|
<option value="null">auto</option>
|
||||||
</option>
|
<option disabled>—</option>
|
||||||
{extension.options.lowlight.listLanguages().map((lang, index) => (
|
{extension.options.lowlight.listLanguages().map((lang, index) => (
|
||||||
<option key={index} value={lang}>
|
<option key={index} value={lang}>
|
||||||
{lang}
|
{lang}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
.tiptap {
|
.tiptap {
|
||||||
.code-block {
|
.code-block {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
select {
|
select {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
background-color: var(--white);
|
background-color: var(--white);
|
||||||
|
@ -33,7 +33,10 @@ const MenuBar = ({ editor }) => {
|
|||||||
return (
|
return (
|
||||||
<div className="control-group">
|
<div className="control-group">
|
||||||
<div className="button-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
|
Toggle code block
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@ -47,13 +50,11 @@ export default () => {
|
|||||||
Document,
|
Document,
|
||||||
Paragraph,
|
Paragraph,
|
||||||
Text,
|
Text,
|
||||||
CodeBlockLowlight
|
CodeBlockLowlight.extend({
|
||||||
.extend({
|
addNodeView() {
|
||||||
addNodeView() {
|
return ReactNodeViewRenderer(CodeBlockComponent)
|
||||||
return ReactNodeViewRenderer(CodeBlockComponent)
|
},
|
||||||
},
|
}).configure({ lowlight }),
|
||||||
})
|
|
||||||
.configure({ lowlight }),
|
|
||||||
],
|
],
|
||||||
content: `
|
content: `
|
||||||
<p>
|
<p>
|
||||||
|
@ -1,12 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<node-view-wrapper class="code-block">
|
<node-view-wrapper class="code-block">
|
||||||
<select contenteditable="false" v-model="selectedLanguage">
|
<select contenteditable="false" v-model="selectedLanguage">
|
||||||
<option :value="null">
|
<option :value="null">auto</option>
|
||||||
auto
|
<option disabled>—</option>
|
||||||
</option>
|
|
||||||
<option disabled>
|
|
||||||
—
|
|
||||||
</option>
|
|
||||||
<option v-for="(language, index) in languages" :value="language" :key="index">
|
<option v-for="(language, index) in languages" :value="language" :key="index">
|
||||||
{{ language }}
|
{{ language }}
|
||||||
</option>
|
</option>
|
||||||
|
@ -2,7 +2,10 @@
|
|||||||
<div v-if="editor" class="container">
|
<div v-if="editor" class="container">
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
<div class="button-">
|
<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
|
Toggle code block
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@ -52,13 +55,11 @@ export default {
|
|||||||
Document,
|
Document,
|
||||||
Paragraph,
|
Paragraph,
|
||||||
Text,
|
Text,
|
||||||
CodeBlockLowlight
|
CodeBlockLowlight.extend({
|
||||||
.extend({
|
addNodeView() {
|
||||||
addNodeView() {
|
return VueNodeViewRenderer(CodeBlockComponent)
|
||||||
return VueNodeViewRenderer(CodeBlockComponent)
|
},
|
||||||
},
|
}).configure({ lowlight }),
|
||||||
})
|
|
||||||
.configure({ lowlight }),
|
|
||||||
],
|
],
|
||||||
content: `
|
content: `
|
||||||
<p>
|
<p>
|
||||||
|
@ -106,8 +106,7 @@ export default ({ editor }) => {
|
|||||||
{
|
{
|
||||||
icon: 'format-clear',
|
icon: 'format-clear',
|
||||||
title: 'Clear format',
|
title: 'Clear format',
|
||||||
action: () => editor.chain().focus().clearNodes().unsetAllMarks()
|
action: () => editor.chain().focus().clearNodes().unsetAllMarks().run(),
|
||||||
.run(),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'divider',
|
type: 'divider',
|
||||||
|
@ -3,14 +3,8 @@ import './MenuItem.scss'
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import remixiconUrl from 'remixicon/fonts/remixicon.symbol.svg'
|
import remixiconUrl from 'remixicon/fonts/remixicon.symbol.svg'
|
||||||
|
|
||||||
export default ({
|
export default ({ icon, title, action, isActive = null }) => (
|
||||||
icon, title, action, isActive = null,
|
<button className={`menu-item${isActive && isActive() ? ' is-active' : ''}`} onClick={action} title={title}>
|
||||||
}) => (
|
|
||||||
<button
|
|
||||||
className={`menu-item${isActive && isActive() ? ' is-active' : ''}`}
|
|
||||||
onClick={action}
|
|
||||||
title={title}
|
|
||||||
>
|
|
||||||
<svg className="remix">
|
<svg className="remix">
|
||||||
<use xlinkHref={`${remixiconUrl}#ri-${icon}`} />
|
<use xlinkHref={`${remixiconUrl}#ri-${icon}`} />
|
||||||
</svg>
|
</svg>
|
||||||
|
@ -9,10 +9,7 @@ import TaskItem from '@tiptap/extension-task-item'
|
|||||||
import TaskList from '@tiptap/extension-task-list'
|
import TaskList from '@tiptap/extension-task-list'
|
||||||
import { EditorContent, useEditor } from '@tiptap/react'
|
import { EditorContent, useEditor } from '@tiptap/react'
|
||||||
import StarterKit from '@tiptap/starter-kit'
|
import StarterKit from '@tiptap/starter-kit'
|
||||||
import React, {
|
import React, { useCallback, useEffect, useState } from 'react'
|
||||||
useCallback, useEffect,
|
|
||||||
useState,
|
|
||||||
} from 'react'
|
|
||||||
import * as Y from 'yjs'
|
import * as Y from 'yjs'
|
||||||
|
|
||||||
import { variables } from '../../../variables.js'
|
import { variables } from '../../../variables.js'
|
||||||
@ -67,10 +64,12 @@ const websocketProvider = new TiptapCollabProvider({
|
|||||||
})
|
})
|
||||||
|
|
||||||
const getInitialUser = () => {
|
const getInitialUser = () => {
|
||||||
return JSON.parse(localStorage.getItem('currentUser')) || {
|
return (
|
||||||
name: getRandomName(),
|
JSON.parse(localStorage.getItem('currentUser')) || {
|
||||||
color: getRandomColor(),
|
name: getRandomName(),
|
||||||
}
|
color: getRandomColor(),
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
|
@ -27,9 +27,9 @@
|
|||||||
background: #0d0d0d;
|
background: #0d0d0d;
|
||||||
border-radius: 0.5rem;
|
border-radius: 0.5rem;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
font-family: "JetBrainsMono", monospace;
|
font-family: 'JetBrainsMono', monospace;
|
||||||
padding: 0.75rem 1rem;
|
padding: 0.75rem 1rem;
|
||||||
|
|
||||||
code {
|
code {
|
||||||
background: none;
|
background: none;
|
||||||
color: inherit;
|
color: inherit;
|
||||||
@ -62,7 +62,7 @@
|
|||||||
margin: 2rem 0;
|
margin: 2rem 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
ul[data-type="taskList"] {
|
ul[data-type='taskList'] {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
||||||
@ -135,7 +135,7 @@
|
|||||||
&::before {
|
&::before {
|
||||||
background: rgba(#0d0d0d, 0.5);
|
background: rgba(#0d0d0d, 0.5);
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
content: " ";
|
content: ' ';
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
height: 0.5rem;
|
height: 0.5rem;
|
||||||
|
@ -125,11 +125,7 @@ export default {
|
|||||||
{
|
{
|
||||||
icon: 'format-clear',
|
icon: 'format-clear',
|
||||||
title: 'Clear format',
|
title: 'Clear format',
|
||||||
action: () => this.editor.chain()
|
action: () => this.editor.chain().focus().clearNodes().unsetAllMarks().run(),
|
||||||
.focus()
|
|
||||||
.clearNodes()
|
|
||||||
.unsetAllMarks()
|
|
||||||
.run(),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'divider',
|
type: 'divider',
|
||||||
|
@ -1,10 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<button
|
<button class="menu-item" :class="{ 'is-active': isActive ? isActive() : null }" @click="action" :title="title">
|
||||||
class="menu-item"
|
|
||||||
:class="{ 'is-active': isActive ? isActive(): null }"
|
|
||||||
@click="action"
|
|
||||||
:title="title"
|
|
||||||
>
|
|
||||||
<svg class="remix">
|
<svg class="remix">
|
||||||
<use :xlink:href="`${remixiconUrl}#ri-${icon}`" />
|
<use :xlink:href="`${remixiconUrl}#ri-${icon}`" />
|
||||||
</svg>
|
</svg>
|
||||||
|
@ -5,11 +5,12 @@
|
|||||||
<div class="editor__footer">
|
<div class="editor__footer">
|
||||||
<div :class="`editor__status editor__status--${status}`">
|
<div :class="`editor__status editor__status--${status}`">
|
||||||
<template v-if="status === 'connected'">
|
<template v-if="status === 'connected'">
|
||||||
{{ editor.storage.collaborationCursor.users.length }} user{{ editor.storage.collaborationCursor.users.length === 1 ? '' : 's' }} online in {{ room }}
|
{{ editor.storage.collaborationCursor.users.length }} user{{
|
||||||
</template>
|
editor.storage.collaborationCursor.users.length === 1 ? '' : 's'
|
||||||
<template v-else>
|
}}
|
||||||
offline
|
online in {{ room }}
|
||||||
</template>
|
</template>
|
||||||
|
<template v-else> offline </template>
|
||||||
</div>
|
</div>
|
||||||
<div class="editor__name">
|
<div class="editor__name">
|
||||||
<button @click="setName">
|
<button @click="setName">
|
||||||
@ -103,9 +104,7 @@ export default {
|
|||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
setName() {
|
setName() {
|
||||||
const name = (window.prompt('Name') || '')
|
const name = (window.prompt('Name') || '').trim().substring(0, 32)
|
||||||
.trim()
|
|
||||||
.substring(0, 32)
|
|
||||||
|
|
||||||
if (name) {
|
if (name) {
|
||||||
return this.updateCurrentUser({
|
return this.updateCurrentUser({
|
||||||
@ -122,20 +121,36 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
getRandomColor() {
|
getRandomColor() {
|
||||||
return getRandomElement([
|
return getRandomElement(['#958DF1', '#F98181', '#FBBC88', '#FAF594', '#70CFF8', '#94FADB', '#B9F18D'])
|
||||||
'#958DF1',
|
|
||||||
'#F98181',
|
|
||||||
'#FBBC88',
|
|
||||||
'#FAF594',
|
|
||||||
'#70CFF8',
|
|
||||||
'#94FADB',
|
|
||||||
'#B9F18D',
|
|
||||||
])
|
|
||||||
},
|
},
|
||||||
|
|
||||||
getRandomName() {
|
getRandomName() {
|
||||||
return getRandomElement([
|
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">
|
<style lang="scss">
|
||||||
.editor {
|
.editor {
|
||||||
background-color: #FFF;
|
background-color: #fff;
|
||||||
border: 3px solid #0D0D0D;
|
border: 3px solid #0d0d0d;
|
||||||
border-radius: 0.75rem;
|
border-radius: 0.75rem;
|
||||||
color: #0D0D0D;
|
color: #0d0d0d;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
max-height: 26rem;
|
max-height: 26rem;
|
||||||
@ -179,8 +194,8 @@ export default {
|
|||||||
|
|
||||||
&__footer {
|
&__footer {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
border-top: 3px solid #0D0D0D;
|
border-top: 3px solid #0d0d0d;
|
||||||
color: #0D0D0D;
|
color: #0d0d0d;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
@ -198,7 +213,7 @@ export default {
|
|||||||
display: flex;
|
display: flex;
|
||||||
|
|
||||||
&::before {
|
&::before {
|
||||||
background: rgba(#0D0D0D, 0.5);
|
background: rgba(#0d0d0d, 0.5);
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
content: ' ';
|
content: ' ';
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
@ -213,7 +228,7 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
&--connected::before {
|
&--connected::before {
|
||||||
background: #B9F18D;
|
background: #b9f18d;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -222,15 +237,15 @@ export default {
|
|||||||
background: none;
|
background: none;
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: 0.4rem;
|
border-radius: 0.4rem;
|
||||||
color: #0D0D0D;
|
color: #0d0d0d;
|
||||||
font: inherit;
|
font: inherit;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
padding: 0.25rem 0.5rem;
|
padding: 0.25rem 0.5rem;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: #0D0D0D;
|
background-color: #0d0d0d;
|
||||||
color: #FFF;
|
color: #fff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -238,8 +253,8 @@ export default {
|
|||||||
|
|
||||||
/* Give a remote user a caret */
|
/* Give a remote user a caret */
|
||||||
.collaboration-cursor__caret {
|
.collaboration-cursor__caret {
|
||||||
border-left: 1px solid #0D0D0D;
|
border-left: 1px solid #0d0d0d;
|
||||||
border-right: 1px solid #0D0D0D;
|
border-right: 1px solid #0d0d0d;
|
||||||
margin-left: -1px;
|
margin-left: -1px;
|
||||||
margin-right: -1px;
|
margin-right: -1px;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
@ -250,7 +265,7 @@ export default {
|
|||||||
/* Render the username above the caret */
|
/* Render the username above the caret */
|
||||||
.collaboration-cursor__label {
|
.collaboration-cursor__label {
|
||||||
border-radius: 3px 3px 3px 0;
|
border-radius: 3px 3px 3px 0;
|
||||||
color: #0D0D0D;
|
color: #0d0d0d;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
@ -289,9 +304,9 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pre {
|
pre {
|
||||||
background: #0D0D0D;
|
background: #0d0d0d;
|
||||||
border-radius: 0.5rem;
|
border-radius: 0.5rem;
|
||||||
color: #FFF;
|
color: #fff;
|
||||||
font-family: 'JetBrainsMono', monospace;
|
font-family: 'JetBrainsMono', monospace;
|
||||||
padding: 0.75rem 1rem;
|
padding: 0.75rem 1rem;
|
||||||
|
|
||||||
@ -304,7 +319,7 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mark {
|
mark {
|
||||||
background-color: #FAF594;
|
background-color: #faf594;
|
||||||
}
|
}
|
||||||
|
|
||||||
img {
|
img {
|
||||||
@ -317,17 +332,17 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
blockquote {
|
blockquote {
|
||||||
border-left: 2px solid rgba(#0D0D0D, 0.1);
|
border-left: 2px solid rgba(#0d0d0d, 0.1);
|
||||||
padding-left: 1rem;
|
padding-left: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
hr {
|
hr {
|
||||||
border: none;
|
border: none;
|
||||||
border-top: 2px solid rgba(#0D0D0D, 0.1);
|
border-top: 2px solid rgba(#0d0d0d, 0.1);
|
||||||
margin: 2rem 0;
|
margin: 2rem 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
ul[data-type="taskList"] {
|
ul[data-type='taskList'] {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
||||||
|
@ -1,11 +1,6 @@
|
|||||||
import './MentionList.scss'
|
import './MentionList.scss'
|
||||||
|
|
||||||
import React, {
|
import React, { forwardRef, useEffect, useImperativeHandle, useState } from 'react'
|
||||||
forwardRef,
|
|
||||||
useEffect,
|
|
||||||
useImperativeHandle,
|
|
||||||
useState,
|
|
||||||
} from 'react'
|
|
||||||
|
|
||||||
export const MentionList = forwardRef((props, ref) => {
|
export const MentionList = forwardRef((props, ref) => {
|
||||||
const [selectedIndex, setSelectedIndex] = useState(0)
|
const [selectedIndex, setSelectedIndex] = useState(0)
|
||||||
@ -19,7 +14,7 @@ export const MentionList = forwardRef((props, ref) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const upHandler = () => {
|
const upHandler = () => {
|
||||||
setSelectedIndex(((selectedIndex + props.items.length) - 1) % props.items.length)
|
setSelectedIndex((selectedIndex + props.items.length - 1) % props.items.length)
|
||||||
}
|
}
|
||||||
|
|
||||||
const downHandler = () => {
|
const downHandler = () => {
|
||||||
@ -55,8 +50,8 @@ export const MentionList = forwardRef((props, ref) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="dropdown-menu">
|
<div className="dropdown-menu">
|
||||||
{props.items.length
|
{props.items.length ? (
|
||||||
? props.items.map((item, index) => (
|
props.items.map((item, index) => (
|
||||||
<button
|
<button
|
||||||
className={index === selectedIndex ? 'is-selected' : ''}
|
className={index === selectedIndex ? 'is-selected' : ''}
|
||||||
key={index}
|
key={index}
|
||||||
@ -65,8 +60,9 @@ export const MentionList = forwardRef((props, ref) => {
|
|||||||
{item}
|
{item}
|
||||||
</button>
|
</button>
|
||||||
))
|
))
|
||||||
: <div className="item">No result</div>
|
) : (
|
||||||
}
|
<div className="item">No result</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -18,8 +18,8 @@
|
|||||||
gap: 0.25rem;
|
gap: 0.25rem;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
&:hover,
|
&:hover,
|
||||||
&:hover.is-selected {
|
&:hover.is-selected {
|
||||||
background-color: var(--gray-3);
|
background-color: var(--gray-3);
|
||||||
}
|
}
|
||||||
|
@ -44,26 +44,17 @@ export default () => {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const percentage = editor
|
const percentage = editor ? Math.round((100 / limit) * characterCount) : 0
|
||||||
? Math.round((100 / limit) * characterCount)
|
|
||||||
: 0
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<EditorContent editor={editor} />
|
<EditorContent editor={editor} />
|
||||||
{editor
|
{editor && (
|
||||||
&& <div className={`character-count ${editor.storage.characterCount.characters() === limit ? 'character-count--warning' : ''}`}>
|
<div
|
||||||
<svg
|
className={`character-count ${editor.storage.characterCount.characters() === limit ? 'character-count--warning' : ''}`}
|
||||||
height="20"
|
>
|
||||||
width="20"
|
<svg height="20" width="20" viewBox="0 0 20 20">
|
||||||
viewBox="0 0 20 20"
|
<circle r="10" cx="10" cy="10" fill="#e9ecef" />
|
||||||
>
|
|
||||||
<circle
|
|
||||||
r="10"
|
|
||||||
cx="10"
|
|
||||||
cy="10"
|
|
||||||
fill="#e9ecef"
|
|
||||||
/>
|
|
||||||
<circle
|
<circle
|
||||||
r="5"
|
r="5"
|
||||||
cx="10"
|
cx="10"
|
||||||
@ -74,17 +65,11 @@ export default () => {
|
|||||||
strokeDasharray={`calc(${percentage} * 31.4 / 100) 31.4`}
|
strokeDasharray={`calc(${percentage} * 31.4 / 100) 31.4`}
|
||||||
transform="rotate(-90) translate(-20)"
|
transform="rotate(-90) translate(-20)"
|
||||||
/>
|
/>
|
||||||
<circle
|
<circle r="6" cx="10" cy="10" fill="white" />
|
||||||
r="6"
|
|
||||||
cx="10"
|
|
||||||
cy="10"
|
|
||||||
fill="white"
|
|
||||||
/>
|
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
{editor.storage.characterCount.characters()} / {limit} characters
|
{editor.storage.characterCount.characters()} / {limit} characters
|
||||||
</div>
|
</div>
|
||||||
}
|
)}
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -23,15 +23,16 @@ context('/src/Examples/Community/React/', () => {
|
|||||||
cy.get('.dropdown-menu').should('be.visible')
|
cy.get('.dropdown-menu').should('be.visible')
|
||||||
|
|
||||||
// select the first user
|
// select the first user
|
||||||
cy.get('.dropdown-menu button').first().then($el => {
|
cy.get('.dropdown-menu button')
|
||||||
const name = $el.text()
|
.first()
|
||||||
|
.then($el => {
|
||||||
|
const name = $el.text()
|
||||||
|
|
||||||
$el.click()
|
$el.click()
|
||||||
|
|
||||||
// check if the user is mentioned
|
|
||||||
cy.get('.tiptap').should('have.text', `@${name} `)
|
|
||||||
cy.get('.character-count').should('contain', '2 / 280 characters')
|
|
||||||
})
|
|
||||||
|
|
||||||
|
// check if the user is mentioned
|
||||||
|
cy.get('.tiptap').should('have.text', `@${name} `)
|
||||||
|
cy.get('.character-count').should('contain', '2 / 280 characters')
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
:first-child {
|
:first-child {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mention {
|
.mention {
|
||||||
background-color: var(--purple-light);
|
background-color: var(--purple-light);
|
||||||
border-radius: 0.4rem;
|
border-radius: 0.4rem;
|
||||||
@ -19,7 +19,7 @@
|
|||||||
color: var(--gray-5);
|
color: var(--gray-5);
|
||||||
display: flex;
|
display: flex;
|
||||||
font-size: 0.75rem;
|
font-size: 0.75rem;
|
||||||
gap: .5rem;
|
gap: 0.5rem;
|
||||||
margin: 1.5rem;
|
margin: 1.5rem;
|
||||||
|
|
||||||
svg {
|
svg {
|
||||||
|
@ -1,8 +1,4 @@
|
|||||||
import {
|
import { computePosition, flip, shift } from '@floating-ui/dom'
|
||||||
computePosition,
|
|
||||||
flip,
|
|
||||||
shift,
|
|
||||||
} from '@floating-ui/dom'
|
|
||||||
import { posToDOMRect, ReactRenderer } from '@tiptap/react'
|
import { posToDOMRect, ReactRenderer } from '@tiptap/react'
|
||||||
|
|
||||||
import { MentionList } from './MentionList.jsx'
|
import { MentionList } from './MentionList.jsx'
|
||||||
@ -27,8 +23,34 @@ const updatePosition = (editor, element) => {
|
|||||||
export default {
|
export default {
|
||||||
items: ({ query }) => {
|
items: ({ query }) => {
|
||||||
return [
|
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',
|
'Lea Thompson',
|
||||||
].filter(item => item.toLowerCase().startsWith(query.toLowerCase())).slice(0, 5)
|
'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: () => {
|
render: () => {
|
||||||
@ -36,7 +58,6 @@ export default {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
onStart: props => {
|
onStart: props => {
|
||||||
|
|
||||||
if (!props.clientRect) {
|
if (!props.clientRect) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -10,9 +10,7 @@
|
|||||||
{{ item }}
|
{{ item }}
|
||||||
</button>
|
</button>
|
||||||
</template>
|
</template>
|
||||||
<div class="item" v-else>
|
<div class="item" v-else>No result</div>
|
||||||
No result
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -63,7 +61,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
upHandler() {
|
upHandler() {
|
||||||
this.selectedIndex = ((this.selectedIndex + this.items.length) - 1) % this.items.length
|
this.selectedIndex = (this.selectedIndex + this.items.length - 1) % this.items.length
|
||||||
},
|
},
|
||||||
|
|
||||||
downHandler() {
|
downHandler() {
|
||||||
|
@ -23,15 +23,16 @@ context('/src/Examples/Community/Vue/', () => {
|
|||||||
cy.get('.dropdown-menu').should('be.visible')
|
cy.get('.dropdown-menu').should('be.visible')
|
||||||
|
|
||||||
// select the first user
|
// select the first user
|
||||||
cy.get('.dropdown-menu button').first().then($el => {
|
cy.get('.dropdown-menu button')
|
||||||
const name = $el.text()
|
.first()
|
||||||
|
.then($el => {
|
||||||
|
const name = $el.text()
|
||||||
|
|
||||||
$el.click()
|
$el.click()
|
||||||
|
|
||||||
// check if the user is mentioned
|
|
||||||
cy.get('.tiptap').should('have.text', `@${name} `)
|
|
||||||
cy.get('.character-count').should('contain', '2 / 280 characters')
|
|
||||||
})
|
|
||||||
|
|
||||||
|
// 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>
|
<template>
|
||||||
<editor-content :editor="editor" />
|
<editor-content :editor="editor" />
|
||||||
|
|
||||||
<div v-if="editor" :class="{'character-count': true, 'character-count--warning': editor.storage.characterCount.characters() === limit}">
|
<div
|
||||||
<svg
|
v-if="editor"
|
||||||
height="20"
|
:class="{
|
||||||
width="20"
|
'character-count': true,
|
||||||
viewBox="0 0 20 20"
|
'character-count--warning': editor.storage.characterCount.characters() === limit,
|
||||||
>
|
}"
|
||||||
<circle
|
>
|
||||||
r="10"
|
<svg height="20" width="20" viewBox="0 0 20 20">
|
||||||
cx="10"
|
<circle r="10" cx="10" cy="10" fill="#e9ecef" />
|
||||||
cy="10"
|
|
||||||
fill="#e9ecef"
|
|
||||||
/>
|
|
||||||
<circle
|
<circle
|
||||||
r="5"
|
r="5"
|
||||||
cx="10"
|
cx="10"
|
||||||
@ -23,12 +20,7 @@
|
|||||||
:stroke-dasharray="`calc(${percentage} * 31.4 / 100) 31.4`"
|
:stroke-dasharray="`calc(${percentage} * 31.4 / 100) 31.4`"
|
||||||
transform="rotate(-90) translate(-20)"
|
transform="rotate(-90) translate(-20)"
|
||||||
/>
|
/>
|
||||||
<circle
|
<circle r="6" cx="10" cy="10" fill="white" />
|
||||||
r="6"
|
|
||||||
cx="10"
|
|
||||||
cy="10"
|
|
||||||
fill="white"
|
|
||||||
/>
|
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
{{ editor.storage.characterCount.characters() }} / {{ limit }} characters
|
{{ editor.storage.characterCount.characters() }} / {{ limit }} characters
|
||||||
@ -115,7 +107,7 @@ export default {
|
|||||||
color: var(--gray-5);
|
color: var(--gray-5);
|
||||||
display: flex;
|
display: flex;
|
||||||
font-size: 0.75rem;
|
font-size: 0.75rem;
|
||||||
gap: .5rem;
|
gap: 0.5rem;
|
||||||
margin: 1.5rem;
|
margin: 1.5rem;
|
||||||
|
|
||||||
svg {
|
svg {
|
||||||
|
@ -1,8 +1,4 @@
|
|||||||
import {
|
import { computePosition, flip, shift } from '@floating-ui/dom'
|
||||||
computePosition,
|
|
||||||
flip,
|
|
||||||
shift,
|
|
||||||
} from '@floating-ui/dom'
|
|
||||||
import { posToDOMRect, VueRenderer } from '@tiptap/vue-3'
|
import { posToDOMRect, VueRenderer } from '@tiptap/vue-3'
|
||||||
|
|
||||||
import MentionList from './MentionList.vue'
|
import MentionList from './MentionList.vue'
|
||||||
@ -27,8 +23,34 @@ const updatePosition = (editor, element) => {
|
|||||||
export default {
|
export default {
|
||||||
items: ({ query }) => {
|
items: ({ query }) => {
|
||||||
return [
|
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',
|
'Lea Thompson',
|
||||||
].filter(item => item.toLowerCase().startsWith(query.toLowerCase())).slice(0, 5)
|
'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: () => {
|
render: () => {
|
||||||
|
@ -37,7 +37,5 @@ export default () => {
|
|||||||
`,
|
`,
|
||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return <EditorContent editor={editor} />
|
||||||
<EditorContent editor={editor} />
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
@ -12,12 +12,16 @@ context('/src/Examples/CustomDocument/React/', () => {
|
|||||||
|
|
||||||
it('should have a headline and a paragraph', () => {
|
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 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', () => {
|
it('should have a tooltip for a paragraph on a new line', () => {
|
||||||
cy.get('.tiptap').type('{enter}')
|
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', () => {
|
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', () => {
|
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').type('{selectall}{backspace}Hello world{enter}')
|
||||||
cy.get('.tiptap h1')
|
cy.get('.tiptap h1').should('exist').should('have.text', 'Hello world')
|
||||||
.should('exist')
|
|
||||||
.should('have.text', 'Hello world')
|
|
||||||
cy.get('.tiptap p[data-placeholder]')
|
cy.get('.tiptap p[data-placeholder]')
|
||||||
.should('exist')
|
.should('exist')
|
||||||
.should('have.attr', 'data-placeholder', 'Can you add some further context?')
|
.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').type('This is a paragraph for this test document')
|
||||||
cy.get('.tiptap p')
|
cy.get('.tiptap p').should('exist').should('have.text', 'This is a paragraph for this test document')
|
||||||
.should('exist')
|
|
||||||
.should('have.text', 'This is a paragraph for this test document')
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -5,11 +5,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* List styles */
|
/* List styles */
|
||||||
ul,
|
ul,
|
||||||
ol {
|
ol {
|
||||||
padding: 0 1rem;
|
padding: 0 1rem;
|
||||||
margin: 1.25rem 1rem 1.25rem 0.4rem;
|
margin: 1.25rem 1rem 1.25rem 0.4rem;
|
||||||
|
|
||||||
li p {
|
li p {
|
||||||
margin-top: 0.25em;
|
margin-top: 0.25em;
|
||||||
margin-bottom: 0.25em;
|
margin-bottom: 0.25em;
|
||||||
@ -17,39 +17,39 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Heading styles */
|
/* Heading styles */
|
||||||
h1,
|
h1,
|
||||||
h2,
|
h2,
|
||||||
h3,
|
h3,
|
||||||
h4,
|
h4,
|
||||||
h5,
|
h5,
|
||||||
h6 {
|
h6 {
|
||||||
line-height: 1.1;
|
line-height: 1.1;
|
||||||
margin-top: 2.5rem;
|
margin-top: 2.5rem;
|
||||||
text-wrap: pretty;
|
text-wrap: pretty;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1,
|
h1,
|
||||||
h2 {
|
h2 {
|
||||||
margin-top: 3.5rem;
|
margin-top: 3.5rem;
|
||||||
margin-bottom: 1.5rem;
|
margin-bottom: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
font-size: 1.4rem;
|
font-size: 1.4rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
h2 {
|
h2 {
|
||||||
font-size: 1.2rem;
|
font-size: 1.2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
h3 {
|
h3 {
|
||||||
font-size: 1.1rem;
|
font-size: 1.1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
h4,
|
h4,
|
||||||
h5,
|
h5,
|
||||||
h6 {
|
h6 {
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Code and preformatted text styles */
|
/* Code and preformatted text styles */
|
||||||
@ -88,7 +88,7 @@
|
|||||||
border-top: 1px solid var(--gray-2);
|
border-top: 1px solid var(--gray-2);
|
||||||
margin: 2rem 0;
|
margin: 2rem 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Placeholder (at the top) */
|
/* Placeholder (at the top) */
|
||||||
/* p.is-editor-empty:first-child::before {
|
/* p.is-editor-empty:first-child::before {
|
||||||
color: var(--gray-4);
|
color: var(--gray-4);
|
||||||
|
@ -12,12 +12,16 @@ context('/src/Examples/CustomDocument/Vue/', () => {
|
|||||||
|
|
||||||
it('should have a headline and a paragraph', () => {
|
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 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', () => {
|
it('should have a tooltip for a paragraph on a new line', () => {
|
||||||
cy.get('.tiptap').type('{enter}')
|
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', () => {
|
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', () => {
|
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').type('{selectall}{backspace}Hello world{enter}')
|
||||||
cy.wait(100)
|
cy.wait(100)
|
||||||
cy.get('.tiptap h1')
|
cy.get('.tiptap h1').should('exist').should('have.text', 'Hello world')
|
||||||
.should('exist')
|
|
||||||
.should('have.text', 'Hello world')
|
|
||||||
cy.get('.tiptap p[data-placeholder]')
|
cy.get('.tiptap p[data-placeholder]')
|
||||||
.should('exist')
|
.should('exist')
|
||||||
.should('have.attr', 'data-placeholder', 'Can you add some further context?')
|
.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').type('This is a paragraph for this test document')
|
||||||
cy.get('.tiptap p')
|
cy.get('.tiptap p').should('exist').should('have.text', 'This is a paragraph for this test document')
|
||||||
.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 { Paragraph as BaseParagraph } from '@tiptap/extension-paragraph'
|
||||||
import {
|
import { NodeViewContent, NodeViewWrapper, ReactNodeViewRenderer } from '@tiptap/react'
|
||||||
NodeViewContent,
|
|
||||||
NodeViewWrapper,
|
|
||||||
ReactNodeViewRenderer,
|
|
||||||
} from '@tiptap/react'
|
|
||||||
|
|
||||||
const ParagraphComponent = ({ node }) => {
|
const ParagraphComponent = ({ node }) => {
|
||||||
return (
|
return (
|
||||||
<NodeViewWrapper style={{ position: 'relative' }}>
|
<NodeViewWrapper style={{ position: 'relative' }}>
|
||||||
<span contentEditable={false} className="label" style={{
|
<span
|
||||||
position: 'absolute', right: '100%', fontSize: '10px', color: '#999',
|
contentEditable={false}
|
||||||
}}>
|
className="label"
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
right: '100%',
|
||||||
|
fontSize: '10px',
|
||||||
|
color: '#999',
|
||||||
|
}}
|
||||||
|
>
|
||||||
{node.textContent.length}
|
{node.textContent.length}
|
||||||
</span>
|
</span>
|
||||||
<NodeViewContent as="p" />
|
<NodeViewContent as="p" />
|
||||||
|
@ -21,7 +21,5 @@ export default () => {
|
|||||||
`,
|
`,
|
||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return <EditorContent editor={editor} />
|
||||||
<EditorContent editor={editor} />
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,9 @@ context('/src/Examples/CustomParagraph/React/', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should have a paragraph and text length', () => {
|
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')
|
cy.get('.tiptap .label').should('exist').should('have.text', '58')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user