mirror of
https://github.com/ueberdosis/tiptap.git
synced 2024-11-27 14:59:27 +08:00
chore(lists): move list keymap to extra extension (#4290)
* move list keymap to extra extension * update docs and readme * move list helpers out of core
This commit is contained in:
parent
a2ce734d68
commit
7e7057ea43
0
demos/src/Extensions/ListKeymap/React/index.html
Normal file
0
demos/src/Extensions/ListKeymap/React/index.html
Normal file
57
demos/src/Extensions/ListKeymap/React/index.jsx
Normal file
57
demos/src/Extensions/ListKeymap/React/index.jsx
Normal file
@ -0,0 +1,57 @@
|
||||
import './styles.scss'
|
||||
|
||||
import BulletList from '@tiptap/extension-bullet-list'
|
||||
import Document from '@tiptap/extension-document'
|
||||
import ListItem from '@tiptap/extension-list-item'
|
||||
import ListKeymap from '@tiptap/extension-list-keymap'
|
||||
import Paragraph from '@tiptap/extension-paragraph'
|
||||
import Text from '@tiptap/extension-text'
|
||||
import { EditorContent, useEditor } from '@tiptap/react'
|
||||
import React from 'react'
|
||||
|
||||
export default () => {
|
||||
const editor = useEditor({
|
||||
extensions: [Document, Paragraph, Text, BulletList, ListItem, ListKeymap],
|
||||
content: `
|
||||
<ul>
|
||||
<li>A list item</li>
|
||||
<li>And another one</li>
|
||||
</ul>
|
||||
`,
|
||||
})
|
||||
|
||||
if (!editor) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<button
|
||||
onClick={() => editor.chain().focus().toggleBulletList().run()}
|
||||
className={editor.isActive('bulletList') ? 'is-active' : ''}
|
||||
>
|
||||
toggleBulletList
|
||||
</button>
|
||||
<button
|
||||
onClick={() => editor.chain().focus().splitListItem('listItem').run()}
|
||||
disabled={!editor.can().splitListItem('listItem')}
|
||||
>
|
||||
splitListItem
|
||||
</button>
|
||||
<button
|
||||
onClick={() => editor.chain().focus().sinkListItem('listItem').run()}
|
||||
disabled={!editor.can().sinkListItem('listItem')}
|
||||
>
|
||||
sinkListItem
|
||||
</button>
|
||||
<button
|
||||
onClick={() => editor.chain().focus().liftListItem('listItem').run()}
|
||||
disabled={!editor.can().liftListItem('listItem')}
|
||||
>
|
||||
liftListItem
|
||||
</button>
|
||||
|
||||
<EditorContent editor={editor} />
|
||||
</>
|
||||
)
|
||||
}
|
11
demos/src/Extensions/ListKeymap/React/styles.scss
Normal file
11
demos/src/Extensions/ListKeymap/React/styles.scss
Normal file
@ -0,0 +1,11 @@
|
||||
/* Basic editor styles */
|
||||
.tiptap {
|
||||
> * + * {
|
||||
margin-top: 0.75em;
|
||||
}
|
||||
|
||||
ul,
|
||||
ol {
|
||||
padding: 0 1rem;
|
||||
}
|
||||
}
|
52
docs/api/extensions/list-keymap.md
Normal file
52
docs/api/extensions/list-keymap.md
Normal file
@ -0,0 +1,52 @@
|
||||
---
|
||||
description: Add extra keymap handlers to change the default backspace and delete behavior for lists.
|
||||
icon: asterisk
|
||||
---
|
||||
|
||||
# ListKeymap
|
||||
[![Version](https://img.shields.io/npm/v/@tiptap/extension-list-keymap.svg?label=version)](https://www.npmjs.com/package/@tiptap/extension-list-keymap)
|
||||
[![Downloads](https://img.shields.io/npm/dm/@tiptap/extension-list-keymap.svg)](https://npmcharts.com/compare/@tiptap/extension-list-keymap?minimal=true)
|
||||
|
||||
This extensions adds extra keymap handlers to change the default backspace and delete behavior for lists. Those are not included in the core package, because they are not required for the most basic use cases.
|
||||
|
||||
## Installation
|
||||
```bash
|
||||
npm install @tiptap/extension-list-keymap
|
||||
```
|
||||
|
||||
## Settings
|
||||
|
||||
### listTypes
|
||||
A array of list items and their parent wrapper node types.
|
||||
|
||||
Default:
|
||||
|
||||
```js
|
||||
[
|
||||
{
|
||||
itemName: 'listItem',
|
||||
wrapperNames: ['bulletList', 'orderedList'],
|
||||
},
|
||||
{
|
||||
itemName: 'taskItem',
|
||||
wrapperNames: ['taskList'],
|
||||
},
|
||||
]
|
||||
```
|
||||
|
||||
```js
|
||||
ListKeymap.configure({
|
||||
listTypes: [
|
||||
{
|
||||
itemName: 'taskItem',
|
||||
wrapperNames: ['customTaskList'],
|
||||
},
|
||||
],
|
||||
})
|
||||
```
|
||||
|
||||
## Source code
|
||||
[packages/extension-list-keymap/](https://github.com/ueberdosis/tiptap/blob/main/packages/extension-list-keymap/)
|
||||
|
||||
## Usage
|
||||
https://embed.tiptap.dev/preview/Extensions/ListKeymap
|
@ -1,3 +1,4 @@
|
||||
export * from '../../../extension-list-keymap/src/listHelpers/index.js'
|
||||
export * from './combineTransactionSteps.js'
|
||||
export * from './createChainableState.js'
|
||||
export * from './createDocument.js'
|
||||
@ -44,7 +45,6 @@ export * from './isNodeActive.js'
|
||||
export * from './isNodeEmpty.js'
|
||||
export * from './isNodeSelection.js'
|
||||
export * from './isTextSelection.js'
|
||||
export * from './listHelpers/index.js'
|
||||
export * from './posToDOMRect.js'
|
||||
export * from './resolveFocusPosition.js'
|
||||
export * from './selectionToInsertionEnd.js'
|
||||
|
@ -1,7 +1,4 @@
|
||||
import {
|
||||
handleBackspace, handleDelete,
|
||||
mergeAttributes, Node,
|
||||
} from '@tiptap/core'
|
||||
import { mergeAttributes, Node } from '@tiptap/core'
|
||||
|
||||
export interface ListItemOptions {
|
||||
HTMLAttributes: Record<string, any>,
|
||||
@ -41,10 +38,6 @@ export const ListItem = Node.create<ListItemOptions>({
|
||||
Enter: () => this.editor.commands.splitListItem(this.name),
|
||||
Tab: () => this.editor.commands.sinkListItem(this.name),
|
||||
'Shift-Tab': () => this.editor.commands.liftListItem(this.name),
|
||||
Delete: ({ editor }) => handleDelete(editor, this.name),
|
||||
'Mod-Delete': ({ editor }) => handleDelete(editor, this.name),
|
||||
Backspace: ({ editor }) => handleBackspace(editor, this.name, [this.options.bulletListTypeName, this.options.orderedListTypeName]),
|
||||
'Mod-Backspace': ({ editor }) => handleBackspace(editor, this.name, [this.options.bulletListTypeName, this.options.orderedListTypeName]),
|
||||
}
|
||||
},
|
||||
})
|
||||
|
4
packages/extension-list-keymap/CHANGELOG.md
Normal file
4
packages/extension-list-keymap/CHANGELOG.md
Normal file
@ -0,0 +1,4 @@
|
||||
# Change Log
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
14
packages/extension-list-keymap/README.md
Normal file
14
packages/extension-list-keymap/README.md
Normal file
@ -0,0 +1,14 @@
|
||||
# @tiptap/extension-list-keymap
|
||||
[![Version](https://img.shields.io/npm/v/@tiptap/extension-list-keymap.svg?label=version)](https://www.npmjs.com/package/@tiptap/extension-list-keymap)
|
||||
[![Downloads](https://img.shields.io/npm/dm/@tiptap/extension-list-keymap.svg)](https://npmcharts.com/compare/tiptap?minimal=true)
|
||||
[![License](https://img.shields.io/npm/l/@tiptap/extension-list-keymap.svg)](https://www.npmjs.com/package/@tiptap/extension-list-keymap)
|
||||
[![Sponsor](https://img.shields.io/static/v1?label=Sponsor&message=%E2%9D%A4&logo=GitHub)](https://github.com/sponsors/ueberdosis)
|
||||
|
||||
## Introduction
|
||||
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*.
|
||||
|
||||
## Official Documentation
|
||||
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/blob/main/LICENSE.md).
|
46
packages/extension-list-keymap/package.json
Normal file
46
packages/extension-list-keymap/package.json
Normal file
@ -0,0 +1,46 @@
|
||||
{
|
||||
"name": "@tiptap/extension-list-keymap",
|
||||
"description": "list keymap extension for tiptap",
|
||||
"version": "2.1.0-rc.12",
|
||||
"homepage": "https://tiptap.dev",
|
||||
"keywords": [
|
||||
"tiptap",
|
||||
"tiptap extension"
|
||||
],
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/packages/extension-list-keymap/src/index.d.ts",
|
||||
"import": "./dist/index.js",
|
||||
"require": "./dist/index.cjs"
|
||||
}
|
||||
},
|
||||
"main": "dist/index.cjs",
|
||||
"module": "dist/index.js",
|
||||
"umd": "dist/index.umd.js",
|
||||
"types": "dist/packages/extension-list-keymap/src/index.d.ts",
|
||||
"files": [
|
||||
"src",
|
||||
"dist"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@tiptap/core": "^2.1.0-rc.12"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^2.0.0"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/ueberdosis/tiptap",
|
||||
"directory": "packages/extension-list-keymap"
|
||||
},
|
||||
"scripts": {
|
||||
"clean": "rm -rf dist",
|
||||
"build": "npm run clean && rollup -c"
|
||||
}
|
||||
}
|
60
packages/extension-list-keymap/rollup.config.js
Normal file
60
packages/extension-list-keymap/rollup.config.js
Normal file
@ -0,0 +1,60 @@
|
||||
import sizes from '@atomico/rollup-plugin-sizes'
|
||||
import babel from '@rollup/plugin-babel'
|
||||
import commonjs from '@rollup/plugin-commonjs'
|
||||
import resolve from '@rollup/plugin-node-resolve'
|
||||
import autoExternal from 'rollup-plugin-auto-external'
|
||||
import sourcemaps from 'rollup-plugin-sourcemaps'
|
||||
import typescript from 'rollup-plugin-typescript2'
|
||||
|
||||
import pkg from './package.json'
|
||||
|
||||
export default {
|
||||
external: [/@tiptap\/pm\/.*/],
|
||||
input: 'src/index.ts',
|
||||
output: [
|
||||
{
|
||||
name: pkg.name,
|
||||
file: pkg.umd,
|
||||
format: 'umd',
|
||||
sourcemap: true,
|
||||
},
|
||||
{
|
||||
name: pkg.name,
|
||||
file: pkg.main,
|
||||
format: 'cjs',
|
||||
sourcemap: true,
|
||||
exports: 'auto',
|
||||
},
|
||||
{
|
||||
name: pkg.name,
|
||||
file: pkg.module,
|
||||
format: 'es',
|
||||
sourcemap: true,
|
||||
},
|
||||
],
|
||||
plugins: [
|
||||
autoExternal({
|
||||
packagePath: './package.json',
|
||||
}),
|
||||
sourcemaps(),
|
||||
resolve(),
|
||||
commonjs(),
|
||||
babel({
|
||||
babelHelpers: 'bundled',
|
||||
exclude: '../../node_modules/**',
|
||||
}),
|
||||
sizes(),
|
||||
typescript({
|
||||
tsconfig: '../../tsconfig.json',
|
||||
tsconfigOverride: {
|
||||
compilerOptions: {
|
||||
declaration: true,
|
||||
paths: {
|
||||
'@tiptap/*': ['packages/*/src'],
|
||||
},
|
||||
},
|
||||
include: null,
|
||||
},
|
||||
}),
|
||||
],
|
||||
}
|
6
packages/extension-list-keymap/src/index.ts
Normal file
6
packages/extension-list-keymap/src/index.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import { ListKeymap } from './list-keymap.js'
|
||||
|
||||
export * from './list-keymap.js'
|
||||
export * as listHelpers from './listHelpers/index.js'
|
||||
|
||||
export default ListKeymap
|
94
packages/extension-list-keymap/src/list-keymap.ts
Normal file
94
packages/extension-list-keymap/src/list-keymap.ts
Normal file
@ -0,0 +1,94 @@
|
||||
import { Extension } from '@tiptap/core'
|
||||
|
||||
import { handleBackspace, handleDelete } from './listHelpers/index.js'
|
||||
|
||||
export type ListKeymapOptions = {
|
||||
listTypes: Array<{
|
||||
itemName: string,
|
||||
wrapperNames: string[],
|
||||
}>
|
||||
}
|
||||
|
||||
export const ListKeymap = Extension.create<ListKeymapOptions>({
|
||||
name: 'listKeymap',
|
||||
|
||||
addOptions() {
|
||||
return {
|
||||
listTypes: [
|
||||
{
|
||||
itemName: 'listItem',
|
||||
wrapperNames: ['bulletList', 'orderedList'],
|
||||
},
|
||||
{
|
||||
itemName: 'taskItem',
|
||||
wrapperNames: ['taskList'],
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
|
||||
addKeyboardShortcuts() {
|
||||
return {
|
||||
Delete: ({ editor }) => {
|
||||
let handled = false
|
||||
|
||||
this.options.listTypes.forEach(({ itemName }) => {
|
||||
if (editor.state.schema.nodes[itemName] === undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
if (handleDelete(editor, itemName)) {
|
||||
handled = true
|
||||
}
|
||||
})
|
||||
|
||||
return handled
|
||||
},
|
||||
'Mod-Delete': ({ editor }) => {
|
||||
let handled = false
|
||||
|
||||
this.options.listTypes.forEach(({ itemName }) => {
|
||||
if (editor.state.schema.nodes[itemName] === undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
if (handleDelete(editor, itemName)) {
|
||||
handled = true
|
||||
}
|
||||
})
|
||||
|
||||
return handled
|
||||
},
|
||||
Backspace: ({ editor }) => {
|
||||
let handled = false
|
||||
|
||||
this.options.listTypes.forEach(({ itemName, wrapperNames }) => {
|
||||
if (editor.state.schema.nodes[itemName] === undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
if (handleBackspace(editor, itemName, wrapperNames)) {
|
||||
handled = true
|
||||
}
|
||||
})
|
||||
|
||||
return handled
|
||||
},
|
||||
'Mod-Backspace': ({ editor }) => {
|
||||
let handled = false
|
||||
|
||||
this.options.listTypes.forEach(({ itemName, wrapperNames }) => {
|
||||
if (editor.state.schema.nodes[itemName] === undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
if (handleBackspace(editor, itemName, wrapperNames)) {
|
||||
handled = true
|
||||
}
|
||||
})
|
||||
|
||||
return handled
|
||||
},
|
||||
}
|
||||
},
|
||||
})
|
@ -1,7 +1,7 @@
|
||||
import { NodeType } from '@tiptap/pm/model'
|
||||
import { EditorState } from '@tiptap/pm/state'
|
||||
|
||||
import { getNodeType } from '../getNodeType.js'
|
||||
import { getNodeType } from '../../../core/src/helpers/getNodeType.js'
|
||||
|
||||
export const findListItemPos = (typeOrName: string | NodeType, state: EditorState) => {
|
||||
const { $from } = state.selection
|
@ -1,6 +1,6 @@
|
||||
import { EditorState } from '@tiptap/pm/state'
|
||||
|
||||
import { getNodeAtPosition } from '../getNodeAtPosition.js'
|
||||
import { getNodeAtPosition } from '../../../core/src/helpers/getNodeAtPosition.js'
|
||||
import { findListItemPos } from './findListItemPos.js'
|
||||
|
||||
export const getNextListDepth = (typeOrName: string, state: EditorState) => {
|
@ -1,8 +1,8 @@
|
||||
import { Node } from '@tiptap/pm/model'
|
||||
|
||||
import { Editor } from '../../Editor.js'
|
||||
import { isAtStartOfNode } from '../isAtStartOfNode.js'
|
||||
import { isNodeActive } from '../isNodeActive.js'
|
||||
import { Editor } from '../../../core/src/Editor.js'
|
||||
import { isAtStartOfNode } from '../../../core/src/helpers/isAtStartOfNode.js'
|
||||
import { isNodeActive } from '../../../core/src/helpers/isNodeActive.js'
|
||||
import { findListItemPos } from './findListItemPos.js'
|
||||
import { hasListBefore } from './hasListBefore.js'
|
||||
import { hasListItemBefore } from './hasListItemBefore.js'
|
@ -1,6 +1,6 @@
|
||||
import { Editor } from '../../Editor.js'
|
||||
import { isAtEndOfNode } from '../isAtEndOfNode.js'
|
||||
import { isNodeActive } from '../isNodeActive.js'
|
||||
import { Editor } from '../../../core/src/Editor.js'
|
||||
import { isAtEndOfNode } from '../../../core/src/helpers/isAtEndOfNode.js'
|
||||
import { isNodeActive } from '../../../core/src/helpers/isNodeActive.js'
|
||||
import { nextListIsDeeper } from './nextListIsDeeper.js'
|
||||
import { nextListIsHigher } from './nextListIsHigher.js'
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Node } from '@tiptap/pm/model'
|
||||
import { EditorState } from '@tiptap/pm/state'
|
||||
|
||||
import { getNodeType } from '../getNodeType.js'
|
||||
import { getNodeType } from '../../../core/src/helpers/getNodeType.js'
|
||||
|
||||
export const listItemHasSubList = (typeOrName: string, state: EditorState, node?: Node) => {
|
||||
if (!node) {
|
@ -1,5 +1,5 @@
|
||||
import {
|
||||
handleBackspace, handleDelete, KeyboardShortcutCommand, mergeAttributes, Node, wrappingInputRule,
|
||||
KeyboardShortcutCommand, mergeAttributes, Node, wrappingInputRule,
|
||||
} from '@tiptap/core'
|
||||
import { Node as ProseMirrorNode } from '@tiptap/pm/model'
|
||||
|
||||
@ -78,10 +78,6 @@ export const TaskItem = Node.create<TaskItemOptions>({
|
||||
} = {
|
||||
Enter: () => this.editor.commands.splitListItem(this.name),
|
||||
'Shift-Tab': () => this.editor.commands.liftListItem(this.name),
|
||||
Delete: ({ editor }) => handleDelete(editor, this.name),
|
||||
'Mod-Delete': ({ editor }) => handleDelete(editor, this.name),
|
||||
Backspace: ({ editor }) => handleBackspace(editor, this.name, [this.options.taskListTypeName]),
|
||||
'Mod-Backspace': ({ editor }) => handleBackspace(editor, this.name, [this.options.taskListTypeName]),
|
||||
}
|
||||
|
||||
if (!this.options.nested) {
|
||||
|
Loading…
Reference in New Issue
Block a user