adding types to linter extension and it's plugins
This commit is contained in:
@@ -33,7 +33,7 @@
|
||||
"y-prosemirror": "^1.0.9",
|
||||
"y-webrtc": "^10.2.0",
|
||||
"y-websocket": "^1.3.15",
|
||||
"yjs": "^13.5.9"
|
||||
"yjs": "^13.5.10"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/plugin-proposal-class-properties": "^7.13.0",
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
// @ts-nocheck
|
||||
import { Extension } from '@tiptap/core'
|
||||
import { Decoration, DecorationSet } from 'prosemirror-view'
|
||||
import { Plugin, PluginKey, TextSelection } from 'prosemirror-state'
|
||||
import { Node as ProsemirrorNode } from 'prosemirror-model'
|
||||
import LinterPlugin, { Result as Issue } from './LinterPlugin'
|
||||
|
||||
function renderIcon(issue) {
|
||||
const icon = document.createElement('div')
|
||||
interface IconDivElement extends HTMLDivElement {
|
||||
issue?: Issue
|
||||
}
|
||||
|
||||
function renderIcon(issue: Issue) {
|
||||
const icon: IconDivElement = document.createElement('div')
|
||||
|
||||
icon.className = 'lint-icon'
|
||||
icon.title = issue.message
|
||||
@@ -13,11 +18,11 @@ function renderIcon(issue) {
|
||||
return icon
|
||||
}
|
||||
|
||||
function runAllLinterPlugins(doc, plugins) {
|
||||
function runAllLinterPlugins(doc: ProsemirrorNode, plugins: Array<typeof LinterPlugin>) {
|
||||
const decorations: [any?] = []
|
||||
|
||||
const results = plugins.map(LinterPlugin => {
|
||||
return new LinterPlugin(doc).scan().getResults()
|
||||
const results = plugins.map(RegisteredLinterPlugin => {
|
||||
return new RegisteredLinterPlugin(doc).scan().getResults()
|
||||
}).flat()
|
||||
|
||||
results.forEach(issue => {
|
||||
@@ -31,7 +36,7 @@ function runAllLinterPlugins(doc, plugins) {
|
||||
}
|
||||
|
||||
export interface LinterOptions {
|
||||
plugins: [any],
|
||||
plugins: Array<typeof LinterPlugin>,
|
||||
}
|
||||
|
||||
export const Linter = Extension.create({
|
||||
@@ -62,8 +67,9 @@ export const Linter = Extension.create({
|
||||
return this.getState(state)
|
||||
},
|
||||
handleClick(view, _, event) {
|
||||
if (/lint-icon/.test(event.target.className)) {
|
||||
const { from, to } = event.target.issue
|
||||
const target = (event.target as IconDivElement)
|
||||
if (/lint-icon/.test(target.className) && target.issue) {
|
||||
const { from, to } = target.issue
|
||||
|
||||
view.dispatch(
|
||||
view.state.tr
|
||||
@@ -73,17 +79,22 @@ export const Linter = Extension.create({
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
},
|
||||
handleDoubleClick(view, _, event) {
|
||||
if (/lint-icon/.test(event.target.className)) {
|
||||
const prob = event.target.issue
|
||||
const target = (event.target as IconDivElement)
|
||||
if (/lint-icon/.test((event.target as HTMLElement).className) && target.issue) {
|
||||
const prob = target.issue
|
||||
|
||||
if (prob.fix) {
|
||||
prob.fix(view)
|
||||
prob.fix(view, prob)
|
||||
view.focus()
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
},
|
||||
},
|
||||
}),
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
interface Result {
|
||||
import { Node as ProsemirrorNode } from 'prosemirror-model'
|
||||
|
||||
export interface Result {
|
||||
message: string,
|
||||
from: number,
|
||||
to: number,
|
||||
fix?: null
|
||||
fix?: Function
|
||||
}
|
||||
|
||||
export default class LinterPlugin {
|
||||
@@ -10,11 +12,11 @@ export default class LinterPlugin {
|
||||
|
||||
private results: Array<Result> = []
|
||||
|
||||
constructor(doc: any) {
|
||||
constructor(doc: ProsemirrorNode) {
|
||||
this.doc = doc
|
||||
}
|
||||
|
||||
record(message: string, from: number, to: number, fix?: null) {
|
||||
record(message: string, from: number, to: number, fix?: Function) {
|
||||
this.results.push({
|
||||
message,
|
||||
from,
|
||||
@@ -23,6 +25,10 @@ export default class LinterPlugin {
|
||||
})
|
||||
}
|
||||
|
||||
scan() {
|
||||
return this
|
||||
}
|
||||
|
||||
getResults() {
|
||||
return this.results
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// @ts-nocheck
|
||||
import LinterPlugin from '../LinterPlugin'
|
||||
|
||||
export class BadWords extends LinterPlugin {
|
||||
@@ -6,7 +5,7 @@ export class BadWords extends LinterPlugin {
|
||||
public regex = /\b(obviously|clearly|evidently|simply)\b/ig
|
||||
|
||||
scan() {
|
||||
this.doc.descendants((node: any, position: any) => {
|
||||
this.doc.descendants((node: any, position: number) => {
|
||||
if (!node.isText) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
// @ts-nocheck
|
||||
import LinterPlugin from '../LinterPlugin'
|
||||
import { EditorView } from 'prosemirror-view'
|
||||
import LinterPlugin, { Result as Issue } from '../LinterPlugin'
|
||||
|
||||
export class HeadingLevel extends LinterPlugin {
|
||||
fixHeader(level) {
|
||||
return function ({ state, dispatch }) {
|
||||
dispatch(state.tr.setNodeMarkup(this.from - 1, null, { level }))
|
||||
fixHeader(level: number) {
|
||||
return function ({ state, dispatch }: EditorView, issue: Issue) {
|
||||
dispatch(state.tr.setNodeMarkup(issue.from - 1, undefined, { level }))
|
||||
}
|
||||
}
|
||||
|
||||
scan() {
|
||||
let lastHeadLevel = null
|
||||
let lastHeadLevel: number | null = null
|
||||
|
||||
this.doc.descendants((node, position) => {
|
||||
if (node.type.name === 'heading') {
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
// @ts-nocheck
|
||||
import LinterPlugin from '../LinterPlugin'
|
||||
import { EditorView } from 'prosemirror-view'
|
||||
import LinterPlugin, { Result as Issue } from '../LinterPlugin'
|
||||
|
||||
export class Punctuation extends LinterPlugin {
|
||||
public regex = / ([,.!?:]) ?/g
|
||||
|
||||
fix(replacement: any) {
|
||||
return function ({ state, dispatch }) {
|
||||
return function ({ state, dispatch }: EditorView, issue: Issue) {
|
||||
dispatch(
|
||||
state.tr.replaceWith(
|
||||
this.from, this.to,
|
||||
issue.from, issue.to,
|
||||
state.schema.text(replacement),
|
||||
),
|
||||
)
|
||||
@@ -17,9 +17,9 @@ export class Punctuation extends LinterPlugin {
|
||||
|
||||
scan() {
|
||||
this.doc.descendants((node, position) => {
|
||||
if (!node.isText) {
|
||||
return
|
||||
}
|
||||
if (!node.isText) return
|
||||
|
||||
if (!node.text) return
|
||||
|
||||
const matches = this.regex.exec(node.text)
|
||||
|
||||
|
||||
42
docs/src/demos/Marks/Subscript/index.spec.js
Normal file
42
docs/src/demos/Marks/Subscript/index.spec.js
Normal file
@@ -0,0 +1,42 @@
|
||||
context('/demos/Marks/Subscript', () => {
|
||||
before(() => {
|
||||
cy.visit('/demos/Marks/Subscript')
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
cy.get('.ProseMirror').then(([{ editor }]) => {
|
||||
editor.commands.setContent('<p>Example Text</p>')
|
||||
cy.get('.ProseMirror').type('{selectall}')
|
||||
})
|
||||
})
|
||||
|
||||
it('should transform inline style to sub tags', () => {
|
||||
cy.get('.ProseMirror').then(([{ editor }]) => {
|
||||
editor.commands.setContent('<p><span style="vertical-align: middle">Example Text</span></p>')
|
||||
expect(editor.getHTML()).to.eq('<p><sub>Example Text</sub></p>')
|
||||
})
|
||||
})
|
||||
|
||||
it('sould omit inline style with a different vertical align', () => {
|
||||
cy.get('.ProseMirror').then(([{ editor }]) => {
|
||||
editor.commands.setContent('<p><b style="vertical-align: middle">Example Text</b></p>')
|
||||
expect(editor.getHTML()).to.eq('<p>Example Text</p>')
|
||||
})
|
||||
})
|
||||
|
||||
it('the button should make the selected text bold', () => {
|
||||
cy.get('button:first')
|
||||
.click()
|
||||
|
||||
cy.get('.ProseMirror')
|
||||
.find('sub')
|
||||
.should('contain', 'Example Text')
|
||||
})
|
||||
|
||||
it('the button should toggle the selected text bold', () => {
|
||||
cy.get('button:first').click()
|
||||
cy.get('.ProseMirror').type('{selectall}')
|
||||
cy.get('button:first').click()
|
||||
cy.get('.ProseMirror sub').should('not.exist')
|
||||
})
|
||||
})
|
||||
49
docs/src/demos/Marks/Subscript/index.vue
Normal file
49
docs/src/demos/Marks/Subscript/index.vue
Normal file
@@ -0,0 +1,49 @@
|
||||
<template>
|
||||
<div v-if="editor">
|
||||
<button @click="editor.chain().focus().toggleSubscript().run()" :class="{ 'is-active': editor.isActive('subscript') }">
|
||||
subscript
|
||||
</button>
|
||||
|
||||
<editor-content :editor="editor" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { Editor, EditorContent } from '@tiptap/vue-2'
|
||||
import Document from '@tiptap/extension-document'
|
||||
import Paragraph from '@tiptap/extension-paragraph'
|
||||
import Text from '@tiptap/extension-text'
|
||||
import Subscript from '@tiptap/extension-subscript'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
EditorContent,
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
editor: null,
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.editor = new Editor({
|
||||
extensions: [
|
||||
Document,
|
||||
Paragraph,
|
||||
Text,
|
||||
Subscript,
|
||||
],
|
||||
content: `
|
||||
<p>This is regular text.</p>
|
||||
<p><sub>This is subscript.</sub></p>
|
||||
<p><span style="vertical-align: sub">And this.</span></p>
|
||||
`,
|
||||
})
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
this.editor.destroy()
|
||||
},
|
||||
}
|
||||
</script>
|
||||
42
docs/src/demos/Marks/Superscript/index.spec.js
Normal file
42
docs/src/demos/Marks/Superscript/index.spec.js
Normal file
@@ -0,0 +1,42 @@
|
||||
context('/demos/Marks/Superscript', () => {
|
||||
before(() => {
|
||||
cy.visit('/demos/Marks/Superscript')
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
cy.get('.ProseMirror').then(([{ editor }]) => {
|
||||
editor.commands.setContent('<p>Example Text</p>')
|
||||
cy.get('.ProseMirror').type('{selectall}')
|
||||
})
|
||||
})
|
||||
|
||||
it('should transform inline style to sup tags', () => {
|
||||
cy.get('.ProseMirror').then(([{ editor }]) => {
|
||||
editor.commands.setContent('<p><span style="vertical-align: super">Example Text</span></p>')
|
||||
expect(editor.getHTML()).to.eq('<p><sup>Example Text</sup></p>')
|
||||
})
|
||||
})
|
||||
|
||||
it('sould omit inline style with a different vertical align', () => {
|
||||
cy.get('.ProseMirror').then(([{ editor }]) => {
|
||||
editor.commands.setContent('<p><span style="vertical-align: middle">Example Text</span></p>')
|
||||
expect(editor.getHTML()).to.eq('<p>Example Text</p>')
|
||||
})
|
||||
})
|
||||
|
||||
it('the button should make the selected text bold', () => {
|
||||
cy.get('button:first')
|
||||
.click()
|
||||
|
||||
cy.get('.ProseMirror')
|
||||
.find('sup')
|
||||
.should('contain', 'Example Text')
|
||||
})
|
||||
|
||||
it('the button should toggle the selected text bold', () => {
|
||||
cy.get('button:first').click()
|
||||
cy.get('.ProseMirror').type('{selectall}')
|
||||
cy.get('button:first').click()
|
||||
cy.get('.ProseMirror sup').should('not.exist')
|
||||
})
|
||||
})
|
||||
49
docs/src/demos/Marks/Superscript/index.vue
Normal file
49
docs/src/demos/Marks/Superscript/index.vue
Normal file
@@ -0,0 +1,49 @@
|
||||
<template>
|
||||
<div v-if="editor">
|
||||
<button @click="editor.chain().focus().toggleSuperscript().run()" :class="{ 'is-active': editor.isActive('superscript') }">
|
||||
superscript
|
||||
</button>
|
||||
|
||||
<editor-content :editor="editor" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { Editor, EditorContent } from '@tiptap/vue-2'
|
||||
import Document from '@tiptap/extension-document'
|
||||
import Paragraph from '@tiptap/extension-paragraph'
|
||||
import Text from '@tiptap/extension-text'
|
||||
import Superscript from '@tiptap/extension-superscript'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
EditorContent,
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
editor: null,
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.editor = new Editor({
|
||||
extensions: [
|
||||
Document,
|
||||
Paragraph,
|
||||
Text,
|
||||
Superscript,
|
||||
],
|
||||
content: `
|
||||
<p>This is regular text.</p>
|
||||
<p><sup>This is superscript.</sup></p>
|
||||
<p><span style="vertical-align: super">And this.</span></p>
|
||||
`,
|
||||
})
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
this.editor.destroy()
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -6,13 +6,15 @@
|
||||
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 |
|
||||
| ---------------------------------- | ----------------- | -------------------------------------------------------------------------------------------- |
|
||||
| [Bold](/api/marks/bold) | Yes | [GitHub](https://github.com/ueberdosis/tiptap/blob/main/packages/extension-bold/) |
|
||||
| [Code](/api/marks/code) | Yes | [GitHub](https://github.com/ueberdosis/tiptap/blob/main/packages/extension-code/) |
|
||||
| [Highlight](/api/marks/highlight) | – | [GitHub](https://github.com/ueberdosis/tiptap/blob/main/packages/extension-highlight/) |
|
||||
| [Italic](/api/marks/italic) | Yes | [GitHub](https://github.com/ueberdosis/tiptap/blob/main/packages/extension-italic/) |
|
||||
| [Link](/api/marks/link) | – | [GitHub](https://github.com/ueberdosis/tiptap/blob/main/packages/extension-link/) |
|
||||
| [Strike](/api/marks/strike) | Yes | [GitHub](https://github.com/ueberdosis/tiptap/blob/main/packages/extension-strike/) |
|
||||
| [TextStyle](/api/marks/text-style) | Yes | [GitHub](https://github.com/ueberdosis/tiptap/blob/main/packages/extension-text-style/) |
|
||||
| [Underline](/api/marks/underline) | – | [GitHub](https://github.com/ueberdosis/tiptap/blob/main/packages/extension-underline/) |
|
||||
| Title | Default Extension | Source Code |
|
||||
| ------------------------------------- | ----------------- | ---------------------------------------------------------------------------------------- |
|
||||
| [Bold](/api/marks/bold) | Yes | [GitHub](https://github.com/ueberdosis/tiptap/blob/main/packages/extension-bold/) |
|
||||
| [Code](/api/marks/code) | Yes | [GitHub](https://github.com/ueberdosis/tiptap/blob/main/packages/extension-code/) |
|
||||
| [Highlight](/api/marks/highlight) | – | [GitHub](https://github.com/ueberdosis/tiptap/blob/main/packages/extension-highlight/) |
|
||||
| [Italic](/api/marks/italic) | Yes | [GitHub](https://github.com/ueberdosis/tiptap/blob/main/packages/extension-italic/) |
|
||||
| [Link](/api/marks/link) | – | [GitHub](https://github.com/ueberdosis/tiptap/blob/main/packages/extension-link/) |
|
||||
| [Strike](/api/marks/strike) | Yes | [GitHub](https://github.com/ueberdosis/tiptap/blob/main/packages/extension-strike/) |
|
||||
| [Subscript](/api/marks/subscript) | – | [GitHub](https://github.com/ueberdosis/tiptap/blob/main/packages/extension-subscript/) |
|
||||
| [Superscript](/api/marks/superscript) | – | [GitHub](https://github.com/ueberdosis/tiptap/blob/main/packages/extension-superscript/) |
|
||||
| [TextStyle](/api/marks/text-style) | Yes | [GitHub](https://github.com/ueberdosis/tiptap/blob/main/packages/extension-text-style/) |
|
||||
| [Underline](/api/marks/underline) | – | [GitHub](https://github.com/ueberdosis/tiptap/blob/main/packages/extension-underline/) |
|
||||
|
||||
32
docs/src/docPages/api/marks/subscript.md
Normal file
32
docs/src/docPages/api/marks/subscript.md
Normal file
@@ -0,0 +1,32 @@
|
||||
# Subscript
|
||||
[](https://www.npmjs.com/package/@tiptap/extension-subscript)
|
||||
[](https://npmcharts.com/compare/@tiptap/extension-subscript?minimal=true)
|
||||
|
||||
Use this extension to render text in <sub>subscript</sub>. If you pass `<sub>` or text with `vertical-align: sub` as inline style in the editor’s initial content, both will be normalized to a `<sub>` HTML tag.
|
||||
|
||||
## Installation
|
||||
```bash
|
||||
# with npm
|
||||
npm install @tiptap/extension-subscript
|
||||
|
||||
# with Yarn
|
||||
yarn add @tiptap/extension-subscript
|
||||
```
|
||||
|
||||
## Settings
|
||||
| Option | Type | Default | Description |
|
||||
| -------------- | -------- | ------- | --------------------------------------------------------------------- |
|
||||
| HTMLAttributes | `Object` | `{}` | Custom HTML attributes that should be added to the rendered HTML tag. |
|
||||
|
||||
## Commands
|
||||
| Command | Parameters | Description |
|
||||
| --------------- | ---------- | ------------------------- |
|
||||
| setSubscript | — | Mark text as subscript. |
|
||||
| toggleSubscript | — | Toggle subscript mark. |
|
||||
| unsetSubscript | — | Remove subscript mark. |
|
||||
|
||||
## Source code
|
||||
[packages/extension-subscript/](https://github.com/ueberdosis/tiptap/blob/main/packages/extension-subscript/)
|
||||
|
||||
## Usage
|
||||
<demo name="Marks/Subscript" highlight="3-5,16,35" />
|
||||
32
docs/src/docPages/api/marks/superscript.md
Normal file
32
docs/src/docPages/api/marks/superscript.md
Normal file
@@ -0,0 +1,32 @@
|
||||
# Superscript
|
||||
[](https://www.npmjs.com/package/@tiptap/extension-superscript)
|
||||
[](https://npmcharts.com/compare/@tiptap/extension-superscript?minimal=true)
|
||||
|
||||
Use this extension to render text in <sup>superscript</sup>. If you pass `<sup>` or text with `vertical-align: super` as inline style in the editor’s initial content, both will be normalized to a `<sup>` HTML tag.
|
||||
|
||||
## Installation
|
||||
```bash
|
||||
# with npm
|
||||
npm install @tiptap/extension-superscript
|
||||
|
||||
# with Yarn
|
||||
yarn add @tiptap/extension-superscript
|
||||
```
|
||||
|
||||
## Settings
|
||||
| Option | Type | Default | Description |
|
||||
| -------------- | -------- | ------- | --------------------------------------------------------------------- |
|
||||
| HTMLAttributes | `Object` | `{}` | Custom HTML attributes that should be added to the rendered HTML tag. |
|
||||
|
||||
## Commands
|
||||
| Command | Parameters | Description |
|
||||
| ----------------- | ---------- | ------------------------- |
|
||||
| setSuperscript | — | Mark text as superscript. |
|
||||
| toggleSuperscript | — | Toggle superscript mark. |
|
||||
| unsetSuperscript | — | Remove superscript mark. |
|
||||
|
||||
## Source code
|
||||
[packages/extension-superscript/](https://github.com/ueberdosis/tiptap/blob/main/packages/extension-superscript/)
|
||||
|
||||
## Usage
|
||||
<demo name="Marks/Superscript" highlight="3-5,17,36" />
|
||||
@@ -1,5 +1,5 @@
|
||||
# Linter
|
||||
|
||||
⚠️ Experiment
|
||||
Linter can be used to check the content as per your wish and highlight it to the user. Linter extension can have multiple plugins for each task you want to achieve.
|
||||
|
||||
<demo name="Experiments/Linter" />
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
|
||||
Some great companies are looking for developers with tiptap or hocuspocus experience right now. If you’re looking for a job to work with tiptap and/or hocuspocus, consider applying:
|
||||
|
||||
**[Frontend Developer](https://bitcrowd.net/jobs) @ bitcrowd**<br>
|
||||
tiptap · Remote · Germany · Full-time
|
||||
|
||||
**[Software Engineer, Fullstack](https://saga.so/careers/software-engineer-fullstack) @ Saga**<br>
|
||||
hocuspocus · Remote · EU time zones · Full-time
|
||||
|
||||
|
||||
@@ -320,6 +320,12 @@
|
||||
link: /api/marks/link
|
||||
- title: Strike
|
||||
link: /api/marks/strike
|
||||
- title: Subscript
|
||||
link: /api/marks/subscript
|
||||
type: new
|
||||
- title: Superscript
|
||||
link: /api/marks/superscript
|
||||
type: new
|
||||
- title: TextStyle
|
||||
link: /api/marks/text-style
|
||||
- title: Underline
|
||||
|
||||
Reference in New Issue
Block a user