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:
+116
@@ -0,0 +1,116 @@
|
||||
## 1.4.0 (2025-10-15)
|
||||
|
||||
### New features
|
||||
|
||||
Nodes can now set a `createGapCursor: true` property on their spec to allow gap cursors to appear next to them.
|
||||
|
||||
## 1.3.2 (2023-05-17)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
Include CommonJS type declarations in the package to please new TypeScript resolution settings.
|
||||
|
||||
## 1.3.1 (2022-06-07)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
Export CSS file from package.json.
|
||||
|
||||
## 1.3.0 (2022-05-30)
|
||||
|
||||
### New features
|
||||
|
||||
Include TypeScript type declarations.
|
||||
|
||||
## 1.2.2 (2022-02-25)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
Make sure compositions started from a gap cursor have their inline context created in advance, so that they don't get aborted right away.
|
||||
|
||||
## 1.2.1 (2021-12-20)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
Fix an issue where a gap cursor would fail to show up at the start or end of some isolating nodes.
|
||||
|
||||
## 1.2.0 (2021-09-20)
|
||||
|
||||
### New features
|
||||
|
||||
The `GapCursor` constructor is now public.
|
||||
|
||||
## 1.1.5 (2020-04-05)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
Fix an issue where the gap cursor plugin would sometimes cause perfectly selectable content to be skipped when moving the selection with the arrow keys.
|
||||
|
||||
## 1.1.4 (2020-03-20)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
Improve behavior around unselectable block nodes.
|
||||
|
||||
## 1.1.3 (2020-01-22)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
Fix a crash in documents that have a textblock as top node.
|
||||
|
||||
## 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-06-24)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
Do not show a gap cursor when the view isn't editable.
|
||||
|
||||
## 1.0.3 (2018-10-01)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
Don't blanket-forbid gap cursors next to textblocks
|
||||
|
||||
## 1.0.2 (2018-03-15)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
Throw errors, rather than constructing invalid objects, when deserializing from invalid JSON data.
|
||||
|
||||
## 1.0.1 (2018-02-16)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
Prevent issue where clicking on a selectable node near a valid gap cursor position would create a gap cursor rather than select the node.
|
||||
|
||||
## 1.0.0 (2017-10-13)
|
||||
|
||||
### New features
|
||||
|
||||
Valid gap cursor positions are not determined in a way that allows them inside nested nodes. By default, any position where a textblock can be inserted is valid gap cursor position.
|
||||
|
||||
Nodes can override whether they allow gap cursors with the `allowGapCursor` property in their spec.
|
||||
|
||||
## 0.23.1 (2017-09-19)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
Moving out of a table with the arrow keys now creates a gap cursor when appropriate.
|
||||
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
# 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
|
||||
|
||||
If you want to make a change that involves a significant overhaul of
|
||||
the code or introduces a user-visible new feature, create an
|
||||
[RFC](https://github.com/ProseMirror/rfcs/) first with your proposal.
|
||||
|
||||
- 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.
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
# prosemirror-gapcursor
|
||||
|
||||
[ [**WEBSITE**](https://prosemirror.net) | [**ISSUES**](https://github.com/prosemirror/prosemirror/issues) | [**FORUM**](https://discuss.prosemirror.net) | [**CHANGELOG**](https://github.com/ProseMirror/prosemirror-gapcursor/blob/master/CHANGELOG.md) ]
|
||||
|
||||
This is a [core module](https://prosemirror.net/docs/ref/#gapcursor) of [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 [plugin](https://prosemirror.net/docs/ref/#gapcursor) implements a
|
||||
block-level cursor that can be used to focus places that don't allow
|
||||
regular selection (such as positions that have a leaf block node,
|
||||
table, or the end of the document both before and after them). Make
|
||||
sure you load `style/gapcursor.css` (or define your own styling for
|
||||
the cursor).
|
||||
|
||||
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.
|
||||
+239
@@ -0,0 +1,239 @@
|
||||
'use strict';
|
||||
|
||||
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
||||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
||||
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } }
|
||||
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
|
||||
function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return _typeof(key) === "symbol" ? key : String(key); }
|
||||
function _toPrimitive(input, hint) { if (_typeof(input) !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (_typeof(res) !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
|
||||
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); Object.defineProperty(subClass, "prototype", { writable: false }); if (superClass) _setPrototypeOf(subClass, superClass); }
|
||||
function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
|
||||
function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }
|
||||
function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } else if (call !== void 0) { throw new TypeError("Derived constructors may only return object or undefined"); } return _assertThisInitialized(self); }
|
||||
function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
|
||||
function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } }
|
||||
function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
|
||||
var prosemirrorKeymap = require('prosemirror-keymap');
|
||||
var prosemirrorState = require('prosemirror-state');
|
||||
var prosemirrorModel = require('prosemirror-model');
|
||||
var prosemirrorView = require('prosemirror-view');
|
||||
var GapCursor = function (_prosemirrorState$Sel) {
|
||||
_inherits(GapCursor, _prosemirrorState$Sel);
|
||||
var _super = _createSuper(GapCursor);
|
||||
function GapCursor($pos) {
|
||||
_classCallCheck(this, GapCursor);
|
||||
return _super.call(this, $pos, $pos);
|
||||
}
|
||||
_createClass(GapCursor, [{
|
||||
key: "map",
|
||||
value: function map(doc, mapping) {
|
||||
var $pos = doc.resolve(mapping.map(this.head));
|
||||
return GapCursor.valid($pos) ? new GapCursor($pos) : prosemirrorState.Selection.near($pos);
|
||||
}
|
||||
}, {
|
||||
key: "content",
|
||||
value: function content() {
|
||||
return prosemirrorModel.Slice.empty;
|
||||
}
|
||||
}, {
|
||||
key: "eq",
|
||||
value: function eq(other) {
|
||||
return other instanceof GapCursor && other.head == this.head;
|
||||
}
|
||||
}, {
|
||||
key: "toJSON",
|
||||
value: function toJSON() {
|
||||
return {
|
||||
type: "gapcursor",
|
||||
pos: this.head
|
||||
};
|
||||
}
|
||||
}, {
|
||||
key: "getBookmark",
|
||||
value: function getBookmark() {
|
||||
return new GapBookmark(this.anchor);
|
||||
}
|
||||
}], [{
|
||||
key: "fromJSON",
|
||||
value: function fromJSON(doc, json) {
|
||||
if (typeof json.pos != "number") throw new RangeError("Invalid input for GapCursor.fromJSON");
|
||||
return new GapCursor(doc.resolve(json.pos));
|
||||
}
|
||||
}, {
|
||||
key: "valid",
|
||||
value: function valid($pos) {
|
||||
var parent = $pos.parent;
|
||||
if (parent.isTextblock || !closedBefore($pos) || !closedAfter($pos)) return false;
|
||||
var override = parent.type.spec.allowGapCursor;
|
||||
if (override != null) return override;
|
||||
var deflt = parent.contentMatchAt($pos.index()).defaultType;
|
||||
return deflt && deflt.isTextblock;
|
||||
}
|
||||
}, {
|
||||
key: "findGapCursorFrom",
|
||||
value: function findGapCursorFrom($pos, dir) {
|
||||
var mustMove = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
|
||||
search: for (;;) {
|
||||
if (!mustMove && GapCursor.valid($pos)) return $pos;
|
||||
var pos = $pos.pos,
|
||||
next = null;
|
||||
for (var d = $pos.depth;; d--) {
|
||||
var parent = $pos.node(d);
|
||||
if (dir > 0 ? $pos.indexAfter(d) < parent.childCount : $pos.index(d) > 0) {
|
||||
next = parent.child(dir > 0 ? $pos.indexAfter(d) : $pos.index(d) - 1);
|
||||
break;
|
||||
} else if (d == 0) {
|
||||
return null;
|
||||
}
|
||||
pos += dir;
|
||||
var $cur = $pos.doc.resolve(pos);
|
||||
if (GapCursor.valid($cur)) return $cur;
|
||||
}
|
||||
for (;;) {
|
||||
var inside = dir > 0 ? next.firstChild : next.lastChild;
|
||||
if (!inside) {
|
||||
if (next.isAtom && !next.isText && !prosemirrorState.NodeSelection.isSelectable(next)) {
|
||||
$pos = $pos.doc.resolve(pos + next.nodeSize * dir);
|
||||
mustMove = false;
|
||||
continue search;
|
||||
}
|
||||
break;
|
||||
}
|
||||
next = inside;
|
||||
pos += dir;
|
||||
var _$cur = $pos.doc.resolve(pos);
|
||||
if (GapCursor.valid(_$cur)) return _$cur;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}]);
|
||||
return GapCursor;
|
||||
}(prosemirrorState.Selection);
|
||||
GapCursor.prototype.visible = false;
|
||||
GapCursor.findFrom = GapCursor.findGapCursorFrom;
|
||||
prosemirrorState.Selection.jsonID("gapcursor", GapCursor);
|
||||
var GapBookmark = function () {
|
||||
function GapBookmark(pos) {
|
||||
_classCallCheck(this, GapBookmark);
|
||||
this.pos = pos;
|
||||
}
|
||||
_createClass(GapBookmark, [{
|
||||
key: "map",
|
||||
value: function map(mapping) {
|
||||
return new GapBookmark(mapping.map(this.pos));
|
||||
}
|
||||
}, {
|
||||
key: "resolve",
|
||||
value: function resolve(doc) {
|
||||
var $pos = doc.resolve(this.pos);
|
||||
return GapCursor.valid($pos) ? new GapCursor($pos) : prosemirrorState.Selection.near($pos);
|
||||
}
|
||||
}]);
|
||||
return GapBookmark;
|
||||
}();
|
||||
function needsGap(type) {
|
||||
return type.isAtom || type.spec.isolating || type.spec.createGapCursor;
|
||||
}
|
||||
function closedBefore($pos) {
|
||||
for (var d = $pos.depth; d >= 0; d--) {
|
||||
var index = $pos.index(d),
|
||||
parent = $pos.node(d);
|
||||
if (index == 0) {
|
||||
if (parent.type.spec.isolating) return true;
|
||||
continue;
|
||||
}
|
||||
for (var before = parent.child(index - 1);; before = before.lastChild) {
|
||||
if (before.childCount == 0 && !before.inlineContent || needsGap(before.type)) return true;
|
||||
if (before.inlineContent) return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
function closedAfter($pos) {
|
||||
for (var d = $pos.depth; d >= 0; d--) {
|
||||
var index = $pos.indexAfter(d),
|
||||
parent = $pos.node(d);
|
||||
if (index == parent.childCount) {
|
||||
if (parent.type.spec.isolating) return true;
|
||||
continue;
|
||||
}
|
||||
for (var after = parent.child(index);; after = after.firstChild) {
|
||||
if (after.childCount == 0 && !after.inlineContent || needsGap(after.type)) return true;
|
||||
if (after.inlineContent) return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
function gapCursor() {
|
||||
return new prosemirrorState.Plugin({
|
||||
props: {
|
||||
decorations: drawGapCursor,
|
||||
createSelectionBetween: function createSelectionBetween(_view, $anchor, $head) {
|
||||
return $anchor.pos == $head.pos && GapCursor.valid($head) ? new GapCursor($head) : null;
|
||||
},
|
||||
handleClick: handleClick,
|
||||
handleKeyDown: handleKeyDown,
|
||||
handleDOMEvents: {
|
||||
beforeinput: beforeinput
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
var handleKeyDown = prosemirrorKeymap.keydownHandler({
|
||||
"ArrowLeft": arrow("horiz", -1),
|
||||
"ArrowRight": arrow("horiz", 1),
|
||||
"ArrowUp": arrow("vert", -1),
|
||||
"ArrowDown": arrow("vert", 1)
|
||||
});
|
||||
function arrow(axis, dir) {
|
||||
var dirStr = axis == "vert" ? dir > 0 ? "down" : "up" : dir > 0 ? "right" : "left";
|
||||
return function (state, dispatch, view) {
|
||||
var sel = state.selection;
|
||||
var $start = dir > 0 ? sel.$to : sel.$from,
|
||||
mustMove = sel.empty;
|
||||
if (sel instanceof prosemirrorState.TextSelection) {
|
||||
if (!view.endOfTextblock(dirStr) || $start.depth == 0) return false;
|
||||
mustMove = false;
|
||||
$start = state.doc.resolve(dir > 0 ? $start.after() : $start.before());
|
||||
}
|
||||
var $found = GapCursor.findGapCursorFrom($start, dir, mustMove);
|
||||
if (!$found) return false;
|
||||
if (dispatch) dispatch(state.tr.setSelection(new GapCursor($found)));
|
||||
return true;
|
||||
};
|
||||
}
|
||||
function handleClick(view, pos, event) {
|
||||
if (!view || !view.editable) return false;
|
||||
var $pos = view.state.doc.resolve(pos);
|
||||
if (!GapCursor.valid($pos)) return false;
|
||||
var clickPos = view.posAtCoords({
|
||||
left: event.clientX,
|
||||
top: event.clientY
|
||||
});
|
||||
if (clickPos && clickPos.inside > -1 && prosemirrorState.NodeSelection.isSelectable(view.state.doc.nodeAt(clickPos.inside))) return false;
|
||||
view.dispatch(view.state.tr.setSelection(new GapCursor($pos)));
|
||||
return true;
|
||||
}
|
||||
function beforeinput(view, event) {
|
||||
if (event.inputType != "insertCompositionText" || !(view.state.selection instanceof GapCursor)) return false;
|
||||
var $from = view.state.selection.$from;
|
||||
var insert = $from.parent.contentMatchAt($from.index()).findWrapping(view.state.schema.nodes.text);
|
||||
if (!insert) return false;
|
||||
var frag = prosemirrorModel.Fragment.empty;
|
||||
for (var i = insert.length - 1; i >= 0; i--) frag = prosemirrorModel.Fragment.from(insert[i].createAndFill(null, frag));
|
||||
var tr = view.state.tr.replace($from.pos, $from.pos, new prosemirrorModel.Slice(frag, 0, 0));
|
||||
tr.setSelection(prosemirrorState.TextSelection.near(tr.doc.resolve($from.pos + 1)));
|
||||
view.dispatch(tr);
|
||||
return false;
|
||||
}
|
||||
function drawGapCursor(state) {
|
||||
if (!(state.selection instanceof GapCursor)) return null;
|
||||
var node = document.createElement("div");
|
||||
node.className = "ProseMirror-gapcursor";
|
||||
return prosemirrorView.DecorationSet.create(state.doc, [prosemirrorView.Decoration.widget(state.selection.head, node, {
|
||||
key: "gapcursor"
|
||||
})]);
|
||||
}
|
||||
exports.GapCursor = GapCursor;
|
||||
exports.gapCursor = gapCursor;
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
import { Selection, Plugin } from 'prosemirror-state';
|
||||
import { ResolvedPos, Node, Slice } from 'prosemirror-model';
|
||||
import { Mappable } from 'prosemirror-transform';
|
||||
|
||||
/**
|
||||
Gap cursor selections are represented using this class. Its
|
||||
`$anchor` and `$head` properties both point at the cursor position.
|
||||
*/
|
||||
declare class GapCursor extends Selection {
|
||||
/**
|
||||
Create a gap cursor.
|
||||
*/
|
||||
constructor($pos: ResolvedPos);
|
||||
map(doc: Node, mapping: Mappable): Selection;
|
||||
content(): Slice;
|
||||
eq(other: Selection): boolean;
|
||||
toJSON(): any;
|
||||
}
|
||||
|
||||
/**
|
||||
Create a gap cursor plugin. When enabled, this will capture clicks
|
||||
near and arrow-key-motion past places that don't have a normally
|
||||
selectable position nearby, and create a gap cursor selection for
|
||||
them. The cursor is drawn as an element with class
|
||||
`ProseMirror-gapcursor`. You can either include
|
||||
`style/gapcursor.css` from the package's directory or add your own
|
||||
styles to make it visible.
|
||||
*/
|
||||
declare function gapCursor(): Plugin;
|
||||
|
||||
export { GapCursor, gapCursor };
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
import { Selection, Plugin } from 'prosemirror-state';
|
||||
import { ResolvedPos, Node, Slice } from 'prosemirror-model';
|
||||
import { Mappable } from 'prosemirror-transform';
|
||||
|
||||
/**
|
||||
Gap cursor selections are represented using this class. Its
|
||||
`$anchor` and `$head` properties both point at the cursor position.
|
||||
*/
|
||||
declare class GapCursor extends Selection {
|
||||
/**
|
||||
Create a gap cursor.
|
||||
*/
|
||||
constructor($pos: ResolvedPos);
|
||||
map(doc: Node, mapping: Mappable): Selection;
|
||||
content(): Slice;
|
||||
eq(other: Selection): boolean;
|
||||
toJSON(): any;
|
||||
}
|
||||
|
||||
/**
|
||||
Create a gap cursor plugin. When enabled, this will capture clicks
|
||||
near and arrow-key-motion past places that don't have a normally
|
||||
selectable position nearby, and create a gap cursor selection for
|
||||
them. The cursor is drawn as an element with class
|
||||
`ProseMirror-gapcursor`. You can either include
|
||||
`style/gapcursor.css` from the package's directory or add your own
|
||||
styles to make it visible.
|
||||
*/
|
||||
declare function gapCursor(): Plugin;
|
||||
|
||||
export { GapCursor, gapCursor };
|
||||
+239
@@ -0,0 +1,239 @@
|
||||
import { keydownHandler } from 'prosemirror-keymap';
|
||||
import { Selection, NodeSelection, TextSelection, Plugin } from 'prosemirror-state';
|
||||
import { Slice, Fragment } from 'prosemirror-model';
|
||||
import { DecorationSet, Decoration } from 'prosemirror-view';
|
||||
|
||||
/**
|
||||
Gap cursor selections are represented using this class. Its
|
||||
`$anchor` and `$head` properties both point at the cursor position.
|
||||
*/
|
||||
class GapCursor extends Selection {
|
||||
/**
|
||||
Create a gap cursor.
|
||||
*/
|
||||
constructor($pos) {
|
||||
super($pos, $pos);
|
||||
}
|
||||
map(doc, mapping) {
|
||||
let $pos = doc.resolve(mapping.map(this.head));
|
||||
return GapCursor.valid($pos) ? new GapCursor($pos) : Selection.near($pos);
|
||||
}
|
||||
content() { return Slice.empty; }
|
||||
eq(other) {
|
||||
return other instanceof GapCursor && other.head == this.head;
|
||||
}
|
||||
toJSON() {
|
||||
return { type: "gapcursor", pos: this.head };
|
||||
}
|
||||
/**
|
||||
@internal
|
||||
*/
|
||||
static fromJSON(doc, json) {
|
||||
if (typeof json.pos != "number")
|
||||
throw new RangeError("Invalid input for GapCursor.fromJSON");
|
||||
return new GapCursor(doc.resolve(json.pos));
|
||||
}
|
||||
/**
|
||||
@internal
|
||||
*/
|
||||
getBookmark() { return new GapBookmark(this.anchor); }
|
||||
/**
|
||||
@internal
|
||||
*/
|
||||
static valid($pos) {
|
||||
let parent = $pos.parent;
|
||||
if (parent.isTextblock || !closedBefore($pos) || !closedAfter($pos))
|
||||
return false;
|
||||
let override = parent.type.spec.allowGapCursor;
|
||||
if (override != null)
|
||||
return override;
|
||||
let deflt = parent.contentMatchAt($pos.index()).defaultType;
|
||||
return deflt && deflt.isTextblock;
|
||||
}
|
||||
/**
|
||||
@internal
|
||||
*/
|
||||
static findGapCursorFrom($pos, dir, mustMove = false) {
|
||||
search: for (;;) {
|
||||
if (!mustMove && GapCursor.valid($pos))
|
||||
return $pos;
|
||||
let pos = $pos.pos, next = null;
|
||||
// Scan up from this position
|
||||
for (let d = $pos.depth;; d--) {
|
||||
let parent = $pos.node(d);
|
||||
if (dir > 0 ? $pos.indexAfter(d) < parent.childCount : $pos.index(d) > 0) {
|
||||
next = parent.child(dir > 0 ? $pos.indexAfter(d) : $pos.index(d) - 1);
|
||||
break;
|
||||
}
|
||||
else if (d == 0) {
|
||||
return null;
|
||||
}
|
||||
pos += dir;
|
||||
let $cur = $pos.doc.resolve(pos);
|
||||
if (GapCursor.valid($cur))
|
||||
return $cur;
|
||||
}
|
||||
// And then down into the next node
|
||||
for (;;) {
|
||||
let inside = dir > 0 ? next.firstChild : next.lastChild;
|
||||
if (!inside) {
|
||||
if (next.isAtom && !next.isText && !NodeSelection.isSelectable(next)) {
|
||||
$pos = $pos.doc.resolve(pos + next.nodeSize * dir);
|
||||
mustMove = false;
|
||||
continue search;
|
||||
}
|
||||
break;
|
||||
}
|
||||
next = inside;
|
||||
pos += dir;
|
||||
let $cur = $pos.doc.resolve(pos);
|
||||
if (GapCursor.valid($cur))
|
||||
return $cur;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
GapCursor.prototype.visible = false;
|
||||
GapCursor.findFrom = GapCursor.findGapCursorFrom;
|
||||
Selection.jsonID("gapcursor", GapCursor);
|
||||
class GapBookmark {
|
||||
constructor(pos) {
|
||||
this.pos = pos;
|
||||
}
|
||||
map(mapping) {
|
||||
return new GapBookmark(mapping.map(this.pos));
|
||||
}
|
||||
resolve(doc) {
|
||||
let $pos = doc.resolve(this.pos);
|
||||
return GapCursor.valid($pos) ? new GapCursor($pos) : Selection.near($pos);
|
||||
}
|
||||
}
|
||||
function needsGap(type) {
|
||||
return type.isAtom || type.spec.isolating || type.spec.createGapCursor;
|
||||
}
|
||||
function closedBefore($pos) {
|
||||
for (let d = $pos.depth; d >= 0; d--) {
|
||||
let index = $pos.index(d), parent = $pos.node(d);
|
||||
// At the start of this parent, look at next one
|
||||
if (index == 0) {
|
||||
if (parent.type.spec.isolating)
|
||||
return true;
|
||||
continue;
|
||||
}
|
||||
// See if the node before (or its first ancestor) is closed
|
||||
for (let before = parent.child(index - 1);; before = before.lastChild) {
|
||||
if ((before.childCount == 0 && !before.inlineContent) || needsGap(before.type))
|
||||
return true;
|
||||
if (before.inlineContent)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Hit start of document
|
||||
return true;
|
||||
}
|
||||
function closedAfter($pos) {
|
||||
for (let d = $pos.depth; d >= 0; d--) {
|
||||
let index = $pos.indexAfter(d), parent = $pos.node(d);
|
||||
if (index == parent.childCount) {
|
||||
if (parent.type.spec.isolating)
|
||||
return true;
|
||||
continue;
|
||||
}
|
||||
for (let after = parent.child(index);; after = after.firstChild) {
|
||||
if ((after.childCount == 0 && !after.inlineContent) || needsGap(after.type))
|
||||
return true;
|
||||
if (after.inlineContent)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
Create a gap cursor plugin. When enabled, this will capture clicks
|
||||
near and arrow-key-motion past places that don't have a normally
|
||||
selectable position nearby, and create a gap cursor selection for
|
||||
them. The cursor is drawn as an element with class
|
||||
`ProseMirror-gapcursor`. You can either include
|
||||
`style/gapcursor.css` from the package's directory or add your own
|
||||
styles to make it visible.
|
||||
*/
|
||||
function gapCursor() {
|
||||
return new Plugin({
|
||||
props: {
|
||||
decorations: drawGapCursor,
|
||||
createSelectionBetween(_view, $anchor, $head) {
|
||||
return $anchor.pos == $head.pos && GapCursor.valid($head) ? new GapCursor($head) : null;
|
||||
},
|
||||
handleClick,
|
||||
handleKeyDown,
|
||||
handleDOMEvents: { beforeinput: beforeinput }
|
||||
}
|
||||
});
|
||||
}
|
||||
const handleKeyDown = keydownHandler({
|
||||
"ArrowLeft": arrow("horiz", -1),
|
||||
"ArrowRight": arrow("horiz", 1),
|
||||
"ArrowUp": arrow("vert", -1),
|
||||
"ArrowDown": arrow("vert", 1)
|
||||
});
|
||||
function arrow(axis, dir) {
|
||||
const dirStr = axis == "vert" ? (dir > 0 ? "down" : "up") : (dir > 0 ? "right" : "left");
|
||||
return function (state, dispatch, view) {
|
||||
let sel = state.selection;
|
||||
let $start = dir > 0 ? sel.$to : sel.$from, mustMove = sel.empty;
|
||||
if (sel instanceof TextSelection) {
|
||||
if (!view.endOfTextblock(dirStr) || $start.depth == 0)
|
||||
return false;
|
||||
mustMove = false;
|
||||
$start = state.doc.resolve(dir > 0 ? $start.after() : $start.before());
|
||||
}
|
||||
let $found = GapCursor.findGapCursorFrom($start, dir, mustMove);
|
||||
if (!$found)
|
||||
return false;
|
||||
if (dispatch)
|
||||
dispatch(state.tr.setSelection(new GapCursor($found)));
|
||||
return true;
|
||||
};
|
||||
}
|
||||
function handleClick(view, pos, event) {
|
||||
if (!view || !view.editable)
|
||||
return false;
|
||||
let $pos = view.state.doc.resolve(pos);
|
||||
if (!GapCursor.valid($pos))
|
||||
return false;
|
||||
let clickPos = view.posAtCoords({ left: event.clientX, top: event.clientY });
|
||||
if (clickPos && clickPos.inside > -1 && NodeSelection.isSelectable(view.state.doc.nodeAt(clickPos.inside)))
|
||||
return false;
|
||||
view.dispatch(view.state.tr.setSelection(new GapCursor($pos)));
|
||||
return true;
|
||||
}
|
||||
// This is a hack that, when a composition starts while a gap cursor
|
||||
// is active, quickly creates an inline context for the composition to
|
||||
// happen in, to avoid it being aborted by the DOM selection being
|
||||
// moved into a valid position.
|
||||
function beforeinput(view, event) {
|
||||
if (event.inputType != "insertCompositionText" || !(view.state.selection instanceof GapCursor))
|
||||
return false;
|
||||
let { $from } = view.state.selection;
|
||||
let insert = $from.parent.contentMatchAt($from.index()).findWrapping(view.state.schema.nodes.text);
|
||||
if (!insert)
|
||||
return false;
|
||||
let frag = Fragment.empty;
|
||||
for (let i = insert.length - 1; i >= 0; i--)
|
||||
frag = Fragment.from(insert[i].createAndFill(null, frag));
|
||||
let tr = view.state.tr.replace($from.pos, $from.pos, new Slice(frag, 0, 0));
|
||||
tr.setSelection(TextSelection.near(tr.doc.resolve($from.pos + 1)));
|
||||
view.dispatch(tr);
|
||||
return false;
|
||||
}
|
||||
function drawGapCursor(state) {
|
||||
if (!(state.selection instanceof GapCursor))
|
||||
return null;
|
||||
let node = document.createElement("div");
|
||||
node.className = "ProseMirror-gapcursor";
|
||||
return DecorationSet.create(state.doc, [Decoration.widget(state.selection.head, node, { key: "gapcursor" })]);
|
||||
}
|
||||
|
||||
export { GapCursor, gapCursor };
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"name": "prosemirror-gapcursor",
|
||||
"version": "1.4.0",
|
||||
"description": "ProseMirror plugin for cursors at normally impossible-to-reach positions",
|
||||
"type": "module",
|
||||
"main": "dist/index.cjs",
|
||||
"module": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./dist/index.js",
|
||||
"require": "./dist/index.cjs"
|
||||
},
|
||||
"./style/gapcursor.css": "./style/gapcursor.css"
|
||||
},
|
||||
"sideEffects": ["./style/gapcursor.css"],
|
||||
"style": "style/gapcursor.css",
|
||||
"license": "MIT",
|
||||
"maintainers": [
|
||||
{
|
||||
"name": "Marijn Haverbeke",
|
||||
"email": "marijn@haverbeke.berlin",
|
||||
"web": "http://marijnhaverbeke.nl"
|
||||
}
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/prosemirror/prosemirror-gapcursor.git"
|
||||
},
|
||||
"dependencies": {
|
||||
"prosemirror-keymap": "^1.0.0",
|
||||
"prosemirror-model": "^1.0.0",
|
||||
"prosemirror-state": "^1.0.0",
|
||||
"prosemirror-view": "^1.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@prosemirror/buildhelper": "^0.1.5"
|
||||
},
|
||||
"scripts": {
|
||||
"prepare": "pm-buildhelper src/index.ts"
|
||||
}
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
This is a plugin that adds a type of selection for focusing places
|
||||
that don't allow regular selection (such as positions that have a leaf
|
||||
block node, table, or the end of the document both before and after
|
||||
them). By default, leaf blocks and isolating nodes will allow gap
|
||||
cursors to appear next to them. You can add a `creatGapCursor: true`
|
||||
property to a block node's spec to make them appear next to other
|
||||
nodes as well.
|
||||
|
||||
You'll probably want to load `style/gapcursor.css`, which contains
|
||||
basic styling for the simulated cursor (as a short, blinking
|
||||
horizontal stripe).
|
||||
|
||||
By default, gap cursor are only allowed in places where the default
|
||||
content node (in the schema content constraints) is a textblock node.
|
||||
You can customize this by adding an `allowGapCursor` property to your
|
||||
node specs—if it's true, gap cursor are allowed everywhere in that
|
||||
node, if it's `false` they are never allowed.
|
||||
|
||||
@gapCursor
|
||||
|
||||
@GapCursor
|
||||
+141
@@ -0,0 +1,141 @@
|
||||
import {Selection, NodeSelection} from "prosemirror-state"
|
||||
import {Slice, ResolvedPos, Node, NodeType} from "prosemirror-model"
|
||||
import {Mappable} from "prosemirror-transform"
|
||||
|
||||
/// Gap cursor selections are represented using this class. Its
|
||||
/// `$anchor` and `$head` properties both point at the cursor position.
|
||||
export class GapCursor extends Selection {
|
||||
/// Create a gap cursor.
|
||||
constructor($pos: ResolvedPos) {
|
||||
super($pos, $pos)
|
||||
}
|
||||
|
||||
map(doc: Node, mapping: Mappable): Selection {
|
||||
let $pos = doc.resolve(mapping.map(this.head))
|
||||
return GapCursor.valid($pos) ? new GapCursor($pos) : Selection.near($pos)
|
||||
}
|
||||
|
||||
content() { return Slice.empty }
|
||||
|
||||
eq(other: Selection): boolean {
|
||||
return other instanceof GapCursor && other.head == this.head
|
||||
}
|
||||
|
||||
toJSON(): any {
|
||||
return {type: "gapcursor", pos: this.head}
|
||||
}
|
||||
|
||||
/// @internal
|
||||
static fromJSON(doc: Node, json: any): GapCursor {
|
||||
if (typeof json.pos != "number") throw new RangeError("Invalid input for GapCursor.fromJSON")
|
||||
return new GapCursor(doc.resolve(json.pos))
|
||||
}
|
||||
|
||||
/// @internal
|
||||
getBookmark() { return new GapBookmark(this.anchor) }
|
||||
|
||||
/// @internal
|
||||
static valid($pos: ResolvedPos) {
|
||||
let parent = $pos.parent
|
||||
if (parent.isTextblock || !closedBefore($pos) || !closedAfter($pos)) return false
|
||||
let override = parent.type.spec.allowGapCursor
|
||||
if (override != null) return override
|
||||
let deflt = parent.contentMatchAt($pos.index()).defaultType
|
||||
return deflt && deflt.isTextblock
|
||||
}
|
||||
|
||||
/// @internal
|
||||
static findGapCursorFrom($pos: ResolvedPos, dir: number, mustMove = false) {
|
||||
search: for (;;) {
|
||||
if (!mustMove && GapCursor.valid($pos)) return $pos
|
||||
let pos = $pos.pos, next = null
|
||||
// Scan up from this position
|
||||
for (let d = $pos.depth;; d--) {
|
||||
let parent = $pos.node(d)
|
||||
if (dir > 0 ? $pos.indexAfter(d) < parent.childCount : $pos.index(d) > 0) {
|
||||
next = parent.child(dir > 0 ? $pos.indexAfter(d) : $pos.index(d) - 1)
|
||||
break
|
||||
} else if (d == 0) {
|
||||
return null
|
||||
}
|
||||
pos += dir
|
||||
let $cur = $pos.doc.resolve(pos)
|
||||
if (GapCursor.valid($cur)) return $cur
|
||||
}
|
||||
|
||||
// And then down into the next node
|
||||
for (;;) {
|
||||
let inside: Node | null = dir > 0 ? next.firstChild : next.lastChild
|
||||
if (!inside) {
|
||||
if (next.isAtom && !next.isText && !NodeSelection.isSelectable(next)) {
|
||||
$pos = $pos.doc.resolve(pos + next.nodeSize * dir)
|
||||
mustMove = false
|
||||
continue search
|
||||
}
|
||||
break
|
||||
}
|
||||
next = inside
|
||||
pos += dir
|
||||
let $cur = $pos.doc.resolve(pos)
|
||||
if (GapCursor.valid($cur)) return $cur
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GapCursor.prototype.visible = false
|
||||
|
||||
;(GapCursor as any).findFrom = GapCursor.findGapCursorFrom
|
||||
|
||||
Selection.jsonID("gapcursor", GapCursor)
|
||||
|
||||
class GapBookmark {
|
||||
constructor(readonly pos: number) {}
|
||||
|
||||
map(mapping: Mappable) {
|
||||
return new GapBookmark(mapping.map(this.pos))
|
||||
}
|
||||
resolve(doc: Node) {
|
||||
let $pos = doc.resolve(this.pos)
|
||||
return GapCursor.valid($pos) ? new GapCursor($pos) : Selection.near($pos)
|
||||
}
|
||||
}
|
||||
|
||||
function needsGap(type: NodeType) {
|
||||
return type.isAtom || type.spec.isolating || type.spec.createGapCursor
|
||||
}
|
||||
|
||||
function closedBefore($pos: ResolvedPos) {
|
||||
for (let d = $pos.depth; d >= 0; d--) {
|
||||
let index = $pos.index(d), parent = $pos.node(d)
|
||||
// At the start of this parent, look at next one
|
||||
if (index == 0) {
|
||||
if (parent.type.spec.isolating) return true
|
||||
continue
|
||||
}
|
||||
// See if the node before (or its first ancestor) is closed
|
||||
for (let before = parent.child(index - 1);; before = before.lastChild!) {
|
||||
if ((before.childCount == 0 && !before.inlineContent) || needsGap(before.type)) return true
|
||||
if (before.inlineContent) return false
|
||||
}
|
||||
}
|
||||
// Hit start of document
|
||||
return true
|
||||
}
|
||||
|
||||
function closedAfter($pos: ResolvedPos) {
|
||||
for (let d = $pos.depth; d >= 0; d--) {
|
||||
let index = $pos.indexAfter(d), parent = $pos.node(d)
|
||||
if (index == parent.childCount) {
|
||||
if (parent.type.spec.isolating) return true
|
||||
continue
|
||||
}
|
||||
for (let after = parent.child(index);; after = after.firstChild!) {
|
||||
if ((after.childCount == 0 && !after.inlineContent) || needsGap(after.type)) return true
|
||||
if (after.inlineContent) return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
import {keydownHandler} from "prosemirror-keymap"
|
||||
import {TextSelection, NodeSelection, Plugin, Command, EditorState} from "prosemirror-state"
|
||||
import {Fragment, Slice} from "prosemirror-model"
|
||||
import {Decoration, DecorationSet, EditorView} from "prosemirror-view"
|
||||
|
||||
import {GapCursor} from "./gapcursor"
|
||||
|
||||
/// Create a gap cursor plugin. When enabled, this will capture clicks
|
||||
/// near and arrow-key-motion past places that don't have a normally
|
||||
/// selectable position nearby, and create a gap cursor selection for
|
||||
/// them. The cursor is drawn as an element with class
|
||||
/// `ProseMirror-gapcursor`. You can either include
|
||||
/// `style/gapcursor.css` from the package's directory or add your own
|
||||
/// styles to make it visible.
|
||||
export function gapCursor(): Plugin {
|
||||
return new Plugin({
|
||||
props: {
|
||||
decorations: drawGapCursor,
|
||||
|
||||
createSelectionBetween(_view, $anchor, $head) {
|
||||
return $anchor.pos == $head.pos && GapCursor.valid($head) ? new GapCursor($head) : null
|
||||
},
|
||||
|
||||
handleClick,
|
||||
handleKeyDown,
|
||||
handleDOMEvents: {beforeinput: beforeinput as any}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export {GapCursor}
|
||||
|
||||
const handleKeyDown = keydownHandler({
|
||||
"ArrowLeft": arrow("horiz", -1),
|
||||
"ArrowRight": arrow("horiz", 1),
|
||||
"ArrowUp": arrow("vert", -1),
|
||||
"ArrowDown": arrow("vert", 1)
|
||||
})
|
||||
|
||||
function arrow(axis: "vert" | "horiz", dir: number): Command {
|
||||
const dirStr = axis == "vert" ? (dir > 0 ? "down" : "up") : (dir > 0 ? "right" : "left")
|
||||
return function(state, dispatch, view) {
|
||||
let sel = state.selection
|
||||
let $start = dir > 0 ? sel.$to : sel.$from, mustMove = sel.empty
|
||||
if (sel instanceof TextSelection) {
|
||||
if (!view!.endOfTextblock(dirStr) || $start.depth == 0) return false
|
||||
mustMove = false
|
||||
$start = state.doc.resolve(dir > 0 ? $start.after() : $start.before())
|
||||
}
|
||||
let $found = GapCursor.findGapCursorFrom($start, dir, mustMove)
|
||||
if (!$found) return false
|
||||
if (dispatch) dispatch(state.tr.setSelection(new GapCursor($found)))
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
function handleClick(view: EditorView, pos: number, event: MouseEvent) {
|
||||
if (!view || !view.editable) return false
|
||||
let $pos = view.state.doc.resolve(pos)
|
||||
if (!GapCursor.valid($pos)) return false
|
||||
let clickPos = view.posAtCoords({left: event.clientX, top: event.clientY})
|
||||
if (clickPos && clickPos.inside > -1 && NodeSelection.isSelectable(view.state.doc.nodeAt(clickPos.inside)!)) return false
|
||||
view.dispatch(view.state.tr.setSelection(new GapCursor($pos)))
|
||||
return true
|
||||
}
|
||||
|
||||
// This is a hack that, when a composition starts while a gap cursor
|
||||
// is active, quickly creates an inline context for the composition to
|
||||
// happen in, to avoid it being aborted by the DOM selection being
|
||||
// moved into a valid position.
|
||||
function beforeinput(view: EditorView, event: InputEvent) {
|
||||
if (event.inputType != "insertCompositionText" || !(view.state.selection instanceof GapCursor)) return false
|
||||
|
||||
let {$from} = view.state.selection
|
||||
let insert = $from.parent.contentMatchAt($from.index()).findWrapping(view.state.schema.nodes.text)
|
||||
if (!insert) return false
|
||||
|
||||
let frag = Fragment.empty
|
||||
for (let i = insert.length - 1; i >= 0; i--) frag = Fragment.from(insert[i].createAndFill(null, frag))
|
||||
let tr = view.state.tr.replace($from.pos, $from.pos, new Slice(frag, 0, 0))
|
||||
tr.setSelection(TextSelection.near(tr.doc.resolve($from.pos + 1)))
|
||||
view.dispatch(tr)
|
||||
return false
|
||||
}
|
||||
|
||||
function drawGapCursor(state: EditorState) {
|
||||
if (!(state.selection instanceof GapCursor)) return null
|
||||
let node = document.createElement("div")
|
||||
node.className = "ProseMirror-gapcursor"
|
||||
return DecorationSet.create(state.doc, [Decoration.widget(state.selection.head, node, {key: "gapcursor"})])
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
.ProseMirror-gapcursor {
|
||||
display: none;
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.ProseMirror-gapcursor:after {
|
||||
content: "";
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: -2px;
|
||||
width: 20px;
|
||||
border-top: 1px solid black;
|
||||
animation: ProseMirror-cursor-blink 1.1s steps(2, start) infinite;
|
||||
}
|
||||
|
||||
@keyframes ProseMirror-cursor-blink {
|
||||
to {
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.ProseMirror-focused .ProseMirror-gapcursor {
|
||||
display: block;
|
||||
}
|
||||
Reference in New Issue
Block a user