mirror of
https://github.com/ueberdosis/tiptap.git
synced 2024-12-15 02:59:01 +08:00
Merge branch 'master' into master
This commit is contained in:
commit
7dcc02b441
13
.github/ISSUE_TEMPLATE/bug_report.md
vendored
13
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@ -1,8 +1,9 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
url: https://github.com/ueberdosis/tiptap/issues/new
|
||||
title: ''
|
||||
labels: 'bug'
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
@ -12,13 +13,13 @@ A clear and concise description of what the bug is.
|
||||
|
||||
**Steps to Reproduce / Codesandbox Example**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
1. Go to '…'
|
||||
2. Click on '…'
|
||||
3. Scroll down to '…'
|
||||
4. See error
|
||||
|
||||
Fork this or create a new Codesandbox replicating your error
|
||||
https://codesandbox.io/s/qxv5m9y6oq?fontsize=14
|
||||
Fork this or create a new Codesandbox replicating your error
|
||||
https://codesandbox.io/s/vue-issue-template-h0g28
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
5
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
5
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Ask a Question
|
||||
url: https://github.com/ueberdosis/tiptap/discussions/new
|
||||
about: Ask the community for help
|
2
.github/ISSUE_TEMPLATE/feature_request.md
vendored
2
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@ -2,7 +2,7 @@
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: 'feature request'
|
||||
labels: feature request
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
55
.github/workflows/ci.yml
vendored
Normal file
55
.github/workflows/ci.yml
vendored
Normal file
@ -0,0 +1,55 @@
|
||||
name: ci
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
audit:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [14.x]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- run: yarn install
|
||||
- run: yarn build:packages
|
||||
- run: yarn audit-ci
|
||||
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [14.x]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- run: yarn install
|
||||
- run: yarn build:packages
|
||||
- run: yarn lint
|
||||
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [10.x, 12.x, 14.x]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- run: yarn install
|
||||
- run: yarn build:packages
|
||||
- run: yarn test
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,3 +1,4 @@
|
||||
.history
|
||||
.DS_Store
|
||||
node_modules
|
||||
dist/
|
||||
|
@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019, Scrumpy UG (limited liability)
|
||||
Copyright (c) 2020, überdosis GbR
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
45
README.md
45
README.md
@ -1,26 +1,33 @@
|
||||
> This repository has been migrated to a new organization. Read more: https://github.com/ueberdosis/tiptap/issues/759
|
||||
|
||||
# tiptap
|
||||
A renderless and extendable rich-text editor for [Vue.js](https://github.com/vuejs/vue)
|
||||
|
||||
[![Gitpod Ready-to-Code](https://img.shields.io/badge/Gitpod-Ready--to--Code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/scrumpy/tiptap)
|
||||
[![](https://img.shields.io/npm/v/tiptap.svg?label=version)](https://www.npmjs.com/package/tiptap)
|
||||
[![](https://img.shields.io/npm/dm/tiptap.svg)](https://npmcharts.com/compare/tiptap?minimal=true)
|
||||
[![](https://img.shields.io/npm/l/tiptap.svg)](https://www.npmjs.com/package/tiptap)
|
||||
[![](https://img.badgesize.io/https://unpkg.com/tiptap/dist/tiptap.min.js?compression=gzip&label=size&colorB=000000)](https://www.npmjs.com/package/tiptap)
|
||||
[![Build Status](https://travis-ci.org/scrumpy/tiptap.svg?branch=master)](https://travis-ci.org/scrumpy/tiptap)
|
||||
[![Gitpod Ready-to-Code](https://img.shields.io/badge/Gitpod-Ready--to--Code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/ueberdosis/tiptap)
|
||||
[![Version](https://img.shields.io/npm/v/tiptap.svg?label=version)](https://www.npmjs.com/package/tiptap)
|
||||
[![Downloads](https://img.shields.io/npm/dm/tiptap.svg)](https://npmcharts.com/compare/tiptap?minimal=true)
|
||||
[![License](https://img.shields.io/npm/l/tiptap.svg)](https://www.npmjs.com/package/tiptap)
|
||||
[![Filesize](https://img.badgesize.io/https://unpkg.com/tiptap/dist/tiptap.min.js?compression=gzip&label=size&colorB=000000)](https://www.npmjs.com/package/tiptap)
|
||||
[![Build Status](https://github.com/ueberdosis/tiptap/workflows/ci/badge.svg)](https://github.com/ueberdosis/tiptap/actions)
|
||||
[![Sponsor](https://img.shields.io/static/v1?label=Sponsor&message=%E2%9D%A4&logo=GitHub)](https://github.com/sponsors/ueberdosis)
|
||||
|
||||
## Why I built tiptap
|
||||
I was looking for a text editor for [Vue.js](https://github.com/vuejs/vue) and found some solutions that didn't really satisfy me. The 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. I 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 `renderless` mean?
|
||||
|
||||
With renderless components you'll have (almost) full control over markup and styling. I 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 [good article](https://adamwathan.me/renderless-components-in-vuejs/) about renderless components by Adam Wathan.
|
||||
With renderless components you'll have (almost) full control over markup and styling. I 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 [good article about renderless 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 pass these two types 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
|
||||
To check out some live examples, visit [tiptap.scrumpy.io](https://tiptap.scrumpy.io/).
|
||||
To check out some live examples, visit [tiptap.dev](https://tiptap.dev/).
|
||||
|
||||
## Installation
|
||||
```
|
||||
@ -86,7 +93,7 @@ export default {
|
||||
| `setContent` | `content, emitUpdate, parseOptions` | Replace the current content. You can pass an HTML string or a JSON document. `emitUpdate` defaults to `false`. `parseOptions` defaults to those provided in constructor. |
|
||||
| `clearContent` | `emitUpdate` | Clears the current content. `emitUpdate` defaults to `false`. |
|
||||
| `setOptions` | `options` | Overwrites the current editor properties. |
|
||||
| `registerPlugin` | `plugin` | Register a Prosemirror plugin. |
|
||||
| `registerPlugin` | `plugin`, `handlePlugins` | Register a Prosemirror plugin. You can pass a function `handlePlugins` with parameters `(plugin, oldPlugins)` to define an order in which `newPlugins` will be called. `handlePlugins` defaults to pushing `plugin` to front of `oldPlugins`. |
|
||||
| `getJSON` | – | Get the current content as JSON. |
|
||||
| `getHTML` | – | Get the current content as HTML. |
|
||||
| `focus` | – | Focus the editor. |
|
||||
@ -111,6 +118,7 @@ The `<editor-menu-bar />` component is renderless and will receive some properti
|
||||
| `commands` | `Array` | A list of all commands. |
|
||||
| `isActive` | `Object` | An object of functions to check if your selected text is a node or mark. `isActive.{node|mark}(attrs)` |
|
||||
| `getMarkAttrs` | `Function` | A function to get all mark attributes of your selection. |
|
||||
| `getNodeAttrs` | `Function` | A function to get all node attributes of your selection. |
|
||||
| `focused` | `Boolean` | Whether the editor is focused. |
|
||||
| `focus` | `Function` | A function to focus the editor. |
|
||||
|
||||
@ -140,6 +148,7 @@ The `<editor-menu-bubble />` component is renderless and will receive some prope
|
||||
| `commands` | `Array` | A list of all commands. |
|
||||
| `isActive` | `Object` | An object of functions to check if your selected text is a node or mark. `isActive.{node|mark}(attrs)` |
|
||||
| `getMarkAttrs` | `Function` | A function to get all mark attributes of your selection. |
|
||||
| `getNodeAttrs` | `Function` | A function to get all node attributes of your selection. |
|
||||
| `focused` | `Boolean` | Whether the editor is focused. |
|
||||
| `focus` | `Function` | A function to focus the editor. |
|
||||
| `menu` | `Object` | An object for positioning your menu. |
|
||||
@ -173,6 +182,7 @@ The `<editor-floating-menu />` component is renderless and will receive some pro
|
||||
| `commands` | `Array` | A list of all commands. |
|
||||
| `isActive` | `Object` | An object of functions to check if your selected text is a node or mark. `isActive.{node|mark}(attrs)` |
|
||||
| `getMarkAttrs` | `Function` | A function to get all mark attributes of your selection. |
|
||||
| `getNodeAttrs` | `Function` | A function to get all node attributes of your selection. |
|
||||
| `focused` | `Boolean` | Whether the editor is focused. |
|
||||
| `focus` | `Function` | A function to focus the editor. |
|
||||
| `menu` | `Object` | An object for positioning your menu. |
|
||||
@ -375,7 +385,7 @@ export default class BlockquoteNode extends Node {
|
||||
|
||||
### Create a Node as a Vue Component
|
||||
|
||||
The real power of the nodes comes in combination with Vue components. Let us build an iframe node, where you can change its URL (this can also be found in our [examples](https://github.com/scrumpy/tiptap/tree/master/examples/Components/Routes/Embeds)).
|
||||
The real power of the nodes comes in combination with Vue components. Let us build an iframe node, where you can change its URL (this can also be found in our [examples](https://github.com/ueberdosis/tiptap/tree/master/examples/Components/Routes/Embeds)).
|
||||
|
||||
```js
|
||||
import { Node } from 'tiptap'
|
||||
@ -420,7 +430,10 @@ export default class IframeNode extends Node {
|
||||
// `updateAttrs` is a function to update attributes defined in `schema`
|
||||
// `view` is the ProseMirror view instance
|
||||
// `options` is an array of your extension options
|
||||
// `selected`
|
||||
// `selected` is a boolean which is true when selected
|
||||
// `editor` is a reference to the TipTap editor instance
|
||||
// `getPos` is a function to retrieve the start position of the node
|
||||
// `decorations` is an array of decorations around the node
|
||||
props: ['node', 'updateAttrs', 'view'],
|
||||
computed: {
|
||||
src: {
|
||||
@ -497,20 +510,26 @@ Please see [CONTRIBUTING](CONTRIBUTING.md) for details.
|
||||
## Credits
|
||||
|
||||
- [Philipp Kühn](https://github.com/philippkuehn)
|
||||
- [Hans Pagel](https://github.com/hanspagel)
|
||||
- [Christoph Flathmann](https://github.com/Chrissi2812)
|
||||
- [Erick Wilder](https://github.com/erickwilder)
|
||||
- [Marius Tolzmann](https://github.com/mariux)
|
||||
- [All Contributors](../../contributors)
|
||||
|
||||
## Packages Using Tiptap
|
||||
## Related projects
|
||||
|
||||
- [html-to-prosemirror](https://github.com/ueberdosis/html-to-prosemirror) by @hanspagel
|
||||
- [prosemirror-to-html](https://github.com/ueberdosis/prosemirror-to-html) by @hanspagel
|
||||
- [tiptap-svelte](https://github.com/andrewjk/tiptap-svelte) by @andrewjk
|
||||
- [Laravel Nova Tiptap Editor Field](https://github.com/manogi/nova-tiptap) by @manogi
|
||||
- [WYSIWYG editor for Vuetify](https://github.com/iliyaZelenko/tiptap-vuetify) by @iliyaZelenko
|
||||
- [Quasar Tiptap Demo](https://github.com/kfields/quasar-tiptap-demo) @kfields
|
||||
- [Python Library that converts tiptap JSON](https://github.com/scrolltech/tiptapy) @scrolltech
|
||||
- [WYSIWYG editor for Element UI](https://github.com/Leecason/element-tiptap) by @Leecason
|
||||
- [WYSIWYG editor for Quasar Framework](https://github.com/donotebase/quasar-tiptap) by @mekery
|
||||
|
||||
## Love our work?
|
||||
[Become a backer](https://www.paypal.me/philippkuehn) ❤️
|
||||
[Sponsor us](https://github.com/sponsors/ueberdosis) ❤️
|
||||
|
||||
## License
|
||||
|
||||
|
@ -1,7 +0,0 @@
|
||||
<template>
|
||||
<a class="ad" href="https://scrumpy.io/" target="_blank">
|
||||
<img class="ad__image" src="https://drop.philipp-kuehn.com/7RIu3ptVUQ.png" alt="Scrumpy. Agile, Made Simple. Get Started." />
|
||||
</a>
|
||||
</template>
|
||||
|
||||
<style lang="scss" src="./style.scss" scoped></style>
|
@ -1,42 +0,0 @@
|
||||
@import "~variables";
|
||||
|
||||
.ad {
|
||||
display: block;
|
||||
padding: 1rem;
|
||||
transition: 0.2s transform;
|
||||
margin: 3rem auto 0 auto;
|
||||
width: 15rem;
|
||||
|
||||
@media (min-width: 1020px) {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
&__image {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
border-radius: 5px;
|
||||
overflow: hidden;
|
||||
transition: 0.2s box-shadow;
|
||||
box-shadow:
|
||||
0 2px 4px 0 rgba(black, 0.05),
|
||||
0 2px 10px 0 rgba(black, 0.07)
|
||||
;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-5px);
|
||||
}
|
||||
|
||||
&:hover &__image {
|
||||
box-shadow:
|
||||
0 2px 1px 0 rgba(black, 0.07),
|
||||
0 5px 20px 0 rgba(black, 0.06),
|
||||
0 8px 40px 0 rgba(black, 0.04)
|
||||
;
|
||||
}
|
||||
|
||||
}
|
@ -20,8 +20,6 @@
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<ad />
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -30,7 +28,6 @@ import Navigation from 'Components/Navigation'
|
||||
import Hero from 'Components/Hero'
|
||||
import Subnavigation from 'Components/Subnavigation'
|
||||
import Icon from 'Components/Icon'
|
||||
import Ad from 'Components/Ad'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@ -38,7 +35,6 @@ export default {
|
||||
Hero,
|
||||
Subnavigation,
|
||||
Icon,
|
||||
Ad,
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
@ -7,20 +7,23 @@
|
||||
</h1>
|
||||
<github-button
|
||||
class="navigation__count"
|
||||
href="https://github.com/scrumpy/tiptap"
|
||||
href="https://github.com/ueberdosis/tiptap"
|
||||
data-show-count="true"
|
||||
aria-label="Star scrumpy/tiptap on GitHub"
|
||||
aria-label="Star ueberdosis/tiptap on GitHub"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<a class="navigation__link" href="https://tiptap.scrumpy.io/docs" target="_blank">
|
||||
<a class="navigation__link" href="https://tiptap.dev/docs" target="_blank">
|
||||
Documentation
|
||||
</a>
|
||||
<a class="navigation__link" href="https://github.com/scrumpy/tiptap/blob/master/CONTRIBUTING.md" target="_blank">
|
||||
<a class="navigation__link" href="https://github.com/ueberdosis/tiptap/blob/master/CONTRIBUTING.md" target="_blank">
|
||||
Contribute
|
||||
</a>
|
||||
<a class="navigation__github-link" href="https://github.com/scrumpy/tiptap" target="_blank">
|
||||
<a class="navigation__link" href="https://github.com/sponsors/ueberdosis" target="_blank">
|
||||
Sponsor
|
||||
</a>
|
||||
<a class="navigation__github-link" href="https://github.com/ueberdosis/tiptap" target="_blank">
|
||||
<icon class="navigation__icon" name="github" />
|
||||
</a>
|
||||
</div>
|
||||
|
@ -35,7 +35,7 @@
|
||||
|
||||
<script>
|
||||
import Fuse from 'fuse.js'
|
||||
import tippy from 'tippy.js'
|
||||
import tippy, { sticky } from 'tippy.js'
|
||||
import Icon from 'Components/Icon'
|
||||
import { Editor, EditorContent, EditorMenuBar } from 'tiptap'
|
||||
import {
|
||||
@ -103,18 +103,17 @@ export default {
|
||||
},
|
||||
// is called on every keyDown event while a suggestion is active
|
||||
onKeyDown: ({ event }) => {
|
||||
// pressing up arrow
|
||||
if (event.keyCode === 38) {
|
||||
if (event.key === 'ArrowUp') {
|
||||
this.upHandler()
|
||||
return true
|
||||
}
|
||||
// pressing down arrow
|
||||
if (event.keyCode === 40) {
|
||||
|
||||
if (event.key === 'ArrowDown') {
|
||||
this.downHandler()
|
||||
return true
|
||||
}
|
||||
// pressing enter
|
||||
if (event.keyCode === 13) {
|
||||
|
||||
if (event.key === 'Enter') {
|
||||
this.enterHandler()
|
||||
return true
|
||||
}
|
||||
@ -135,7 +134,7 @@ export default {
|
||||
keys: ['name'],
|
||||
})
|
||||
|
||||
return fuse.search(query)
|
||||
return fuse.search(query).map(item => item.item)
|
||||
},
|
||||
}),
|
||||
new Code(),
|
||||
@ -159,7 +158,6 @@ export default {
|
||||
filteredUsers: [],
|
||||
navigatedUserIndex: 0,
|
||||
insertMention: () => {},
|
||||
observer: null,
|
||||
}
|
||||
},
|
||||
|
||||
@ -217,44 +215,35 @@ export default {
|
||||
return
|
||||
}
|
||||
|
||||
this.popup = tippy(node, {
|
||||
content: this.$refs.suggestions,
|
||||
trigger: 'mouseenter',
|
||||
// ref: https://atomiks.github.io/tippyjs/v6/all-props/
|
||||
this.popup = tippy('.page', {
|
||||
getReferenceClientRect: node.getBoundingClientRect,
|
||||
appendTo: () => document.body,
|
||||
interactive: true,
|
||||
sticky: true, // make sure position of tippy is updated when content changes
|
||||
plugins: [sticky],
|
||||
content: this.$refs.suggestions,
|
||||
trigger: 'mouseenter', // manual
|
||||
showOnCreate: true,
|
||||
theme: 'dark',
|
||||
placement: 'top-start',
|
||||
inertia: true,
|
||||
duration: [400, 200],
|
||||
showOnInit: true,
|
||||
arrow: true,
|
||||
arrowType: 'round',
|
||||
})
|
||||
|
||||
// we have to update tippy whenever the DOM is updated
|
||||
if (MutationObserver) {
|
||||
this.observer = new MutationObserver(() => {
|
||||
this.popup.popperInstance.scheduleUpdate()
|
||||
})
|
||||
this.observer.observe(this.$refs.suggestions, {
|
||||
childList: true,
|
||||
subtree: true,
|
||||
characterData: true,
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
destroyPopup() {
|
||||
if (this.popup) {
|
||||
this.popup.destroy()
|
||||
this.popup[0].destroy()
|
||||
this.popup = null
|
||||
}
|
||||
|
||||
if (this.observer) {
|
||||
this.observer.disconnect()
|
||||
}
|
||||
},
|
||||
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
this.destroyPopup()
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -306,36 +295,12 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
.tippy-tooltip.dark-theme {
|
||||
.tippy-box[data-theme~=dark] {
|
||||
background-color: $color-black;
|
||||
padding: 0;
|
||||
font-size: 1rem;
|
||||
text-align: inherit;
|
||||
color: $color-white;
|
||||
border-radius: 5px;
|
||||
|
||||
.tippy-backdrop {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.tippy-roundarrow {
|
||||
fill: $color-black;
|
||||
}
|
||||
|
||||
.tippy-popper[x-placement^=top] & .tippy-arrow {
|
||||
border-top-color: $color-black;
|
||||
}
|
||||
|
||||
.tippy-popper[x-placement^=bottom] & .tippy-arrow {
|
||||
border-bottom-color: $color-black;
|
||||
}
|
||||
|
||||
.tippy-popper[x-placement^=left] & .tippy-arrow {
|
||||
border-left-color: $color-black;
|
||||
}
|
||||
|
||||
.tippy-popper[x-placement^=right] & .tippy-arrow {
|
||||
border-right-color: $color-black;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -27,8 +27,7 @@
|
||||
}
|
||||
|
||||
p code {
|
||||
display: inline-block;
|
||||
padding: 0 0.4rem;
|
||||
padding: 0.2rem 0.4rem;
|
||||
border-radius: 5px;
|
||||
font-size: 0.8rem;
|
||||
font-weight: bold;
|
||||
|
@ -15,154 +15,154 @@ const routes = [
|
||||
path: '/',
|
||||
component: () => import('Components/Routes/Basic'),
|
||||
meta: {
|
||||
githubUrl: 'https://github.com/scrumpy/tiptap/tree/master/examples/Components/Routes/Basic',
|
||||
githubUrl: 'https://github.com/ueberdosis/tiptap/tree/master/examples/Components/Routes/Basic',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/menu-bubble',
|
||||
component: () => import('Components/Routes/MenuBubble'),
|
||||
meta: {
|
||||
githubUrl: 'https://github.com/scrumpy/tiptap/tree/master/examples/Components/Routes/MenuBubble',
|
||||
githubUrl: 'https://github.com/ueberdosis/tiptap/tree/master/examples/Components/Routes/MenuBubble',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/floating-menu',
|
||||
component: () => import('Components/Routes/FloatingMenu'),
|
||||
meta: {
|
||||
githubUrl: 'https://github.com/scrumpy/tiptap/tree/master/examples/Components/Routes/FloatingMenu',
|
||||
githubUrl: 'https://github.com/ueberdosis/tiptap/tree/master/examples/Components/Routes/FloatingMenu',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/links',
|
||||
component: () => import('Components/Routes/Links'),
|
||||
meta: {
|
||||
githubUrl: 'https://github.com/scrumpy/tiptap/tree/master/examples/Components/Routes/Links',
|
||||
githubUrl: 'https://github.com/ueberdosis/tiptap/tree/master/examples/Components/Routes/Links',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/images',
|
||||
component: () => import('Components/Routes/Images'),
|
||||
meta: {
|
||||
githubUrl: 'https://github.com/scrumpy/tiptap/tree/master/examples/Components/Routes/Images',
|
||||
githubUrl: 'https://github.com/ueberdosis/tiptap/tree/master/examples/Components/Routes/Images',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/hiding-menu-bar',
|
||||
component: () => import('Components/Routes/HidingMenuBar'),
|
||||
meta: {
|
||||
githubUrl: 'https://github.com/scrumpy/tiptap/tree/master/examples/Components/Routes/HidingMenuBar',
|
||||
githubUrl: 'https://github.com/ueberdosis/tiptap/tree/master/examples/Components/Routes/HidingMenuBar',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/tables',
|
||||
component: () => import('Components/Routes/Tables'),
|
||||
meta: {
|
||||
githubUrl: 'https://github.com/scrumpy/tiptap/tree/master/examples/Components/Routes/Tables',
|
||||
githubUrl: 'https://github.com/ueberdosis/tiptap/tree/master/examples/Components/Routes/Tables',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/todo-list',
|
||||
component: () => import('Components/Routes/TodoList'),
|
||||
meta: {
|
||||
githubUrl: 'https://github.com/scrumpy/tiptap/tree/master/examples/Components/Routes/TodoList',
|
||||
githubUrl: 'https://github.com/ueberdosis/tiptap/tree/master/examples/Components/Routes/TodoList',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/search-and-replace',
|
||||
component: () => import('Components/Routes/SearchAndReplace'),
|
||||
meta: {
|
||||
githubUrl: 'https://github.com/scrumpy/tiptap/tree/master/examples/Components/Routes/SearchAndReplace',
|
||||
githubUrl: 'https://github.com/ueberdosis/tiptap/tree/master/examples/Components/Routes/SearchAndReplace',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/suggestions',
|
||||
component: () => import('Components/Routes/Suggestions'),
|
||||
meta: {
|
||||
githubUrl: 'https://github.com/scrumpy/tiptap/tree/master/examples/Components/Routes/Suggestions',
|
||||
githubUrl: 'https://github.com/ueberdosis/tiptap/tree/master/examples/Components/Routes/Suggestions',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/markdown-shortcuts',
|
||||
component: () => import('Components/Routes/MarkdownShortcuts'),
|
||||
meta: {
|
||||
githubUrl: 'https://github.com/scrumpy/tiptap/tree/master/examples/Components/Routes/MarkdownShortcuts',
|
||||
githubUrl: 'https://github.com/ueberdosis/tiptap/tree/master/examples/Components/Routes/MarkdownShortcuts',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/code-highlighting',
|
||||
component: () => import('Components/Routes/CodeHighlighting'),
|
||||
meta: {
|
||||
githubUrl: 'https://github.com/scrumpy/tiptap/tree/master/examples/Components/Routes/CodeHighlighting',
|
||||
githubUrl: 'https://github.com/ueberdosis/tiptap/tree/master/examples/Components/Routes/CodeHighlighting',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/history',
|
||||
component: () => import('Components/Routes/History'),
|
||||
meta: {
|
||||
githubUrl: 'https://github.com/scrumpy/tiptap/tree/master/examples/Components/Routes/History',
|
||||
githubUrl: 'https://github.com/ueberdosis/tiptap/tree/master/examples/Components/Routes/History',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/read-only',
|
||||
component: () => import('Components/Routes/ReadOnly'),
|
||||
meta: {
|
||||
githubUrl: 'https://github.com/scrumpy/tiptap/tree/master/examples/Components/Routes/ReadOnly',
|
||||
githubUrl: 'https://github.com/ueberdosis/tiptap/tree/master/examples/Components/Routes/ReadOnly',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/embeds',
|
||||
component: () => import('Components/Routes/Embeds'),
|
||||
meta: {
|
||||
githubUrl: 'https://github.com/scrumpy/tiptap/tree/master/examples/Components/Routes/Embeds',
|
||||
githubUrl: 'https://github.com/ueberdosis/tiptap/tree/master/examples/Components/Routes/Embeds',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/placeholder',
|
||||
component: () => import('Components/Routes/Placeholder'),
|
||||
meta: {
|
||||
githubUrl: 'https://github.com/scrumpy/tiptap/tree/master/examples/Components/Routes/Placeholder',
|
||||
githubUrl: 'https://github.com/ueberdosis/tiptap/tree/master/examples/Components/Routes/Placeholder',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/focus',
|
||||
component: () => import('Components/Routes/Focus'),
|
||||
meta: {
|
||||
githubUrl: 'https://github.com/scrumpy/tiptap/tree/master/examples/Components/Routes/Focus',
|
||||
githubUrl: 'https://github.com/ueberdosis/tiptap/tree/master/examples/Components/Routes/Focus',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/collaboration',
|
||||
component: () => import('Components/Routes/Collaboration'),
|
||||
meta: {
|
||||
githubUrl: 'https://github.com/scrumpy/tiptap/tree/master/examples/Components/Routes/Collaboration',
|
||||
githubUrl: 'https://github.com/ueberdosis/tiptap/tree/master/examples/Components/Routes/Collaboration',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/title',
|
||||
component: () => import('Components/Routes/Title'),
|
||||
meta: {
|
||||
githubUrl: 'https://github.com/scrumpy/tiptap/tree/master/examples/Components/Routes/Title',
|
||||
githubUrl: 'https://github.com/ueberdosis/tiptap/tree/master/examples/Components/Routes/Title',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/trailing-paragraph',
|
||||
component: () => import('Components/Routes/TrailingParagraph'),
|
||||
meta: {
|
||||
githubUrl: 'https://github.com/scrumpy/tiptap/tree/master/examples/Components/Routes/TrailingParagraph',
|
||||
githubUrl: 'https://github.com/ueberdosis/tiptap/tree/master/examples/Components/Routes/TrailingParagraph',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/drag-handle',
|
||||
component: () => import('Components/Routes/DragHandle'),
|
||||
meta: {
|
||||
githubUrl: 'https://github.com/scrumpy/tiptap/tree/master/examples/Components/Routes/DragHandle',
|
||||
githubUrl: 'https://github.com/ueberdosis/tiptap/tree/master/examples/Components/Routes/DragHandle',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/export',
|
||||
component: () => import('Components/Routes/Export'),
|
||||
meta: {
|
||||
githubUrl: 'https://github.com/scrumpy/tiptap/tree/master/examples/Components/Routes/Export',
|
||||
githubUrl: 'https://github.com/ueberdosis/tiptap/tree/master/examples/Components/Routes/Export',
|
||||
},
|
||||
},
|
||||
]
|
||||
|
11
netlify.toml
11
netlify.toml
@ -1,3 +1,13 @@
|
||||
[[redirects]]
|
||||
from = "https://tiptap.scrumpy.io"
|
||||
to = "https://www.tiptap.dev"
|
||||
status = 301
|
||||
|
||||
[[redirects]]
|
||||
from = "https://tiptap.scrumpy.io/*"
|
||||
to = "https://www.tiptap.dev/:splat"
|
||||
status = 301
|
||||
|
||||
[[redirects]]
|
||||
from = "/docs/*"
|
||||
to = "https://tiptap-docs.netlify.com/:splat"
|
||||
@ -7,3 +17,4 @@
|
||||
from = "/*"
|
||||
to = "/index.html"
|
||||
status = 200
|
||||
|
||||
|
@ -47,7 +47,7 @@
|
||||
"eslint-plugin-import": "^2.18.2",
|
||||
"eslint-plugin-vue": "6.0.1",
|
||||
"file-loader": "^5.0.2",
|
||||
"fuse.js": "^3.4.6",
|
||||
"fuse.js": "^6.0.0",
|
||||
"glob": "^7.1.6",
|
||||
"html-webpack-plugin": "^3.2.0",
|
||||
"http-proxy-middleware": "^0.20.0",
|
||||
@ -76,7 +76,7 @@
|
||||
"sass-loader": "^8.0.0",
|
||||
"style-loader": "^1.0.1",
|
||||
"terser": "^4.4.2",
|
||||
"tippy.js": "^4.3.5",
|
||||
"tippy.js": "^6.1.0",
|
||||
"vue": "^2.6.10",
|
||||
"vue-loader": "^15.7.2",
|
||||
"vue-router": "^3.1.3",
|
||||
@ -91,6 +91,6 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"socket.io-client": "^2.3.0",
|
||||
"vue-github-button": "^1.1.2"
|
||||
"vue-github-button": "^1.2.0"
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
{
|
||||
"name": "tiptap-commands",
|
||||
"version": "1.12.5",
|
||||
"version": "1.14.1",
|
||||
"description": "Commands for tiptap",
|
||||
"homepage": "https://tiptap.scrumpy.io",
|
||||
"homepage": "https://tiptap.dev",
|
||||
"license": "MIT",
|
||||
"main": "dist/commands.common.js",
|
||||
"module": "dist/commands.esm.js",
|
||||
@ -14,19 +14,19 @@
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/scrumpy/tiptap.git"
|
||||
"url": "git+https://github.com/ueberdosis/tiptap.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/scrumpy/tiptap/issues"
|
||||
"url": "https://github.com/ueberdosis/tiptap/issues"
|
||||
},
|
||||
"dependencies": {
|
||||
"prosemirror-commands": "1.1.2",
|
||||
"prosemirror-inputrules": "1.1.2",
|
||||
"prosemirror-model": "1.8.2",
|
||||
"prosemirror-schema-list": "1.1.2",
|
||||
"prosemirror-state": "1.3.2",
|
||||
"prosemirror-tables": "1.0.0",
|
||||
"prosemirror-utils": "0.9.6",
|
||||
"tiptap-utils": "^1.8.3"
|
||||
"prosemirror-commands": "^1.1.4",
|
||||
"prosemirror-inputrules": "^1.1.2",
|
||||
"prosemirror-model": "^1.10.0",
|
||||
"prosemirror-schema-list": "^1.1.2",
|
||||
"prosemirror-state": "^1.3.3",
|
||||
"prosemirror-tables": "^1.1.0",
|
||||
"prosemirror-utils": "^0.9.6",
|
||||
"tiptap-utils": "^1.10.1"
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ import { Slice, Fragment } from 'prosemirror-model'
|
||||
|
||||
export default function (regexp, type, getAttrs) {
|
||||
|
||||
const handler = fragment => {
|
||||
const handler = (fragment, parent) => {
|
||||
const nodes = []
|
||||
|
||||
fragment.forEach(child => {
|
||||
@ -16,7 +16,7 @@ export default function (regexp, type, getAttrs) {
|
||||
|
||||
// eslint-disable-next-line
|
||||
while (!isLink && (match = regexp.exec(text)) !== null) {
|
||||
if (match[1]) {
|
||||
if (parent && parent.type.allowsMarkType(type) && match[1]) {
|
||||
const start = match.index
|
||||
const end = start + match[0].length
|
||||
const textStart = start + match[0].indexOf(match[1])
|
||||
@ -43,7 +43,7 @@ export default function (regexp, type, getAttrs) {
|
||||
nodes.push(child.cut(pos))
|
||||
}
|
||||
} else {
|
||||
nodes.push(child.copy(handler(child.content)))
|
||||
nodes.push(child.copy(handler(child.content, child)))
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -1,14 +1,14 @@
|
||||
import { wrapIn, lift } from 'prosemirror-commands'
|
||||
import { nodeIsActive } from 'tiptap-utils'
|
||||
|
||||
export default function (type) {
|
||||
export default function (type, attrs = {}) {
|
||||
return (state, dispatch, view) => {
|
||||
const isActive = nodeIsActive(state, type)
|
||||
const isActive = nodeIsActive(state, type, attrs)
|
||||
|
||||
if (isActive) {
|
||||
return lift(state, dispatch)
|
||||
}
|
||||
|
||||
return wrapIn(type)(state, dispatch, view)
|
||||
return wrapIn(type, attrs)(state, dispatch, view)
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
{
|
||||
"name": "tiptap-extensions",
|
||||
"version": "1.28.6",
|
||||
"version": "1.31.1",
|
||||
"description": "Extensions for tiptap",
|
||||
"homepage": "https://tiptap.scrumpy.io",
|
||||
"homepage": "https://tiptap.dev",
|
||||
"license": "MIT",
|
||||
"main": "dist/extensions.common.js",
|
||||
"module": "dist/extensions.esm.js",
|
||||
@ -15,23 +15,23 @@
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/scrumpy/tiptap.git"
|
||||
"url": "git+https://github.com/ueberdosis/tiptap.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/scrumpy/tiptap/issues"
|
||||
"url": "https://github.com/ueberdosis/tiptap/issues"
|
||||
},
|
||||
"dependencies": {
|
||||
"lowlight": "1.13.0",
|
||||
"prosemirror-collab": "1.2.2",
|
||||
"prosemirror-history": "1.1.3",
|
||||
"prosemirror-model": "1.8.2",
|
||||
"prosemirror-state": "1.3.2",
|
||||
"prosemirror-tables": "1.0.0",
|
||||
"prosemirror-transform": "1.2.3",
|
||||
"prosemirror-utils": "0.9.6",
|
||||
"prosemirror-view": "1.13.7",
|
||||
"tiptap": "^1.26.6",
|
||||
"tiptap-commands": "^1.12.5"
|
||||
"lowlight": "^1.14.0",
|
||||
"prosemirror-collab": "^1.2.2",
|
||||
"prosemirror-history": "^1.1.3",
|
||||
"prosemirror-model": "^1.10.0",
|
||||
"prosemirror-state": "^1.3.3",
|
||||
"prosemirror-tables": "^1.1.0",
|
||||
"prosemirror-transform": "^1.2.6",
|
||||
"prosemirror-utils": "^0.9.6",
|
||||
"prosemirror-view": "^1.15.0",
|
||||
"tiptap": "^1.29.1",
|
||||
"tiptap-commands": "^1.14.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vue": "^2.5.17",
|
||||
|
@ -17,12 +17,6 @@ export default class Placeholder extends Extension {
|
||||
}
|
||||
}
|
||||
|
||||
get update() {
|
||||
return view => {
|
||||
view.updateState(view.state)
|
||||
}
|
||||
}
|
||||
|
||||
get plugins() {
|
||||
return [
|
||||
new Plugin({
|
||||
|
@ -11,6 +11,7 @@ export default class Link extends Mark {
|
||||
get defaultOptions() {
|
||||
return {
|
||||
openOnClick: true,
|
||||
target: null,
|
||||
}
|
||||
}
|
||||
|
||||
@ -20,6 +21,9 @@ export default class Link extends Mark {
|
||||
href: {
|
||||
default: null,
|
||||
},
|
||||
target: {
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
inclusive: false,
|
||||
parseDOM: [
|
||||
@ -27,12 +31,14 @@ export default class Link extends Mark {
|
||||
tag: 'a[href]',
|
||||
getAttrs: dom => ({
|
||||
href: dom.getAttribute('href'),
|
||||
target: dom.getAttribute('target'),
|
||||
}),
|
||||
},
|
||||
],
|
||||
toDOM: node => ['a', {
|
||||
...node.attrs,
|
||||
rel: 'noopener noreferrer nofollow',
|
||||
target: this.options.target,
|
||||
}, 0],
|
||||
}
|
||||
}
|
||||
@ -71,7 +77,7 @@ export default class Link extends Mark {
|
||||
|
||||
if (attrs.href && event.target instanceof HTMLAnchorElement) {
|
||||
event.stopPropagation()
|
||||
window.open(attrs.href)
|
||||
window.open(attrs.href, attrs.target)
|
||||
}
|
||||
},
|
||||
},
|
||||
|
@ -29,6 +29,39 @@ export default class TodoItem extends Node {
|
||||
<div class="todo-content" ref="content" :contenteditable="view.editable.toString()"></div>
|
||||
</li>
|
||||
`,
|
||||
/*
|
||||
The render function enables TodoItem to work in `runtimeonly` builds,
|
||||
which is required for frameworks requiring strict CSP policies. For
|
||||
example, doing this is required in Chrome Extensions. Having both
|
||||
the template and the render function ensures there are no issues
|
||||
converting the node to JSON and rendering the component.
|
||||
*/
|
||||
render(h) {
|
||||
return h('li', {
|
||||
attrs: {
|
||||
'data-type': this.node.type.name,
|
||||
'data-done': this.node.attrs.done.toString(),
|
||||
'data-drag-handle': '',
|
||||
},
|
||||
}, [
|
||||
h('span', {
|
||||
class: 'todo-checkbox',
|
||||
attrs: {
|
||||
contenteditable: false,
|
||||
},
|
||||
on: {
|
||||
click: this.onChange,
|
||||
},
|
||||
}),
|
||||
h('div', {
|
||||
class: 'todo-content',
|
||||
attrs: {
|
||||
contenteditable: this.view.editable.toString(),
|
||||
},
|
||||
ref: 'content',
|
||||
}),
|
||||
])
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -62,17 +62,20 @@ export default function HighlightPlugin({ name }) {
|
||||
name: new PluginKey('highlight'),
|
||||
state: {
|
||||
init: (_, { doc }) => getDecorations({ doc, name }),
|
||||
apply: (transaction, decorationSet, oldState, state) => {
|
||||
apply: (transaction, decorationSet, oldState, newState) => {
|
||||
// TODO: find way to cache decorations
|
||||
// see: https://discuss.prosemirror.net/t/how-to-update-multiple-inline-decorations-on-node-change/1493
|
||||
|
||||
const nodeName = state.selection.$head.parent.type.name
|
||||
const previousNodeName = oldState.selection.$head.parent.type.name
|
||||
|
||||
if (transaction.docChanged && [nodeName, previousNodeName].includes(name)) {
|
||||
// https://discuss.prosemirror.net/t/how-to-update-multiple-inline-decorations-on-node-change/1493
|
||||
const oldNodeName = oldState.selection.$head.parent.type.name
|
||||
const newNodeName = newState.selection.$head.parent.type.name
|
||||
const oldNodes = findBlockNodes(oldState.doc)
|
||||
.filter(item => item.node.type.name === name)
|
||||
const newNodes = findBlockNodes(newState.doc)
|
||||
.filter(item => item.node.type.name === name)
|
||||
// Apply decorations if selection includes named node, or transaction changes named node.
|
||||
if (transaction.docChanged && ([oldNodeName, newNodeName].includes(name)
|
||||
|| newNodes.length !== oldNodes.length)) {
|
||||
return getDecorations({ doc: transaction.doc, name })
|
||||
}
|
||||
|
||||
return decorationSet.map(transaction.mapping, transaction.doc)
|
||||
},
|
||||
},
|
||||
|
@ -1,8 +1,8 @@
|
||||
{
|
||||
"name": "tiptap-utils",
|
||||
"version": "1.8.3",
|
||||
"version": "1.10.1",
|
||||
"description": "Utility functions for tiptap",
|
||||
"homepage": "https://tiptap.scrumpy.io",
|
||||
"homepage": "https://tiptap.dev",
|
||||
"license": "MIT",
|
||||
"main": "dist/utils.common.js",
|
||||
"module": "dist/utils.esm.js",
|
||||
@ -14,15 +14,15 @@
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/scrumpy/tiptap.git"
|
||||
"url": "git+https://github.com/ueberdosis/tiptap.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/scrumpy/tiptap/issues"
|
||||
"url": "https://github.com/ueberdosis/tiptap/issues"
|
||||
},
|
||||
"dependencies": {
|
||||
"prosemirror-model": "1.8.2",
|
||||
"prosemirror-state": "1.3.2",
|
||||
"prosemirror-tables": "1.0.0",
|
||||
"prosemirror-utils": "0.9.6"
|
||||
"prosemirror-model": "^1.10.0",
|
||||
"prosemirror-state": "^1.3.3",
|
||||
"prosemirror-tables": "^1.1.0",
|
||||
"prosemirror-utils": "^0.9.6"
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
export { default as getMarkAttrs } from './utils/getMarkAttrs'
|
||||
export { default as getNodeAttrs } from './utils/getNodeAttrs'
|
||||
export { default as getMarkRange } from './utils/getMarkRange'
|
||||
export { default as markIsActive } from './utils/markIsActive'
|
||||
export { default as nodeEqualsType } from './utils/nodeEqualsType'
|
||||
|
18
packages/tiptap-utils/src/utils/getNodeAttrs.js
Normal file
18
packages/tiptap-utils/src/utils/getNodeAttrs.js
Normal file
@ -0,0 +1,18 @@
|
||||
export default function getNodeAttrs(state, type) {
|
||||
const { from, to } = state.selection
|
||||
let nodes = []
|
||||
|
||||
state.doc.nodesBetween(from, to, node => {
|
||||
nodes = [...nodes, node]
|
||||
})
|
||||
|
||||
const node = nodes
|
||||
.reverse()
|
||||
.find(nodeItem => nodeItem.type.name === type.name)
|
||||
|
||||
if (node) {
|
||||
return node.attrs
|
||||
}
|
||||
|
||||
return {}
|
||||
}
|
@ -12,5 +12,5 @@ export default function nodeIsActive(state, type, attrs = {}) {
|
||||
return !!node
|
||||
}
|
||||
|
||||
return node.node.hasMarkup(type, attrs)
|
||||
return node.node.hasMarkup(type, { ...node.node.attrs, ...attrs })
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
{
|
||||
"name": "tiptap",
|
||||
"version": "1.26.6",
|
||||
"version": "1.29.1",
|
||||
"description": "A rich-text editor for Vue.js",
|
||||
"homepage": "https://tiptap.scrumpy.io",
|
||||
"homepage": "https://tiptap.dev",
|
||||
"license": "MIT",
|
||||
"main": "dist/tiptap.common.js",
|
||||
"module": "dist/tiptap.esm.js",
|
||||
@ -14,22 +14,22 @@
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/scrumpy/tiptap.git"
|
||||
"url": "git+https://github.com/ueberdosis/tiptap.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/scrumpy/tiptap/issues"
|
||||
"url": "https://github.com/ueberdosis/tiptap/issues"
|
||||
},
|
||||
"dependencies": {
|
||||
"prosemirror-commands": "1.1.2",
|
||||
"prosemirror-commands": "1.1.4",
|
||||
"prosemirror-dropcursor": "1.3.2",
|
||||
"prosemirror-gapcursor": "1.1.2",
|
||||
"prosemirror-gapcursor": "1.1.5",
|
||||
"prosemirror-inputrules": "1.1.2",
|
||||
"prosemirror-keymap": "1.1.3",
|
||||
"prosemirror-model": "1.8.2",
|
||||
"prosemirror-state": "1.3.2",
|
||||
"prosemirror-view": "1.13.7",
|
||||
"tiptap-commands": "^1.12.5",
|
||||
"tiptap-utils": "^1.8.3"
|
||||
"prosemirror-keymap": "1.1.4",
|
||||
"prosemirror-model": "1.10.0",
|
||||
"prosemirror-state": "1.3.3",
|
||||
"prosemirror-view": "1.15.0",
|
||||
"tiptap-commands": "^1.14.1",
|
||||
"tiptap-utils": "^1.10.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vue": "^2.5.17",
|
||||
|
@ -55,6 +55,7 @@ export default {
|
||||
commands: this.editor.commands,
|
||||
isActive: this.editor.isActive,
|
||||
getMarkAttrs: this.editor.getMarkAttrs.bind(this.editor),
|
||||
getNodeAttrs: this.editor.getNodeAttrs.bind(this.editor),
|
||||
menu: this.menu,
|
||||
})
|
||||
},
|
||||
|
@ -52,6 +52,7 @@ export default {
|
||||
commands: this.editor.commands,
|
||||
isActive: this.editor.isActive,
|
||||
getMarkAttrs: this.editor.getMarkAttrs.bind(this.editor),
|
||||
getNodeAttrs: this.editor.getNodeAttrs.bind(this.editor),
|
||||
})
|
||||
},
|
||||
|
||||
|
@ -60,6 +60,7 @@ export default {
|
||||
commands: this.editor.commands,
|
||||
isActive: this.editor.isActive,
|
||||
getMarkAttrs: this.editor.getMarkAttrs.bind(this.editor),
|
||||
getNodeAttrs: this.editor.getNodeAttrs.bind(this.editor),
|
||||
menu: this.menu,
|
||||
})
|
||||
},
|
||||
|
@ -11,7 +11,12 @@ import { gapCursor } from 'prosemirror-gapcursor'
|
||||
import { keymap } from 'prosemirror-keymap'
|
||||
import { baseKeymap } from 'prosemirror-commands'
|
||||
import { inputRules, undoInputRule } from 'prosemirror-inputrules'
|
||||
import { markIsActive, nodeIsActive, getMarkAttrs } from 'tiptap-utils'
|
||||
import {
|
||||
markIsActive,
|
||||
nodeIsActive,
|
||||
getMarkAttrs,
|
||||
getNodeAttrs,
|
||||
} from 'tiptap-utils'
|
||||
import {
|
||||
injectCSS,
|
||||
camelCase,
|
||||
@ -267,9 +272,9 @@ export default class Editor extends Emitter {
|
||||
}
|
||||
|
||||
if (typeof content === 'string') {
|
||||
const element = document.createElement('div')
|
||||
element.innerHTML = content.trim()
|
||||
|
||||
const htmlString = `<div>${content}</div>`
|
||||
const parser = new window.DOMParser()
|
||||
const element = parser.parseFromString(htmlString, 'text/html').body
|
||||
return DOMParser.fromSchema(this.schema).parse(element, parseOptions)
|
||||
}
|
||||
|
||||
@ -475,6 +480,12 @@ export default class Editor extends Emitter {
|
||||
return this.activeMarkAttrs[type]
|
||||
}
|
||||
|
||||
getNodeAttrs(type = null) {
|
||||
return {
|
||||
...getNodeAttrs(this.state, this.schema.nodes[type]),
|
||||
}
|
||||
}
|
||||
|
||||
get isActive() {
|
||||
return Object
|
||||
.entries({
|
||||
@ -487,14 +498,11 @@ export default class Editor extends Emitter {
|
||||
}), {})
|
||||
}
|
||||
|
||||
registerPlugin(plugin = null) {
|
||||
if (!plugin) {
|
||||
return
|
||||
}
|
||||
|
||||
const newState = this.state.reconfigure({
|
||||
plugins: this.state.plugins.concat([plugin]),
|
||||
})
|
||||
registerPlugin(plugin = null, handlePlugins) {
|
||||
const plugins = typeof handlePlugins === 'function'
|
||||
? handlePlugins(plugin, this.state.plugins)
|
||||
: [plugin, ...this.state.plugins]
|
||||
const newState = this.state.reconfigure({ plugins })
|
||||
this.view.updateState(newState)
|
||||
}
|
||||
|
||||
|
@ -18,20 +18,22 @@ class Menu {
|
||||
|
||||
// the mousedown event is fired before blur so we can prevent it
|
||||
this.mousedownHandler = this.handleClick.bind(this)
|
||||
this.options.element.addEventListener('mousedown', this.mousedownHandler)
|
||||
this.options.element.addEventListener('mousedown', this.mousedownHandler, { capture: true })
|
||||
|
||||
this.options.editor.on('focus', ({ view }) => {
|
||||
this.focusHandler = ({ view }) => {
|
||||
this.update(view)
|
||||
})
|
||||
}
|
||||
this.options.editor.on('focus', this.focusHandler)
|
||||
|
||||
this.options.editor.on('blur', ({ event }) => {
|
||||
this.blurHandler = ({ event }) => {
|
||||
if (this.preventHide) {
|
||||
this.preventHide = false
|
||||
return
|
||||
}
|
||||
|
||||
this.hide(event)
|
||||
})
|
||||
}
|
||||
this.options.editor.on('blur', this.blurHandler)
|
||||
|
||||
// sometimes we have to update the position
|
||||
// because of a loaded images for example
|
||||
@ -100,6 +102,7 @@ class Menu {
|
||||
hide(event) {
|
||||
if (event
|
||||
&& event.relatedTarget
|
||||
&& this.options.element.parentNode
|
||||
&& this.options.element.parentNode.contains(event.relatedTarget)
|
||||
) {
|
||||
return
|
||||
@ -115,6 +118,9 @@ class Menu {
|
||||
if (this.resizeObserver) {
|
||||
this.resizeObserver.unobserve(this.editorView.dom)
|
||||
}
|
||||
|
||||
this.options.editor.off('focus', this.focusHandler)
|
||||
this.options.editor.off('blur', this.blurHandler)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -8,16 +8,17 @@ class Menu {
|
||||
|
||||
// the mousedown event is fired before blur so we can prevent it
|
||||
this.mousedownHandler = this.handleClick.bind(this)
|
||||
this.options.element.addEventListener('mousedown', this.mousedownHandler)
|
||||
this.options.element.addEventListener('mousedown', this.mousedownHandler, { capture: true })
|
||||
|
||||
this.options.editor.on('blur', () => {
|
||||
this.blurHandler = () => {
|
||||
if (this.preventHide) {
|
||||
this.preventHide = false
|
||||
return
|
||||
}
|
||||
|
||||
this.options.editor.emit('menubar:focusUpdate', false)
|
||||
})
|
||||
}
|
||||
this.options.editor.on('blur', this.blurHandler)
|
||||
}
|
||||
|
||||
handleClick() {
|
||||
@ -26,6 +27,7 @@ class Menu {
|
||||
|
||||
destroy() {
|
||||
this.options.element.removeEventListener('mousedown', this.mousedownHandler)
|
||||
this.options.editor.off('blur', this.blurHandler)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -50,7 +50,6 @@ function coordsAtPos(view, pos, end = false) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class Menu {
|
||||
|
||||
constructor({ options, editorView }) {
|
||||
@ -71,20 +70,22 @@ class Menu {
|
||||
|
||||
// the mousedown event is fired before blur so we can prevent it
|
||||
this.mousedownHandler = this.handleClick.bind(this)
|
||||
this.options.element.addEventListener('mousedown', this.mousedownHandler)
|
||||
this.options.element.addEventListener('mousedown', this.mousedownHandler, { capture: true })
|
||||
|
||||
this.options.editor.on('focus', ({ view }) => {
|
||||
this.focusHandler = ({ view }) => {
|
||||
this.update(view)
|
||||
})
|
||||
}
|
||||
this.options.editor.on('focus', this.focusHandler)
|
||||
|
||||
this.options.editor.on('blur', ({ event }) => {
|
||||
this.blurHandler = ({ event }) => {
|
||||
if (this.preventHide) {
|
||||
this.preventHide = false
|
||||
return
|
||||
}
|
||||
|
||||
this.hide(event)
|
||||
})
|
||||
}
|
||||
this.options.editor.on('blur', this.blurHandler)
|
||||
}
|
||||
|
||||
handleClick() {
|
||||
@ -155,6 +156,7 @@ class Menu {
|
||||
hide(event) {
|
||||
if (event
|
||||
&& event.relatedTarget
|
||||
&& this.options.element.parentNode
|
||||
&& this.options.element.parentNode.contains(event.relatedTarget)
|
||||
) {
|
||||
return
|
||||
@ -166,6 +168,8 @@ class Menu {
|
||||
|
||||
destroy() {
|
||||
this.options.element.removeEventListener('mousedown', this.mousedownHandler)
|
||||
this.options.editor.off('focus', this.focusHandler)
|
||||
this.options.editor.off('blur', this.blurHandler)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -45,6 +45,10 @@ export default class ComponentView {
|
||||
this.setSelection = this.extension.setSelection
|
||||
}
|
||||
|
||||
if (typeof this.extension.update === 'function') {
|
||||
this.update = this.extension.update
|
||||
}
|
||||
|
||||
this.vm = new Component({
|
||||
parent: this.parent,
|
||||
propsData: props,
|
||||
|
@ -23,10 +23,6 @@ export default class Extension {
|
||||
return 'extension'
|
||||
}
|
||||
|
||||
get update() {
|
||||
return () => {}
|
||||
}
|
||||
|
||||
get defaultOptions() {
|
||||
return {}
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ export default class ExtensionManager {
|
||||
Object.assign(obj, { [prop]: value })
|
||||
|
||||
if (changed) {
|
||||
extension.update(view)
|
||||
view.updateState(view.state)
|
||||
}
|
||||
|
||||
return true
|
||||
|
Loading…
Reference in New Issue
Block a user