mirror of
https://github.com/ueberdosis/tiptap.git
synced 2024-11-25 12:39:03 +08:00
Merge branch 'main' into feature/tippy-menus
This commit is contained in:
commit
cadabd09e0
@ -38,6 +38,7 @@ module.exports = {
|
||||
'no-alert': 'off',
|
||||
'no-console': ['warn', { allow: ['warn', 'error'] }],
|
||||
semi: ['error', 'never'],
|
||||
'import/order': 'off',
|
||||
'import/extensions': 'off',
|
||||
'import/no-extraneous-dependencies': 'off',
|
||||
'import/no-unresolved': 'off',
|
||||
|
113
README.md
113
README.md
@ -1,78 +1,65 @@
|
||||
# tiptap 2 (Sponsors preview!)
|
||||
# tiptap 2
|
||||
A headless, framework-agnostic and extendable rich text editor, based on [ProseMirror](https://github.com/ProseMirror/prosemirror).
|
||||
|
||||
[![Build Status](https://github.com/ueberdosis/tiptap-next/workflows/build/badge.svg)](https://github.com/ueberdosis/tiptap-next/actions)
|
||||
[![Version](https://img.shields.io/npm/v/@tiptap/core.svg?label=version)](https://www.npmjs.com/package/@tiptap/core)
|
||||
[![Downloads](https://img.shields.io/npm/dm/@tiptap/core.svg)](https://npmcharts.com/compare/@tiptap/core?minimal=true)
|
||||
[![License](https://img.shields.io/npm/l/@tiptap/core.svg)](https://www.npmjs.com/package/@tiptap/core)
|
||||
[![Build Status](https://github.com/ueberdosis/tiptap-next/workflows/build/badge.svg)](https://github.com/ueberdosis/tiptap-next/actions)
|
||||
[![Sponsor](https://img.shields.io/static/v1?label=Sponsor&message=%E2%9D%A4&logo=GitHub)](https://github.com/sponsors/ueberdosis)
|
||||
[![Chat](https://img.shields.io/badge/chat-on%20discord-7289da.svg?sanitize=true)](https://discord.gg/WtJ49jGshW)
|
||||
[![Sponsor](https://img.shields.io/static/v1?label=Sponsor&message=%E2%9D%A4&logo=GitHub)](https://github.com/sponsors/ueberdosis)
|
||||
|
||||
A headless and extendable rich text editor, based on [ProseMirror](https://github.com/ProseMirror/prosemirror), which is already in use at many well-known companies such as *New York Times*, *The Guardian* or *Atlassian*.
|
||||
> If you’re looking for tiptap 1, [click here](https://github.com/ueberdosis/tiptap/tree/v1).
|
||||
|
||||
## Feedback
|
||||
We are looking for your feedback to improve tiptap 2 before the first public release! Share everything that helps to make it better for everyone!
|
||||
|
||||
* Join the Discord server! [Link](https://discord.gg/WtJ49jGshW)
|
||||
* Create issues on GitHub! [Link](https://github.com/ueberdosis/tiptap-next/issues)
|
||||
* Send an email! [humans@tiptap.dev](mailto:humans@tiptap.dev)
|
||||
* Follow us on Twitter! [@tiptap_editor](https://twitter.com/tiptap_editor) [@hanspagel](https://twitter.com/hanspagel) [@_philippkuehn](https://twitter.com/_philippkuehn)
|
||||
|
||||
## Why we built tiptap
|
||||
We were looking for a text editor for [Vue.js](https://github.com/vuejs/vue) and found some solutions that didn’t really satisfy us. An editor should be easy to extend and not based on old dependencies such as jQuery. For React there is already a great editor called [Slate.js](https://github.com/ianstormtaylor/slate), which impresses with its modularity. We came across [ProseMirror](https://github.com/prosemirror) and decided to build on it. ProseMirror is a toolkit for building rich text editors that are already in use at many well-known companies such as *Atlassian* or *New York Times*.
|
||||
|
||||
### What does `headless` mean?
|
||||
With headless components you'll have (almost) full control over markup and styling. We don’t want to tell you what a menu should look like or where it should be rendered in the DOM. That’s all up to you. There is also a [great article about headless components](https://adamwathan.me/renderless-components-in-vuejs/) by Adam Wathan.
|
||||
|
||||
### How is the data stored under the hood?
|
||||
You can save your data as a raw `HTML` string or can get a `JSON`-serializable representation of your document. And of course, you can also pass `HTML` or `JSON` content back to the editor.
|
||||
|
||||
## 💖 Sponsor the development
|
||||
Are you using tiptap in production? We need your sponsorship to maintain, update and develop tiptap. [Become a Sponsor now!](https://github.com/sponsors/ueberdosis)
|
||||
## Examples
|
||||
Have a look at the [examples to see tiptap in action](https://next.tiptap.dev/examples).
|
||||
|
||||
## Documentation
|
||||
To check out some live examples, visit [next.tiptap.dev](https://next.tiptap.dev/).
|
||||
The full documentation is a available on [next.tiptap.dev](https://next.tiptap.dev/installation).
|
||||
|
||||
## Community
|
||||
For help, discussion about best practices, or any other conversation that would benefit from being searchable:
|
||||
|
||||
[Discuss tiptap on GitHub](https://github.com/ueberdosis/tiptap/discussions)
|
||||
|
||||
For casual chit-chat with others using the framework:
|
||||
|
||||
[Join the tiptap Discord Server](https://discord.gg/WtJ49jGshW)
|
||||
|
||||
## 💖 Sponsors
|
||||
* [überdosis](https://ueberdosis.io/)
|
||||
* [Ziff Media Group](https://ziffmedia.com/)
|
||||
* [Incyte Studios](https://incytestudios.com/)
|
||||
* [dotCMS](http://dotcms.com/)
|
||||
* [Restruct](https://restruct.nl/)
|
||||
|
||||
[mymind](https://mymind.com/),
|
||||
[DocIQ](https://www.dociq.io/),
|
||||
[Apostrophe CMS](https://apostrophecms.com/),
|
||||
[Ycode](https://www.ycode.com/),
|
||||
[@impactvelocity](https://github.com/impactvelocity),
|
||||
[Flow Mobile](https://www.flowmobile.app/),
|
||||
[Gretel](http://www.gretel.co/),
|
||||
[Omics Data Automation](https://www.omicsautomation.com/),
|
||||
[Novadiscovery](http://www.novadiscovery.com/),
|
||||
[Atlan Technologies](https://atlan.com/),
|
||||
[Gamma](https://gamma.app/),
|
||||
[Kirchner Consulting](https://kirchnerconsulting.ch/),
|
||||
[IT Xpert](https://itxpert.ch/) and hundreds of awesome inviduals.
|
||||
|
||||
Using tiptap in production? Invest in the future of tiptap and [become a sponsor!](https://github.com/sponsors/ueberdosis)
|
||||
|
||||
## Contributing
|
||||
Please see [CONTRIBUTING](CONTRIBUTING.md) for details.
|
||||
|
||||
## Maintainers
|
||||
- [Philipp Kühn](https://github.com/philippkuehn) (developer)
|
||||
- [Hans Pagel](https://github.com/hanspagel) (maintainer)
|
||||
|
||||
## Premium Sponsors
|
||||
- [überdosis](https://ueberdosis.io/)
|
||||
- [mymind](https://mymind.com/)
|
||||
- [DocIQ](https://www.dociq.io/)
|
||||
- [Apostrophe CMS](https://apostrophecms.com/)
|
||||
- [Ycode](https://www.ycode.com/)
|
||||
- [@impactvelocity](https://github.com/impactvelocity)
|
||||
- [Flow Mobile](https://www.flowmobile.app/)
|
||||
|
||||
## Credits
|
||||
- [Sam Willis](https://github.com/samwillis)
|
||||
- [Christoph Flathmann](https://github.com/Chrissi2812)
|
||||
- [Erick Wilder](https://github.com/erickwilder)
|
||||
- [Marius Tolzmann](https://github.com/mariux)
|
||||
- [jjangga0214](https://github.com/jjangga0214)
|
||||
- [Maya Nedeljkovich](https://github.com/mayacoda)
|
||||
- [Ryan Bliss](https://github.com/ryanbliss)
|
||||
- [Gregor](https://github.com/gambolputty)
|
||||
- [All Contributors](../../contributors)
|
||||
|
||||
## Links
|
||||
- https://github.com/ueberdosis/awesome-tiptap
|
||||
|
||||
## Become a sponsor
|
||||
Your benefits as a sponsor:
|
||||
|
||||
* Give back to the open source community
|
||||
* Get early access to private repositories
|
||||
* Ensure the further maintenace and development of tiptap
|
||||
* Your issues and pull requests get a `sponsor 💖` label
|
||||
* Get a sponsor badge in all your comments on GitHub
|
||||
* Show support in your GitHub profile
|
||||
* Receive monthly reports about our open source work
|
||||
|
||||
Does that sound good? [Become a sponsor!](https://github.com/sponsors/ueberdosis)
|
||||
## Contributors
|
||||
[Sam Willis](https://github.com/samwillis),
|
||||
[Christoph Flathmann](https://github.com/Chrissi2812),
|
||||
[Erick Wilder](https://github.com/erickwilder),
|
||||
[Marius Tolzmann](https://github.com/mariux),
|
||||
[jjangga0214](https://github.com/jjangga0214),
|
||||
[Maya Nedeljkovich](https://github.com/mayacoda),
|
||||
[Ryan Bliss](https://github.com/ryanbliss),
|
||||
[Gregor](https://github.com/gambolputty) and [many more](../../contributors).
|
||||
|
||||
## License
|
||||
The MIT License (MIT). Please see [License File](LICENSE.md) for more information.
|
||||
|
@ -16,10 +16,10 @@ import Text from '@tiptap/extension-text'
|
||||
import CodeBlockLowlight from '@tiptap/extension-code-block-lowlight'
|
||||
import CodeBlockComponent from './CodeBlockComponent'
|
||||
|
||||
// install all highlight.js languages
|
||||
import 'lowlight'
|
||||
// load all highlight.js languages
|
||||
import lowlight from 'lowlight'
|
||||
|
||||
// or install specific languages only
|
||||
// load specific languages only
|
||||
// import lowlight from 'lowlight/lib/core'
|
||||
// import javascript from 'highlight.js/lib/languages/javascript'
|
||||
// lowlight.registerLanguage('javascript', javascript)
|
||||
@ -41,11 +41,13 @@ export default {
|
||||
Document,
|
||||
Paragraph,
|
||||
Text,
|
||||
CodeBlockLowlight.extend({
|
||||
addNodeView() {
|
||||
return VueNodeViewRenderer(CodeBlockComponent)
|
||||
},
|
||||
}),
|
||||
CodeBlockLowlight
|
||||
.extend({
|
||||
addNodeView() {
|
||||
return VueNodeViewRenderer(CodeBlockComponent)
|
||||
},
|
||||
})
|
||||
.configure({ lowlight }),
|
||||
],
|
||||
content: `
|
||||
<p>
|
||||
|
@ -70,7 +70,7 @@ export default {
|
||||
|
||||
this.editor = new Editor({
|
||||
extensions: [
|
||||
...defaultExtensions().filter(extension => extension.config.name !== 'history'),
|
||||
...defaultExtensions().filter(extension => extension.name !== 'history'),
|
||||
Highlight,
|
||||
TaskList,
|
||||
TaskItem,
|
||||
|
@ -6,128 +6,129 @@ export const SmilieReplacer = Extension.create({
|
||||
|
||||
addInputRules() {
|
||||
return [
|
||||
new InputRule(/-___-/, '😑'),
|
||||
new InputRule(/:'-\)/, '😂'),
|
||||
new InputRule(/':-\)/, '😅'),
|
||||
new InputRule(/':-D/, '😅'),
|
||||
new InputRule(/>:-\)/, '😆'),
|
||||
new InputRule(/-__-/, '😑'),
|
||||
new InputRule(/':-\(/, '😓'),
|
||||
new InputRule(/:'-\(/, '😢'),
|
||||
new InputRule(/>:-\(/, '😠'),
|
||||
new InputRule(/O:-\)/, '😇'),
|
||||
new InputRule(/0:-3/, '😇'),
|
||||
new InputRule(/0:-\)/, '😇'),
|
||||
new InputRule(/0;\^\)/, '😇'),
|
||||
new InputRule(/O;-\)/, '😇'),
|
||||
new InputRule(/0;-\)/, '😇'),
|
||||
new InputRule(/O:-3/, '😇'),
|
||||
new InputRule(/:'\)/, '😂'),
|
||||
new InputRule(/:-D/, '😃'),
|
||||
new InputRule(/':\)/, '😅'),
|
||||
new InputRule(/'=\)/, '😅'),
|
||||
new InputRule(/':D/, '😅'),
|
||||
new InputRule(/'=D/, '😅'),
|
||||
new InputRule(/>:\)/, '😆'),
|
||||
new InputRule(/>;\)/, '😆'),
|
||||
new InputRule(/>=\)/, '😆'),
|
||||
new InputRule(/;-\)/, '😉'),
|
||||
new InputRule(/\*-\)/, '😉'),
|
||||
new InputRule(/;-\]/, '😉'),
|
||||
new InputRule(/;\^\)/, '😉'),
|
||||
new InputRule(/B-\)/, '😎'),
|
||||
new InputRule(/8-\)/, '😎'),
|
||||
new InputRule(/B-D/, '😎'),
|
||||
new InputRule(/8-D/, '😎'),
|
||||
new InputRule(/:-\*/, '😘'),
|
||||
new InputRule(/:\^\*/, '😘'),
|
||||
new InputRule(/:-\)/, '🙂'),
|
||||
new InputRule(/-_-/, '😑'),
|
||||
new InputRule(/:-X/, '😶'),
|
||||
new InputRule(/:-#/, '😶'),
|
||||
new InputRule(/:-x/, '😶'),
|
||||
new InputRule(/>.</, '😣'),
|
||||
new InputRule(/:-O/, '😮'),
|
||||
new InputRule(/:-o/, '😮'),
|
||||
new InputRule(/O_O/, '😮'),
|
||||
new InputRule(/>:O/, '😮'),
|
||||
new InputRule(/:-P/, '😛'),
|
||||
new InputRule(/:-p/, '😛'),
|
||||
new InputRule(/:-Þ/, '😛'),
|
||||
new InputRule(/:-þ/, '😛'),
|
||||
new InputRule(/:-b/, '😛'),
|
||||
new InputRule(/>:P/, '😜'),
|
||||
new InputRule(/X-P/, '😜'),
|
||||
new InputRule(/x-p/, '😜'),
|
||||
new InputRule(/':\(/, '😓'),
|
||||
new InputRule(/'=\(/, '😓'),
|
||||
new InputRule(/>:\\/, '😕'),
|
||||
new InputRule(/>:\//, '😕'),
|
||||
new InputRule(/:-\//, '😕'),
|
||||
new InputRule(/:-./, '😕'),
|
||||
new InputRule(/>:\[/, '😞'),
|
||||
new InputRule(/:-\(/, '😞'),
|
||||
new InputRule(/:-\[/, '😞'),
|
||||
new InputRule(/:'\(/, '😢'),
|
||||
new InputRule(/;-\(/, '😢'),
|
||||
new InputRule(/#-\)/, '😵'),
|
||||
new InputRule(/%-\)/, '😵'),
|
||||
new InputRule(/X-\)/, '😵'),
|
||||
new InputRule(/>:\(/, '😠'),
|
||||
new InputRule(/0:3/, '😇'),
|
||||
new InputRule(/0:\)/, '😇'),
|
||||
new InputRule(/O:\)/, '😇'),
|
||||
new InputRule(/O=\)/, '😇'),
|
||||
new InputRule(/O:3/, '😇'),
|
||||
new InputRule(/<\/3/, '💔'),
|
||||
new InputRule(/:D/, '😃'),
|
||||
new InputRule(/=D/, '😃'),
|
||||
new InputRule(/;\)/, '😉'),
|
||||
new InputRule(/\*\)/, '😉'),
|
||||
new InputRule(/;\]/, '😉'),
|
||||
new InputRule(/;D/, '😉'),
|
||||
new InputRule(/B\)/, '😎'),
|
||||
new InputRule(/8\)/, '😎'),
|
||||
new InputRule(/:\*/, '😘'),
|
||||
new InputRule(/=\*/, '😘'),
|
||||
new InputRule(/:\)/, '🙂'),
|
||||
new InputRule(/=\]/, '🙂'),
|
||||
new InputRule(/=\)/, '🙂'),
|
||||
new InputRule(/:\]/, '🙂'),
|
||||
new InputRule(/:X/, '😶'),
|
||||
new InputRule(/:#/, '😶'),
|
||||
new InputRule(/=X/, '😶'),
|
||||
new InputRule(/=x/, '😶'),
|
||||
new InputRule(/:x/, '😶'),
|
||||
new InputRule(/=#/, '😶'),
|
||||
new InputRule(/:O/, '😮'),
|
||||
new InputRule(/:o/, '😮'),
|
||||
new InputRule(/:P/, '😛'),
|
||||
new InputRule(/=P/, '😛'),
|
||||
new InputRule(/:p/, '😛'),
|
||||
new InputRule(/=p/, '😛'),
|
||||
new InputRule(/:Þ/, '😛'),
|
||||
new InputRule(/:þ/, '😛'),
|
||||
new InputRule(/:b/, '😛'),
|
||||
new InputRule(/d:/, '😛'),
|
||||
new InputRule(/:\//, '😕'),
|
||||
new InputRule(/:\\/, '😕'),
|
||||
new InputRule(/=\//, '😕'),
|
||||
new InputRule(/=\\/, '😕'),
|
||||
new InputRule(/:L/, '😕'),
|
||||
new InputRule(/=L/, '😕'),
|
||||
new InputRule(/:\(/, '😞'),
|
||||
new InputRule(/:\[/, '😞'),
|
||||
new InputRule(/=\(/, '😞'),
|
||||
new InputRule(/;\(/, '😢'),
|
||||
new InputRule(/D:/, '😨'),
|
||||
new InputRule(/:\$/, '😳'),
|
||||
new InputRule(/=\$/, '😳'),
|
||||
new InputRule(/#\)/, '😵'),
|
||||
new InputRule(/%\)/, '😵'),
|
||||
new InputRule(/X\)/, '😵'),
|
||||
new InputRule(/:@/, '😠'),
|
||||
new InputRule(/<3/, '❤️'),
|
||||
new InputRule(/-___- /, '😑 '),
|
||||
new InputRule(/:'-\) /, '😂 '),
|
||||
new InputRule(/':-\) /, '😅 '),
|
||||
new InputRule(/':-D /, '😅 '),
|
||||
new InputRule(/>:-\) /, '😆 '),
|
||||
new InputRule(/-__- /, '😑 '),
|
||||
new InputRule(/':-\( /, '😓 '),
|
||||
new InputRule(/:'-\( /, '😢 '),
|
||||
new InputRule(/>:-\( /, '😠 '),
|
||||
new InputRule(/O:-\) /, '😇 '),
|
||||
new InputRule(/0:-3 /, '😇 '),
|
||||
new InputRule(/0:-\) /, '😇 '),
|
||||
new InputRule(/0;\^\) /, '😇 '),
|
||||
new InputRule(/O;-\) /, '😇 '),
|
||||
new InputRule(/0;-\) /, '😇 '),
|
||||
new InputRule(/O:-3 /, '😇 '),
|
||||
new InputRule(/:'\) /, '😂 '),
|
||||
new InputRule(/:-D /, '😃 '),
|
||||
new InputRule(/':\) /, '😅 '),
|
||||
new InputRule(/'=\) /, '😅 '),
|
||||
new InputRule(/':D /, '😅 '),
|
||||
new InputRule(/'=D /, '😅 '),
|
||||
new InputRule(/>:\) /, '😆 '),
|
||||
new InputRule(/>;\) /, '😆 '),
|
||||
new InputRule(/>=\) /, '😆 '),
|
||||
new InputRule(/;-\) /, '😉 '),
|
||||
new InputRule(/\*-\) /, '😉 '),
|
||||
new InputRule(/;-\] /, '😉 '),
|
||||
new InputRule(/;\^\) /, '😉 '),
|
||||
new InputRule(/B-\) /, '😎 '),
|
||||
new InputRule(/8-\) /, '😎 '),
|
||||
new InputRule(/B-D /, '😎 '),
|
||||
new InputRule(/8-D /, '😎 '),
|
||||
new InputRule(/:-\* /, '😘 '),
|
||||
new InputRule(/:\^\* /, '😘 '),
|
||||
new InputRule(/:-\) /, '🙂 '),
|
||||
new InputRule(/-_- /, '😑 '),
|
||||
new InputRule(/:-X /, '😶 '),
|
||||
new InputRule(/:-# /, '😶 '),
|
||||
new InputRule(/:-x /, '😶 '),
|
||||
new InputRule(/>.< /, '😣 '),
|
||||
new InputRule(/:-O /, '😮 '),
|
||||
new InputRule(/:-o /, '😮 '),
|
||||
new InputRule(/O_O /, '😮 '),
|
||||
new InputRule(/>:O /, '😮 '),
|
||||
new InputRule(/:-P /, '😛 '),
|
||||
new InputRule(/:-p /, '😛 '),
|
||||
new InputRule(/:-Þ /, '😛 '),
|
||||
new InputRule(/:-þ /, '😛 '),
|
||||
new InputRule(/:-b /, '😛 '),
|
||||
new InputRule(/>:P /, '😜 '),
|
||||
new InputRule(/X-P /, '😜 '),
|
||||
new InputRule(/x-p /, '😜 '),
|
||||
new InputRule(/':\( /, '😓 '),
|
||||
new InputRule(/'=\( /, '😓 '),
|
||||
new InputRule(/>:\\ /, '😕 '),
|
||||
new InputRule(/>:\/ /, '😕 '),
|
||||
new InputRule(/:-\/ /, '😕 '),
|
||||
new InputRule(/:-. /, '😕 '),
|
||||
new InputRule(/>:\[ /, '😞 '),
|
||||
new InputRule(/:-\( /, '😞 '),
|
||||
new InputRule(/:-\[ /, '😞 '),
|
||||
new InputRule(/:'\( /, '😢 '),
|
||||
new InputRule(/;-\( /, '😢 '),
|
||||
new InputRule(/#-\) /, '😵 '),
|
||||
new InputRule(/%-\) /, '😵 '),
|
||||
new InputRule(/X-\) /, '😵 '),
|
||||
new InputRule(/>:\( /, '😠 '),
|
||||
new InputRule(/0:3 /, '😇 '),
|
||||
new InputRule(/0:\) /, '😇 '),
|
||||
new InputRule(/O:\) /, '😇 '),
|
||||
new InputRule(/O=\) /, '😇 '),
|
||||
new InputRule(/O:3 /, '😇 '),
|
||||
new InputRule(/<\/3 /, '💔 '),
|
||||
new InputRule(/:D /, '😃 '),
|
||||
new InputRule(/=D /, '😃 '),
|
||||
new InputRule(/;\) /, '😉 '),
|
||||
new InputRule(/\*\) /, '😉 '),
|
||||
new InputRule(/;\] /, '😉 '),
|
||||
new InputRule(/;D /, '😉 '),
|
||||
new InputRule(/B\) /, '😎 '),
|
||||
new InputRule(/8\) /, '😎 '),
|
||||
new InputRule(/:\* /, '😘 '),
|
||||
new InputRule(/=\* /, '😘 '),
|
||||
new InputRule(/:\) /, '🙂 '),
|
||||
new InputRule(/=\] /, '🙂 '),
|
||||
new InputRule(/=\) /, '🙂 '),
|
||||
new InputRule(/:\] /, '🙂 '),
|
||||
new InputRule(/:X /, '😶 '),
|
||||
new InputRule(/:# /, '😶 '),
|
||||
new InputRule(/=X /, '😶 '),
|
||||
new InputRule(/=x /, '😶 '),
|
||||
new InputRule(/:x /, '😶 '),
|
||||
new InputRule(/=# /, '😶 '),
|
||||
new InputRule(/:O /, '😮 '),
|
||||
new InputRule(/:o /, '😮 '),
|
||||
new InputRule(/:P /, '😛 '),
|
||||
new InputRule(/=P /, '😛 '),
|
||||
new InputRule(/:p /, '😛 '),
|
||||
new InputRule(/=p /, '😛 '),
|
||||
new InputRule(/:Þ /, '😛 '),
|
||||
new InputRule(/:þ /, '😛 '),
|
||||
new InputRule(/:b /, '😛 '),
|
||||
new InputRule(/d: /, '😛 '),
|
||||
new InputRule(/:\/ /, '😕 '),
|
||||
new InputRule(/:\\ /, '😕 '),
|
||||
new InputRule(/=\/ /, '😕 '),
|
||||
new InputRule(/=\\ /, '😕 '),
|
||||
new InputRule(/:L /, '😕 '),
|
||||
new InputRule(/=L /, '😕 '),
|
||||
new InputRule(/:\( /, '😞 '),
|
||||
new InputRule(/:\[ /, '😞 '),
|
||||
new InputRule(/=\( /, '😞 '),
|
||||
new InputRule(/;\( /, '😢 '),
|
||||
new InputRule(/D: /, '😨 '),
|
||||
new InputRule(/:\$ /, '😳 '),
|
||||
new InputRule(/=\$ /, '😳 '),
|
||||
new InputRule(/#\) /, '😵 '),
|
||||
new InputRule(/%\) /, '😵 '),
|
||||
new InputRule(/X\) /, '😵 '),
|
||||
new InputRule(/:@ /, '😠 '),
|
||||
new InputRule(/<3 /, '❤️ '),
|
||||
new InputRule(/\/shrug/, '¯\\_(ツ)_/¯'),
|
||||
]
|
||||
},
|
||||
})
|
||||
|
@ -17,7 +17,7 @@ const CustomDocument = Document.extend({
|
||||
})
|
||||
|
||||
const CustomTaskItem = TaskItem.extend({
|
||||
content: 'text*',
|
||||
content: 'inline*',
|
||||
})
|
||||
|
||||
export default {
|
||||
|
69
docs/src/demos/Experiments/WordBreak/index.vue
Normal file
69
docs/src/demos/Experiments/WordBreak/index.vue
Normal file
@ -0,0 +1,69 @@
|
||||
<template>
|
||||
<div v-if="editor">
|
||||
<button @click="editor.chain().focus().setWordBreak().run()" :class="{ 'is-active': editor.isActive('wordBreak') }">
|
||||
wbr
|
||||
</button>
|
||||
|
||||
<editor-content :editor="editor" />
|
||||
|
||||
<h2>HTML</h2>
|
||||
{{ editor.getHTML() }}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { Editor, EditorContent } from '@tiptap/vue-2'
|
||||
import { defaultExtensions } from '@tiptap/starter-kit'
|
||||
import { WordBreak } from './word-break'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
EditorContent,
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
editor: null,
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.editor = new Editor({
|
||||
extensions: [
|
||||
...defaultExtensions(),
|
||||
WordBreak,
|
||||
],
|
||||
content: `
|
||||
<p>super<wbr>longword</p>
|
||||
`,
|
||||
})
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
this.editor.destroy()
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
::v-deep {
|
||||
.ProseMirror {
|
||||
> * + * {
|
||||
margin-top: 0.75em;
|
||||
}
|
||||
}
|
||||
|
||||
.word-break {
|
||||
display: inline-block;
|
||||
height: 1em;
|
||||
margin: 0 0.1em;
|
||||
line-height: 1em;
|
||||
border: 1px dashed #868e96;
|
||||
color: #868e96;
|
||||
|
||||
&::before {
|
||||
content: '-';
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
71
docs/src/demos/Experiments/WordBreak/word-break.ts
Normal file
71
docs/src/demos/Experiments/WordBreak/word-break.ts
Normal file
@ -0,0 +1,71 @@
|
||||
import { Command, Node, mergeAttributes } from '@tiptap/core'
|
||||
import { exitCode } from 'prosemirror-commands'
|
||||
|
||||
export interface WordBreakOptions {
|
||||
HTMLAttributes: {
|
||||
[key: string]: any
|
||||
},
|
||||
}
|
||||
|
||||
declare module '@tiptap/core' {
|
||||
interface Commands {
|
||||
wordBreak: {
|
||||
/**
|
||||
* Add a hard break
|
||||
*/
|
||||
setWordBreak: () => Command,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const WordBreak = Node.create<WordBreakOptions>({
|
||||
name: 'wordBreak',
|
||||
|
||||
defaultOptions: {
|
||||
HTMLAttributes: {},
|
||||
},
|
||||
|
||||
inline: true,
|
||||
|
||||
group: 'inline',
|
||||
|
||||
selectable: false,
|
||||
|
||||
parseHTML() {
|
||||
return [
|
||||
{ tag: 'wbr' },
|
||||
]
|
||||
},
|
||||
|
||||
renderHTML({ HTMLAttributes }) {
|
||||
return ['wbr', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes)]
|
||||
},
|
||||
|
||||
addCommands() {
|
||||
return {
|
||||
setWordBreak: () => ({ commands, state, dispatch }) => {
|
||||
return commands.first([
|
||||
() => exitCode(state, dispatch),
|
||||
() => {
|
||||
if (dispatch) {
|
||||
state.tr.replaceSelectionWith(this.type.create()).scrollIntoView()
|
||||
}
|
||||
|
||||
return true
|
||||
},
|
||||
])
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
addNodeView() {
|
||||
return () => {
|
||||
const dom = document.createElement('span')
|
||||
dom.classList.add('word-break')
|
||||
|
||||
return {
|
||||
dom,
|
||||
}
|
||||
}
|
||||
},
|
||||
})
|
@ -125,4 +125,61 @@ context('/demos/Nodes/CodeBlock', () => {
|
||||
.should('contain', 'Code')
|
||||
})
|
||||
})
|
||||
|
||||
it('reverts the markdown shortcut when pressing backspace', () => {
|
||||
cy.get('.ProseMirror').then(([{ editor }]) => {
|
||||
editor.commands.clearContent()
|
||||
|
||||
cy.get('.ProseMirror')
|
||||
.type('``` {backspace}')
|
||||
|
||||
cy.get('.ProseMirror pre')
|
||||
.should('not.exist')
|
||||
})
|
||||
})
|
||||
|
||||
it('removes the code block when pressing backspace', () => {
|
||||
cy.get('.ProseMirror').then(([{ editor }]) => {
|
||||
editor.commands.clearContent()
|
||||
|
||||
cy.get('.ProseMirror pre')
|
||||
.should('not.exist')
|
||||
|
||||
cy.get('.ProseMirror')
|
||||
.type('Paragraph{enter}``` A{backspace}{backspace}')
|
||||
|
||||
cy.get('.ProseMirror pre')
|
||||
.should('not.exist')
|
||||
})
|
||||
})
|
||||
|
||||
it('removes the code block when pressing backspace, even with blank lines', () => {
|
||||
cy.get('.ProseMirror').then(([{ editor }]) => {
|
||||
editor.commands.clearContent()
|
||||
|
||||
cy.get('.ProseMirror pre')
|
||||
.should('not.exist')
|
||||
|
||||
cy.get('.ProseMirror')
|
||||
.type('Paragraph{enter}{enter}``` A{backspace}{backspace}')
|
||||
|
||||
cy.get('.ProseMirror pre')
|
||||
.should('not.exist')
|
||||
})
|
||||
})
|
||||
|
||||
it('removes the code block when pressing backspace, even at start of document', () => {
|
||||
cy.get('.ProseMirror').then(([{ editor }]) => {
|
||||
editor.commands.clearContent()
|
||||
|
||||
cy.get('.ProseMirror pre')
|
||||
.should('not.exist')
|
||||
|
||||
cy.get('.ProseMirror')
|
||||
.type('``` A{leftArrow}{backspace}')
|
||||
|
||||
cy.get('.ProseMirror pre')
|
||||
.should('not.exist')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -15,10 +15,10 @@ import Paragraph from '@tiptap/extension-paragraph'
|
||||
import Text from '@tiptap/extension-text'
|
||||
import CodeBlockLowlight from '@tiptap/extension-code-block-lowlight'
|
||||
|
||||
// install all highlight.js languages
|
||||
import 'lowlight'
|
||||
// load all highlight.js languages
|
||||
import lowlight from 'lowlight'
|
||||
|
||||
// or install specific languages only
|
||||
// load specific languages only
|
||||
// import lowlight from 'lowlight/lib/core'
|
||||
// import javascript from 'highlight.js/lib/languages/javascript'
|
||||
// lowlight.registerLanguage('javascript', javascript)
|
||||
@ -40,7 +40,9 @@ export default {
|
||||
Document,
|
||||
Paragraph,
|
||||
Text,
|
||||
CodeBlockLowlight,
|
||||
CodeBlockLowlight.configure({
|
||||
lowlight,
|
||||
}),
|
||||
],
|
||||
content: `
|
||||
<p>
|
||||
|
@ -1,5 +0,0 @@
|
||||
# Advanced Example
|
||||
|
||||
Use a custom list of extensions.
|
||||
|
||||
<demo name="HandleExtensions" />
|
@ -182,6 +182,8 @@ new Editor({
|
||||
})
|
||||
```
|
||||
|
||||
You can use that to hook into event handlers and pass - for example - a custom paste handler, too.
|
||||
|
||||
### Parse options
|
||||
Passed content is parsed by ProseMirror. To hook into the parsing, you can pass `parseOptions` which are then handled by [ProseMirror](https://prosemirror.net/docs/ref/#model.ParseOptions).
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# Concept
|
||||
# Introduction
|
||||
tiptap is a friendly wrapper around [ProseMirror](https://ProseMirror.net). Although tiptap tries to hide most of the complexity of ProseMirror, it’s built on top of its APIs and we recommend you to read through the [ProseMirror Guide](https://ProseMirror.net/docs/guide/) for advanced usage.
|
||||
|
||||
### Structure
|
||||
@ -20,7 +20,8 @@ ProseMirror has its own vocabulary and you’ll stumble upon all those words now
|
||||
| State | Everything to describe the current content and selection of your editor. |
|
||||
| Transaction | A change to the state (updated selection, content, …) |
|
||||
| Extension | Registeres new functionality. |
|
||||
| Node | Adds blocks, like heading, paragraph. |
|
||||
| Mark | Adds inline formatting, for example bold or italic. |
|
||||
| Node | A type of content, for example a heading or a paragraph. |
|
||||
| Mark | Can be applied to nodes, for example for inline formatting. |
|
||||
| Command | Execute an action inside the editor, that somehow changes the state. |
|
||||
| Decoration | Styling on top of the document, for example to highlight mistakes. |
|
||||
|
@ -3,7 +3,7 @@
|
||||
## toc
|
||||
|
||||
## Introduction
|
||||
Marks add new capabilities to tiptap. Marks are like different types of inline styling, for example bold, italic or highlights.
|
||||
One or multiple marks can be applied to [nodes](/api/nodes), for example to add inline formatting like bold and italic, or other additional information.
|
||||
|
||||
## List of supported marks
|
||||
| Title | Default Extension | Source Code |
|
||||
|
@ -3,7 +3,7 @@
|
||||
## toc
|
||||
|
||||
## Introduction
|
||||
Nodes add new capabilities to tiptap. Nodes are like block types, for example a paragraph, heading, or code block.
|
||||
If you think of the document as a tree, then nodes are just a type of content in that tree. Examples of nodes are paragraphs, headings, or code blocks. But nodes don’t have to be blocks. They can also be rendered inline with the text, for example for **@mentions**.
|
||||
|
||||
## List of supported nodes
|
||||
| Title | Default Extension | Source Code |
|
||||
|
@ -6,7 +6,7 @@ There is no extension or example yet, but it’s definitely on our list to build
|
||||
If you want to give it a shot yourself, you could start altering the [`Mention`](/api/nodes/mention) node. This uses the [`Suggestion`](/api/utilities/suggestion) utility, which should help with an autocomplete and such things.
|
||||
|
||||
:::pro Fund the development ♥
|
||||
We need your support to maintain, update, support and develop tiptap 2. If you’re waiting for this extension, [become a sponsor and fund our work](/sponsor).
|
||||
We need your support to maintain, update, support and develop tiptap. If you’re waiting for this extension, [become a sponsor and fund our work](/sponsor).
|
||||
:::
|
||||
|
||||
## Bring your own emoji picker
|
||||
|
@ -1,7 +1,7 @@
|
||||
# Hashtag
|
||||
|
||||
:::pro Fund the development ♥
|
||||
We need your support to maintain, update, support and develop tiptap 2. If you’re waiting for this extension, [become a sponsor and fund our work](/sponsor).
|
||||
We need your support to maintain, update, support and develop tiptap. If you’re waiting for this extension, [become a sponsor and fund our work](/sponsor).
|
||||
:::
|
||||
|
||||
TODO
|
||||
|
@ -39,7 +39,7 @@ yarn add @tiptap/vue-2
|
||||
|
||||
And yes, we plan to support React, too. Meanwhile, you can roll your own `ReactRenderer`, but don’t forget to share it with the community.
|
||||
|
||||
It’s also possible to use plain JavaScript, but that is probably a lot more work.
|
||||
It’s also possible to use Vanilla JavaScript, but that is probably a lot more work.
|
||||
|
||||
## Settings
|
||||
| Option | Type | Default | Description |
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
This extension renders a task item list element, which is a `<li>` tag with a `data-type` attribute set to `taskItem`. It also renders a checkbox inside the list element, which updates a `checked` attribute.
|
||||
|
||||
This extension doesn’t require any JavaScript framework, it’s based on plain JavaScript.
|
||||
This extension doesn’t require any JavaScript framework, it’s based on Vanilla JavaScript.
|
||||
|
||||
## Installation
|
||||
```bash
|
||||
|
@ -2,7 +2,7 @@
|
||||
[![Version](https://img.shields.io/npm/v/@tiptap/extension-task-list.svg?label=version)](https://www.npmjs.com/package/@tiptap/extension-task-list)
|
||||
[![Downloads](https://img.shields.io/npm/dm/@tiptap/extension-task-list.svg)](https://npmcharts.com/compare/@tiptap/extension-task-list?minimal=true)
|
||||
|
||||
This extension enables you to use task lists in the editor. They are rendered as `<ul data-type="task_list">`. This implementation doesn’t require any framework, it’s using plain JavaScript only.
|
||||
This extension enables you to use task lists in the editor. They are rendered as `<ul data-type="task_list">`. This implementation doesn’t require any framework, it’s using Vanilla JavaScript only.
|
||||
|
||||
Type <code>[ ] </code> or <code>[x] </code> at the beginning of a new line and it will magically transform to a task list.
|
||||
|
||||
|
@ -22,19 +22,17 @@ Be nice! The content of this editor is shared with other users from the Internet
|
||||
In case you’re wondering what kind of sorcery you need on the server to achieve this, here is the whole backend code for the demo:
|
||||
|
||||
:::warning Request early access
|
||||
Our plug & play collaboration backend hocuspocus is still work in progress. If you want to give it a try, [request early access](https://hocuspocus.dev/).
|
||||
Our plug & play collaboration backend hocuspocus is still work in progress. If you want to give it a try, [request early access](https://www.hocuspocus.dev).
|
||||
:::
|
||||
|
||||
```js
|
||||
import { Server } from '@hocuspocus/server'
|
||||
import { RocksDB } from '@hocuspocus/rocksdb'
|
||||
import { RocksDB } from '@hocuspocus/extension-rocksdb'
|
||||
|
||||
const server = Server.configure({
|
||||
port: 80,
|
||||
extensions: [
|
||||
new RocksDB({
|
||||
path: './database',
|
||||
})
|
||||
new RocksDB({ path: './database' }),
|
||||
],
|
||||
})
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
# Images
|
||||
|
||||
:::pro Fund the development ♥
|
||||
We need your support to maintain, update, support and develop tiptap 2. If you’re hoping for more features related to images, [become a sponsor and fund our work](/sponsor).
|
||||
We need your support to maintain, update, support and develop tiptap. If you’re hoping for more features related to images, [become a sponsor and fund our work](/sponsor).
|
||||
:::
|
||||
|
||||
<demo name="Examples/Images" />
|
||||
|
@ -6,3 +6,8 @@ Thanks to [node views](/guide/node-views) you can add interactivity to your node
|
||||
Vue: 'Guide/NodeViews/VueComponent',
|
||||
React: 'Guide/NodeViews/ReactComponent',
|
||||
}" />
|
||||
|
||||
<demos :items="{
|
||||
Vue: 'Guide/NodeViews/VueComponentContent',
|
||||
React: 'Guide/NodeViews/ReactComponentContent',
|
||||
}" />
|
||||
|
@ -1,3 +1,3 @@
|
||||
# Code block language
|
||||
# Syntax highlighting
|
||||
|
||||
<demo name="Examples/CodeBlockLanguage" />
|
@ -9,3 +9,4 @@ Congratulations! You’ve found our playground with a list of experiments. Be aw
|
||||
* [@tiptap/extension-iframe?](/experiments/embeds)
|
||||
* [@tiptap/extension-toggle-list?](/experiments/details)
|
||||
* [@tiptap/extension-collaboration-annotation](/experiments/collaboration-annotation)
|
||||
* [@tiptap/extension-word-break](/experiments/word-break)
|
||||
|
5
docs/src/docPages/experiments/word-break.md
Normal file
5
docs/src/docPages/experiments/word-break.md
Normal file
@ -0,0 +1,5 @@
|
||||
# Word break
|
||||
|
||||
⚠️ Experiment
|
||||
|
||||
<demo name="Experiments/WordBreak" />
|
@ -1,7 +1,7 @@
|
||||
# Accessibility
|
||||
|
||||
:::pro Fund the development ♥
|
||||
We need your support to maintain, update, support and develop tiptap 2. If you’re waiting for progress here, [become a sponsor and fund our work](/sponsor).
|
||||
We need your support to maintain, update, support and develop tiptap. If you’re waiting for progress here, [become a sponsor and fund our work](/sponsor).
|
||||
:::
|
||||
|
||||
## toc
|
||||
|
@ -216,7 +216,7 @@ Yes, it’s magic. As already mentioned, that is all based on the fantastic Y.js
|
||||
Our collaborative editing backend handles the syncing, authorization, persistence and scaling. Let’s go through a few common use cases here!
|
||||
|
||||
:::warning Request early access
|
||||
Our plug & play collaboration backend hocuspocus is still work in progress. If you want to give it a try, [request early access](https://hocuspocus.dev/).
|
||||
Our plug & play collaboration backend hocuspocus is still work in progress. If you want to give it a try, [request early access](https://www.hocuspocus.dev).
|
||||
:::
|
||||
|
||||
### The document name
|
||||
|
@ -127,7 +127,7 @@ import { Editor, defaultExtensions } from '@tiptap/starter-kit'
|
||||
|
||||
new Editor({
|
||||
extensions: [
|
||||
...defaultExtensions().filter(extension => extension.config.name !== 'history'),
|
||||
...defaultExtensions().filter(extension => extension.name !== 'history'),
|
||||
],
|
||||
})
|
||||
```
|
||||
|
@ -111,7 +111,7 @@ This section needs some work. Do you know what else needs to be taken into accou
|
||||
:::
|
||||
|
||||
### Icons
|
||||
Most editor menus use icons for their buttons. In some of our demos, we use the open-source icon set [Remix Icon](https://remixicon.com/), that’s free to use. But it’s totally up to you what you use. Here are a few icon sets you can consider:
|
||||
Most editor menus use icons for their buttons. In some of our demos, we use the open source icon set [Remix Icon](https://remixicon.com/), that’s free to use. But it’s totally up to you what you use. Here are a few icon sets you can consider:
|
||||
|
||||
* [Remix Icon](https://remixicon.com/#editor)
|
||||
* [Font Awesome](https://fontawesome.com/icons?c=editors)
|
||||
|
@ -3,7 +3,7 @@
|
||||
## toc
|
||||
|
||||
## Introduction
|
||||
Using frameworks like Vue or React can feel too complex, if you’re used to work without those two. Good news: You can use plain JavaScript in your node views. There is just a little bit you need to know, but let’s go through this one by one.
|
||||
Using frameworks like Vue or React can feel too complex, if you’re used to work without those two. Good news: You can use Vanilla JavaScript in your node views. There is just a little bit you need to know, but let’s go through this one by one.
|
||||
|
||||
## Render a node view with JavaScript
|
||||
Here is what you need to do to render a node view inside your editor:
|
||||
|
@ -3,7 +3,7 @@
|
||||
## toc
|
||||
|
||||
## Introduction
|
||||
Using plain JavaScript can feel complex if you are used to work in React. Good news: You can use regular React components in your node views, too. There is just a little bit you need to know, but let’s go through this one by one.
|
||||
Using Vanilla JavaScript can feel complex if you are used to work in React. Good news: You can use regular React components in your node views, too. There is just a little bit you need to know, but let’s go through this one by one.
|
||||
|
||||
## Render a React component
|
||||
Here is what you need to do to render React components inside your editor:
|
||||
|
@ -3,7 +3,7 @@
|
||||
## toc
|
||||
|
||||
## Introduction
|
||||
Using plain JavaScript can feel complex if you are used to work in Vue. Good news: You can use regular Vue components in your node views, too. There is just a little bit you need to know, but let’s go through this one by one.
|
||||
Using Vanilla JavaScript can feel complex if you are used to work in Vue. Good news: You can use regular Vue components in your node views, too. There is just a little bit you need to know, but let’s go through this one by one.
|
||||
|
||||
## Render a Vue component
|
||||
Here is what you need to do to render Vue components inside your editor:
|
||||
|
@ -67,7 +67,7 @@ Use this interactive example to fiddle around:
|
||||
<demo name="Guide/Content/ExportHTML" hide-source/>
|
||||
|
||||
### Option 3: Y.js
|
||||
Our editor has amazing support for Y.js, which is amazing to add [realtime collaboration, offline editing, or syncing between devices](/guide/collaborative-editing).
|
||||
Our editor has top notch support for Y.js, which is amazing to add features like [realtime collaboration, offline editing, or syncing between devices](/guide/collaborative-editing).
|
||||
|
||||
Internally, Y.js stores a history of all changes. That can be in the browser, on a server, synced with other connected clients, or on a USB stick. But, it’s important to know that Y.js needs those stored changes. A simple JSON document is not enough to merge changes.
|
||||
|
||||
@ -79,13 +79,13 @@ That said, it’s amazing and we’re about to provide an amazing backend, that
|
||||
Unfortunately, **tiptap doesn’t support Markdown as an input or output format**. We considered to add support for it, but those are the reasons why we decided to not do it:
|
||||
|
||||
* Both, HTML and JSON, can have deeply nested structures, Markdown is flat.
|
||||
* There are enough packages to convert HTML to Markdown and vice-versa.
|
||||
* Markdown standards vary.
|
||||
* tiptap’s strength is cutomization, that doesn’t work very well with Markdown.
|
||||
* There are enough packages to convert HTML to Markdown and vice-versa.
|
||||
|
||||
You should really consider to work with HTML or JSON to store your content, they are perfectly fine for most use cases.
|
||||
|
||||
If you still think you need Markdown, ProseMirror has an [example on how to deal with Markdown](https://prosemirror.net/examples/markdown/), [Nextcloud Text](https://github.com/nextcloud/text) uses tiptap 1 to work with Markdown. Maybe you can learn from them. Or if you’re looking for a really good Markdown editor, try [CodeMirror](https://codemirror.net/).
|
||||
If you still think you need Markdown, ProseMirror has an [example on how to deal with Markdown](https://prosemirror.net/examples/markdown/), [Nextcloud Text](https://github.com/nextcloud/text) uses tiptap 1 to work with Markdown. Maybe you can learn from them. Or if you are looking for a really good Markdown editor, try [CodeMirror](https://codemirror.net/).
|
||||
|
||||
That said, tiptap does support [Markdown shortcuts](/examples/markdown-shortcuts) to format your content. Also you’re free to let your content look like Markdown, for example add a `#` before an `<h1>` with CSS.
|
||||
|
||||
@ -127,4 +127,4 @@ We’re about to go through a few cases to help with that, for example we provid
|
||||
[Share your experiences with us!](mailto:humans@tiptap.dev) We’d like to add more information here.
|
||||
|
||||
## Security
|
||||
There’s no reason to use on or the other because of security concerns. If someone wants to send malicious content to your server, it doesn’t matter if it’s JSON or HTML. You should always validate user input.
|
||||
There is no reason to use on or the other because of security concerns. If someone wants to send malicious content to your server, it doesn’t matter if it’s JSON or HTML. It doesn’t even matter if you’re using tiptap or not. You should always validate user input.
|
||||
|
@ -5,7 +5,7 @@
|
||||
## Introduction
|
||||
The whole tiptap is code base is written in TypeScript. If you haven’t heard of it or never used it, no worries. You don’t have to.
|
||||
|
||||
TypeScript extends JavaScript by adding types (hence the name). It adds new syntax, which doesn’t exist in plain JavaScript. It’s actually removed before running in the browser, but this step – the compilation – is important to find bugs early. It checks if you passe the right types of data to functions. For a big and complex project, that’s very valuable. It means we’ll get notified of lot of bugs, before shipping code to you.
|
||||
TypeScript extends JavaScript by adding types (hence the name). It adds new syntax, which doesn’t exist in Vanilla JavaScript. It’s actually removed before running in the browser, but this step – the compilation – is important to find bugs early. It checks if you passe the right types of data to functions. For a big and complex project, that’s very valuable. It means we’ll get notified of lot of bugs, before shipping code to you.
|
||||
|
||||
**Anyway, if you don’t use TypeScript in your project, that’s fine.** You will still be able to use tiptap and nevertheless get a nice autocomplete for the tiptap API (if your editor supports it, but most do).
|
||||
|
||||
|
@ -3,17 +3,17 @@
|
||||
## toc
|
||||
|
||||
## Introduction
|
||||
tiptap 2 is framework-agnostic and even works with plain JavaScript, if that’s your thing. We’re working on guides for all the different frameworks and workflows. The following steps should help you to integrate tiptap in your JavaScript project.
|
||||
tiptap is framework-agnostic and even works with Vanilla JavaScript, if that’s your thing. The following integration guides should help you to integrate tiptap in your JavaScript project.
|
||||
|
||||
## Integration guides
|
||||
* [CDN](/installation/cdn)
|
||||
* [CodeSandbox](/installation/codesandbox)
|
||||
* [Vue 2](/installation/vue2)
|
||||
<!-- * [CodeSandbox](/installation/codesandbox) -->
|
||||
* [React](/installation/react)
|
||||
* [Vue 3](/installation/vue3)
|
||||
* [Vue 2](/installation/vue2)
|
||||
* [Nuxt.js](/installation/nuxt)
|
||||
* [React](/installation/react) (Draft)
|
||||
* [Svelte](/installation/svelte) (Draft)
|
||||
* [Alpine.js](/installation/alpine) (Draft)
|
||||
* [Svelte](/installation/svelte)
|
||||
* [Alpine.js](/installation/alpine)
|
||||
* [Livewire](/installation/livewire) (Draft)
|
||||
|
||||
## Vanilla JavaScript
|
||||
@ -22,7 +22,7 @@ tiptap 2 is framework-agnostic and even works with plain JavaScript, if that’s
|
||||
* [Node](https://nodejs.org/en/download/) installed on your machine
|
||||
|
||||
### 1. Install the dependencies
|
||||
For the following example you will need `@tiptap/core` (the actual editor) and `@tiptap/starter-kit` which has everything to get started quickly, for example the most common extensions.
|
||||
For the following example you will need `@tiptap/core` (the actual editor) and `@tiptap/starter-kit` which has the most common extensions to get started quickly.
|
||||
|
||||
```bash
|
||||
# install with npm
|
||||
@ -40,7 +40,7 @@ Add the following HTML where you want the editor to be mounted:
|
||||
```
|
||||
|
||||
### 3. Initialize the editor
|
||||
Now, let’s initialize the editor in JavaScript:
|
||||
Let’s initialize the editor in JavaScript now:
|
||||
|
||||
```js
|
||||
import { Editor } from '@tiptap/core'
|
||||
@ -53,4 +53,4 @@ new Editor({
|
||||
})
|
||||
```
|
||||
|
||||
Open your project in the browser and you should see tiptap. Good work! Time to give yourself a pat on the back.
|
||||
Open your project in the browser to see tiptap in action. Good work! Time to give yourself a pat on the back.
|
||||
|
@ -10,7 +10,7 @@ title: Headless WYSIWYG Text Editor
|
||||
|
||||
tiptap is a headless wrapper around [ProseMirror](https://ProseMirror.net) – a toolkit for building rich text WYSIWYG editors, which is already in use at many well-known companies such as *New York Times*, *The Guardian* or *Atlassian*.
|
||||
|
||||
Create exactly the rich text editor you want out of customizable building blocks. tiptap comes with sensible defaults, a lot of extensions and a friendly API to customize every aspect. It’s backed by a welcoming community, open-source, and free.
|
||||
Create exactly the rich text editor you want out of customizable building blocks. tiptap comes with sensible defaults, a lot of extensions and a friendly API to customize every aspect. It’s backed by a welcoming community, open source, and free.
|
||||
|
||||
## Example
|
||||
<demo name="Examples/CollaborativeEditing" hide-source inline />
|
||||
@ -18,9 +18,9 @@ Create exactly the rich text editor you want out of customizable building blocks
|
||||
## Features
|
||||
**Headless.** We don’t tell you what a menu should look like or where it should be rendered in the DOM. That’s why tiptap is headless and comes without any CSS. You are in full control over markup, styling and behaviour.
|
||||
|
||||
**Framework-agnostic.** No matter what framework you use, you’ll enjoy tiptap. Out of the box, it works with plain JavaScript and Vue.js, but it’s also possible to use it in [React](/installation/react), Svelte and others.
|
||||
**Framework-agnostic.** No matter what framework you use, you’ll enjoy tiptap. Out of the box, it works with Vanilla JavaScript and Vue.js, but it’s also possible to use it in [React](/installation/react), Svelte and others.
|
||||
|
||||
**TypeScript.** tiptap 2 is written in TypeScript. That helps us to find bugs early and gives you a nice autocomplete for the API (if your IDE supports that) on top of the extensive human written documentation.
|
||||
**TypeScript.** tiptap is written in TypeScript. That helps us to find bugs early and gives you a nice autocomplete for the API (if your IDE supports that) on top of the extensive human written documentation.
|
||||
|
||||
**Collaborative.** Real-time collaboration, syncing between different devices and working offline used to be hard. We provide everything you need to keep everything in sync, conflict-free with the power of [Y.js](https://github.com/yjs/yjs). Our production-grade setup requires less than 20 lines of code.
|
||||
|
||||
|
@ -5,10 +5,6 @@
|
||||
## Introduction
|
||||
tiptap would be nothing without its lively community. Contributions have always been and will always be welcome. Here is a little bit you should know, before you send your contribution:
|
||||
|
||||
:::warning Private repository
|
||||
Currently, the repository is private. That means PRs are disabled, too. We’ll release a public version of tiptap 2 soonish, please wait with your PRs until the repository is public.
|
||||
:::
|
||||
|
||||
## Welcome examples
|
||||
* Failing regression tests as bug reports
|
||||
* Documentation improvements, e. g. fix a typo, add a section
|
||||
|
@ -172,6 +172,9 @@ The reference implementation for collaborative editing uses Y.js now. That’s a
|
||||
|
||||
Read more about [the new collaborative editing experience](/guide/collaborative-editing) in our guide.
|
||||
|
||||
### Marks don’t support node view anymore
|
||||
For marks, node views are [not well supported in ProseMirror](https://discuss.prosemirror.net/t/there-is-a-bug-in-marks-nodeview/2722/2). There is also [a related issue](https://github.com/ueberdosis/tiptap/issues/613) for tiptap 1. That’s why we removed it in tiptap 2.
|
||||
|
||||
### Become a sponsor
|
||||
tiptap wouldn’t exist without the funding of its community. If you fell in love with tiptap, don’t forget to [become a sponsor](/sponsor) and make the maintenance, development and support sustainable.
|
||||
|
||||
|
@ -1,12 +0,0 @@
|
||||
# Monthly reports
|
||||
|
||||
| Month | Tasks | Sponsors | Funding | Worked |
|
||||
| --------- | ---------------------------------------------------------------------- | -------- | ------- | ------------- |
|
||||
| 01/2021 | Maintenance tiptap v1, … | … | … | … |
|
||||
| 12/2020 | Invited all sponsors, gathered feedback | 60 | $1,035 | 60 h |
|
||||
| 11/2020 | Wrote documentation, improved collaborative editing, advanced examples | 31 | $468 | 188 h |
|
||||
| 10/2020 | Rewrote extensions for tiptap 2 | 25 | $423 | 102 h |
|
||||
| 09/2020 | Developed the new API, wrote documentation | | | 125 h |
|
||||
| 08/2020 | Set up tiptap 2 | | | 56 h |
|
||||
| **Total** | | | | **531 h** |
|
||||
|
@ -3,21 +3,20 @@
|
||||
## Introduction
|
||||
To deliver a top-notch developer experience and user experience, we put ~~hundreds~~ thousands of hours of unpaid work into tiptap. Your funding helps us to make this work more and more financially sustainable. This enables us to provide helpful support, maintain all our packages, keep everything up to date, and develop new features and extensions for tiptap.
|
||||
|
||||
Give back to the open-source community and [sponsor us on GitHub](https://github.com/sponsors/ueberdosis)! ♥
|
||||
Give back to the open source community and [sponsor us on GitHub](https://github.com/sponsors/ueberdosis)! ♥
|
||||
|
||||
## Your benefits as a sponsor
|
||||
* Give back to the open-source community
|
||||
* Get early access to private repositories
|
||||
* Ensure the further maintenace and development of tiptap
|
||||
* Your issues and pull requests get a `sponsor ♥` label
|
||||
* Get a sponsor badge in all your comments on GitHub
|
||||
* Invest in the future of tiptap
|
||||
* Give back to the open source community
|
||||
* Show support in your GitHub profile
|
||||
* Receive monthly reports about our open-source work
|
||||
|
||||
Does that sound good? [Sponsor us on GitHub!](https://github.com/sponsors/ueberdosis)
|
||||
Sounds good? [Sponsor us on GitHub!](https://github.com/sponsors/ueberdosis)
|
||||
|
||||
## The maintainers of tiptap
|
||||
If you’re thankful for tiptap, you should say thank you to all 12 lovely people of [überdosis](https://twitter.com/_ueberdosis). The amazing company we’re all building together and the amazing company that funded the initial development costs of tiptap 2.
|
||||
If you are thankful for tiptap, you should say thank you to the lovely people of [überdosis](https://twitter.com/_ueberdosis). The amazing company we are all building together and the amazing company that funded the initial development costs of tiptap.
|
||||
|
||||
AND you should definitely hire us if you want us to design und build an amazing digital product for you. Bonus points if it’s somehow text editing related.
|
||||
|
||||
|
@ -30,19 +30,17 @@
|
||||
skip: true
|
||||
- title: Svelte
|
||||
link: /installation/svelte
|
||||
type: new
|
||||
skip: true
|
||||
# - title: CodeSandbox
|
||||
# link: /installation/codesandbox
|
||||
# skip: true
|
||||
- title: Alpine.js
|
||||
link: /installation/alpine
|
||||
type: new
|
||||
skip: true
|
||||
- title: Livewire
|
||||
link: /installation/livewire
|
||||
type: draft
|
||||
skip: true
|
||||
# - title: Livewire
|
||||
# link: /installation/livewire
|
||||
# type: draft
|
||||
# skip: true
|
||||
- title: Upgrade guide
|
||||
link: /overview/upgrade-guide
|
||||
- title: Become a sponsor
|
||||
@ -59,15 +57,12 @@
|
||||
link: /examples/default
|
||||
- title: Collaborative editing
|
||||
link: /examples/collaborative-editing
|
||||
# type: pro
|
||||
- title: Markdown shortcuts
|
||||
link: /examples/markdown-shortcuts
|
||||
- title: Menus
|
||||
link: /examples/menus
|
||||
type: new
|
||||
- title: Tables
|
||||
link: /examples/tables
|
||||
# type: pro
|
||||
- title: Images
|
||||
link: /examples/images
|
||||
- title: Formatting
|
||||
@ -86,8 +81,8 @@
|
||||
link: /examples/savvy
|
||||
- title: Interactivity
|
||||
link: /examples/interactivity
|
||||
- title: Code block language
|
||||
link: /examples/code-block-language
|
||||
- title: Syntax highlighting
|
||||
link: /examples/syntax-highlighting
|
||||
type: new
|
||||
|
||||
- title: Guide
|
||||
@ -96,7 +91,6 @@
|
||||
link: /guide/configuration
|
||||
- title: Menus
|
||||
link: /guide/menus
|
||||
type: new
|
||||
- title: Styling
|
||||
link: /guide/styling
|
||||
- title: Output
|
||||
@ -105,21 +99,17 @@
|
||||
link: /guide/accessibility
|
||||
- title: Collaborative editing
|
||||
link: /guide/collaborative-editing
|
||||
# type: pro
|
||||
- title: Custom extensions
|
||||
link: /guide/custom-extensions
|
||||
- title: Overwrite & extend
|
||||
link: /guide/extend-extensions
|
||||
- title: Interactive node views
|
||||
link: /guide/node-views
|
||||
type: new
|
||||
items:
|
||||
- title: With JavaScript
|
||||
link: /guide/node-views/js
|
||||
type: new
|
||||
- title: With React
|
||||
link: /guide/node-views/react
|
||||
type: new
|
||||
- title: With Vue
|
||||
link: /guide/node-views/vue
|
||||
- title: A few examples
|
||||
@ -129,8 +119,8 @@
|
||||
|
||||
- title: API
|
||||
items:
|
||||
- title: Concept
|
||||
link: /api/concept
|
||||
- title: Introduction
|
||||
link: /api/introduction
|
||||
- title: Editor
|
||||
link: /api/editor
|
||||
- title: Commands
|
||||
@ -185,16 +175,12 @@
|
||||
link: /api/nodes/paragraph
|
||||
- title: Table
|
||||
link: /api/nodes/table
|
||||
# type: pro
|
||||
- title: TableRow
|
||||
link: /api/nodes/table-row
|
||||
# type: pro
|
||||
- title: TableCell
|
||||
link: /api/nodes/table-cell
|
||||
# type: pro
|
||||
- title: TableHeader
|
||||
link: /api/nodes/table-header
|
||||
# type: pro
|
||||
- title: TaskList
|
||||
link: /api/nodes/task-list
|
||||
- title: TaskItem
|
||||
@ -223,9 +209,6 @@
|
||||
- title: Extensions
|
||||
link: /api/extensions
|
||||
items:
|
||||
# - title: Annotation
|
||||
# link: /api/extensions/annotation
|
||||
# type: draft
|
||||
- title: BubbleMenu
|
||||
link: /api/extensions/bubble-menu
|
||||
type: new
|
||||
@ -233,10 +216,8 @@
|
||||
link: /api/extensions/character-count
|
||||
- title: Collaboration
|
||||
link: /api/extensions/collaboration
|
||||
# type: pro
|
||||
- title: CollaborationCursor
|
||||
link: /api/extensions/collaboration-cursor
|
||||
# type: pro
|
||||
- title: Dropcursor
|
||||
link: /api/extensions/dropcursor
|
||||
- title: FloatingMenu
|
||||
@ -265,9 +246,9 @@
|
||||
link: /api/utilities/html
|
||||
- title: Suggestion
|
||||
link: /api/utilities/suggestion
|
||||
- title: Events
|
||||
link: /api/events
|
||||
- title: Schema
|
||||
link: /api/schema
|
||||
- title: Keyboard shortcuts
|
||||
link: /api/keyboard-shortcuts
|
||||
- title: Schema
|
||||
link: /api/schema
|
||||
- title: Events
|
||||
link: /api/events
|
||||
|
@ -6,7 +6,7 @@
|
||||
The headless editor framework for web artisans.
|
||||
</h1>
|
||||
<p class="is-large">
|
||||
tiptap gives you full control about every single aspect of your text editor experience. It’s customizable, comes with a ton of extensions, is open-source, has an extensive documentation, and is simply a joy to use. Join our welcoming community and start building cool things! It’s free.
|
||||
tiptap gives you full control about every single aspect of your text editor experience. It’s customizable, comes with a ton of extensions, is open source, has an extensive documentation, and is simply a joy to use. Join our welcoming community and start building cool things!
|
||||
</p>
|
||||
<btn-wrapper>
|
||||
<btn type="primary" icon="arrow-right" to="/installation">
|
||||
@ -49,7 +49,7 @@
|
||||
Framework-agnostic
|
||||
</h3>
|
||||
<p>
|
||||
Out of the box, tiptap works with plain JavaScript and Vue.js, but it’s also possible to use it in <g-link to="/installation/react">React</g-link>, <g-link to="/installation/svelte">Svelte</g-link> and others.
|
||||
Out of the box, tiptap works with Vanilla JavaScript and Vue.js, but it’s also possible to use it in <g-link to="/installation/react">React</g-link>, <g-link to="/installation/svelte">Svelte</g-link> and others.
|
||||
</p>
|
||||
<div>
|
||||
<btn type="tertiary" icon="arrow-right" to="/installation">
|
||||
|
4
docs/static/_redirects
vendored
4
docs/static/_redirects
vendored
@ -1,4 +1,4 @@
|
||||
/overview /
|
||||
/examples /examples/basic
|
||||
/guide /guide/get-started
|
||||
/examples /examples/default
|
||||
/guide /guide/configuration
|
||||
/api /api/concept
|
||||
|
@ -26,10 +26,10 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@atomico/rollup-plugin-sizes": "^1.1.4",
|
||||
"@babel/core": "^7.13.14",
|
||||
"@babel/core": "^7.13.15",
|
||||
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.13.0",
|
||||
"@babel/plugin-proposal-optional-chaining": "^7.13.12",
|
||||
"@babel/preset-env": "^7.13.12",
|
||||
"@babel/preset-env": "^7.13.15",
|
||||
"@babel/preset-react": "^7.13.13",
|
||||
"@lerna/batch-packages": "^3.16.0",
|
||||
"@lerna/filter-packages": "^3.18.0",
|
||||
@ -54,7 +54,7 @@
|
||||
"rollup-plugin-terser": "^7.0.2",
|
||||
"rollup-plugin-typescript2": "^0.30.0",
|
||||
"rollup-plugin-vue": "5",
|
||||
"typescript": "^4.2.2",
|
||||
"typescript": "^4.2.4",
|
||||
"vue": "^2.6.12"
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,47 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [2.0.0-beta.31](https://github.com/ueberdosis/tiptap-next/compare/@tiptap/core@2.0.0-beta.30...@tiptap/core@2.0.0-beta.31) (2021-04-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* fix type for emitUpdate, fix [#276](https://github.com/ueberdosis/tiptap-next/issues/276) ([4137e00](https://github.com/ueberdosis/tiptap-next/commit/4137e00d987c152b883022525056df94ad033be7))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [2.0.0-beta.30](https://github.com/ueberdosis/tiptap-next/compare/@tiptap/core@2.0.0-beta.29...@tiptap/core@2.0.0-beta.30) (2021-04-15)
|
||||
|
||||
**Note:** Version bump only for package @tiptap/core
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [2.0.0-beta.29](https://github.com/ueberdosis/tiptap-next/compare/@tiptap/core@2.0.0-beta.28...@tiptap/core@2.0.0-beta.29) (2021-04-12)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* fix adding mark only with insertContent(), fix [#264](https://github.com/ueberdosis/tiptap-next/issues/264) ([0a63123](https://github.com/ueberdosis/tiptap-next/commit/0a6312382f38af5b8fdf7f94fc4b6c1de1a15e25))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [2.0.0-beta.28](https://github.com/ueberdosis/tiptap-next/compare/@tiptap/core@2.0.0-beta.27...@tiptap/core@2.0.0-beta.28) (2021-04-12)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add parentConfig to extension context for more extendable extensions, fix [#259](https://github.com/ueberdosis/tiptap-next/issues/259) ([5e1ec5d](https://github.com/ueberdosis/tiptap-next/commit/5e1ec5d2a66be164f505d631f97861ab9344ba96))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [2.0.0-beta.27](https://github.com/ueberdosis/tiptap-next/compare/@tiptap/core@2.0.0-beta.26...@tiptap/core@2.0.0-beta.27) (2021-04-09)
|
||||
|
||||
|
||||
|
@ -11,4 +11,4 @@ tiptap is a headless wrapper around [ProseMirror](https://ProseMirror.net) – a
|
||||
Documentation can be found on the [tiptap website](https://tiptap.dev).
|
||||
|
||||
## License
|
||||
tiptap is open-sourced software licensed under the [MIT license](https://github.com/ueberdosis/tiptap-next/blob/main/LICENSE.md).
|
||||
tiptap is open sourced software licensed under the [MIT license](https://github.com/ueberdosis/tiptap-next/blob/main/LICENSE.md).
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@tiptap/core",
|
||||
"description": "headless rich text editor",
|
||||
"version": "2.0.0-beta.27",
|
||||
"version": "2.0.0-beta.31",
|
||||
"homepage": "https://tiptap.dev",
|
||||
"keywords": [
|
||||
"tiptap",
|
||||
|
@ -3,8 +3,9 @@ import { Command as ProseMirrorCommand } from 'prosemirror-commands'
|
||||
import { InputRule } from 'prosemirror-inputrules'
|
||||
import { Editor } from './Editor'
|
||||
import { Node } from './Node'
|
||||
import { Mark } from './Mark'
|
||||
import mergeDeep from './utilities/mergeDeep'
|
||||
import { GlobalAttributes, RawCommands } from './types'
|
||||
import { GlobalAttributes, RawCommands, ParentConfig } from './types'
|
||||
import { ExtensionConfig } from '.'
|
||||
|
||||
declare module '@tiptap/core' {
|
||||
@ -31,6 +32,7 @@ declare module '@tiptap/core' {
|
||||
*/
|
||||
addGlobalAttributes?: (this: {
|
||||
options: Options,
|
||||
parent: ParentConfig<ExtensionConfig<Options>>['addGlobalAttributes'],
|
||||
}) => GlobalAttributes | {},
|
||||
|
||||
/**
|
||||
@ -39,6 +41,7 @@ declare module '@tiptap/core' {
|
||||
addCommands?: (this: {
|
||||
options: Options,
|
||||
editor: Editor,
|
||||
parent: ParentConfig<ExtensionConfig<Options>>['addCommands'],
|
||||
}) => Partial<RawCommands>,
|
||||
|
||||
/**
|
||||
@ -47,6 +50,7 @@ declare module '@tiptap/core' {
|
||||
addKeyboardShortcuts?: (this: {
|
||||
options: Options,
|
||||
editor: Editor,
|
||||
parent: ParentConfig<ExtensionConfig<Options>>['addKeyboardShortcuts'],
|
||||
}) => {
|
||||
[key: string]: ProseMirrorCommand,
|
||||
},
|
||||
@ -57,6 +61,7 @@ declare module '@tiptap/core' {
|
||||
addInputRules?: (this: {
|
||||
options: Options,
|
||||
editor: Editor,
|
||||
parent: ParentConfig<ExtensionConfig<Options>>['addInputRules'],
|
||||
}) => InputRule[],
|
||||
|
||||
/**
|
||||
@ -65,6 +70,7 @@ declare module '@tiptap/core' {
|
||||
addPasteRules?: (this: {
|
||||
options: Options,
|
||||
editor: Editor,
|
||||
parent: ParentConfig<ExtensionConfig<Options>>['addPasteRules'],
|
||||
}) => Plugin[],
|
||||
|
||||
/**
|
||||
@ -73,6 +79,7 @@ declare module '@tiptap/core' {
|
||||
addProseMirrorPlugins?: (this: {
|
||||
options: Options,
|
||||
editor: Editor,
|
||||
parent: ParentConfig<ExtensionConfig<Options>>['addProseMirrorPlugins'],
|
||||
}) => Plugin[],
|
||||
|
||||
/**
|
||||
@ -81,6 +88,7 @@ declare module '@tiptap/core' {
|
||||
extendNodeSchema?: ((
|
||||
this: {
|
||||
options: Options,
|
||||
parent: ParentConfig<ExtensionConfig<Options>>['extendNodeSchema'],
|
||||
},
|
||||
extension: Node,
|
||||
) => {
|
||||
@ -93,8 +101,9 @@ declare module '@tiptap/core' {
|
||||
extendMarkSchema?: ((
|
||||
this: {
|
||||
options: Options,
|
||||
parent: ParentConfig<ExtensionConfig<Options>>['extendMarkSchema'],
|
||||
},
|
||||
extension: Node,
|
||||
extension: Mark,
|
||||
) => {
|
||||
[key: string]: any,
|
||||
}) | null,
|
||||
@ -105,6 +114,7 @@ declare module '@tiptap/core' {
|
||||
onBeforeCreate?: ((this: {
|
||||
options: Options,
|
||||
editor: Editor,
|
||||
parent: ParentConfig<ExtensionConfig<Options>>['onBeforeCreate'],
|
||||
}) => void) | null,
|
||||
|
||||
/**
|
||||
@ -113,6 +123,7 @@ declare module '@tiptap/core' {
|
||||
onCreate?: ((this: {
|
||||
options: Options,
|
||||
editor: Editor,
|
||||
parent: ParentConfig<ExtensionConfig<Options>>['onCreate'],
|
||||
}) => void) | null,
|
||||
|
||||
/**
|
||||
@ -121,6 +132,7 @@ declare module '@tiptap/core' {
|
||||
onUpdate?: ((this: {
|
||||
options: Options,
|
||||
editor: Editor,
|
||||
parent: ParentConfig<ExtensionConfig<Options>>['onUpdate'],
|
||||
}) => void) | null,
|
||||
|
||||
/**
|
||||
@ -129,6 +141,7 @@ declare module '@tiptap/core' {
|
||||
onSelectionUpdate?: ((this: {
|
||||
options: Options,
|
||||
editor: Editor,
|
||||
parent: ParentConfig<ExtensionConfig<Options>>['onSelectionUpdate'],
|
||||
}) => void) | null,
|
||||
|
||||
/**
|
||||
@ -138,6 +151,7 @@ declare module '@tiptap/core' {
|
||||
this: {
|
||||
options: Options,
|
||||
editor: Editor,
|
||||
parent: ParentConfig<ExtensionConfig<Options>>['onTransaction'],
|
||||
},
|
||||
props: {
|
||||
transaction: Transaction,
|
||||
@ -151,6 +165,7 @@ declare module '@tiptap/core' {
|
||||
this: {
|
||||
options: Options,
|
||||
editor: Editor,
|
||||
parent: ParentConfig<ExtensionConfig<Options>>['onFocus'],
|
||||
},
|
||||
props: {
|
||||
event: FocusEvent,
|
||||
@ -164,6 +179,7 @@ declare module '@tiptap/core' {
|
||||
this: {
|
||||
options: Options,
|
||||
editor: Editor,
|
||||
parent: ParentConfig<ExtensionConfig<Options>>['onBlur'],
|
||||
},
|
||||
props: {
|
||||
event: FocusEvent,
|
||||
@ -176,6 +192,7 @@ declare module '@tiptap/core' {
|
||||
onDestroy?: ((this: {
|
||||
options: Options,
|
||||
editor: Editor,
|
||||
parent: ParentConfig<ExtensionConfig<Options>>['onDestroy'],
|
||||
}) => void) | null,
|
||||
}
|
||||
}
|
||||
@ -183,43 +200,56 @@ declare module '@tiptap/core' {
|
||||
export class Extension<Options = any> {
|
||||
type = 'extension'
|
||||
|
||||
name = 'extension'
|
||||
|
||||
parent: Extension | null = null
|
||||
|
||||
child: Extension | null = null
|
||||
|
||||
options: Options
|
||||
|
||||
config: ExtensionConfig = {
|
||||
name: 'extension',
|
||||
name: this.name,
|
||||
priority: 100,
|
||||
defaultOptions: {},
|
||||
}
|
||||
|
||||
options!: Options
|
||||
|
||||
constructor(config: ExtensionConfig<Options>) {
|
||||
constructor(config: Partial<ExtensionConfig<Options>> = {}) {
|
||||
this.config = {
|
||||
...this.config,
|
||||
...config,
|
||||
}
|
||||
|
||||
this.name = this.config.name
|
||||
this.options = this.config.defaultOptions
|
||||
}
|
||||
|
||||
static create<O>(config: ExtensionConfig<O>) {
|
||||
static create<O>(config: Partial<ExtensionConfig<O>> = {}) {
|
||||
return new Extension<O>(config)
|
||||
}
|
||||
|
||||
configure(options: Partial<Options> = {}) {
|
||||
return Extension
|
||||
.create<Options>(this.config as ExtensionConfig<Options>)
|
||||
.#configure(options)
|
||||
}
|
||||
|
||||
#configure = (options: Partial<Options>) => {
|
||||
this.options = mergeDeep(this.config.defaultOptions, options) as Options
|
||||
this.options = mergeDeep(this.options, options) as Options
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
extend<ExtendedOptions = Options>(extendedConfig: Partial<ExtensionConfig<ExtendedOptions>>) {
|
||||
return new Extension<ExtendedOptions>({
|
||||
...this.config,
|
||||
...extendedConfig,
|
||||
} as ExtensionConfig<ExtendedOptions>)
|
||||
extend<ExtendedOptions = Options>(extendedConfig: Partial<ExtensionConfig<ExtendedOptions>> = {}) {
|
||||
const extension = new Extension<ExtendedOptions>(extendedConfig)
|
||||
|
||||
extension.parent = this
|
||||
|
||||
this.child = extension
|
||||
|
||||
extension.name = extendedConfig.name
|
||||
? extendedConfig.name
|
||||
: this.name
|
||||
|
||||
extension.options = {
|
||||
...extension.parent.options,
|
||||
...extension.options,
|
||||
}
|
||||
|
||||
return extension
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,8 @@ import { inputRules as inputRulesPlugin } from 'prosemirror-inputrules'
|
||||
import { EditorView, Decoration } from 'prosemirror-view'
|
||||
import { Plugin } from 'prosemirror-state'
|
||||
import { Editor } from './Editor'
|
||||
import { Extensions, NodeViewRenderer, RawCommands } from './types'
|
||||
import { Extensions, RawCommands, AnyConfig } from './types'
|
||||
import getExtensionField from './helpers/getExtensionField'
|
||||
import getSchema from './helpers/getSchema'
|
||||
import getSchemaTypeByName from './helpers/getSchemaTypeByName'
|
||||
import getNodeType from './helpers/getNodeType'
|
||||
@ -12,6 +13,7 @@ import splitExtensions from './helpers/splitExtensions'
|
||||
import getAttributesFromExtensions from './helpers/getAttributesFromExtensions'
|
||||
import getRenderedAttributes from './helpers/getRenderedAttributes'
|
||||
import callOrReturn from './utilities/callOrReturn'
|
||||
import { NodeConfig } from '.'
|
||||
|
||||
export default class ExtensionManager {
|
||||
|
||||
@ -32,47 +34,95 @@ export default class ExtensionManager {
|
||||
const context = {
|
||||
options: extension.options,
|
||||
editor: this.editor,
|
||||
type: getSchemaTypeByName(extension.config.name, this.schema),
|
||||
type: getSchemaTypeByName(extension.name, this.schema),
|
||||
}
|
||||
|
||||
if (extension.type === 'mark') {
|
||||
const keepOnSplit = callOrReturn(extension.config.keepOnSplit, context) ?? true
|
||||
const keepOnSplit = callOrReturn(getExtensionField(extension, 'keepOnSplit', context)) ?? true
|
||||
|
||||
if (keepOnSplit) {
|
||||
this.splittableMarks.push(extension.config.name)
|
||||
this.splittableMarks.push(extension.name)
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof extension.config.onBeforeCreate === 'function') {
|
||||
this.editor.on('beforeCreate', extension.config.onBeforeCreate.bind(context))
|
||||
const onBeforeCreate = getExtensionField<AnyConfig['onBeforeCreate']>(
|
||||
extension,
|
||||
'onBeforeCreate',
|
||||
context,
|
||||
)
|
||||
|
||||
if (onBeforeCreate) {
|
||||
this.editor.on('beforeCreate', onBeforeCreate)
|
||||
}
|
||||
|
||||
if (typeof extension.config.onCreate === 'function') {
|
||||
this.editor.on('create', extension.config.onCreate.bind(context))
|
||||
const onCreate = getExtensionField<AnyConfig['onCreate']>(
|
||||
extension,
|
||||
'onCreate',
|
||||
context,
|
||||
)
|
||||
|
||||
if (onCreate) {
|
||||
this.editor.on('create', onCreate)
|
||||
}
|
||||
|
||||
if (typeof extension.config.onUpdate === 'function') {
|
||||
this.editor.on('update', extension.config.onUpdate.bind(context))
|
||||
const onUpdate = getExtensionField<AnyConfig['onUpdate']>(
|
||||
extension,
|
||||
'onUpdate',
|
||||
context,
|
||||
)
|
||||
|
||||
if (onUpdate) {
|
||||
this.editor.on('update', onUpdate)
|
||||
}
|
||||
|
||||
if (typeof extension.config.onSelectionUpdate === 'function') {
|
||||
this.editor.on('selectionUpdate', extension.config.onSelectionUpdate.bind(context))
|
||||
const onSelectionUpdate = getExtensionField<AnyConfig['onSelectionUpdate']>(
|
||||
extension,
|
||||
'onSelectionUpdate',
|
||||
context,
|
||||
)
|
||||
|
||||
if (onSelectionUpdate) {
|
||||
this.editor.on('selectionUpdate', onSelectionUpdate)
|
||||
}
|
||||
|
||||
if (typeof extension.config.onTransaction === 'function') {
|
||||
this.editor.on('transaction', extension.config.onTransaction.bind(context))
|
||||
const onTransaction = getExtensionField<AnyConfig['onTransaction']>(
|
||||
extension,
|
||||
'onTransaction',
|
||||
context,
|
||||
)
|
||||
|
||||
if (onTransaction) {
|
||||
this.editor.on('transaction', onTransaction)
|
||||
}
|
||||
|
||||
if (typeof extension.config.onFocus === 'function') {
|
||||
this.editor.on('focus', extension.config.onFocus.bind(context))
|
||||
const onFocus = getExtensionField<AnyConfig['onFocus']>(
|
||||
extension,
|
||||
'onFocus',
|
||||
context,
|
||||
)
|
||||
|
||||
if (onFocus) {
|
||||
this.editor.on('focus', onFocus)
|
||||
}
|
||||
|
||||
if (typeof extension.config.onBlur === 'function') {
|
||||
this.editor.on('blur', extension.config.onBlur.bind(context))
|
||||
const onBlur = getExtensionField<AnyConfig['onBlur']>(
|
||||
extension,
|
||||
'onBlur',
|
||||
context,
|
||||
)
|
||||
|
||||
if (onBlur) {
|
||||
this.editor.on('blur', onBlur)
|
||||
}
|
||||
|
||||
if (typeof extension.config.onDestroy === 'function') {
|
||||
this.editor.on('destroy', extension.config.onDestroy.bind(context))
|
||||
const onDestroy = getExtensionField<AnyConfig['onDestroy']>(
|
||||
extension,
|
||||
'onDestroy',
|
||||
context,
|
||||
)
|
||||
|
||||
if (onDestroy) {
|
||||
this.editor.on('destroy', onDestroy)
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -81,11 +131,14 @@ export default class ExtensionManager {
|
||||
const defaultPriority = 100
|
||||
|
||||
return extensions.sort((a, b) => {
|
||||
if ((a.config.priority || defaultPriority) > (b.config.priority || defaultPriority)) {
|
||||
const priorityA = getExtensionField<AnyConfig['priority']>(a, 'priority') || defaultPriority
|
||||
const priorityB = getExtensionField<AnyConfig['priority']>(b, 'priority') || defaultPriority
|
||||
|
||||
if (priorityA > priorityB) {
|
||||
return -1
|
||||
}
|
||||
|
||||
if ((a.config.priority || defaultPriority) < (b.config.priority || defaultPriority)) {
|
||||
if (priorityA < priorityB) {
|
||||
return 1
|
||||
}
|
||||
|
||||
@ -98,16 +151,22 @@ export default class ExtensionManager {
|
||||
const context = {
|
||||
options: extension.options,
|
||||
editor: this.editor,
|
||||
type: getSchemaTypeByName(extension.config.name, this.schema),
|
||||
type: getSchemaTypeByName(extension.name, this.schema),
|
||||
}
|
||||
|
||||
if (!extension.config.addCommands) {
|
||||
const addCommands = getExtensionField<AnyConfig['addCommands']>(
|
||||
extension,
|
||||
'addCommands',
|
||||
context,
|
||||
)
|
||||
|
||||
if (!addCommands) {
|
||||
return commands
|
||||
}
|
||||
|
||||
return {
|
||||
...commands,
|
||||
...extension.config.addCommands.bind(context)(),
|
||||
...addCommands(),
|
||||
}
|
||||
}, {} as RawCommands)
|
||||
}
|
||||
@ -119,19 +178,31 @@ export default class ExtensionManager {
|
||||
const context = {
|
||||
options: extension.options,
|
||||
editor: this.editor,
|
||||
type: getSchemaTypeByName(extension.config.name, this.schema),
|
||||
type: getSchemaTypeByName(extension.name, this.schema),
|
||||
}
|
||||
|
||||
const plugins: Plugin[] = []
|
||||
|
||||
if (extension.config.addKeyboardShortcuts) {
|
||||
const keyMapPlugin = keymap(extension.config.addKeyboardShortcuts.bind(context)())
|
||||
const addKeyboardShortcuts = getExtensionField<AnyConfig['addKeyboardShortcuts']>(
|
||||
extension,
|
||||
'addKeyboardShortcuts',
|
||||
context,
|
||||
)
|
||||
|
||||
if (addKeyboardShortcuts) {
|
||||
const keyMapPlugin = keymap(addKeyboardShortcuts())
|
||||
|
||||
plugins.push(keyMapPlugin)
|
||||
}
|
||||
|
||||
if (this.editor.options.enableInputRules && extension.config.addInputRules) {
|
||||
const inputRules = extension.config.addInputRules.bind(context)()
|
||||
const addInputRules = getExtensionField<AnyConfig['addInputRules']>(
|
||||
extension,
|
||||
'addInputRules',
|
||||
context,
|
||||
)
|
||||
|
||||
if (this.editor.options.enableInputRules && addInputRules) {
|
||||
const inputRules = addInputRules()
|
||||
const inputRulePlugins = inputRules.length
|
||||
? [inputRulesPlugin({ rules: inputRules })]
|
||||
: []
|
||||
@ -139,14 +210,26 @@ export default class ExtensionManager {
|
||||
plugins.push(...inputRulePlugins)
|
||||
}
|
||||
|
||||
if (this.editor.options.enablePasteRules && extension.config.addPasteRules) {
|
||||
const pasteRulePlugins = extension.config.addPasteRules.bind(context)()
|
||||
const addPasteRules = getExtensionField<AnyConfig['addPasteRules']>(
|
||||
extension,
|
||||
'addPasteRules',
|
||||
context,
|
||||
)
|
||||
|
||||
if (this.editor.options.enablePasteRules && addPasteRules) {
|
||||
const pasteRulePlugins = addPasteRules()
|
||||
|
||||
plugins.push(...pasteRulePlugins)
|
||||
}
|
||||
|
||||
if (extension.config.addProseMirrorPlugins) {
|
||||
const proseMirrorPlugins = extension.config.addProseMirrorPlugins.bind(context)()
|
||||
const addProseMirrorPlugins = getExtensionField<AnyConfig['addProseMirrorPlugins']>(
|
||||
extension,
|
||||
'addProseMirrorPlugins',
|
||||
context,
|
||||
)
|
||||
|
||||
if (addProseMirrorPlugins) {
|
||||
const proseMirrorPlugins = addProseMirrorPlugins()
|
||||
|
||||
plugins.push(...proseMirrorPlugins)
|
||||
}
|
||||
@ -165,15 +248,23 @@ export default class ExtensionManager {
|
||||
const { nodeExtensions } = splitExtensions(this.extensions)
|
||||
|
||||
return Object.fromEntries(nodeExtensions
|
||||
.filter(extension => !!extension.config.addNodeView)
|
||||
.filter(extension => !!getExtensionField(extension, 'addNodeView'))
|
||||
.map(extension => {
|
||||
const extensionAttributes = this.attributes.filter(attribute => attribute.type === extension.config.name)
|
||||
const extensionAttributes = this.attributes.filter(attribute => attribute.type === extension.name)
|
||||
const context = {
|
||||
options: extension.options,
|
||||
editor,
|
||||
type: getNodeType(extension.config.name, this.schema),
|
||||
type: getNodeType(extension.name, this.schema),
|
||||
}
|
||||
const addNodeView = getExtensionField<NodeConfig['addNodeView']>(
|
||||
extension,
|
||||
'addNodeView',
|
||||
context,
|
||||
)
|
||||
|
||||
if (!addNodeView) {
|
||||
return []
|
||||
}
|
||||
const renderer = extension.config.addNodeView?.call(context) as NodeViewRenderer
|
||||
|
||||
const nodeview = (
|
||||
node: ProsemirrorNode,
|
||||
@ -183,7 +274,7 @@ export default class ExtensionManager {
|
||||
) => {
|
||||
const HTMLAttributes = getRenderedAttributes(node, extensionAttributes)
|
||||
|
||||
return renderer({
|
||||
return addNodeView()({
|
||||
editor,
|
||||
node,
|
||||
getPos,
|
||||
@ -193,7 +284,7 @@ export default class ExtensionManager {
|
||||
})
|
||||
}
|
||||
|
||||
return [extension.config.name, nodeview]
|
||||
return [extension.name, nodeview]
|
||||
}))
|
||||
}
|
||||
|
||||
@ -202,17 +293,23 @@ export default class ExtensionManager {
|
||||
const { nodeExtensions } = splitExtensions(this.extensions)
|
||||
|
||||
return Object.fromEntries(nodeExtensions
|
||||
.filter(extension => !!extension.config.renderText)
|
||||
.filter(extension => !!getExtensionField(extension, 'renderText'))
|
||||
.map(extension => {
|
||||
const context = {
|
||||
options: extension.options,
|
||||
editor,
|
||||
type: getNodeType(extension.config.name, this.schema),
|
||||
type: getNodeType(extension.name, this.schema),
|
||||
}
|
||||
|
||||
const textSerializer = (props: { node: ProsemirrorNode }) => extension.config.renderText?.call(context, props)
|
||||
const renderText = getExtensionField<NodeConfig['renderText']>(extension, 'renderText', context)
|
||||
|
||||
return [extension.config.name, textSerializer]
|
||||
if (!renderText) {
|
||||
return []
|
||||
}
|
||||
|
||||
const textSerializer = (props: { node: ProsemirrorNode }) => renderText(props)
|
||||
|
||||
return [extension.name, textSerializer]
|
||||
}))
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,13 @@ import { Plugin, Transaction } from 'prosemirror-state'
|
||||
import { Command as ProseMirrorCommand } from 'prosemirror-commands'
|
||||
import { InputRule } from 'prosemirror-inputrules'
|
||||
import mergeDeep from './utilities/mergeDeep'
|
||||
import { Attributes, RawCommands, GlobalAttributes } from './types'
|
||||
import {
|
||||
Attributes,
|
||||
RawCommands,
|
||||
GlobalAttributes,
|
||||
ParentConfig,
|
||||
} from './types'
|
||||
import { Node } from './Node'
|
||||
import { MarkConfig } from '.'
|
||||
import { Editor } from './Editor'
|
||||
|
||||
@ -36,6 +42,7 @@ declare module '@tiptap/core' {
|
||||
*/
|
||||
addGlobalAttributes?: (this: {
|
||||
options: Options,
|
||||
parent: ParentConfig<MarkConfig<Options>>['addGlobalAttributes'],
|
||||
}) => GlobalAttributes | {},
|
||||
|
||||
/**
|
||||
@ -45,6 +52,7 @@ declare module '@tiptap/core' {
|
||||
options: Options,
|
||||
editor: Editor,
|
||||
type: MarkType,
|
||||
parent: ParentConfig<MarkConfig<Options>>['addCommands'],
|
||||
}) => Partial<RawCommands>,
|
||||
|
||||
/**
|
||||
@ -54,6 +62,7 @@ declare module '@tiptap/core' {
|
||||
options: Options,
|
||||
editor: Editor,
|
||||
type: MarkType,
|
||||
parent: ParentConfig<MarkConfig<Options>>['addKeyboardShortcuts'],
|
||||
}) => {
|
||||
[key: string]: ProseMirrorCommand,
|
||||
},
|
||||
@ -65,6 +74,7 @@ declare module '@tiptap/core' {
|
||||
options: Options,
|
||||
editor: Editor,
|
||||
type: MarkType,
|
||||
parent: ParentConfig<MarkConfig<Options>>['addInputRules'],
|
||||
}) => InputRule[],
|
||||
|
||||
/**
|
||||
@ -74,6 +84,7 @@ declare module '@tiptap/core' {
|
||||
options: Options,
|
||||
editor: Editor,
|
||||
type: MarkType,
|
||||
parent: ParentConfig<MarkConfig<Options>>['addPasteRules'],
|
||||
}) => Plugin[],
|
||||
|
||||
/**
|
||||
@ -83,6 +94,7 @@ declare module '@tiptap/core' {
|
||||
options: Options,
|
||||
editor: Editor,
|
||||
type: MarkType,
|
||||
parent: ParentConfig<MarkConfig<Options>>['addProseMirrorPlugins'],
|
||||
}) => Plugin[],
|
||||
|
||||
/**
|
||||
@ -91,6 +103,7 @@ declare module '@tiptap/core' {
|
||||
extendNodeSchema?: ((
|
||||
this: {
|
||||
options: Options,
|
||||
parent: ParentConfig<MarkConfig<Options>>['extendNodeSchema'],
|
||||
},
|
||||
extension: Node,
|
||||
) => {
|
||||
@ -103,8 +116,9 @@ declare module '@tiptap/core' {
|
||||
extendMarkSchema?: ((
|
||||
this: {
|
||||
options: Options,
|
||||
parent: ParentConfig<MarkConfig<Options>>['extendMarkSchema'],
|
||||
},
|
||||
extension: Node,
|
||||
extension: Mark,
|
||||
) => {
|
||||
[key: string]: any,
|
||||
}) | null,
|
||||
@ -112,10 +126,11 @@ declare module '@tiptap/core' {
|
||||
/**
|
||||
* The editor is not ready yet.
|
||||
*/
|
||||
onBeforeCreate?: ((this: {
|
||||
onBeforeCreate?: ((this: {
|
||||
options: Options,
|
||||
editor: Editor,
|
||||
type: MarkType,
|
||||
parent: ParentConfig<MarkConfig<Options>>['onBeforeCreate'],
|
||||
}) => void) | null,
|
||||
|
||||
/**
|
||||
@ -125,6 +140,7 @@ declare module '@tiptap/core' {
|
||||
options: Options,
|
||||
editor: Editor,
|
||||
type: MarkType,
|
||||
parent: ParentConfig<MarkConfig<Options>>['onCreate'],
|
||||
}) => void) | null,
|
||||
|
||||
/**
|
||||
@ -134,6 +150,7 @@ declare module '@tiptap/core' {
|
||||
options: Options,
|
||||
editor: Editor,
|
||||
type: MarkType,
|
||||
parent: ParentConfig<MarkConfig<Options>>['onUpdate'],
|
||||
}) => void) | null,
|
||||
|
||||
/**
|
||||
@ -143,6 +160,7 @@ declare module '@tiptap/core' {
|
||||
options: Options,
|
||||
editor: Editor,
|
||||
type: MarkType,
|
||||
parent: ParentConfig<MarkConfig<Options>>['onSelectionUpdate'],
|
||||
}) => void) | null,
|
||||
|
||||
/**
|
||||
@ -153,6 +171,7 @@ declare module '@tiptap/core' {
|
||||
options: Options,
|
||||
editor: Editor,
|
||||
type: MarkType,
|
||||
parent: ParentConfig<MarkConfig<Options>>['onTransaction'],
|
||||
},
|
||||
props: {
|
||||
transaction: Transaction,
|
||||
@ -167,6 +186,7 @@ declare module '@tiptap/core' {
|
||||
options: Options,
|
||||
editor: Editor,
|
||||
type: MarkType,
|
||||
parent: ParentConfig<MarkConfig<Options>>['onFocus'],
|
||||
},
|
||||
props: {
|
||||
event: FocusEvent,
|
||||
@ -181,6 +201,7 @@ declare module '@tiptap/core' {
|
||||
options: Options,
|
||||
editor: Editor,
|
||||
type: MarkType,
|
||||
parent: ParentConfig<MarkConfig<Options>>['onBlur'],
|
||||
},
|
||||
props: {
|
||||
event: FocusEvent,
|
||||
@ -194,6 +215,7 @@ declare module '@tiptap/core' {
|
||||
options: Options,
|
||||
editor: Editor,
|
||||
type: MarkType,
|
||||
parent: ParentConfig<MarkConfig<Options>>['onDestroy'],
|
||||
}) => void) | null,
|
||||
|
||||
/**
|
||||
@ -204,22 +226,34 @@ declare module '@tiptap/core' {
|
||||
/**
|
||||
* Inclusive
|
||||
*/
|
||||
inclusive?: MarkSpec['inclusive'] | ((this: { options: Options }) => MarkSpec['inclusive']),
|
||||
inclusive?: MarkSpec['inclusive'] | ((this: {
|
||||
options: Options,
|
||||
parent: ParentConfig<MarkConfig<Options>>['inclusive'],
|
||||
}) => MarkSpec['inclusive']),
|
||||
|
||||
/**
|
||||
* Excludes
|
||||
*/
|
||||
excludes?: MarkSpec['excludes'] | ((this: { options: Options }) => MarkSpec['excludes']),
|
||||
excludes?: MarkSpec['excludes'] | ((this: {
|
||||
options: Options,
|
||||
parent: ParentConfig<MarkConfig<Options>>['excludes'],
|
||||
}) => MarkSpec['excludes']),
|
||||
|
||||
/**
|
||||
* Group
|
||||
*/
|
||||
group?: MarkSpec['group'] | ((this: { options: Options }) => MarkSpec['group']),
|
||||
group?: MarkSpec['group'] | ((this: {
|
||||
options: Options,
|
||||
parent: ParentConfig<MarkConfig<Options>>['group'],
|
||||
}) => MarkSpec['group']),
|
||||
|
||||
/**
|
||||
* Spanning
|
||||
*/
|
||||
spanning?: MarkSpec['spanning'] | ((this: { options: Options }) => MarkSpec['spanning']),
|
||||
spanning?: MarkSpec['spanning'] | ((this: {
|
||||
options: Options,
|
||||
parent: ParentConfig<MarkConfig<Options>>['spanning'],
|
||||
}) => MarkSpec['spanning']),
|
||||
|
||||
/**
|
||||
* Parse HTML
|
||||
@ -227,6 +261,7 @@ declare module '@tiptap/core' {
|
||||
parseHTML?: (
|
||||
this: {
|
||||
options: Options,
|
||||
parent: ParentConfig<MarkConfig<Options>>['parseHTML'],
|
||||
},
|
||||
) => MarkSpec['parseDOM'],
|
||||
|
||||
@ -236,11 +271,12 @@ declare module '@tiptap/core' {
|
||||
renderHTML?: ((
|
||||
this: {
|
||||
options: Options,
|
||||
parent: ParentConfig<MarkConfig<Options>>['renderHTML'],
|
||||
},
|
||||
props: {
|
||||
mark: ProseMirrorMark,
|
||||
HTMLAttributes: { [key: string]: any },
|
||||
}
|
||||
},
|
||||
) => DOMOutputSpec) | null,
|
||||
|
||||
/**
|
||||
@ -249,6 +285,7 @@ declare module '@tiptap/core' {
|
||||
addAttributes?: (
|
||||
this: {
|
||||
options: Options,
|
||||
parent: ParentConfig<MarkConfig<Options>>['addAttributes'],
|
||||
},
|
||||
) => Attributes | {},
|
||||
}
|
||||
@ -257,43 +294,56 @@ declare module '@tiptap/core' {
|
||||
export class Mark<Options = any> {
|
||||
type = 'mark'
|
||||
|
||||
name = 'mark'
|
||||
|
||||
parent: Mark | null = null
|
||||
|
||||
child: Mark | null = null
|
||||
|
||||
options: Options
|
||||
|
||||
config: MarkConfig = {
|
||||
name: 'mark',
|
||||
name: this.name,
|
||||
priority: 100,
|
||||
defaultOptions: {},
|
||||
}
|
||||
|
||||
options!: Options
|
||||
|
||||
constructor(config: MarkConfig<Options>) {
|
||||
constructor(config: Partial<MarkConfig<Options>> = {}) {
|
||||
this.config = {
|
||||
...this.config,
|
||||
...config,
|
||||
}
|
||||
|
||||
this.name = this.config.name
|
||||
this.options = this.config.defaultOptions
|
||||
}
|
||||
|
||||
static create<O>(config: MarkConfig<O>) {
|
||||
static create<O>(config: Partial<MarkConfig<O>> = {}) {
|
||||
return new Mark<O>(config)
|
||||
}
|
||||
|
||||
configure(options: Partial<Options> = {}) {
|
||||
return Mark
|
||||
.create<Options>(this.config as MarkConfig<Options>)
|
||||
.#configure(options)
|
||||
}
|
||||
|
||||
#configure = (options: Partial<Options>) => {
|
||||
this.options = mergeDeep(this.config.defaultOptions, options) as Options
|
||||
this.options = mergeDeep(this.options, options) as Options
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
extend<ExtendedOptions = Options>(extendedConfig: Partial<MarkConfig<ExtendedOptions>>) {
|
||||
return new Mark<ExtendedOptions>({
|
||||
...this.config,
|
||||
...extendedConfig,
|
||||
} as MarkConfig<ExtendedOptions>)
|
||||
extend<ExtendedOptions = Options>(extendedConfig: Partial<MarkConfig<ExtendedOptions>> = {}) {
|
||||
const extension = new Mark<ExtendedOptions>(extendedConfig)
|
||||
|
||||
extension.parent = this
|
||||
|
||||
this.child = extension
|
||||
|
||||
extension.name = extendedConfig.name
|
||||
? extendedConfig.name
|
||||
: this.name
|
||||
|
||||
extension.options = {
|
||||
...extension.parent.options,
|
||||
...extension.options,
|
||||
}
|
||||
|
||||
return extension
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ import {
|
||||
NodeViewRenderer,
|
||||
GlobalAttributes,
|
||||
RawCommands,
|
||||
ParentConfig,
|
||||
} from './types'
|
||||
import { NodeConfig } from '.'
|
||||
import { Editor } from './Editor'
|
||||
@ -41,6 +42,7 @@ declare module '@tiptap/core' {
|
||||
*/
|
||||
addGlobalAttributes?: (this: {
|
||||
options: Options,
|
||||
parent: ParentConfig<NodeConfig<Options>>['addGlobalAttributes'],
|
||||
}) => GlobalAttributes | {},
|
||||
|
||||
/**
|
||||
@ -50,6 +52,7 @@ declare module '@tiptap/core' {
|
||||
options: Options,
|
||||
editor: Editor,
|
||||
type: NodeType,
|
||||
parent: ParentConfig<NodeConfig<Options>>['addCommands'],
|
||||
}) => Partial<RawCommands>,
|
||||
|
||||
/**
|
||||
@ -59,6 +62,7 @@ declare module '@tiptap/core' {
|
||||
options: Options,
|
||||
editor: Editor,
|
||||
type: NodeType,
|
||||
parent: ParentConfig<NodeConfig<Options>>['addKeyboardShortcuts'],
|
||||
}) => {
|
||||
[key: string]: ProseMirrorCommand,
|
||||
},
|
||||
@ -70,6 +74,7 @@ declare module '@tiptap/core' {
|
||||
options: Options,
|
||||
editor: Editor,
|
||||
type: NodeType,
|
||||
parent: ParentConfig<NodeConfig<Options>>['addInputRules'],
|
||||
}) => InputRule[],
|
||||
|
||||
/**
|
||||
@ -79,6 +84,7 @@ declare module '@tiptap/core' {
|
||||
options: Options,
|
||||
editor: Editor,
|
||||
type: NodeType,
|
||||
parent: ParentConfig<NodeConfig<Options>>['addPasteRules'],
|
||||
}) => Plugin[],
|
||||
|
||||
/**
|
||||
@ -88,6 +94,7 @@ declare module '@tiptap/core' {
|
||||
options: Options,
|
||||
editor: Editor,
|
||||
type: NodeType,
|
||||
parent: ParentConfig<NodeConfig<Options>>['addProseMirrorPlugins'],
|
||||
}) => Plugin[],
|
||||
|
||||
/**
|
||||
@ -96,6 +103,7 @@ declare module '@tiptap/core' {
|
||||
extendNodeSchema?: ((
|
||||
this: {
|
||||
options: Options,
|
||||
parent: ParentConfig<NodeConfig<Options>>['extendNodeSchema'],
|
||||
},
|
||||
extension: Node,
|
||||
) => {
|
||||
@ -108,6 +116,7 @@ declare module '@tiptap/core' {
|
||||
extendMarkSchema?: ((
|
||||
this: {
|
||||
options: Options,
|
||||
parent: ParentConfig<NodeConfig<Options>>['extendMarkSchema'],
|
||||
},
|
||||
extension: Node,
|
||||
) => {
|
||||
@ -121,6 +130,7 @@ declare module '@tiptap/core' {
|
||||
options: Options,
|
||||
editor: Editor,
|
||||
type: NodeType,
|
||||
parent: ParentConfig<NodeConfig<Options>>['onBeforeCreate'],
|
||||
}) => void) | null,
|
||||
|
||||
/**
|
||||
@ -130,6 +140,7 @@ declare module '@tiptap/core' {
|
||||
options: Options,
|
||||
editor: Editor,
|
||||
type: NodeType,
|
||||
parent: ParentConfig<NodeConfig<Options>>['onCreate'],
|
||||
}) => void) | null,
|
||||
|
||||
/**
|
||||
@ -139,6 +150,7 @@ declare module '@tiptap/core' {
|
||||
options: Options,
|
||||
editor: Editor,
|
||||
type: NodeType,
|
||||
parent: ParentConfig<NodeConfig<Options>>['onUpdate'],
|
||||
}) => void) | null,
|
||||
|
||||
/**
|
||||
@ -148,6 +160,7 @@ declare module '@tiptap/core' {
|
||||
options: Options,
|
||||
editor: Editor,
|
||||
type: NodeType,
|
||||
parent: ParentConfig<NodeConfig<Options>>['onSelectionUpdate'],
|
||||
}) => void) | null,
|
||||
|
||||
/**
|
||||
@ -158,6 +171,7 @@ declare module '@tiptap/core' {
|
||||
options: Options,
|
||||
editor: Editor,
|
||||
type: NodeType,
|
||||
parent: ParentConfig<NodeConfig<Options>>['onTransaction'],
|
||||
},
|
||||
props: {
|
||||
transaction: Transaction,
|
||||
@ -172,6 +186,7 @@ declare module '@tiptap/core' {
|
||||
options: Options,
|
||||
editor: Editor,
|
||||
type: NodeType,
|
||||
parent: ParentConfig<NodeConfig<Options>>['onFocus'],
|
||||
},
|
||||
props: {
|
||||
event: FocusEvent,
|
||||
@ -186,6 +201,7 @@ declare module '@tiptap/core' {
|
||||
options: Options,
|
||||
editor: Editor,
|
||||
type: NodeType,
|
||||
parent: ParentConfig<NodeConfig<Options>>['onBlur'],
|
||||
},
|
||||
props: {
|
||||
event: FocusEvent,
|
||||
@ -199,6 +215,7 @@ declare module '@tiptap/core' {
|
||||
options: Options,
|
||||
editor: Editor,
|
||||
type: NodeType,
|
||||
parent: ParentConfig<NodeConfig<Options>>['onDestroy'],
|
||||
}) => void) | null,
|
||||
|
||||
/**
|
||||
@ -208,6 +225,7 @@ declare module '@tiptap/core' {
|
||||
options: Options,
|
||||
editor: Editor,
|
||||
type: NodeType,
|
||||
parent: ParentConfig<NodeConfig<Options>>['addNodeView'],
|
||||
}) => NodeViewRenderer) | null,
|
||||
|
||||
/**
|
||||
@ -218,52 +236,82 @@ declare module '@tiptap/core' {
|
||||
/**
|
||||
* Content
|
||||
*/
|
||||
content?: NodeSpec['content'] | ((this: { options: Options }) => NodeSpec['content']),
|
||||
content?: NodeSpec['content'] | ((this: {
|
||||
options: Options,
|
||||
parent: ParentConfig<NodeConfig<Options>>['content'],
|
||||
}) => NodeSpec['content']),
|
||||
|
||||
/**
|
||||
* Marks
|
||||
*/
|
||||
marks?: NodeSpec['marks'] | ((this: { options: Options }) => NodeSpec['marks']),
|
||||
marks?: NodeSpec['marks'] | ((this: {
|
||||
options: Options,
|
||||
parent: ParentConfig<NodeConfig<Options>>['marks'],
|
||||
}) => NodeSpec['marks']),
|
||||
|
||||
/**
|
||||
* Group
|
||||
*/
|
||||
group?: NodeSpec['group'] | ((this: { options: Options }) => NodeSpec['group']),
|
||||
group?: NodeSpec['group'] | ((this: {
|
||||
options: Options,
|
||||
parent: ParentConfig<NodeConfig<Options>>['group'],
|
||||
}) => NodeSpec['group']),
|
||||
|
||||
/**
|
||||
* Inline
|
||||
*/
|
||||
inline?: NodeSpec['inline'] | ((this: { options: Options }) => NodeSpec['inline']),
|
||||
inline?: NodeSpec['inline'] | ((this: {
|
||||
options: Options,
|
||||
parent: ParentConfig<NodeConfig<Options>>['inline'],
|
||||
}) => NodeSpec['inline']),
|
||||
|
||||
/**
|
||||
* Atom
|
||||
*/
|
||||
atom?: NodeSpec['atom'] | ((this: { options: Options }) => NodeSpec['atom']),
|
||||
atom?: NodeSpec['atom'] | ((this: {
|
||||
options: Options,
|
||||
parent: ParentConfig<NodeConfig<Options>>['atom'],
|
||||
}) => NodeSpec['atom']),
|
||||
|
||||
/**
|
||||
* Selectable
|
||||
*/
|
||||
selectable?: NodeSpec['selectable'] | ((this: { options: Options }) => NodeSpec['selectable']),
|
||||
selectable?: NodeSpec['selectable'] | ((this: {
|
||||
options: Options,
|
||||
parent: ParentConfig<NodeConfig<Options>>['selectable'],
|
||||
}) => NodeSpec['selectable']),
|
||||
|
||||
/**
|
||||
* Draggable
|
||||
*/
|
||||
draggable?: NodeSpec['draggable'] | ((this: { options: Options }) => NodeSpec['draggable']),
|
||||
draggable?: NodeSpec['draggable'] | ((this: {
|
||||
options: Options,
|
||||
parent: ParentConfig<NodeConfig<Options>>['draggable'],
|
||||
}) => NodeSpec['draggable']),
|
||||
|
||||
/**
|
||||
* Code
|
||||
*/
|
||||
code?: NodeSpec['code'] | ((this: { options: Options }) => NodeSpec['code']),
|
||||
code?: NodeSpec['code'] | ((this: {
|
||||
options: Options,
|
||||
parent: ParentConfig<NodeConfig<Options>>['code'],
|
||||
}) => NodeSpec['code']),
|
||||
|
||||
/**
|
||||
* Defining
|
||||
*/
|
||||
defining?: NodeSpec['defining'] | ((this: { options: Options }) => NodeSpec['defining']),
|
||||
defining?: NodeSpec['defining'] | ((this: {
|
||||
options: Options,
|
||||
parent: ParentConfig<NodeConfig<Options>>['defining'],
|
||||
}) => NodeSpec['defining']),
|
||||
|
||||
/**
|
||||
* Isolating
|
||||
*/
|
||||
isolating?: NodeSpec['isolating'] | ((this: { options: Options }) => NodeSpec['isolating']),
|
||||
isolating?: NodeSpec['isolating'] | ((this: {
|
||||
options: Options,
|
||||
parent: ParentConfig<NodeConfig<Options>>['isolating'],
|
||||
}) => NodeSpec['isolating']),
|
||||
|
||||
/**
|
||||
* Parse HTML
|
||||
@ -271,6 +319,7 @@ declare module '@tiptap/core' {
|
||||
parseHTML?: (
|
||||
this: {
|
||||
options: Options,
|
||||
parent: ParentConfig<NodeConfig<Options>>['parseHTML'],
|
||||
},
|
||||
) => NodeSpec['parseDOM'],
|
||||
|
||||
@ -280,6 +329,7 @@ declare module '@tiptap/core' {
|
||||
renderHTML?: ((
|
||||
this: {
|
||||
options: Options,
|
||||
parent: ParentConfig<NodeConfig<Options>>['renderHTML'],
|
||||
},
|
||||
props: {
|
||||
node: ProseMirrorNode,
|
||||
@ -295,6 +345,7 @@ declare module '@tiptap/core' {
|
||||
options: Options,
|
||||
editor: Editor,
|
||||
type: NodeType,
|
||||
parent: ParentConfig<NodeConfig<Options>>['renderText'],
|
||||
},
|
||||
props: {
|
||||
node: ProseMirrorNode,
|
||||
@ -307,6 +358,7 @@ declare module '@tiptap/core' {
|
||||
addAttributes?: (
|
||||
this: {
|
||||
options: Options,
|
||||
parent: ParentConfig<NodeConfig<Options>>['addAttributes'],
|
||||
},
|
||||
) => Attributes | {},
|
||||
}
|
||||
@ -315,43 +367,56 @@ declare module '@tiptap/core' {
|
||||
export class Node<Options = any> {
|
||||
type = 'node'
|
||||
|
||||
name = 'node'
|
||||
|
||||
parent: Node | null = null
|
||||
|
||||
child: Node | null = null
|
||||
|
||||
options: Options
|
||||
|
||||
config: NodeConfig = {
|
||||
name: 'node',
|
||||
name: this.name,
|
||||
priority: 100,
|
||||
defaultOptions: {},
|
||||
}
|
||||
|
||||
options!: Options
|
||||
|
||||
constructor(config: NodeConfig<Options>) {
|
||||
constructor(config: Partial<NodeConfig<Options>> = {}) {
|
||||
this.config = {
|
||||
...this.config,
|
||||
...config,
|
||||
}
|
||||
|
||||
this.name = this.config.name
|
||||
this.options = this.config.defaultOptions
|
||||
}
|
||||
|
||||
static create<O>(config: NodeConfig<O>) {
|
||||
static create<O>(config: Partial<NodeConfig<O>> = {}) {
|
||||
return new Node<O>(config)
|
||||
}
|
||||
|
||||
configure(options: Partial<Options> = {}) {
|
||||
return Node
|
||||
.create<Options>(this.config as NodeConfig<Options>)
|
||||
.#configure(options)
|
||||
}
|
||||
|
||||
#configure = (options: Partial<Options>) => {
|
||||
this.options = mergeDeep(this.config.defaultOptions, options) as Options
|
||||
this.options = mergeDeep(this.options, options) as Options
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
extend<ExtendedOptions = Options>(extendedConfig: Partial<NodeConfig<ExtendedOptions>>) {
|
||||
return new Node<ExtendedOptions>({
|
||||
...this.config,
|
||||
...extendedConfig,
|
||||
} as NodeConfig<ExtendedOptions>)
|
||||
extend<ExtendedOptions = Options>(extendedConfig: Partial<NodeConfig<ExtendedOptions>> = {}) {
|
||||
const extension = new Node<ExtendedOptions>(extendedConfig)
|
||||
|
||||
extension.parent = this
|
||||
|
||||
this.child = extension
|
||||
|
||||
extension.name = extendedConfig.name
|
||||
? extendedConfig.name
|
||||
: this.name
|
||||
|
||||
extension.options = {
|
||||
...extension.parent.options,
|
||||
...extension.options,
|
||||
}
|
||||
|
||||
return extension
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ declare module '@tiptap/core' {
|
||||
/**
|
||||
* Clear the whole document.
|
||||
*/
|
||||
clearContent: (emitUpdate: Boolean) => Command,
|
||||
clearContent: (emitUpdate?: Boolean) => Command,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,14 @@
|
||||
import splitExtensions from './splitExtensions'
|
||||
import getExtensionField from './getExtensionField'
|
||||
import {
|
||||
Extensions,
|
||||
GlobalAttributes,
|
||||
Attributes,
|
||||
Attribute,
|
||||
ExtensionAttribute,
|
||||
AnyConfig,
|
||||
} from '../types'
|
||||
import { NodeConfig, MarkConfig } from '..'
|
||||
|
||||
/**
|
||||
* Get a list of all extension attributes defined in `addAttribute` and `addGlobalAttribute`.
|
||||
@ -28,11 +31,18 @@ export default function getAttributesFromExtensions(extensions: Extensions): Ext
|
||||
options: extension.options,
|
||||
}
|
||||
|
||||
if (!extension.config.addGlobalAttributes) {
|
||||
const addGlobalAttributes = getExtensionField<AnyConfig['addGlobalAttributes']>(
|
||||
extension,
|
||||
'addGlobalAttributes',
|
||||
context,
|
||||
)
|
||||
|
||||
if (!addGlobalAttributes) {
|
||||
return
|
||||
}
|
||||
|
||||
const globalAttributes = extension.config.addGlobalAttributes.bind(context)() as GlobalAttributes
|
||||
// TODO: remove `as GlobalAttributes`
|
||||
const globalAttributes = addGlobalAttributes() as GlobalAttributes
|
||||
|
||||
globalAttributes.forEach(globalAttribute => {
|
||||
globalAttribute.types.forEach(type => {
|
||||
@ -57,17 +67,24 @@ export default function getAttributesFromExtensions(extensions: Extensions): Ext
|
||||
options: extension.options,
|
||||
}
|
||||
|
||||
if (!extension.config.addAttributes) {
|
||||
const addAttributes = getExtensionField<NodeConfig['addAttributes'] | MarkConfig['addAttributes']>(
|
||||
extension,
|
||||
'addAttributes',
|
||||
context,
|
||||
)
|
||||
|
||||
if (!addAttributes) {
|
||||
return
|
||||
}
|
||||
|
||||
const attributes = extension.config.addAttributes.bind(context)() as Attributes
|
||||
// TODO: remove `as Attributes`
|
||||
const attributes = addAttributes() as Attributes
|
||||
|
||||
Object
|
||||
.entries(attributes)
|
||||
.forEach(([name, attribute]) => {
|
||||
extensionAttributes.push({
|
||||
type: extension.config.name,
|
||||
type: extension.name,
|
||||
name,
|
||||
attribute: {
|
||||
...defaultAttribute,
|
||||
|
25
packages/core/src/helpers/getExtensionField.ts
Normal file
25
packages/core/src/helpers/getExtensionField.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import { AnyExtension, AnyObject, RemoveThis } from '../types'
|
||||
|
||||
export default function getExtensionField<T = any>(
|
||||
extension: AnyExtension,
|
||||
field: string,
|
||||
context: AnyObject = {},
|
||||
): RemoveThis<T> {
|
||||
|
||||
if (extension.config[field] === undefined && extension.parent) {
|
||||
return getExtensionField(extension.parent, field, context)
|
||||
}
|
||||
|
||||
if (typeof extension.config[field] === 'function') {
|
||||
const value = extension.config[field].bind({
|
||||
...context,
|
||||
parent: extension.parent
|
||||
? getExtensionField(extension.parent, field, context)
|
||||
: null,
|
||||
})
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
return extension.config[field]
|
||||
}
|
@ -1,12 +1,13 @@
|
||||
import { NodeSpec, MarkSpec, Schema } from 'prosemirror-model'
|
||||
import { Extensions } from '../types'
|
||||
import { ExtensionConfig, NodeConfig, MarkConfig } from '..'
|
||||
import { AnyConfig, Extensions } from '../types'
|
||||
import { NodeConfig, MarkConfig } from '..'
|
||||
import splitExtensions from './splitExtensions'
|
||||
import getAttributesFromExtensions from './getAttributesFromExtensions'
|
||||
import getRenderedAttributes from './getRenderedAttributes'
|
||||
import isEmptyObject from '../utilities/isEmptyObject'
|
||||
import injectExtensionAttributesToParseRule from './injectExtensionAttributesToParseRule'
|
||||
import callOrReturn from '../utilities/callOrReturn'
|
||||
import getExtensionField from './getExtensionField'
|
||||
|
||||
function cleanUpSchemaItem<T>(data: T) {
|
||||
return Object.fromEntries(Object.entries(data).filter(([key, value]) => {
|
||||
@ -21,112 +22,110 @@ function cleanUpSchemaItem<T>(data: T) {
|
||||
export default function getSchema(extensions: Extensions): Schema {
|
||||
const allAttributes = getAttributesFromExtensions(extensions)
|
||||
const { nodeExtensions, markExtensions } = splitExtensions(extensions)
|
||||
const topNode = nodeExtensions.find(extension => extension.config.topNode)?.config.name
|
||||
const nodeSchemaExtenders: (
|
||||
| ExtensionConfig['extendNodeSchema']
|
||||
| NodeConfig['extendNodeSchema']
|
||||
| MarkConfig['extendNodeSchema']
|
||||
)[] = []
|
||||
const markSchemaExtenders: (
|
||||
| ExtensionConfig['extendNodeSchema']
|
||||
| NodeConfig['extendNodeSchema']
|
||||
| MarkConfig['extendNodeSchema']
|
||||
)[] = []
|
||||
|
||||
extensions.forEach(extension => {
|
||||
if (typeof extension.config.extendNodeSchema === 'function') {
|
||||
nodeSchemaExtenders.push(extension.config.extendNodeSchema)
|
||||
}
|
||||
|
||||
if (typeof extension.config.extendMarkSchema === 'function') {
|
||||
markSchemaExtenders.push(extension.config.extendMarkSchema)
|
||||
}
|
||||
})
|
||||
const topNode = nodeExtensions.find(extension => getExtensionField(extension, 'topNode'))?.name
|
||||
|
||||
const nodes = Object.fromEntries(nodeExtensions.map(extension => {
|
||||
const extensionAttributes = allAttributes.filter(attribute => attribute.type === extension.config.name)
|
||||
const context = { options: extension.options }
|
||||
const extensionAttributes = allAttributes.filter(attribute => attribute.type === extension.name)
|
||||
const context = {
|
||||
options: extension.options,
|
||||
}
|
||||
|
||||
const extraNodeFields = nodeSchemaExtenders.reduce((fields, nodeSchemaExtender) => {
|
||||
const extraFields = callOrReturn(nodeSchemaExtender, context, extension)
|
||||
const extraNodeFields = extensions.reduce((fields, e) => {
|
||||
const extendNodeSchema = getExtensionField<AnyConfig['extendNodeSchema']>(
|
||||
e,
|
||||
'extendNodeSchema',
|
||||
context,
|
||||
)
|
||||
|
||||
return {
|
||||
...fields,
|
||||
...extraFields,
|
||||
...(extendNodeSchema ? extendNodeSchema(extension) : {}),
|
||||
}
|
||||
}, {})
|
||||
|
||||
const schema: NodeSpec = cleanUpSchemaItem({
|
||||
...extraNodeFields,
|
||||
content: callOrReturn(extension.config.content, context),
|
||||
marks: callOrReturn(extension.config.marks, context),
|
||||
group: callOrReturn(extension.config.group, context),
|
||||
inline: callOrReturn(extension.config.inline, context),
|
||||
atom: callOrReturn(extension.config.atom, context),
|
||||
selectable: callOrReturn(extension.config.selectable, context),
|
||||
draggable: callOrReturn(extension.config.draggable, context),
|
||||
code: callOrReturn(extension.config.code, context),
|
||||
defining: callOrReturn(extension.config.defining, context),
|
||||
isolating: callOrReturn(extension.config.isolating, context),
|
||||
content: callOrReturn(getExtensionField<NodeConfig['content']>(extension, 'content', context)),
|
||||
marks: callOrReturn(getExtensionField<NodeConfig['marks']>(extension, 'marks', context)),
|
||||
group: callOrReturn(getExtensionField<NodeConfig['group']>(extension, 'group', context)),
|
||||
inline: callOrReturn(getExtensionField<NodeConfig['inline']>(extension, 'inline', context)),
|
||||
atom: callOrReturn(getExtensionField<NodeConfig['atom']>(extension, 'atom', context)),
|
||||
selectable: callOrReturn(getExtensionField<NodeConfig['selectable']>(extension, 'selectable', context)),
|
||||
draggable: callOrReturn(getExtensionField<NodeConfig['draggable']>(extension, 'draggable', context)),
|
||||
code: callOrReturn(getExtensionField<NodeConfig['code']>(extension, 'code', context)),
|
||||
defining: callOrReturn(getExtensionField<NodeConfig['defining']>(extension, 'defining', context)),
|
||||
isolating: callOrReturn(getExtensionField<NodeConfig['isolating']>(extension, 'isolating', context)),
|
||||
attrs: Object.fromEntries(extensionAttributes.map(extensionAttribute => {
|
||||
return [extensionAttribute.name, { default: extensionAttribute?.attribute?.default }]
|
||||
})),
|
||||
})
|
||||
|
||||
if (extension.config.parseHTML) {
|
||||
schema.parseDOM = extension.config.parseHTML
|
||||
.bind(context)()
|
||||
?.map(parseRule => injectExtensionAttributesToParseRule(parseRule, extensionAttributes))
|
||||
const parseHTML = callOrReturn(getExtensionField<NodeConfig['parseHTML']>(extension, 'parseHTML', context))
|
||||
|
||||
if (parseHTML) {
|
||||
schema.parseDOM = parseHTML
|
||||
.map(parseRule => injectExtensionAttributesToParseRule(parseRule, extensionAttributes))
|
||||
}
|
||||
|
||||
if (extension.config.renderHTML) {
|
||||
schema.toDOM = node => (extension.config.renderHTML as Function)?.bind(context)({
|
||||
const renderHTML = getExtensionField<NodeConfig['renderHTML']>(extension, 'renderHTML', context)
|
||||
|
||||
if (renderHTML) {
|
||||
schema.toDOM = node => renderHTML({
|
||||
node,
|
||||
HTMLAttributes: getRenderedAttributes(node, extensionAttributes),
|
||||
})
|
||||
}
|
||||
|
||||
return [extension.config.name, schema]
|
||||
return [extension.name, schema]
|
||||
}))
|
||||
|
||||
const marks = Object.fromEntries(markExtensions.map(extension => {
|
||||
const extensionAttributes = allAttributes.filter(attribute => attribute.type === extension.config.name)
|
||||
const context = { options: extension.options }
|
||||
const extensionAttributes = allAttributes.filter(attribute => attribute.type === extension.name)
|
||||
const context = {
|
||||
options: extension.options,
|
||||
}
|
||||
|
||||
const extraMarkFields = markSchemaExtenders.reduce((fields, markSchemaExtender) => {
|
||||
const extraFields = callOrReturn(markSchemaExtender, context, extension)
|
||||
const extraMarkFields = extensions.reduce((fields, e) => {
|
||||
const extendMarkSchema = getExtensionField<AnyConfig['extendMarkSchema']>(
|
||||
e,
|
||||
'extendMarkSchema',
|
||||
context,
|
||||
)
|
||||
|
||||
return {
|
||||
...fields,
|
||||
...extraFields,
|
||||
...(extendMarkSchema ? extendMarkSchema(extension) : {}),
|
||||
}
|
||||
}, {})
|
||||
|
||||
const schema: MarkSpec = cleanUpSchemaItem({
|
||||
...extraMarkFields,
|
||||
inclusive: callOrReturn(extension.config.inclusive, context),
|
||||
excludes: callOrReturn(extension.config.excludes, context),
|
||||
group: callOrReturn(extension.config.group, context),
|
||||
spanning: callOrReturn(extension.config.spanning, context),
|
||||
inclusive: callOrReturn(getExtensionField<NodeConfig['inclusive']>(extension, 'inclusive', context)),
|
||||
excludes: callOrReturn(getExtensionField<NodeConfig['excludes']>(extension, 'excludes', context)),
|
||||
group: callOrReturn(getExtensionField<NodeConfig['group']>(extension, 'group', context)),
|
||||
spanning: callOrReturn(getExtensionField<NodeConfig['spanning']>(extension, 'spanning', context)),
|
||||
attrs: Object.fromEntries(extensionAttributes.map(extensionAttribute => {
|
||||
return [extensionAttribute.name, { default: extensionAttribute?.attribute?.default }]
|
||||
})),
|
||||
})
|
||||
|
||||
if (extension.config.parseHTML) {
|
||||
schema.parseDOM = extension.config.parseHTML
|
||||
.bind(context)()
|
||||
?.map(parseRule => injectExtensionAttributesToParseRule(parseRule, extensionAttributes))
|
||||
const parseHTML = callOrReturn(getExtensionField<MarkConfig['parseHTML']>(extension, 'parseHTML', context))
|
||||
|
||||
if (parseHTML) {
|
||||
schema.parseDOM = parseHTML
|
||||
.map(parseRule => injectExtensionAttributesToParseRule(parseRule, extensionAttributes))
|
||||
}
|
||||
|
||||
if (extension.config.renderHTML) {
|
||||
schema.toDOM = mark => (extension.config.renderHTML as Function)?.bind(context)({
|
||||
const renderHTML = getExtensionField<MarkConfig['renderHTML']>(extension, 'renderHTML', context)
|
||||
|
||||
if (renderHTML) {
|
||||
schema.toDOM = mark => renderHTML({
|
||||
mark,
|
||||
HTMLAttributes: getRenderedAttributes(mark, extensionAttributes),
|
||||
})
|
||||
}
|
||||
|
||||
return [extension.config.name, schema]
|
||||
return [extension.name, schema]
|
||||
}))
|
||||
|
||||
return new Schema({
|
||||
|
@ -1,20 +1,23 @@
|
||||
import { Extensions } from '../types'
|
||||
import { NodeConfig } from '..'
|
||||
import splitExtensions from './splitExtensions'
|
||||
import callOrReturn from '../utilities/callOrReturn'
|
||||
import getExtensionField from '../helpers/getExtensionField'
|
||||
|
||||
export default function isList(name: string, extensions: Extensions): boolean {
|
||||
const { nodeExtensions } = splitExtensions(extensions)
|
||||
const extension = nodeExtensions.find(item => item.config.name === name)
|
||||
const extension = nodeExtensions.find(item => item.name === name)
|
||||
|
||||
if (!extension) {
|
||||
return false
|
||||
}
|
||||
|
||||
const groups = callOrReturn(extension.config.group, { options: extension.options })
|
||||
const context = { options: extension.options }
|
||||
const group = callOrReturn(getExtensionField<NodeConfig['group']>(extension, 'group', context))
|
||||
|
||||
if (typeof groups !== 'string') {
|
||||
if (typeof group !== 'string') {
|
||||
return false
|
||||
}
|
||||
|
||||
return groups.split(' ').includes('list')
|
||||
return group.split(' ').includes('list')
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ export { default as markPasteRule } from './pasteRules/markPasteRule'
|
||||
export { default as callOrReturn } from './utilities/callOrReturn'
|
||||
export { default as mergeAttributes } from './utilities/mergeAttributes'
|
||||
|
||||
export { default as getExtensionField } from './helpers/getExtensionField'
|
||||
export { default as findChildren } from './helpers/findChildren'
|
||||
export { default as findParentNode } from './helpers/findParentNode'
|
||||
export { default as findParentNodeClosestToPos } from './helpers/findParentNodeClosestToPos'
|
||||
|
@ -14,9 +14,30 @@ import { Extension } from './Extension'
|
||||
import { Node } from './Node'
|
||||
import { Mark } from './Mark'
|
||||
import { Editor } from './Editor'
|
||||
import { Commands } from '.'
|
||||
import {
|
||||
Commands,
|
||||
ExtensionConfig,
|
||||
NodeConfig,
|
||||
MarkConfig,
|
||||
} from '.'
|
||||
|
||||
export type Extensions = (Extension | Node | Mark)[]
|
||||
export type AnyConfig = ExtensionConfig | NodeConfig | MarkConfig
|
||||
export type AnyExtension = Extension | Node | Mark
|
||||
export type Extensions = AnyExtension[]
|
||||
|
||||
export type ParentConfig<T> = Partial<{
|
||||
[P in keyof T]: Required<T>[P] extends (...args: any) => any
|
||||
? (...args: Parameters<Required<T>[P]>) => ReturnType<Required<T>[P]>
|
||||
: T[P]
|
||||
}>
|
||||
|
||||
export type RemoveThis<T> = T extends (...args: any) => any
|
||||
? (...args: Parameters<T>) => ReturnType<T>
|
||||
: T
|
||||
|
||||
export type MaybeReturnType<T> = T extends (...args: any) => any
|
||||
? ReturnType<T>
|
||||
: T
|
||||
|
||||
export interface EditorOptions {
|
||||
element: Element,
|
||||
|
@ -1,3 +1,5 @@
|
||||
import { MaybeReturnType } from '../types'
|
||||
|
||||
/**
|
||||
* Optionally calls `value` as a function.
|
||||
* Otherwise it is returned directly.
|
||||
@ -5,7 +7,7 @@
|
||||
* @param context Optional context to bind to function.
|
||||
* @param props Optional props to pass to function.
|
||||
*/
|
||||
export default function callOrReturn(value: any, context: any = undefined, ...props: any[]): any {
|
||||
export default function callOrReturn<T>(value: T, context: any = undefined, ...props: any[]): MaybeReturnType<T> {
|
||||
if (typeof value === 'function') {
|
||||
if (context) {
|
||||
return value.bind(context)(...props)
|
||||
@ -14,5 +16,5 @@ export default function callOrReturn(value: any, context: any = undefined, ...pr
|
||||
return value(...props)
|
||||
}
|
||||
|
||||
return value
|
||||
return value as MaybeReturnType<T>
|
||||
}
|
||||
|
@ -1,7 +1,3 @@
|
||||
export default function elementFromString(value: string): HTMLElement {
|
||||
const htmlString = `<div>${value}</div>`
|
||||
const parser = new window.DOMParser()
|
||||
const element = parser.parseFromString(htmlString, 'text/html').body
|
||||
|
||||
return element
|
||||
return new window.DOMParser().parseFromString(value, 'text/html').body
|
||||
}
|
||||
|
@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [2.0.0-beta.2](https://github.com/ueberdosis/tiptap-next/compare/@tiptap/extension-blockquote@2.0.0-beta.1...@tiptap/extension-blockquote@2.0.0-beta.2) (2021-04-15)
|
||||
|
||||
**Note:** Version bump only for package @tiptap/extension-blockquote
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [2.0.0-beta.1](https://github.com/ueberdosis/tiptap-next/compare/@tiptap/extension-blockquote@2.0.0-alpha.11...@tiptap/extension-blockquote@2.0.0-beta.1) (2021-03-05)
|
||||
|
||||
**Note:** Version bump only for package @tiptap/extension-blockquote
|
||||
|
@ -11,4 +11,4 @@ tiptap is a headless wrapper around [ProseMirror](https://ProseMirror.net) – a
|
||||
Documentation can be found on the [tiptap website](https://tiptap.dev).
|
||||
|
||||
## License
|
||||
tiptap is open-sourced software licensed under the [MIT license](https://github.com/ueberdosis/tiptap-next/blob/main/LICENSE.md).
|
||||
tiptap is open sourced software licensed under the [MIT license](https://github.com/ueberdosis/tiptap-next/blob/main/LICENSE.md).
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@tiptap/extension-blockquote",
|
||||
"description": "blockquote extension for tiptap",
|
||||
"version": "2.0.0-beta.1",
|
||||
"version": "2.0.0-beta.2",
|
||||
"homepage": "https://tiptap.dev",
|
||||
"keywords": [
|
||||
"tiptap",
|
||||
|
@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [2.0.0-beta.2](https://github.com/ueberdosis/tiptap-next/compare/@tiptap/extension-bold@2.0.0-beta.1...@tiptap/extension-bold@2.0.0-beta.2) (2021-04-15)
|
||||
|
||||
**Note:** Version bump only for package @tiptap/extension-bold
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [2.0.0-beta.1](https://github.com/ueberdosis/tiptap-next/compare/@tiptap/extension-bold@2.0.0-alpha.11...@tiptap/extension-bold@2.0.0-beta.1) (2021-03-05)
|
||||
|
||||
**Note:** Version bump only for package @tiptap/extension-bold
|
||||
|
@ -11,4 +11,4 @@ tiptap is a headless wrapper around [ProseMirror](https://ProseMirror.net) – a
|
||||
Documentation can be found on the [tiptap website](https://tiptap.dev).
|
||||
|
||||
## License
|
||||
tiptap is open-sourced software licensed under the [MIT license](https://github.com/ueberdosis/tiptap-next/blob/main/LICENSE.md).
|
||||
tiptap is open sourced software licensed under the [MIT license](https://github.com/ueberdosis/tiptap-next/blob/main/LICENSE.md).
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@tiptap/extension-bold",
|
||||
"description": "bold extension for tiptap",
|
||||
"version": "2.0.0-beta.1",
|
||||
"version": "2.0.0-beta.2",
|
||||
"homepage": "https://tiptap.dev",
|
||||
"keywords": [
|
||||
"tiptap",
|
||||
|
@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [2.0.0-beta.6](https://github.com/ueberdosis/tiptap-next/compare/@tiptap/extension-bubble-menu@2.0.0-beta.5...@tiptap/extension-bubble-menu@2.0.0-beta.6) (2021-04-15)
|
||||
|
||||
**Note:** Version bump only for package @tiptap/extension-bubble-menu
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [2.0.0-beta.5](https://github.com/ueberdosis/tiptap-next/compare/@tiptap/extension-bubble-menu@2.0.0-beta.4...@tiptap/extension-bubble-menu@2.0.0-beta.5) (2021-04-01)
|
||||
|
||||
**Note:** Version bump only for package @tiptap/extension-bubble-menu
|
||||
|
@ -11,4 +11,4 @@ tiptap is a headless wrapper around [ProseMirror](https://ProseMirror.net) – a
|
||||
Documentation can be found on the [tiptap website](https://tiptap.dev).
|
||||
|
||||
## License
|
||||
tiptap is open-sourced software licensed under the [MIT license](https://github.com/ueberdosis/tiptap-next/blob/main/LICENSE.md).
|
||||
tiptap is open sourced software licensed under the [MIT license](https://github.com/ueberdosis/tiptap-next/blob/main/LICENSE.md).
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@tiptap/extension-bubble-menu",
|
||||
"description": "bubble-menu extension for tiptap",
|
||||
"version": "2.0.0-beta.5",
|
||||
"version": "2.0.0-beta.6",
|
||||
"homepage": "https://tiptap.dev",
|
||||
"keywords": [
|
||||
"tiptap",
|
||||
|
@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [2.0.0-beta.2](https://github.com/ueberdosis/tiptap-next/compare/@tiptap/extension-bullet-list@2.0.0-beta.1...@tiptap/extension-bullet-list@2.0.0-beta.2) (2021-04-15)
|
||||
|
||||
**Note:** Version bump only for package @tiptap/extension-bullet-list
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [2.0.0-beta.1](https://github.com/ueberdosis/tiptap-next/compare/@tiptap/extension-bullet-list@2.0.0-alpha.11...@tiptap/extension-bullet-list@2.0.0-beta.1) (2021-03-05)
|
||||
|
||||
**Note:** Version bump only for package @tiptap/extension-bullet-list
|
||||
|
@ -11,4 +11,4 @@ tiptap is a headless wrapper around [ProseMirror](https://ProseMirror.net) – a
|
||||
Documentation can be found on the [tiptap website](https://tiptap.dev).
|
||||
|
||||
## License
|
||||
tiptap is open-sourced software licensed under the [MIT license](https://github.com/ueberdosis/tiptap-next/blob/main/LICENSE.md).
|
||||
tiptap is open sourced software licensed under the [MIT license](https://github.com/ueberdosis/tiptap-next/blob/main/LICENSE.md).
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@tiptap/extension-bullet-list",
|
||||
"description": "bullet list extension for tiptap",
|
||||
"version": "2.0.0-beta.1",
|
||||
"version": "2.0.0-beta.2",
|
||||
"homepage": "https://tiptap.dev",
|
||||
"keywords": [
|
||||
"tiptap",
|
||||
|
@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [2.0.0-beta.2](https://github.com/ueberdosis/tiptap-next/compare/@tiptap/extension-character-count@2.0.0-beta.1...@tiptap/extension-character-count@2.0.0-beta.2) (2021-04-15)
|
||||
|
||||
**Note:** Version bump only for package @tiptap/extension-character-count
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [2.0.0-beta.1](https://github.com/ueberdosis/tiptap-next/compare/@tiptap/extension-character-count@2.0.0-alpha.11...@tiptap/extension-character-count@2.0.0-beta.1) (2021-03-05)
|
||||
|
||||
**Note:** Version bump only for package @tiptap/extension-character-count
|
||||
|
@ -11,4 +11,4 @@ tiptap is a headless wrapper around [ProseMirror](https://ProseMirror.net) – a
|
||||
Documentation can be found on the [tiptap website](https://tiptap.dev).
|
||||
|
||||
## License
|
||||
tiptap is open-sourced software licensed under the [MIT license](https://github.com/ueberdosis/tiptap-next/blob/main/LICENSE.md).
|
||||
tiptap is open sourced software licensed under the [MIT license](https://github.com/ueberdosis/tiptap-next/blob/main/LICENSE.md).
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@tiptap/extension-character-count",
|
||||
"description": "font family extension for tiptap",
|
||||
"version": "2.0.0-beta.1",
|
||||
"version": "2.0.0-beta.2",
|
||||
"homepage": "https://tiptap.dev",
|
||||
"keywords": [
|
||||
"tiptap",
|
||||
|
@ -3,6 +3,52 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [2.0.0-beta.7](https://github.com/ueberdosis/tiptap-next/compare/@tiptap/extension-code-block-lowlight@2.0.0-beta.6...@tiptap/extension-code-block-lowlight@2.0.0-beta.7) (2021-04-15)
|
||||
|
||||
**Note:** Version bump only for package @tiptap/extension-code-block-lowlight
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [2.0.0-beta.6](https://github.com/ueberdosis/tiptap-next/compare/@tiptap/extension-code-block-lowlight@2.0.0-beta.4...@tiptap/extension-code-block-lowlight@2.0.0-beta.6) (2021-04-14)
|
||||
|
||||
**Note:** Version bump only for package @tiptap/extension-code-block-lowlight
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [2.0.0-beta.4](https://github.com/ueberdosis/tiptap-next/compare/@tiptap/extension-code-block-lowlight@2.0.0-beta.3...@tiptap/extension-code-block-lowlight@2.0.0-beta.4) (2021-04-14)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* fix lowlight decorations for vue 3 ([daa5dc0](https://github.com/ueberdosis/tiptap-next/commit/daa5dc0fb1ec2db6889565fc9c091f3dbdbbda6d))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [2.0.0-beta.3](https://github.com/ueberdosis/tiptap-next/compare/@tiptap/extension-code-block-lowlight@2.0.0-beta.2...@tiptap/extension-code-block-lowlight@2.0.0-beta.3) (2021-04-12)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add parentConfig to extension context for more extendable extensions, fix [#259](https://github.com/ueberdosis/tiptap-next/issues/259) ([5e1ec5d](https://github.com/ueberdosis/tiptap-next/commit/5e1ec5d2a66be164f505d631f97861ab9344ba96))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [2.0.0-beta.2](https://github.com/ueberdosis/tiptap-next/compare/@tiptap/extension-code-block-lowlight@2.0.0-beta.1...@tiptap/extension-code-block-lowlight@2.0.0-beta.2) (2021-04-11)
|
||||
|
||||
**Note:** Version bump only for package @tiptap/extension-code-block-lowlight
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# 2.0.0-beta.1 (2021-04-08)
|
||||
|
||||
**Note:** Version bump only for package @tiptap/extension-code-block-lowlight
|
||||
|
@ -11,4 +11,4 @@ tiptap is a headless wrapper around [ProseMirror](https://ProseMirror.net) – a
|
||||
Documentation can be found on the [tiptap website](https://tiptap.dev).
|
||||
|
||||
## License
|
||||
tiptap is open-sourced software licensed under the [MIT license](https://github.com/ueberdosis/tiptap-next/blob/main/LICENSE.md).
|
||||
tiptap is open sourced software licensed under the [MIT license](https://github.com/ueberdosis/tiptap-next/blob/main/LICENSE.md).
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@tiptap/extension-code-block-lowlight",
|
||||
"description": "code block extension for tiptap",
|
||||
"version": "2.0.0-beta.1",
|
||||
"version": "2.0.0-beta.7",
|
||||
"homepage": "https://tiptap.dev",
|
||||
"keywords": [
|
||||
"tiptap",
|
||||
@ -25,7 +25,7 @@
|
||||
"@tiptap/core": "^2.0.0-beta.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@tiptap/extension-code-block": "^2.0.0-beta.1",
|
||||
"@tiptap/extension-code-block": "^2.0.0-beta.4",
|
||||
"@types/lowlight": "^0.0.1",
|
||||
"lowlight": "^1.20.0",
|
||||
"prosemirror-model": "^1.14.0",
|
||||
|
@ -1,10 +1,24 @@
|
||||
import CodeBlock from '@tiptap/extension-code-block'
|
||||
import lowlight from 'lowlight/lib/core'
|
||||
import CodeBlock, { CodeBlockOptions } from '@tiptap/extension-code-block'
|
||||
import { LowlightPlugin } from './lowlight-plugin'
|
||||
|
||||
export const CodeBlockLowlight = CodeBlock.extend({
|
||||
export interface CodeBlockLowlightOptions extends CodeBlockOptions {
|
||||
lowlight: any,
|
||||
}
|
||||
|
||||
export const CodeBlockLowlight = CodeBlock.extend<CodeBlockLowlightOptions>({
|
||||
defaultOptions: {
|
||||
...CodeBlock.options,
|
||||
lowlight,
|
||||
},
|
||||
|
||||
addProseMirrorPlugins() {
|
||||
return [
|
||||
LowlightPlugin({ name: 'codeBlock' }),
|
||||
...this.parent?.() || [],
|
||||
LowlightPlugin({
|
||||
name: 'codeBlock',
|
||||
lowlight: this.options.lowlight,
|
||||
}),
|
||||
]
|
||||
},
|
||||
})
|
||||
|
@ -2,7 +2,6 @@ import { Plugin, PluginKey } from 'prosemirror-state'
|
||||
import { Decoration, DecorationSet } from 'prosemirror-view'
|
||||
import { Node as ProsemirrorNode } from 'prosemirror-model'
|
||||
import { findChildren } from '@tiptap/core'
|
||||
import lowlight from 'lowlight/lib/core'
|
||||
|
||||
function parseNodes(nodes: any[], className: string[] = []): { text: string, classes: string[] }[] {
|
||||
return nodes
|
||||
@ -26,7 +25,7 @@ function parseNodes(nodes: any[], className: string[] = []): { text: string, cla
|
||||
.flat()
|
||||
}
|
||||
|
||||
function getDecorations({ doc, name }: { doc: ProsemirrorNode, name: string}) {
|
||||
function getDecorations({ doc, name, lowlight }: { doc: ProsemirrorNode, name: string, lowlight: any }) {
|
||||
const decorations: Decoration[] = []
|
||||
|
||||
findChildren(doc, node => node.type.name === name)
|
||||
@ -58,12 +57,12 @@ function getDecorations({ doc, name }: { doc: ProsemirrorNode, name: string}) {
|
||||
return DecorationSet.create(doc, decorations)
|
||||
}
|
||||
|
||||
export function LowlightPlugin({ name }: { name: string }) {
|
||||
export function LowlightPlugin({ name, lowlight }: { name: string, lowlight: any }) {
|
||||
return new Plugin({
|
||||
key: new PluginKey('lowlight'),
|
||||
|
||||
state: {
|
||||
init: (_, { doc }) => getDecorations({ doc, name }),
|
||||
init: (_, { doc }) => getDecorations({ doc, name, lowlight }),
|
||||
apply: (transaction, decorationSet, oldState, newState) => {
|
||||
const oldNodeName = oldState.selection.$head.parent.type.name
|
||||
const newNodeName = newState.selection.$head.parent.type.name
|
||||
@ -95,7 +94,7 @@ export function LowlightPlugin({ name }: { name: string }) {
|
||||
})
|
||||
)
|
||||
) {
|
||||
return getDecorations({ doc: transaction.doc, name })
|
||||
return getDecorations({ doc: transaction.doc, name, lowlight })
|
||||
}
|
||||
|
||||
return decorationSet.map(transaction.mapping, transaction.doc)
|
||||
|
@ -3,6 +3,25 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [2.0.0-beta.4](https://github.com/ueberdosis/tiptap-next/compare/@tiptap/extension-code-block@2.0.0-beta.3...@tiptap/extension-code-block@2.0.0-beta.4) (2021-04-15)
|
||||
|
||||
**Note:** Version bump only for package @tiptap/extension-code-block
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [2.0.0-beta.3](https://github.com/ueberdosis/tiptap-next/compare/@tiptap/extension-code-block@2.0.0-beta.2...@tiptap/extension-code-block@2.0.0-beta.3) (2021-04-11)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* remove codeblock when at start of document, fix [#262](https://github.com/ueberdosis/tiptap-next/issues/262) ([92f6ea2](https://github.com/ueberdosis/tiptap-next/commit/92f6ea25cc7623d0bd34f5a2342be6f5aae951aa))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [2.0.0-beta.2](https://github.com/ueberdosis/tiptap-next/compare/@tiptap/extension-code-block@2.0.0-beta.1...@tiptap/extension-code-block@2.0.0-beta.2) (2021-04-02)
|
||||
|
||||
**Note:** Version bump only for package @tiptap/extension-code-block
|
||||
|
@ -11,4 +11,4 @@ tiptap is a headless wrapper around [ProseMirror](https://ProseMirror.net) – a
|
||||
Documentation can be found on the [tiptap website](https://tiptap.dev).
|
||||
|
||||
## License
|
||||
tiptap is open-sourced software licensed under the [MIT license](https://github.com/ueberdosis/tiptap-next/blob/main/LICENSE.md).
|
||||
tiptap is open sourced software licensed under the [MIT license](https://github.com/ueberdosis/tiptap-next/blob/main/LICENSE.md).
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@tiptap/extension-code-block",
|
||||
"description": "code block extension for tiptap",
|
||||
"version": "2.0.0-beta.2",
|
||||
"version": "2.0.0-beta.4",
|
||||
"homepage": "https://tiptap.dev",
|
||||
"keywords": [
|
||||
"tiptap",
|
||||
|
@ -101,6 +101,22 @@ export const CodeBlock = Node.create<CodeBlockOptions>({
|
||||
addKeyboardShortcuts() {
|
||||
return {
|
||||
'Mod-Alt-c': () => this.editor.commands.toggleCodeBlock(),
|
||||
|
||||
// remove code block when at start of document or code block is empty
|
||||
Backspace: state => {
|
||||
const { empty, $anchor } = state.selection
|
||||
const isAtStart = $anchor.pos === 1
|
||||
|
||||
if (!empty || $anchor.parent.type.name !== 'codeBlock') {
|
||||
return false
|
||||
}
|
||||
|
||||
if (isAtStart || !$anchor.parent.textContent.length) {
|
||||
return this.editor.commands.clearNodes()
|
||||
}
|
||||
|
||||
return false
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [2.0.0-beta.2](https://github.com/ueberdosis/tiptap-next/compare/@tiptap/extension-code@2.0.0-beta.1...@tiptap/extension-code@2.0.0-beta.2) (2021-04-15)
|
||||
|
||||
**Note:** Version bump only for package @tiptap/extension-code
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [2.0.0-beta.1](https://github.com/ueberdosis/tiptap-next/compare/@tiptap/extension-code@2.0.0-alpha.11...@tiptap/extension-code@2.0.0-beta.1) (2021-03-05)
|
||||
|
||||
**Note:** Version bump only for package @tiptap/extension-code
|
||||
|
@ -11,4 +11,4 @@ tiptap is a headless wrapper around [ProseMirror](https://ProseMirror.net) – a
|
||||
Documentation can be found on the [tiptap website](https://tiptap.dev).
|
||||
|
||||
## License
|
||||
tiptap is open-sourced software licensed under the [MIT license](https://github.com/ueberdosis/tiptap-next/blob/main/LICENSE.md).
|
||||
tiptap is open sourced software licensed under the [MIT license](https://github.com/ueberdosis/tiptap-next/blob/main/LICENSE.md).
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@tiptap/extension-code",
|
||||
"description": "code extension for tiptap",
|
||||
"version": "2.0.0-beta.1",
|
||||
"version": "2.0.0-beta.2",
|
||||
"homepage": "https://tiptap.dev",
|
||||
"keywords": [
|
||||
"tiptap",
|
||||
|
@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [2.0.0-beta.6](https://github.com/ueberdosis/tiptap-next/compare/@tiptap/extension-collaboration-cursor@2.0.0-beta.5...@tiptap/extension-collaboration-cursor@2.0.0-beta.6) (2021-04-15)
|
||||
|
||||
**Note:** Version bump only for package @tiptap/extension-collaboration-cursor
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [2.0.0-beta.5](https://github.com/ueberdosis/tiptap-next/compare/@tiptap/extension-collaboration-cursor@2.0.0-beta.4...@tiptap/extension-collaboration-cursor@2.0.0-beta.5) (2021-04-07)
|
||||
|
||||
|
||||
|
@ -11,4 +11,4 @@ tiptap is a headless wrapper around [ProseMirror](https://ProseMirror.net) – a
|
||||
Documentation can be found on the [tiptap website](https://tiptap.dev).
|
||||
|
||||
## License
|
||||
tiptap is open-sourced software licensed under the [MIT license](https://github.com/ueberdosis/tiptap-next/blob/main/LICENSE.md).
|
||||
tiptap is open sourced software licensed under the [MIT license](https://github.com/ueberdosis/tiptap-next/blob/main/LICENSE.md).
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@tiptap/extension-collaboration-cursor",
|
||||
"description": "collaboration cursor extension for tiptap",
|
||||
"version": "2.0.0-beta.5",
|
||||
"version": "2.0.0-beta.6",
|
||||
"homepage": "https://tiptap.dev",
|
||||
"keywords": [
|
||||
"tiptap",
|
||||
|
@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [2.0.0-beta.5](https://github.com/ueberdosis/tiptap-next/compare/@tiptap/extension-collaboration@2.0.0-beta.4...@tiptap/extension-collaboration@2.0.0-beta.5) (2021-04-15)
|
||||
|
||||
**Note:** Version bump only for package @tiptap/extension-collaboration
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [2.0.0-beta.4](https://github.com/ueberdosis/tiptap-next/compare/@tiptap/extension-collaboration@2.0.0-beta.3...@tiptap/extension-collaboration@2.0.0-beta.4) (2021-03-16)
|
||||
|
||||
**Note:** Version bump only for package @tiptap/extension-collaboration
|
||||
|
@ -11,4 +11,4 @@ tiptap is a headless wrapper around [ProseMirror](https://ProseMirror.net) – a
|
||||
Documentation can be found on the [tiptap website](https://tiptap.dev).
|
||||
|
||||
## License
|
||||
tiptap is open-sourced software licensed under the [MIT license](https://github.com/ueberdosis/tiptap-next/blob/main/LICENSE.md).
|
||||
tiptap is open sourced software licensed under the [MIT license](https://github.com/ueberdosis/tiptap-next/blob/main/LICENSE.md).
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@tiptap/extension-collaboration",
|
||||
"description": "collaboration extension for tiptap",
|
||||
"version": "2.0.0-beta.4",
|
||||
"version": "2.0.0-beta.5",
|
||||
"homepage": "https://tiptap.dev",
|
||||
"keywords": [
|
||||
"tiptap",
|
||||
|
@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [2.0.0-beta.2](https://github.com/ueberdosis/tiptap-next/compare/@tiptap/extension-document@2.0.0-beta.1...@tiptap/extension-document@2.0.0-beta.2) (2021-04-15)
|
||||
|
||||
**Note:** Version bump only for package @tiptap/extension-document
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [2.0.0-beta.1](https://github.com/ueberdosis/tiptap-next/compare/@tiptap/extension-document@2.0.0-alpha.11...@tiptap/extension-document@2.0.0-beta.1) (2021-03-05)
|
||||
|
||||
**Note:** Version bump only for package @tiptap/extension-document
|
||||
|
@ -11,4 +11,4 @@ tiptap is a headless wrapper around [ProseMirror](https://ProseMirror.net) – a
|
||||
Documentation can be found on the [tiptap website](https://tiptap.dev).
|
||||
|
||||
## License
|
||||
tiptap is open-sourced software licensed under the [MIT license](https://github.com/ueberdosis/tiptap-next/blob/main/LICENSE.md).
|
||||
tiptap is open sourced software licensed under the [MIT license](https://github.com/ueberdosis/tiptap-next/blob/main/LICENSE.md).
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@tiptap/extension-document",
|
||||
"description": "document extension for tiptap",
|
||||
"version": "2.0.0-beta.1",
|
||||
"version": "2.0.0-beta.2",
|
||||
"homepage": "https://tiptap.dev",
|
||||
"keywords": [
|
||||
"tiptap",
|
||||
|
@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [2.0.0-beta.3](https://github.com/ueberdosis/tiptap-next/compare/@tiptap/extension-dropcursor@2.0.0-beta.2...@tiptap/extension-dropcursor@2.0.0-beta.3) (2021-04-15)
|
||||
|
||||
**Note:** Version bump only for package @tiptap/extension-dropcursor
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [2.0.0-beta.2](https://github.com/ueberdosis/tiptap-next/compare/@tiptap/extension-dropcursor@2.0.0-beta.1...@tiptap/extension-dropcursor@2.0.0-beta.2) (2021-04-06)
|
||||
|
||||
**Note:** Version bump only for package @tiptap/extension-dropcursor
|
||||
|
@ -11,4 +11,4 @@ tiptap is a headless wrapper around [ProseMirror](https://ProseMirror.net) – a
|
||||
Documentation can be found on the [tiptap website](https://tiptap.dev).
|
||||
|
||||
## License
|
||||
tiptap is open-sourced software licensed under the [MIT license](https://github.com/ueberdosis/tiptap-next/blob/main/LICENSE.md).
|
||||
tiptap is open sourced software licensed under the [MIT license](https://github.com/ueberdosis/tiptap-next/blob/main/LICENSE.md).
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@tiptap/extension-dropcursor",
|
||||
"description": "dropcursor extension for tiptap",
|
||||
"version": "2.0.0-beta.2",
|
||||
"version": "2.0.0-beta.3",
|
||||
"homepage": "https://tiptap.dev",
|
||||
"keywords": [
|
||||
"tiptap",
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user