gdpr audit implemented, email log, vollmachten, pdf delete cancel data privacy and vollmachten, removed message no id card in engergy car, and other contracts that are not telecom contracts, added insert counter for engery
This commit is contained in:
+8
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"libs": ["browser"],
|
||||
"plugins": {
|
||||
"node": {},
|
||||
"complete_strings": {},
|
||||
"es_modules": {}
|
||||
}
|
||||
}
|
||||
+140
@@ -0,0 +1,140 @@
|
||||
## 1.5.1 (2025-03-04)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
Fix an issue where `liftListItem` would, in some circumstances, join lists of different types when they ended up on the same level.
|
||||
|
||||
## 1.5.0 (2024-12-04)
|
||||
|
||||
### New features
|
||||
|
||||
The new `wrapRangeInList` function provides the implementation of the `wrapInList` command as a more flexible function.
|
||||
|
||||
## 1.4.1 (2024-07-14)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
Add attribute type validation for ordered list nodes.
|
||||
|
||||
## 1.4.0 (2024-06-10)
|
||||
|
||||
### New features
|
||||
|
||||
The new `splitListItemKeepMarks` command can be used like `splitListItem` but will keep the active marks from the original item.
|
||||
|
||||
## 1.3.0 (2023-06-01)
|
||||
|
||||
### New features
|
||||
|
||||
`splitListItem` now takes an optional second argument to set the attributes of the new list item node. Rename parameter
|
||||
|
||||
## 1.2.3 (2023-05-17)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
Include CommonJS type declarations in the package to please new TypeScript resolution settings.
|
||||
|
||||
## 1.2.2 (2022-09-09)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
Fix an issue where `liftListItem` could create adjacent sublists when lifting an item with sub-items.
|
||||
|
||||
## 1.2.1 (2022-07-20)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
Fix a regression where lifting a nested list could crash.
|
||||
|
||||
## 1.2.0 (2022-05-30)
|
||||
|
||||
### New features
|
||||
|
||||
Include TypeScript type declarations.
|
||||
|
||||
## 1.1.6 (2021-09-21)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
Fix a crash in `liftListItem` that happens when list items that can't be merged are lifted together.
|
||||
|
||||
## 1.1.5 (2021-07-06)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
Fix an issue where `splitListItem` would delete content when activated in a sublist that had content directly after it.
|
||||
|
||||
## 1.1.4 (2020-08-11)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
Fix a regression where `liftListItem` couldn't lift a paragraph from the end of a composite list item.
|
||||
|
||||
## 1.1.3 (2020-08-04)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
Fix an issue where `splitListItem` could delete other content in the item when pressing enter in an empty paragraph that had other content below it in a list item.
|
||||
|
||||
## 1.1.2 (2019-11-20)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
Rename ES module files to use a .js extension, since Webpack gets confused by .mjs
|
||||
|
||||
## 1.1.1 (2019-11-19)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
The file referred to in the package's `module` field now is compiled down to ES5.
|
||||
|
||||
## 1.1.0 (2019-11-08)
|
||||
|
||||
### New features
|
||||
|
||||
Add a `module` field to package json file.
|
||||
|
||||
## 1.0.4 (2019-10-08)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
Fix regression where `splitListItem` doesn't work at the end of an item when the content for list items has different first and non-first allowed nodes.
|
||||
|
||||
## 1.0.3 (2019-04-19)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
`sinkListItem` will no longer copy the attributes of the parent list when creating an inner list.
|
||||
|
||||
## 1.0.2 (2019-01-31)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
`sinkListItem` no longer preserves marks from the outer list when creating an inner list.
|
||||
|
||||
## 1.0.1 (2018-03-16)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
Fixes a bug that caused [`wrapInList`](https://prosemirror.net/docs/ref/#schema-list.wrapInList) to split list items in the wrong place.
|
||||
|
||||
## 0.23.0 (2017-09-13)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
The [`splitListItem` command](https://prosemirror.net/docs/ref/version/0.23.0.html#schema-list.splitListItem) now splits the parent list item when executed in a (trailing) empty list item in a nested list.
|
||||
|
||||
## 0.20.0 (2017-04-03)
|
||||
|
||||
### New features
|
||||
|
||||
The [`liftListItem`](https://prosemirror.net/docs/ref/version/0.20.0.html#schema-list.liftListItem) command can now lift items out of a list entirely, when the parent node isn't another list.
|
||||
|
||||
## 0.11.0 (2016-09-21)
|
||||
|
||||
### Breaking changes
|
||||
|
||||
New module combining the node [specs](https://prosemirror.net/docs/ref/version/0.11.0.html#model.NodeSpec) from
|
||||
[schema-basic](https://prosemirror.net/docs/ref/version/0.11.0.html#schema-basic), and the list-related
|
||||
[commands](https://prosemirror.net/docs/ref/version/0.11.0.html#commands) from the commands module.
|
||||
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
# How to contribute
|
||||
|
||||
- [Getting help](#getting-help)
|
||||
- [Submitting bug reports](#submitting-bug-reports)
|
||||
- [Contributing code](#contributing-code)
|
||||
|
||||
## Getting help
|
||||
|
||||
Community discussion, questions, and informal bug reporting is done on the
|
||||
[discuss.ProseMirror forum](http://discuss.prosemirror.net).
|
||||
|
||||
## Submitting bug reports
|
||||
|
||||
Report bugs on the
|
||||
[GitHub issue tracker](http://github.com/prosemirror/prosemirror/issues).
|
||||
Before reporting a bug, please read these pointers.
|
||||
|
||||
- The issue tracker is for *bugs*, not requests for help. Questions
|
||||
should be asked on the [forum](http://discuss.prosemirror.net).
|
||||
|
||||
- Include information about the version of the code that exhibits the
|
||||
problem. For browser-related issues, include the browser and browser
|
||||
version on which the problem occurred.
|
||||
|
||||
- Mention very precisely what went wrong. "X is broken" is not a good
|
||||
bug report. What did you expect to happen? What happened instead?
|
||||
Describe the exact steps a maintainer has to take to make the
|
||||
problem occur. A screencast can be useful, but is no substitute for
|
||||
a textual description.
|
||||
|
||||
- A great way to make it easy to reproduce your problem, if it can not
|
||||
be trivially reproduced on the website demos, is to submit a script
|
||||
that triggers the issue.
|
||||
|
||||
## Contributing code
|
||||
|
||||
- Make sure you have a [GitHub Account](https://github.com/signup/free)
|
||||
|
||||
- Fork the relevant repository
|
||||
([how to fork a repo](https://help.github.com/articles/fork-a-repo))
|
||||
|
||||
- Create a local checkout of the code. You can use the
|
||||
[main repository](https://github.com/prosemirror/prosemirror) to
|
||||
easily check out all core modules.
|
||||
|
||||
- Make your changes, and commit them
|
||||
|
||||
- Follow the code style of the rest of the project (see below). Run
|
||||
`npm run lint` (in the main repository checkout) to make sure that
|
||||
the linter is happy.
|
||||
|
||||
- If your changes are easy to test or likely to regress, add tests in
|
||||
the relevant `test/` directory. Either put them in an existing
|
||||
`test-*.js` file, if they fit there, or add a new file.
|
||||
|
||||
- Make sure all tests pass. Run `npm run test` to verify tests pass
|
||||
(you will need Node.js v6+).
|
||||
|
||||
- Submit a pull request ([how to create a pull request](https://help.github.com/articles/fork-a-repo)).
|
||||
Don't put more than one feature/fix in a single pull request.
|
||||
|
||||
By contributing code to ProseMirror you
|
||||
|
||||
- Agree to license the contributed code under the project's [MIT
|
||||
license](https://github.com/ProseMirror/prosemirror/blob/master/LICENSE).
|
||||
|
||||
- Confirm that you have the right to contribute and license the code
|
||||
in question. (Either you hold all rights on the code, or the rights
|
||||
holder has explicitly granted the right to use it like this,
|
||||
through a compatible open source license or through a direct
|
||||
agreement with you.)
|
||||
|
||||
### Coding standards
|
||||
|
||||
- ES6 syntax, targeting an ES5 runtime (i.e. don't use library
|
||||
elements added by ES6, don't use ES7/ES.next syntax).
|
||||
|
||||
- 2 spaces per indentation level, no tabs.
|
||||
|
||||
- No semicolons except when necessary.
|
||||
|
||||
- Follow the surrounding code when it comes to spacing, brace
|
||||
placement, etc.
|
||||
|
||||
- Brace-less single-statement bodies are encouraged (whenever they
|
||||
don't impact readability).
|
||||
|
||||
- [getdocs](https://github.com/marijnh/getdocs)-style doc comments
|
||||
above items that are part of the public API.
|
||||
|
||||
- When documenting non-public items, you can put the type after a
|
||||
single colon, so that getdocs doesn't pick it up and add it to the
|
||||
API reference.
|
||||
|
||||
- The linter (`npm run lint`) complains about unused variables and
|
||||
functions. Prefix their names with an underscore to muffle it.
|
||||
|
||||
- ProseMirror does *not* follow JSHint or JSLint prescribed style.
|
||||
Patches that try to 'fix' code to pass one of these linters will not
|
||||
be accepted.
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
Copyright (C) 2015-2017 by Marijn Haverbeke <marijn@haverbeke.berlin> and others
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
# prosemirror-schema-list
|
||||
|
||||
[ [**WEBSITE**](https://prosemirror.net) | [**ISSUES**](https://github.com/prosemirror/prosemirror/issues) | [**FORUM**](https://discuss.prosemirror.net) | [**CHANGELOG**](https://github.com/ProseMirror/prosemirror-schema-list/blob/master/CHANGELOG.md) ]
|
||||
|
||||
This is a [schema module](https://prosemirror.net/docs/ref/#schema-list) for [ProseMirror](https://prosemirror.net).
|
||||
ProseMirror is a well-behaved rich semantic content editor based on
|
||||
contentEditable, with support for collaborative editing and custom
|
||||
document schemas.
|
||||
|
||||
This [module](https://prosemirror.net/docs/ref/#schema-list) exports
|
||||
schema elements and commands for including lists in a ProseMirror
|
||||
editor.
|
||||
|
||||
The [project page](https://prosemirror.net) has more information, a
|
||||
number of [examples](https://prosemirror.net/examples/) and the
|
||||
[documentation](https://prosemirror.net/docs/).
|
||||
|
||||
This code is released under an
|
||||
[MIT license](https://github.com/prosemirror/prosemirror/tree/master/LICENSE).
|
||||
There's a [forum](http://discuss.prosemirror.net) for general
|
||||
discussion and support requests, and the
|
||||
[Github bug tracker](https://github.com/prosemirror/prosemirror/issues)
|
||||
is the place to report issues.
|
||||
|
||||
We aim to be an inclusive, welcoming community. To make that explicit,
|
||||
we have a [code of
|
||||
conduct](http://contributor-covenant.org/version/1/1/0/) that applies
|
||||
to communication around the project.
|
||||
+253
@@ -0,0 +1,253 @@
|
||||
'use strict';
|
||||
|
||||
var prosemirrorTransform = require('prosemirror-transform');
|
||||
var prosemirrorModel = require('prosemirror-model');
|
||||
var prosemirrorState = require('prosemirror-state');
|
||||
var olDOM = ["ol", 0],
|
||||
ulDOM = ["ul", 0],
|
||||
liDOM = ["li", 0];
|
||||
var orderedList = {
|
||||
attrs: {
|
||||
order: {
|
||||
"default": 1,
|
||||
validate: "number"
|
||||
}
|
||||
},
|
||||
parseDOM: [{
|
||||
tag: "ol",
|
||||
getAttrs: function getAttrs(dom) {
|
||||
return {
|
||||
order: dom.hasAttribute("start") ? +dom.getAttribute("start") : 1
|
||||
};
|
||||
}
|
||||
}],
|
||||
toDOM: function toDOM(node) {
|
||||
return node.attrs.order == 1 ? olDOM : ["ol", {
|
||||
start: node.attrs.order
|
||||
}, 0];
|
||||
}
|
||||
};
|
||||
var bulletList = {
|
||||
parseDOM: [{
|
||||
tag: "ul"
|
||||
}],
|
||||
toDOM: function toDOM() {
|
||||
return ulDOM;
|
||||
}
|
||||
};
|
||||
var listItem = {
|
||||
parseDOM: [{
|
||||
tag: "li"
|
||||
}],
|
||||
toDOM: function toDOM() {
|
||||
return liDOM;
|
||||
},
|
||||
defining: true
|
||||
};
|
||||
function add(obj, props) {
|
||||
var copy = {};
|
||||
for (var prop in obj) copy[prop] = obj[prop];
|
||||
for (var _prop in props) copy[_prop] = props[_prop];
|
||||
return copy;
|
||||
}
|
||||
function addListNodes(nodes, itemContent, listGroup) {
|
||||
return nodes.append({
|
||||
ordered_list: add(orderedList, {
|
||||
content: "list_item+",
|
||||
group: listGroup
|
||||
}),
|
||||
bullet_list: add(bulletList, {
|
||||
content: "list_item+",
|
||||
group: listGroup
|
||||
}),
|
||||
list_item: add(listItem, {
|
||||
content: itemContent
|
||||
})
|
||||
});
|
||||
}
|
||||
function wrapInList(listType) {
|
||||
var attrs = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
|
||||
return function (state, dispatch) {
|
||||
var _state$selection = state.selection,
|
||||
$from = _state$selection.$from,
|
||||
$to = _state$selection.$to;
|
||||
var range = $from.blockRange($to);
|
||||
if (!range) return false;
|
||||
var tr = dispatch ? state.tr : null;
|
||||
if (!wrapRangeInList(tr, range, listType, attrs)) return false;
|
||||
if (dispatch) dispatch(tr.scrollIntoView());
|
||||
return true;
|
||||
};
|
||||
}
|
||||
function wrapRangeInList(tr, range, listType) {
|
||||
var attrs = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null;
|
||||
var doJoin = false,
|
||||
outerRange = range,
|
||||
doc = range.$from.doc;
|
||||
if (range.depth >= 2 && range.$from.node(range.depth - 1).type.compatibleContent(listType) && range.startIndex == 0) {
|
||||
if (range.$from.index(range.depth - 1) == 0) return false;
|
||||
var $insert = doc.resolve(range.start - 2);
|
||||
outerRange = new prosemirrorModel.NodeRange($insert, $insert, range.depth);
|
||||
if (range.endIndex < range.parent.childCount) range = new prosemirrorModel.NodeRange(range.$from, doc.resolve(range.$to.end(range.depth)), range.depth);
|
||||
doJoin = true;
|
||||
}
|
||||
var wrap = prosemirrorTransform.findWrapping(outerRange, listType, attrs, range);
|
||||
if (!wrap) return false;
|
||||
if (tr) doWrapInList(tr, range, wrap, doJoin, listType);
|
||||
return true;
|
||||
}
|
||||
function doWrapInList(tr, range, wrappers, joinBefore, listType) {
|
||||
var content = prosemirrorModel.Fragment.empty;
|
||||
for (var i = wrappers.length - 1; i >= 0; i--) content = prosemirrorModel.Fragment.from(wrappers[i].type.create(wrappers[i].attrs, content));
|
||||
tr.step(new prosemirrorTransform.ReplaceAroundStep(range.start - (joinBefore ? 2 : 0), range.end, range.start, range.end, new prosemirrorModel.Slice(content, 0, 0), wrappers.length, true));
|
||||
var found = 0;
|
||||
for (var _i = 0; _i < wrappers.length; _i++) if (wrappers[_i].type == listType) found = _i + 1;
|
||||
var splitDepth = wrappers.length - found;
|
||||
var splitPos = range.start + wrappers.length - (joinBefore ? 2 : 0),
|
||||
parent = range.parent;
|
||||
for (var _i2 = range.startIndex, e = range.endIndex, first = true; _i2 < e; _i2++, first = false) {
|
||||
if (!first && prosemirrorTransform.canSplit(tr.doc, splitPos, splitDepth)) {
|
||||
tr.split(splitPos, splitDepth);
|
||||
splitPos += 2 * splitDepth;
|
||||
}
|
||||
splitPos += parent.child(_i2).nodeSize;
|
||||
}
|
||||
return tr;
|
||||
}
|
||||
function splitListItem(itemType, itemAttrs) {
|
||||
return function (state, dispatch) {
|
||||
var _state$selection2 = state.selection,
|
||||
$from = _state$selection2.$from,
|
||||
$to = _state$selection2.$to,
|
||||
node = _state$selection2.node;
|
||||
if (node && node.isBlock || $from.depth < 2 || !$from.sameParent($to)) return false;
|
||||
var grandParent = $from.node(-1);
|
||||
if (grandParent.type != itemType) return false;
|
||||
if ($from.parent.content.size == 0 && $from.node(-1).childCount == $from.indexAfter(-1)) {
|
||||
if ($from.depth == 3 || $from.node(-3).type != itemType || $from.index(-2) != $from.node(-2).childCount - 1) return false;
|
||||
if (dispatch) {
|
||||
var wrap = prosemirrorModel.Fragment.empty;
|
||||
var depthBefore = $from.index(-1) ? 1 : $from.index(-2) ? 2 : 3;
|
||||
for (var d = $from.depth - depthBefore; d >= $from.depth - 3; d--) wrap = prosemirrorModel.Fragment.from($from.node(d).copy(wrap));
|
||||
var depthAfter = $from.indexAfter(-1) < $from.node(-2).childCount ? 1 : $from.indexAfter(-2) < $from.node(-3).childCount ? 2 : 3;
|
||||
wrap = wrap.append(prosemirrorModel.Fragment.from(itemType.createAndFill()));
|
||||
var start = $from.before($from.depth - (depthBefore - 1));
|
||||
var _tr = state.tr.replace(start, $from.after(-depthAfter), new prosemirrorModel.Slice(wrap, 4 - depthBefore, 0));
|
||||
var sel = -1;
|
||||
_tr.doc.nodesBetween(start, _tr.doc.content.size, function (node, pos) {
|
||||
if (sel > -1) return false;
|
||||
if (node.isTextblock && node.content.size == 0) sel = pos + 1;
|
||||
});
|
||||
if (sel > -1) _tr.setSelection(prosemirrorState.Selection.near(_tr.doc.resolve(sel)));
|
||||
dispatch(_tr.scrollIntoView());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
var nextType = $to.pos == $from.end() ? grandParent.contentMatchAt(0).defaultType : null;
|
||||
var tr = state.tr["delete"]($from.pos, $to.pos);
|
||||
var types = nextType ? [itemAttrs ? {
|
||||
type: itemType,
|
||||
attrs: itemAttrs
|
||||
} : null, {
|
||||
type: nextType
|
||||
}] : undefined;
|
||||
if (!prosemirrorTransform.canSplit(tr.doc, $from.pos, 2, types)) return false;
|
||||
if (dispatch) dispatch(tr.split($from.pos, 2, types).scrollIntoView());
|
||||
return true;
|
||||
};
|
||||
}
|
||||
function splitListItemKeepMarks(itemType, itemAttrs) {
|
||||
var split = splitListItem(itemType, itemAttrs);
|
||||
return function (state, dispatch) {
|
||||
return split(state, dispatch && function (tr) {
|
||||
var marks = state.storedMarks || state.selection.$to.parentOffset && state.selection.$from.marks();
|
||||
if (marks) tr.ensureMarks(marks);
|
||||
dispatch(tr);
|
||||
});
|
||||
};
|
||||
}
|
||||
function liftListItem(itemType) {
|
||||
return function (state, dispatch) {
|
||||
var _state$selection3 = state.selection,
|
||||
$from = _state$selection3.$from,
|
||||
$to = _state$selection3.$to;
|
||||
var range = $from.blockRange($to, function (node) {
|
||||
return node.childCount > 0 && node.firstChild.type == itemType;
|
||||
});
|
||||
if (!range) return false;
|
||||
if (!dispatch) return true;
|
||||
if ($from.node(range.depth - 1).type == itemType) return liftToOuterList(state, dispatch, itemType, range);else return liftOutOfList(state, dispatch, range);
|
||||
};
|
||||
}
|
||||
function liftToOuterList(state, dispatch, itemType, range) {
|
||||
var tr = state.tr,
|
||||
end = range.end,
|
||||
endOfList = range.$to.end(range.depth);
|
||||
if (end < endOfList) {
|
||||
tr.step(new prosemirrorTransform.ReplaceAroundStep(end - 1, endOfList, end, endOfList, new prosemirrorModel.Slice(prosemirrorModel.Fragment.from(itemType.create(null, range.parent.copy())), 1, 0), 1, true));
|
||||
range = new prosemirrorModel.NodeRange(tr.doc.resolve(range.$from.pos), tr.doc.resolve(endOfList), range.depth);
|
||||
}
|
||||
var target = prosemirrorTransform.liftTarget(range);
|
||||
if (target == null) return false;
|
||||
tr.lift(range, target);
|
||||
var $after = tr.doc.resolve(tr.mapping.map(end, -1) - 1);
|
||||
if (prosemirrorTransform.canJoin(tr.doc, $after.pos) && $after.nodeBefore.type == $after.nodeAfter.type) tr.join($after.pos);
|
||||
dispatch(tr.scrollIntoView());
|
||||
return true;
|
||||
}
|
||||
function liftOutOfList(state, dispatch, range) {
|
||||
var tr = state.tr,
|
||||
list = range.parent;
|
||||
for (var pos = range.end, i = range.endIndex - 1, e = range.startIndex; i > e; i--) {
|
||||
pos -= list.child(i).nodeSize;
|
||||
tr["delete"](pos - 1, pos + 1);
|
||||
}
|
||||
var $start = tr.doc.resolve(range.start),
|
||||
item = $start.nodeAfter;
|
||||
if (tr.mapping.map(range.end) != range.start + $start.nodeAfter.nodeSize) return false;
|
||||
var atStart = range.startIndex == 0,
|
||||
atEnd = range.endIndex == list.childCount;
|
||||
var parent = $start.node(-1),
|
||||
indexBefore = $start.index(-1);
|
||||
if (!parent.canReplace(indexBefore + (atStart ? 0 : 1), indexBefore + 1, item.content.append(atEnd ? prosemirrorModel.Fragment.empty : prosemirrorModel.Fragment.from(list)))) return false;
|
||||
var start = $start.pos,
|
||||
end = start + item.nodeSize;
|
||||
tr.step(new prosemirrorTransform.ReplaceAroundStep(start - (atStart ? 1 : 0), end + (atEnd ? 1 : 0), start + 1, end - 1, new prosemirrorModel.Slice((atStart ? prosemirrorModel.Fragment.empty : prosemirrorModel.Fragment.from(list.copy(prosemirrorModel.Fragment.empty))).append(atEnd ? prosemirrorModel.Fragment.empty : prosemirrorModel.Fragment.from(list.copy(prosemirrorModel.Fragment.empty))), atStart ? 0 : 1, atEnd ? 0 : 1), atStart ? 0 : 1));
|
||||
dispatch(tr.scrollIntoView());
|
||||
return true;
|
||||
}
|
||||
function sinkListItem(itemType) {
|
||||
return function (state, dispatch) {
|
||||
var _state$selection4 = state.selection,
|
||||
$from = _state$selection4.$from,
|
||||
$to = _state$selection4.$to;
|
||||
var range = $from.blockRange($to, function (node) {
|
||||
return node.childCount > 0 && node.firstChild.type == itemType;
|
||||
});
|
||||
if (!range) return false;
|
||||
var startIndex = range.startIndex;
|
||||
if (startIndex == 0) return false;
|
||||
var parent = range.parent,
|
||||
nodeBefore = parent.child(startIndex - 1);
|
||||
if (nodeBefore.type != itemType) return false;
|
||||
if (dispatch) {
|
||||
var nestedBefore = nodeBefore.lastChild && nodeBefore.lastChild.type == parent.type;
|
||||
var inner = prosemirrorModel.Fragment.from(nestedBefore ? itemType.create() : null);
|
||||
var slice = new prosemirrorModel.Slice(prosemirrorModel.Fragment.from(itemType.create(null, prosemirrorModel.Fragment.from(parent.type.create(null, inner)))), nestedBefore ? 3 : 1, 0);
|
||||
var before = range.start,
|
||||
after = range.end;
|
||||
dispatch(state.tr.step(new prosemirrorTransform.ReplaceAroundStep(before - (nestedBefore ? 3 : 1), after, before, after, slice, 1, true)).scrollIntoView());
|
||||
}
|
||||
return true;
|
||||
};
|
||||
}
|
||||
exports.addListNodes = addListNodes;
|
||||
exports.bulletList = bulletList;
|
||||
exports.liftListItem = liftListItem;
|
||||
exports.listItem = listItem;
|
||||
exports.orderedList = orderedList;
|
||||
exports.sinkListItem = sinkListItem;
|
||||
exports.splitListItem = splitListItem;
|
||||
exports.splitListItemKeepMarks = splitListItemKeepMarks;
|
||||
exports.wrapInList = wrapInList;
|
||||
exports.wrapRangeInList = wrapRangeInList;
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
import { NodeSpec, NodeType, Attrs, NodeRange } from 'prosemirror-model';
|
||||
import OrderedMap from 'orderedmap';
|
||||
import { Command, Transaction } from 'prosemirror-state';
|
||||
|
||||
/**
|
||||
An ordered list [node spec](https://prosemirror.net/docs/ref/#model.NodeSpec). Has a single
|
||||
attribute, `order`, which determines the number at which the list
|
||||
starts counting, and defaults to 1. Represented as an `<ol>`
|
||||
element.
|
||||
*/
|
||||
declare const orderedList: NodeSpec;
|
||||
/**
|
||||
A bullet list node spec, represented in the DOM as `<ul>`.
|
||||
*/
|
||||
declare const bulletList: NodeSpec;
|
||||
/**
|
||||
A list item (`<li>`) spec.
|
||||
*/
|
||||
declare const listItem: NodeSpec;
|
||||
/**
|
||||
Convenience function for adding list-related node types to a map
|
||||
specifying the nodes for a schema. Adds
|
||||
[`orderedList`](https://prosemirror.net/docs/ref/#schema-list.orderedList) as `"ordered_list"`,
|
||||
[`bulletList`](https://prosemirror.net/docs/ref/#schema-list.bulletList) as `"bullet_list"`, and
|
||||
[`listItem`](https://prosemirror.net/docs/ref/#schema-list.listItem) as `"list_item"`.
|
||||
|
||||
`itemContent` determines the content expression for the list items.
|
||||
If you want the commands defined in this module to apply to your
|
||||
list structure, it should have a shape like `"paragraph block*"` or
|
||||
`"paragraph (ordered_list | bullet_list)*"`. `listGroup` can be
|
||||
given to assign a group name to the list node types, for example
|
||||
`"block"`.
|
||||
*/
|
||||
declare function addListNodes(nodes: OrderedMap<NodeSpec>, itemContent: string, listGroup?: string): OrderedMap<NodeSpec>;
|
||||
/**
|
||||
Returns a command function that wraps the selection in a list with
|
||||
the given type an attributes. If `dispatch` is null, only return a
|
||||
value to indicate whether this is possible, but don't actually
|
||||
perform the change.
|
||||
*/
|
||||
declare function wrapInList(listType: NodeType, attrs?: Attrs | null): Command;
|
||||
/**
|
||||
Try to wrap the given node range in a list of the given type.
|
||||
Return `true` when this is possible, `false` otherwise. When `tr`
|
||||
is non-null, the wrapping is added to that transaction. When it is
|
||||
`null`, the function only queries whether the wrapping is
|
||||
possible.
|
||||
*/
|
||||
declare function wrapRangeInList(tr: Transaction | null, range: NodeRange, listType: NodeType, attrs?: Attrs | null): boolean;
|
||||
/**
|
||||
Build a command that splits a non-empty textblock at the top level
|
||||
of a list item by also splitting that list item.
|
||||
*/
|
||||
declare function splitListItem(itemType: NodeType, itemAttrs?: Attrs): Command;
|
||||
/**
|
||||
Acts like [`splitListItem`](https://prosemirror.net/docs/ref/#schema-list.splitListItem), but
|
||||
without resetting the set of active marks at the cursor.
|
||||
*/
|
||||
declare function splitListItemKeepMarks(itemType: NodeType, itemAttrs?: Attrs): Command;
|
||||
/**
|
||||
Create a command to lift the list item around the selection up into
|
||||
a wrapping list.
|
||||
*/
|
||||
declare function liftListItem(itemType: NodeType): Command;
|
||||
/**
|
||||
Create a command to sink the list item around the selection down
|
||||
into an inner list.
|
||||
*/
|
||||
declare function sinkListItem(itemType: NodeType): Command;
|
||||
|
||||
export { addListNodes, bulletList, liftListItem, listItem, orderedList, sinkListItem, splitListItem, splitListItemKeepMarks, wrapInList, wrapRangeInList };
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
import { NodeSpec, NodeType, Attrs, NodeRange } from 'prosemirror-model';
|
||||
import OrderedMap from 'orderedmap';
|
||||
import { Command, Transaction } from 'prosemirror-state';
|
||||
|
||||
/**
|
||||
An ordered list [node spec](https://prosemirror.net/docs/ref/#model.NodeSpec). Has a single
|
||||
attribute, `order`, which determines the number at which the list
|
||||
starts counting, and defaults to 1. Represented as an `<ol>`
|
||||
element.
|
||||
*/
|
||||
declare const orderedList: NodeSpec;
|
||||
/**
|
||||
A bullet list node spec, represented in the DOM as `<ul>`.
|
||||
*/
|
||||
declare const bulletList: NodeSpec;
|
||||
/**
|
||||
A list item (`<li>`) spec.
|
||||
*/
|
||||
declare const listItem: NodeSpec;
|
||||
/**
|
||||
Convenience function for adding list-related node types to a map
|
||||
specifying the nodes for a schema. Adds
|
||||
[`orderedList`](https://prosemirror.net/docs/ref/#schema-list.orderedList) as `"ordered_list"`,
|
||||
[`bulletList`](https://prosemirror.net/docs/ref/#schema-list.bulletList) as `"bullet_list"`, and
|
||||
[`listItem`](https://prosemirror.net/docs/ref/#schema-list.listItem) as `"list_item"`.
|
||||
|
||||
`itemContent` determines the content expression for the list items.
|
||||
If you want the commands defined in this module to apply to your
|
||||
list structure, it should have a shape like `"paragraph block*"` or
|
||||
`"paragraph (ordered_list | bullet_list)*"`. `listGroup` can be
|
||||
given to assign a group name to the list node types, for example
|
||||
`"block"`.
|
||||
*/
|
||||
declare function addListNodes(nodes: OrderedMap<NodeSpec>, itemContent: string, listGroup?: string): OrderedMap<NodeSpec>;
|
||||
/**
|
||||
Returns a command function that wraps the selection in a list with
|
||||
the given type an attributes. If `dispatch` is null, only return a
|
||||
value to indicate whether this is possible, but don't actually
|
||||
perform the change.
|
||||
*/
|
||||
declare function wrapInList(listType: NodeType, attrs?: Attrs | null): Command;
|
||||
/**
|
||||
Try to wrap the given node range in a list of the given type.
|
||||
Return `true` when this is possible, `false` otherwise. When `tr`
|
||||
is non-null, the wrapping is added to that transaction. When it is
|
||||
`null`, the function only queries whether the wrapping is
|
||||
possible.
|
||||
*/
|
||||
declare function wrapRangeInList(tr: Transaction | null, range: NodeRange, listType: NodeType, attrs?: Attrs | null): boolean;
|
||||
/**
|
||||
Build a command that splits a non-empty textblock at the top level
|
||||
of a list item by also splitting that list item.
|
||||
*/
|
||||
declare function splitListItem(itemType: NodeType, itemAttrs?: Attrs): Command;
|
||||
/**
|
||||
Acts like [`splitListItem`](https://prosemirror.net/docs/ref/#schema-list.splitListItem), but
|
||||
without resetting the set of active marks at the cursor.
|
||||
*/
|
||||
declare function splitListItemKeepMarks(itemType: NodeType, itemAttrs?: Attrs): Command;
|
||||
/**
|
||||
Create a command to lift the list item around the selection up into
|
||||
a wrapping list.
|
||||
*/
|
||||
declare function liftListItem(itemType: NodeType): Command;
|
||||
/**
|
||||
Create a command to sink the list item around the selection down
|
||||
into an inner list.
|
||||
*/
|
||||
declare function sinkListItem(itemType: NodeType): Command;
|
||||
|
||||
export { addListNodes, bulletList, liftListItem, listItem, orderedList, sinkListItem, splitListItem, splitListItemKeepMarks, wrapInList, wrapRangeInList };
|
||||
+289
@@ -0,0 +1,289 @@
|
||||
import { findWrapping, ReplaceAroundStep, canSplit, liftTarget, canJoin } from 'prosemirror-transform';
|
||||
import { NodeRange, Fragment, Slice } from 'prosemirror-model';
|
||||
import { Selection } from 'prosemirror-state';
|
||||
|
||||
const olDOM = ["ol", 0], ulDOM = ["ul", 0], liDOM = ["li", 0];
|
||||
/**
|
||||
An ordered list [node spec](https://prosemirror.net/docs/ref/#model.NodeSpec). Has a single
|
||||
attribute, `order`, which determines the number at which the list
|
||||
starts counting, and defaults to 1. Represented as an `<ol>`
|
||||
element.
|
||||
*/
|
||||
const orderedList = {
|
||||
attrs: { order: { default: 1, validate: "number" } },
|
||||
parseDOM: [{ tag: "ol", getAttrs(dom) {
|
||||
return { order: dom.hasAttribute("start") ? +dom.getAttribute("start") : 1 };
|
||||
} }],
|
||||
toDOM(node) {
|
||||
return node.attrs.order == 1 ? olDOM : ["ol", { start: node.attrs.order }, 0];
|
||||
}
|
||||
};
|
||||
/**
|
||||
A bullet list node spec, represented in the DOM as `<ul>`.
|
||||
*/
|
||||
const bulletList = {
|
||||
parseDOM: [{ tag: "ul" }],
|
||||
toDOM() { return ulDOM; }
|
||||
};
|
||||
/**
|
||||
A list item (`<li>`) spec.
|
||||
*/
|
||||
const listItem = {
|
||||
parseDOM: [{ tag: "li" }],
|
||||
toDOM() { return liDOM; },
|
||||
defining: true
|
||||
};
|
||||
function add(obj, props) {
|
||||
let copy = {};
|
||||
for (let prop in obj)
|
||||
copy[prop] = obj[prop];
|
||||
for (let prop in props)
|
||||
copy[prop] = props[prop];
|
||||
return copy;
|
||||
}
|
||||
/**
|
||||
Convenience function for adding list-related node types to a map
|
||||
specifying the nodes for a schema. Adds
|
||||
[`orderedList`](https://prosemirror.net/docs/ref/#schema-list.orderedList) as `"ordered_list"`,
|
||||
[`bulletList`](https://prosemirror.net/docs/ref/#schema-list.bulletList) as `"bullet_list"`, and
|
||||
[`listItem`](https://prosemirror.net/docs/ref/#schema-list.listItem) as `"list_item"`.
|
||||
|
||||
`itemContent` determines the content expression for the list items.
|
||||
If you want the commands defined in this module to apply to your
|
||||
list structure, it should have a shape like `"paragraph block*"` or
|
||||
`"paragraph (ordered_list | bullet_list)*"`. `listGroup` can be
|
||||
given to assign a group name to the list node types, for example
|
||||
`"block"`.
|
||||
*/
|
||||
function addListNodes(nodes, itemContent, listGroup) {
|
||||
return nodes.append({
|
||||
ordered_list: add(orderedList, { content: "list_item+", group: listGroup }),
|
||||
bullet_list: add(bulletList, { content: "list_item+", group: listGroup }),
|
||||
list_item: add(listItem, { content: itemContent })
|
||||
});
|
||||
}
|
||||
/**
|
||||
Returns a command function that wraps the selection in a list with
|
||||
the given type an attributes. If `dispatch` is null, only return a
|
||||
value to indicate whether this is possible, but don't actually
|
||||
perform the change.
|
||||
*/
|
||||
function wrapInList(listType, attrs = null) {
|
||||
return function (state, dispatch) {
|
||||
let { $from, $to } = state.selection;
|
||||
let range = $from.blockRange($to);
|
||||
if (!range)
|
||||
return false;
|
||||
let tr = dispatch ? state.tr : null;
|
||||
if (!wrapRangeInList(tr, range, listType, attrs))
|
||||
return false;
|
||||
if (dispatch)
|
||||
dispatch(tr.scrollIntoView());
|
||||
return true;
|
||||
};
|
||||
}
|
||||
/**
|
||||
Try to wrap the given node range in a list of the given type.
|
||||
Return `true` when this is possible, `false` otherwise. When `tr`
|
||||
is non-null, the wrapping is added to that transaction. When it is
|
||||
`null`, the function only queries whether the wrapping is
|
||||
possible.
|
||||
*/
|
||||
function wrapRangeInList(tr, range, listType, attrs = null) {
|
||||
let doJoin = false, outerRange = range, doc = range.$from.doc;
|
||||
// This is at the top of an existing list item
|
||||
if (range.depth >= 2 && range.$from.node(range.depth - 1).type.compatibleContent(listType) && range.startIndex == 0) {
|
||||
// Don't do anything if this is the top of the list
|
||||
if (range.$from.index(range.depth - 1) == 0)
|
||||
return false;
|
||||
let $insert = doc.resolve(range.start - 2);
|
||||
outerRange = new NodeRange($insert, $insert, range.depth);
|
||||
if (range.endIndex < range.parent.childCount)
|
||||
range = new NodeRange(range.$from, doc.resolve(range.$to.end(range.depth)), range.depth);
|
||||
doJoin = true;
|
||||
}
|
||||
let wrap = findWrapping(outerRange, listType, attrs, range);
|
||||
if (!wrap)
|
||||
return false;
|
||||
if (tr)
|
||||
doWrapInList(tr, range, wrap, doJoin, listType);
|
||||
return true;
|
||||
}
|
||||
function doWrapInList(tr, range, wrappers, joinBefore, listType) {
|
||||
let content = Fragment.empty;
|
||||
for (let i = wrappers.length - 1; i >= 0; i--)
|
||||
content = Fragment.from(wrappers[i].type.create(wrappers[i].attrs, content));
|
||||
tr.step(new ReplaceAroundStep(range.start - (joinBefore ? 2 : 0), range.end, range.start, range.end, new Slice(content, 0, 0), wrappers.length, true));
|
||||
let found = 0;
|
||||
for (let i = 0; i < wrappers.length; i++)
|
||||
if (wrappers[i].type == listType)
|
||||
found = i + 1;
|
||||
let splitDepth = wrappers.length - found;
|
||||
let splitPos = range.start + wrappers.length - (joinBefore ? 2 : 0), parent = range.parent;
|
||||
for (let i = range.startIndex, e = range.endIndex, first = true; i < e; i++, first = false) {
|
||||
if (!first && canSplit(tr.doc, splitPos, splitDepth)) {
|
||||
tr.split(splitPos, splitDepth);
|
||||
splitPos += 2 * splitDepth;
|
||||
}
|
||||
splitPos += parent.child(i).nodeSize;
|
||||
}
|
||||
return tr;
|
||||
}
|
||||
/**
|
||||
Build a command that splits a non-empty textblock at the top level
|
||||
of a list item by also splitting that list item.
|
||||
*/
|
||||
function splitListItem(itemType, itemAttrs) {
|
||||
return function (state, dispatch) {
|
||||
let { $from, $to, node } = state.selection;
|
||||
if ((node && node.isBlock) || $from.depth < 2 || !$from.sameParent($to))
|
||||
return false;
|
||||
let grandParent = $from.node(-1);
|
||||
if (grandParent.type != itemType)
|
||||
return false;
|
||||
if ($from.parent.content.size == 0 && $from.node(-1).childCount == $from.indexAfter(-1)) {
|
||||
// In an empty block. If this is a nested list, the wrapping
|
||||
// list item should be split. Otherwise, bail out and let next
|
||||
// command handle lifting.
|
||||
if ($from.depth == 3 || $from.node(-3).type != itemType ||
|
||||
$from.index(-2) != $from.node(-2).childCount - 1)
|
||||
return false;
|
||||
if (dispatch) {
|
||||
let wrap = Fragment.empty;
|
||||
let depthBefore = $from.index(-1) ? 1 : $from.index(-2) ? 2 : 3;
|
||||
// Build a fragment containing empty versions of the structure
|
||||
// from the outer list item to the parent node of the cursor
|
||||
for (let d = $from.depth - depthBefore; d >= $from.depth - 3; d--)
|
||||
wrap = Fragment.from($from.node(d).copy(wrap));
|
||||
let depthAfter = $from.indexAfter(-1) < $from.node(-2).childCount ? 1
|
||||
: $from.indexAfter(-2) < $from.node(-3).childCount ? 2 : 3;
|
||||
// Add a second list item with an empty default start node
|
||||
wrap = wrap.append(Fragment.from(itemType.createAndFill()));
|
||||
let start = $from.before($from.depth - (depthBefore - 1));
|
||||
let tr = state.tr.replace(start, $from.after(-depthAfter), new Slice(wrap, 4 - depthBefore, 0));
|
||||
let sel = -1;
|
||||
tr.doc.nodesBetween(start, tr.doc.content.size, (node, pos) => {
|
||||
if (sel > -1)
|
||||
return false;
|
||||
if (node.isTextblock && node.content.size == 0)
|
||||
sel = pos + 1;
|
||||
});
|
||||
if (sel > -1)
|
||||
tr.setSelection(Selection.near(tr.doc.resolve(sel)));
|
||||
dispatch(tr.scrollIntoView());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
let nextType = $to.pos == $from.end() ? grandParent.contentMatchAt(0).defaultType : null;
|
||||
let tr = state.tr.delete($from.pos, $to.pos);
|
||||
let types = nextType ? [itemAttrs ? { type: itemType, attrs: itemAttrs } : null, { type: nextType }] : undefined;
|
||||
if (!canSplit(tr.doc, $from.pos, 2, types))
|
||||
return false;
|
||||
if (dispatch)
|
||||
dispatch(tr.split($from.pos, 2, types).scrollIntoView());
|
||||
return true;
|
||||
};
|
||||
}
|
||||
/**
|
||||
Acts like [`splitListItem`](https://prosemirror.net/docs/ref/#schema-list.splitListItem), but
|
||||
without resetting the set of active marks at the cursor.
|
||||
*/
|
||||
function splitListItemKeepMarks(itemType, itemAttrs) {
|
||||
let split = splitListItem(itemType, itemAttrs);
|
||||
return (state, dispatch) => {
|
||||
return split(state, dispatch && (tr => {
|
||||
let marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks());
|
||||
if (marks)
|
||||
tr.ensureMarks(marks);
|
||||
dispatch(tr);
|
||||
}));
|
||||
};
|
||||
}
|
||||
/**
|
||||
Create a command to lift the list item around the selection up into
|
||||
a wrapping list.
|
||||
*/
|
||||
function liftListItem(itemType) {
|
||||
return function (state, dispatch) {
|
||||
let { $from, $to } = state.selection;
|
||||
let range = $from.blockRange($to, node => node.childCount > 0 && node.firstChild.type == itemType);
|
||||
if (!range)
|
||||
return false;
|
||||
if (!dispatch)
|
||||
return true;
|
||||
if ($from.node(range.depth - 1).type == itemType) // Inside a parent list
|
||||
return liftToOuterList(state, dispatch, itemType, range);
|
||||
else // Outer list node
|
||||
return liftOutOfList(state, dispatch, range);
|
||||
};
|
||||
}
|
||||
function liftToOuterList(state, dispatch, itemType, range) {
|
||||
let tr = state.tr, end = range.end, endOfList = range.$to.end(range.depth);
|
||||
if (end < endOfList) {
|
||||
// There are siblings after the lifted items, which must become
|
||||
// children of the last item
|
||||
tr.step(new ReplaceAroundStep(end - 1, endOfList, end, endOfList, new Slice(Fragment.from(itemType.create(null, range.parent.copy())), 1, 0), 1, true));
|
||||
range = new NodeRange(tr.doc.resolve(range.$from.pos), tr.doc.resolve(endOfList), range.depth);
|
||||
}
|
||||
const target = liftTarget(range);
|
||||
if (target == null)
|
||||
return false;
|
||||
tr.lift(range, target);
|
||||
let $after = tr.doc.resolve(tr.mapping.map(end, -1) - 1);
|
||||
if (canJoin(tr.doc, $after.pos) && $after.nodeBefore.type == $after.nodeAfter.type)
|
||||
tr.join($after.pos);
|
||||
dispatch(tr.scrollIntoView());
|
||||
return true;
|
||||
}
|
||||
function liftOutOfList(state, dispatch, range) {
|
||||
let tr = state.tr, list = range.parent;
|
||||
// Merge the list items into a single big item
|
||||
for (let pos = range.end, i = range.endIndex - 1, e = range.startIndex; i > e; i--) {
|
||||
pos -= list.child(i).nodeSize;
|
||||
tr.delete(pos - 1, pos + 1);
|
||||
}
|
||||
let $start = tr.doc.resolve(range.start), item = $start.nodeAfter;
|
||||
if (tr.mapping.map(range.end) != range.start + $start.nodeAfter.nodeSize)
|
||||
return false;
|
||||
let atStart = range.startIndex == 0, atEnd = range.endIndex == list.childCount;
|
||||
let parent = $start.node(-1), indexBefore = $start.index(-1);
|
||||
if (!parent.canReplace(indexBefore + (atStart ? 0 : 1), indexBefore + 1, item.content.append(atEnd ? Fragment.empty : Fragment.from(list))))
|
||||
return false;
|
||||
let start = $start.pos, end = start + item.nodeSize;
|
||||
// Strip off the surrounding list. At the sides where we're not at
|
||||
// the end of the list, the existing list is closed. At sides where
|
||||
// this is the end, it is overwritten to its end.
|
||||
tr.step(new ReplaceAroundStep(start - (atStart ? 1 : 0), end + (atEnd ? 1 : 0), start + 1, end - 1, new Slice((atStart ? Fragment.empty : Fragment.from(list.copy(Fragment.empty)))
|
||||
.append(atEnd ? Fragment.empty : Fragment.from(list.copy(Fragment.empty))), atStart ? 0 : 1, atEnd ? 0 : 1), atStart ? 0 : 1));
|
||||
dispatch(tr.scrollIntoView());
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
Create a command to sink the list item around the selection down
|
||||
into an inner list.
|
||||
*/
|
||||
function sinkListItem(itemType) {
|
||||
return function (state, dispatch) {
|
||||
let { $from, $to } = state.selection;
|
||||
let range = $from.blockRange($to, node => node.childCount > 0 && node.firstChild.type == itemType);
|
||||
if (!range)
|
||||
return false;
|
||||
let startIndex = range.startIndex;
|
||||
if (startIndex == 0)
|
||||
return false;
|
||||
let parent = range.parent, nodeBefore = parent.child(startIndex - 1);
|
||||
if (nodeBefore.type != itemType)
|
||||
return false;
|
||||
if (dispatch) {
|
||||
let nestedBefore = nodeBefore.lastChild && nodeBefore.lastChild.type == parent.type;
|
||||
let inner = Fragment.from(nestedBefore ? itemType.create() : null);
|
||||
let slice = new Slice(Fragment.from(itemType.create(null, Fragment.from(parent.type.create(null, inner)))), nestedBefore ? 3 : 1, 0);
|
||||
let before = range.start, after = range.end;
|
||||
dispatch(state.tr.step(new ReplaceAroundStep(before - (nestedBefore ? 3 : 1), after, before, after, slice, 1, true))
|
||||
.scrollIntoView());
|
||||
}
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
export { addListNodes, bulletList, liftListItem, listItem, orderedList, sinkListItem, splitListItem, splitListItemKeepMarks, wrapInList, wrapRangeInList };
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"name": "prosemirror-schema-list",
|
||||
"version": "1.5.1",
|
||||
"description": "List-related schema elements and commands for ProseMirror",
|
||||
"type": "module",
|
||||
"main": "dist/index.cjs",
|
||||
"module": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"exports": {
|
||||
"import": "./dist/index.js",
|
||||
"require": "./dist/index.cjs"
|
||||
},
|
||||
"sideEffects": false,
|
||||
"license": "MIT",
|
||||
"maintainers": [
|
||||
{
|
||||
"name": "Marijn Haverbeke",
|
||||
"email": "marijn@haverbeke.berlin",
|
||||
"web": "http://marijnhaverbeke.nl"
|
||||
}
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/prosemirror/prosemirror-schema-list.git"
|
||||
},
|
||||
"dependencies": {
|
||||
"prosemirror-model": "^1.0.0",
|
||||
"prosemirror-transform": "^1.7.3",
|
||||
"prosemirror-state": "^1.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"prosemirror-state": "^1.0.0",
|
||||
"@prosemirror/buildhelper": "^0.1.5",
|
||||
"prosemirror-test-builder": "^1.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "pm-runtests",
|
||||
"prepare": "pm-buildhelper src/schema-list.ts"
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
This module exports list-related schema elements and commands. The
|
||||
commands assume lists to be nestable, with the restriction that the
|
||||
first child of a list item is a plain paragraph.
|
||||
|
||||
These are the node specs:
|
||||
|
||||
@orderedList
|
||||
@bulletList
|
||||
@listItem
|
||||
|
||||
@addListNodes
|
||||
|
||||
Using this would look something like this:
|
||||
|
||||
```javascript
|
||||
const mySchema = new Schema({
|
||||
nodes: addListNodes(baseSchema.spec.nodes, "paragraph block*", "block"),
|
||||
marks: baseSchema.spec.marks
|
||||
})
|
||||
```
|
||||
|
||||
The following functions are [commands](/docs/guide/#commands) and utilities:
|
||||
|
||||
@wrapInList
|
||||
@wrapRangeInList
|
||||
@splitListItem
|
||||
@splitListItemKeepMarks
|
||||
@liftListItem
|
||||
@sinkListItem
|
||||
+267
@@ -0,0 +1,267 @@
|
||||
import {findWrapping, liftTarget, canSplit, ReplaceAroundStep, canJoin} from "prosemirror-transform"
|
||||
import {Slice, Fragment, NodeSpec, DOMOutputSpec, NodeType, Attrs, NodeRange} from "prosemirror-model"
|
||||
import OrderedMap from "orderedmap"
|
||||
import {Command, EditorState, Transaction, NodeSelection, Selection} from "prosemirror-state"
|
||||
|
||||
const olDOM: DOMOutputSpec = ["ol", 0], ulDOM: DOMOutputSpec = ["ul", 0], liDOM: DOMOutputSpec = ["li", 0]
|
||||
|
||||
/// An ordered list [node spec](#model.NodeSpec). Has a single
|
||||
/// attribute, `order`, which determines the number at which the list
|
||||
/// starts counting, and defaults to 1. Represented as an `<ol>`
|
||||
/// element.
|
||||
export const orderedList = {
|
||||
attrs: {order: {default: 1, validate: "number"}},
|
||||
parseDOM: [{tag: "ol", getAttrs(dom: HTMLElement) {
|
||||
return {order: dom.hasAttribute("start") ? +dom.getAttribute("start")! : 1}
|
||||
}}],
|
||||
toDOM(node) {
|
||||
return node.attrs.order == 1 ? olDOM : ["ol", {start: node.attrs.order}, 0]
|
||||
}
|
||||
} as NodeSpec
|
||||
|
||||
/// A bullet list node spec, represented in the DOM as `<ul>`.
|
||||
export const bulletList: NodeSpec = {
|
||||
parseDOM: [{tag: "ul"}],
|
||||
toDOM() { return ulDOM }
|
||||
}
|
||||
|
||||
/// A list item (`<li>`) spec.
|
||||
export const listItem: NodeSpec = {
|
||||
parseDOM: [{tag: "li"}],
|
||||
toDOM() { return liDOM },
|
||||
defining: true
|
||||
}
|
||||
|
||||
function add(obj: {[prop: string]: any}, props: {[prop: string]: any}) {
|
||||
let copy: {[prop: string]: any} = {}
|
||||
for (let prop in obj) copy[prop] = obj[prop]
|
||||
for (let prop in props) copy[prop] = props[prop]
|
||||
return copy
|
||||
}
|
||||
|
||||
/// Convenience function for adding list-related node types to a map
|
||||
/// specifying the nodes for a schema. Adds
|
||||
/// [`orderedList`](#schema-list.orderedList) as `"ordered_list"`,
|
||||
/// [`bulletList`](#schema-list.bulletList) as `"bullet_list"`, and
|
||||
/// [`listItem`](#schema-list.listItem) as `"list_item"`.
|
||||
///
|
||||
/// `itemContent` determines the content expression for the list items.
|
||||
/// If you want the commands defined in this module to apply to your
|
||||
/// list structure, it should have a shape like `"paragraph block*"` or
|
||||
/// `"paragraph (ordered_list | bullet_list)*"`. `listGroup` can be
|
||||
/// given to assign a group name to the list node types, for example
|
||||
/// `"block"`.
|
||||
export function addListNodes(nodes: OrderedMap<NodeSpec>, itemContent: string, listGroup?: string): OrderedMap<NodeSpec> {
|
||||
return nodes.append({
|
||||
ordered_list: add(orderedList, {content: "list_item+", group: listGroup}),
|
||||
bullet_list: add(bulletList, {content: "list_item+", group: listGroup}),
|
||||
list_item: add(listItem, {content: itemContent})
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns a command function that wraps the selection in a list with
|
||||
/// the given type an attributes. If `dispatch` is null, only return a
|
||||
/// value to indicate whether this is possible, but don't actually
|
||||
/// perform the change.
|
||||
export function wrapInList(listType: NodeType, attrs: Attrs | null = null): Command {
|
||||
return function(state: EditorState, dispatch?: (tr: Transaction) => void) {
|
||||
let {$from, $to} = state.selection
|
||||
let range = $from.blockRange($to)
|
||||
if (!range) return false
|
||||
let tr = dispatch ? state.tr : null
|
||||
if (!wrapRangeInList(tr, range, listType, attrs)) return false
|
||||
if (dispatch) dispatch(tr!.scrollIntoView())
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
/// Try to wrap the given node range in a list of the given type.
|
||||
/// Return `true` when this is possible, `false` otherwise. When `tr`
|
||||
/// is non-null, the wrapping is added to that transaction. When it is
|
||||
/// `null`, the function only queries whether the wrapping is
|
||||
/// possible.
|
||||
export function wrapRangeInList(tr: Transaction | null, range: NodeRange, listType: NodeType, attrs: Attrs | null = null) {
|
||||
let doJoin = false, outerRange = range, doc = range.$from.doc
|
||||
// This is at the top of an existing list item
|
||||
if (range.depth >= 2 && range.$from.node(range.depth - 1).type.compatibleContent(listType) && range.startIndex == 0) {
|
||||
// Don't do anything if this is the top of the list
|
||||
if (range.$from.index(range.depth - 1) == 0) return false
|
||||
let $insert = doc.resolve(range.start - 2)
|
||||
outerRange = new NodeRange($insert, $insert, range.depth)
|
||||
if (range.endIndex < range.parent.childCount)
|
||||
range = new NodeRange(range.$from, doc.resolve(range.$to.end(range.depth)), range.depth)
|
||||
doJoin = true
|
||||
}
|
||||
let wrap = findWrapping(outerRange, listType, attrs, range)
|
||||
if (!wrap) return false
|
||||
if (tr) doWrapInList(tr, range, wrap, doJoin, listType)
|
||||
return true
|
||||
}
|
||||
|
||||
function doWrapInList(tr: Transaction, range: NodeRange, wrappers: {type: NodeType, attrs?: Attrs | null}[],
|
||||
joinBefore: boolean, listType: NodeType) {
|
||||
let content = Fragment.empty
|
||||
for (let i = wrappers.length - 1; i >= 0; i--)
|
||||
content = Fragment.from(wrappers[i].type.create(wrappers[i].attrs, content))
|
||||
|
||||
tr.step(new ReplaceAroundStep(range.start - (joinBefore ? 2 : 0), range.end, range.start, range.end,
|
||||
new Slice(content, 0, 0), wrappers.length, true))
|
||||
|
||||
let found = 0
|
||||
for (let i = 0; i < wrappers.length; i++) if (wrappers[i].type == listType) found = i + 1
|
||||
let splitDepth = wrappers.length - found
|
||||
|
||||
let splitPos = range.start + wrappers.length - (joinBefore ? 2 : 0), parent = range.parent
|
||||
for (let i = range.startIndex, e = range.endIndex, first = true; i < e; i++, first = false) {
|
||||
if (!first && canSplit(tr.doc, splitPos, splitDepth)) {
|
||||
tr.split(splitPos, splitDepth)
|
||||
splitPos += 2 * splitDepth
|
||||
}
|
||||
splitPos += parent.child(i).nodeSize
|
||||
}
|
||||
return tr
|
||||
}
|
||||
|
||||
/// Build a command that splits a non-empty textblock at the top level
|
||||
/// of a list item by also splitting that list item.
|
||||
export function splitListItem(itemType: NodeType, itemAttrs?: Attrs): Command {
|
||||
return function(state: EditorState, dispatch?: (tr: Transaction) => void) {
|
||||
let {$from, $to, node} = state.selection as NodeSelection
|
||||
if ((node && node.isBlock) || $from.depth < 2 || !$from.sameParent($to)) return false
|
||||
let grandParent = $from.node(-1)
|
||||
if (grandParent.type != itemType) return false
|
||||
if ($from.parent.content.size == 0 && $from.node(-1).childCount == $from.indexAfter(-1)) {
|
||||
// In an empty block. If this is a nested list, the wrapping
|
||||
// list item should be split. Otherwise, bail out and let next
|
||||
// command handle lifting.
|
||||
if ($from.depth == 3 || $from.node(-3).type != itemType ||
|
||||
$from.index(-2) != $from.node(-2).childCount - 1) return false
|
||||
if (dispatch) {
|
||||
let wrap = Fragment.empty
|
||||
let depthBefore = $from.index(-1) ? 1 : $from.index(-2) ? 2 : 3
|
||||
// Build a fragment containing empty versions of the structure
|
||||
// from the outer list item to the parent node of the cursor
|
||||
for (let d = $from.depth - depthBefore; d >= $from.depth - 3; d--)
|
||||
wrap = Fragment.from($from.node(d).copy(wrap))
|
||||
let depthAfter = $from.indexAfter(-1) < $from.node(-2).childCount ? 1
|
||||
: $from.indexAfter(-2) < $from.node(-3).childCount ? 2 : 3
|
||||
// Add a second list item with an empty default start node
|
||||
wrap = wrap.append(Fragment.from(itemType.createAndFill()))
|
||||
let start = $from.before($from.depth - (depthBefore - 1))
|
||||
let tr = state.tr.replace(start, $from.after(-depthAfter), new Slice(wrap, 4 - depthBefore, 0))
|
||||
let sel = -1
|
||||
tr.doc.nodesBetween(start, tr.doc.content.size, (node, pos) => {
|
||||
if (sel > -1) return false
|
||||
if (node.isTextblock && node.content.size == 0) sel = pos + 1
|
||||
})
|
||||
if (sel > -1) tr.setSelection(Selection.near(tr.doc.resolve(sel)))
|
||||
dispatch(tr.scrollIntoView())
|
||||
}
|
||||
return true
|
||||
}
|
||||
let nextType = $to.pos == $from.end() ? grandParent.contentMatchAt(0).defaultType : null
|
||||
let tr = state.tr.delete($from.pos, $to.pos)
|
||||
let types = nextType ? [itemAttrs ? {type: itemType, attrs: itemAttrs} : null, {type: nextType}] : undefined
|
||||
if (!canSplit(tr.doc, $from.pos, 2, types)) return false
|
||||
if (dispatch) dispatch(tr.split($from.pos, 2, types).scrollIntoView())
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
/// Acts like [`splitListItem`](#schema-list.splitListItem), but
|
||||
/// without resetting the set of active marks at the cursor.
|
||||
export function splitListItemKeepMarks(itemType: NodeType, itemAttrs?: Attrs): Command {
|
||||
let split = splitListItem(itemType, itemAttrs)
|
||||
return (state, dispatch) => {
|
||||
return split(state, dispatch && (tr => {
|
||||
let marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks())
|
||||
if (marks) tr.ensureMarks(marks)
|
||||
dispatch(tr)
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a command to lift the list item around the selection up into
|
||||
/// a wrapping list.
|
||||
export function liftListItem(itemType: NodeType): Command {
|
||||
return function(state: EditorState, dispatch?: (tr: Transaction) => void) {
|
||||
let {$from, $to} = state.selection
|
||||
let range = $from.blockRange($to, node => node.childCount > 0 && node.firstChild!.type == itemType)
|
||||
if (!range) return false
|
||||
if (!dispatch) return true
|
||||
if ($from.node(range.depth - 1).type == itemType) // Inside a parent list
|
||||
return liftToOuterList(state, dispatch, itemType, range)
|
||||
else // Outer list node
|
||||
return liftOutOfList(state, dispatch, range)
|
||||
}
|
||||
}
|
||||
|
||||
function liftToOuterList(state: EditorState, dispatch: (tr: Transaction) => void, itemType: NodeType, range: NodeRange) {
|
||||
let tr = state.tr, end = range.end, endOfList = range.$to.end(range.depth)
|
||||
if (end < endOfList) {
|
||||
// There are siblings after the lifted items, which must become
|
||||
// children of the last item
|
||||
tr.step(new ReplaceAroundStep(end - 1, endOfList, end, endOfList,
|
||||
new Slice(Fragment.from(itemType.create(null, range.parent.copy())), 1, 0), 1, true))
|
||||
range = new NodeRange(tr.doc.resolve(range.$from.pos), tr.doc.resolve(endOfList), range.depth)
|
||||
}
|
||||
const target = liftTarget(range)
|
||||
if (target == null) return false
|
||||
tr.lift(range, target)
|
||||
let $after = tr.doc.resolve(tr.mapping.map(end, -1) - 1)
|
||||
if (canJoin(tr.doc, $after.pos) && $after.nodeBefore!.type == $after.nodeAfter!.type) tr.join($after.pos)
|
||||
dispatch(tr.scrollIntoView())
|
||||
return true
|
||||
}
|
||||
|
||||
function liftOutOfList(state: EditorState, dispatch: (tr: Transaction) => void, range: NodeRange) {
|
||||
let tr = state.tr, list = range.parent
|
||||
// Merge the list items into a single big item
|
||||
for (let pos = range.end, i = range.endIndex - 1, e = range.startIndex; i > e; i--) {
|
||||
pos -= list.child(i).nodeSize
|
||||
tr.delete(pos - 1, pos + 1)
|
||||
}
|
||||
let $start = tr.doc.resolve(range.start), item = $start.nodeAfter!
|
||||
if (tr.mapping.map(range.end) != range.start + $start.nodeAfter!.nodeSize) return false
|
||||
let atStart = range.startIndex == 0, atEnd = range.endIndex == list.childCount
|
||||
let parent = $start.node(-1), indexBefore = $start.index(-1)
|
||||
if (!parent.canReplace(indexBefore + (atStart ? 0 : 1), indexBefore + 1,
|
||||
item.content.append(atEnd ? Fragment.empty : Fragment.from(list))))
|
||||
return false
|
||||
let start = $start.pos, end = start + item.nodeSize
|
||||
// Strip off the surrounding list. At the sides where we're not at
|
||||
// the end of the list, the existing list is closed. At sides where
|
||||
// this is the end, it is overwritten to its end.
|
||||
tr.step(new ReplaceAroundStep(start - (atStart ? 1 : 0), end + (atEnd ? 1 : 0), start + 1, end - 1,
|
||||
new Slice((atStart ? Fragment.empty : Fragment.from(list.copy(Fragment.empty)))
|
||||
.append(atEnd ? Fragment.empty : Fragment.from(list.copy(Fragment.empty))),
|
||||
atStart ? 0 : 1, atEnd ? 0 : 1), atStart ? 0 : 1))
|
||||
dispatch(tr.scrollIntoView())
|
||||
return true
|
||||
}
|
||||
|
||||
/// Create a command to sink the list item around the selection down
|
||||
/// into an inner list.
|
||||
export function sinkListItem(itemType: NodeType): Command {
|
||||
return function(state, dispatch) {
|
||||
let {$from, $to} = state.selection
|
||||
let range = $from.blockRange($to, node => node.childCount > 0 && node.firstChild!.type == itemType)
|
||||
if (!range) return false
|
||||
let startIndex = range.startIndex
|
||||
if (startIndex == 0) return false
|
||||
let parent = range.parent, nodeBefore = parent.child(startIndex - 1)
|
||||
if (nodeBefore.type != itemType) return false
|
||||
|
||||
if (dispatch) {
|
||||
let nestedBefore = nodeBefore.lastChild && nodeBefore.lastChild.type == parent.type
|
||||
let inner = Fragment.from(nestedBefore ? itemType.create() : null)
|
||||
let slice = new Slice(Fragment.from(itemType.create(null, Fragment.from(parent.type.create(null, inner)))),
|
||||
nestedBefore ? 3 : 1, 0)
|
||||
let before = range.start, after = range.end
|
||||
dispatch(state.tr.step(new ReplaceAroundStep(before - (nestedBefore ? 3 : 1), after,
|
||||
before, after, slice, 1, true))
|
||||
.scrollIntoView())
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user