ckeditor5-elements-interaction 0.0.0-stage → 49.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +48 -2
- package/dist/augmentation.d.ts +19 -0
- package/dist/augmentation.d.ts.map +1 -0
- package/dist/augmentation.js +2 -0
- package/dist/augmentation.js.map +1 -0
- package/dist/doubleClickObserver.d.ts +20 -0
- package/dist/doubleClickObserver.d.ts.map +1 -0
- package/dist/doubleClickObserver.js +19 -0
- package/dist/doubleClickObserver.js.map +1 -0
- package/dist/elementClickable.d.ts +57 -0
- package/dist/elementClickable.d.ts.map +1 -0
- package/dist/elementClickable.js +60 -0
- package/dist/elementClickable.js.map +1 -0
- package/dist/elementSelectable.d.ts +77 -0
- package/dist/elementSelectable.d.ts.map +1 -0
- package/dist/elementSelectable.js +198 -0
- package/dist/elementSelectable.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/navigation.d.ts +22 -0
- package/dist/navigation.d.ts.map +1 -0
- package/dist/navigation.js +49 -0
- package/dist/navigation.js.map +1 -0
- package/package.json +49 -3
- package/src/augmentation.ts +26 -0
- package/src/doubleClickObserver.ts +28 -0
- package/src/elementClickable.ts +117 -0
- package/src/elementSelectable.ts +274 -0
- package/src/index.ts +19 -0
- package/src/navigation.ts +79 -0
- package/theme/selectable.css +8 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ivan Kildyushov
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,49 @@
|
|
|
1
|
-
#
|
|
1
|
+
# ckeditor5-elements-interaction
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Element interaction for CKEditor 5 Elements, for the elements that match a model name or a filter function, without
|
|
4
|
+
CKEditor's widgets:
|
|
5
|
+
|
|
6
|
+
- `ElementSelectable` selects an element as a whole, by a click or by the arrow keys. It does what widgets do for
|
|
7
|
+
selecting, without changing the view of the element and without the `Widget` plugin.
|
|
8
|
+
- `ElementClickable` calls a function, with the model element, when an element is clicked or double-clicked.
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
This package is part of `ckeditor5-elements`. Install the whole package to use it:
|
|
13
|
+
|
|
14
|
+
```sh
|
|
15
|
+
npm install ckeditor5 ckeditor5-elements
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Import the stylesheet of the package (`ckeditor5-elements/index.css` has the styles of all the features).
|
|
19
|
+
|
|
20
|
+
## Usage
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
import { ElementClickable, ElementSelectable } from 'ckeditor5-elements';
|
|
24
|
+
|
|
25
|
+
// In the editor config:
|
|
26
|
+
plugins: [..., ElementSelectable, ElementClickable],
|
|
27
|
+
elementSelectable: ['callout', 'divider', element => element.hasAttribute('card')],
|
|
28
|
+
elementClickable: [
|
|
29
|
+
// By name. The default event is `click`.
|
|
30
|
+
{ element: 'imageBlock', callback: (element, editor) => console.log(element.getAttribute('src')) },
|
|
31
|
+
// By a filter, on double click.
|
|
32
|
+
{ element: element => element.name.startsWith('heading'), event: 'dblclick', callback: (element, editor) => {} }
|
|
33
|
+
]
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Selecting:
|
|
37
|
+
|
|
38
|
+
- A click on an element selects it; a container is selected by a click on the element itself, and a click in its text puts
|
|
39
|
+
the cursor there.
|
|
40
|
+
- At the end of the text, the right arrow key selects the next selectable element (the left key, at the start, the
|
|
41
|
+
previous one). With an element selected, the arrow keys move the cursor out of it.
|
|
42
|
+
- Esc selects the closest selectable element around the cursor.
|
|
43
|
+
|
|
44
|
+
See the [Selectable elements](https://ivankild.github.io/ckeditor5-elements/guide/selectable) and
|
|
45
|
+
[Clickable elements](https://ivankild.github.io/ckeditor5-elements/guide/clickable) guides of the documentation.
|
|
46
|
+
|
|
47
|
+
## License
|
|
48
|
+
|
|
49
|
+
MIT
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ElementClickable, ElementClickableDefinition, ElementSelectable, ElementSelectableMatcher } from './index.js';
|
|
2
|
+
declare module 'ckeditor5' {
|
|
3
|
+
interface PluginsMap {
|
|
4
|
+
[ElementSelectable.pluginName]: ElementSelectable;
|
|
5
|
+
[ElementClickable.pluginName]: ElementClickable;
|
|
6
|
+
}
|
|
7
|
+
interface EditorConfig {
|
|
8
|
+
/**
|
|
9
|
+
* The elements that can be selected as a whole: names of model elements, or filter functions. See
|
|
10
|
+
* {@link ElementSelectable}.
|
|
11
|
+
*/
|
|
12
|
+
elementSelectable?: ElementSelectableMatcher[];
|
|
13
|
+
/**
|
|
14
|
+
* The elements that react to clicks and double clicks. See {@link ElementClickable}.
|
|
15
|
+
*/
|
|
16
|
+
elementClickable?: ElementClickableDefinition[];
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=augmentation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"augmentation.d.ts","sourceRoot":"","sources":["../src/augmentation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,gBAAgB,EAChB,0BAA0B,EAC1B,iBAAiB,EACjB,wBAAwB,EACxB,MAAM,YAAY,CAAC;AAEpB,OAAO,QAAQ,WAAW,CAAC;IAC1B,UAAU,UAAU;QACnB,CAAC,iBAAiB,CAAC,UAAU,CAAC,EAAE,iBAAiB,CAAC;QAClD,CAAC,gBAAgB,CAAC,UAAU,CAAC,EAAE,gBAAgB,CAAC;KAChD;IAED,UAAU,YAAY;QACrB;;;WAGG;QACH,iBAAiB,CAAC,EAAE,wBAAwB,EAAE,CAAC;QAE/C;;WAEG;QACH,gBAAgB,CAAC,EAAE,0BAA0B,EAAE,CAAC;KAChD;CACD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"augmentation.js","sourceRoot":"","sources":["../src/augmentation.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { DomEventObserver, type ViewDocumentDomEventData } from 'ckeditor5';
|
|
2
|
+
/**
|
|
3
|
+
* Fires `dblclick` on the view document when the user double-clicks in the editing view. (CKEditor has an observer for
|
|
4
|
+
* `click`, but not for `dblclick`.)
|
|
5
|
+
*/
|
|
6
|
+
export default class DoubleClickObserver extends DomEventObserver<'dblclick'> {
|
|
7
|
+
get domEventType(): 'dblclick';
|
|
8
|
+
/**
|
|
9
|
+
* Fires `dblclick` on the view document for the DOM event.
|
|
10
|
+
*/
|
|
11
|
+
onDomEvent(domEvent: MouseEvent): void;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Fired on the view document by the {@link DoubleClickObserver}.
|
|
15
|
+
*/
|
|
16
|
+
export interface ViewDocumentDoubleClickEvent {
|
|
17
|
+
name: 'dblclick';
|
|
18
|
+
args: [data: ViewDocumentDomEventData<MouseEvent>];
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=doubleClickObserver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doubleClickObserver.d.ts","sourceRoot":"","sources":["../src/doubleClickObserver.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,KAAK,wBAAwB,EAAE,MAAM,WAAW,CAAC;AAE5E;;;GAGG;AACH,MAAM,CAAC,OAAO,OAAO,mBAAoB,SAAQ,gBAAgB,CAAC,UAAU,CAAC;IAG5E,IAAW,YAAY,IAAI,UAAU,CAEpC;IAED;;OAEG;IACI,UAAU,CAAC,QAAQ,EAAE,UAAU,GAAG,IAAI;CAG7C;AAED;;GAEG;AACH,MAAM,WAAW,4BAA4B;IAC5C,IAAI,EAAE,UAAU,CAAC;IACjB,IAAI,EAAE,CAAC,IAAI,EAAE,wBAAwB,CAAC,UAAU,CAAC,CAAC,CAAC;CACnD"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { DomEventObserver } from 'ckeditor5';
|
|
2
|
+
/**
|
|
3
|
+
* Fires `dblclick` on the view document when the user double-clicks in the editing view. (CKEditor has an observer for
|
|
4
|
+
* `click`, but not for `dblclick`.)
|
|
5
|
+
*/
|
|
6
|
+
export default class DoubleClickObserver extends DomEventObserver {
|
|
7
|
+
// The base class declares it as an accessor, so it cannot be a field here.
|
|
8
|
+
// eslint-disable-next-line @typescript-eslint/class-literal-property-style
|
|
9
|
+
get domEventType() {
|
|
10
|
+
return 'dblclick';
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Fires `dblclick` on the view document for the DOM event.
|
|
14
|
+
*/
|
|
15
|
+
onDomEvent(domEvent) {
|
|
16
|
+
this.fire(domEvent.type, domEvent);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=doubleClickObserver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doubleClickObserver.js","sourceRoot":"","sources":["../src/doubleClickObserver.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAiC,MAAM,WAAW,CAAC;AAE5E;;;GAGG;AACH,MAAM,CAAC,OAAO,OAAO,mBAAoB,SAAQ,gBAA4B;IAC5E,2EAA2E;IAC3E,2EAA2E;IAC3E,IAAW,YAAY;QACtB,OAAO,UAAU,CAAC;IACnB,CAAC;IAED;;OAEG;IACI,UAAU,CAAC,QAAoB;QACrC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IACpC,CAAC;CACD"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { Plugin, type Editor, type ModelElement, type ViewElement } from 'ckeditor5';
|
|
2
|
+
import { type ElementMatcher } from 'ckeditor5-elements-core';
|
|
3
|
+
/**
|
|
4
|
+
* The mouse events an element can be made clickable with.
|
|
5
|
+
*/
|
|
6
|
+
export type ElementClickableEvent = 'click' | 'dblclick';
|
|
7
|
+
/**
|
|
8
|
+
* Makes the elements that match `element` react to clicks.
|
|
9
|
+
*/
|
|
10
|
+
export interface ElementClickableDefinition {
|
|
11
|
+
/**
|
|
12
|
+
* The elements the definition is for: the name of a model element (e.g. `'imageBlock'`), or a function that returns
|
|
13
|
+
* whether it is for the given element (e.g. `element => element.hasAttribute('linkHref')`).
|
|
14
|
+
*/
|
|
15
|
+
element: ElementMatcher;
|
|
16
|
+
/**
|
|
17
|
+
* The mouse event: `'click'` (the default) or `'dblclick'`.
|
|
18
|
+
*/
|
|
19
|
+
event?: ElementClickableEvent;
|
|
20
|
+
/**
|
|
21
|
+
* Called with the element that was clicked, the editor, and the DOM event.
|
|
22
|
+
*/
|
|
23
|
+
callback: (element: ModelElement, editor: Editor, domEvent: MouseEvent) => void;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Calls a function when an element is clicked or double-clicked, with the model element. Elements are matched by name or
|
|
27
|
+
* by a filter function, and the definitions are the `elementClickable` editor config option (see
|
|
28
|
+
* {@link ElementClickableDefinition}):
|
|
29
|
+
*
|
|
30
|
+
* ```ts
|
|
31
|
+
* elementClickable: [
|
|
32
|
+
* { element: 'imageBlock', callback: (element, editor) => console.log(element.getAttribute('src')) },
|
|
33
|
+
* { element: element => element.name.startsWith('heading'), event: 'dblclick', callback: edit }
|
|
34
|
+
* ];
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* The click is matched against the element that was clicked and its ancestors, the innermost first: the first element
|
|
38
|
+
* that matches a definition for the event calls its callback, and the event goes no further. For two definitions that
|
|
39
|
+
* match the same element, the first one in the config wins.
|
|
40
|
+
*
|
|
41
|
+
* It works in read-only mode too. The click still does what it does in the editor, e.g. selects an image: the plugin
|
|
42
|
+
* only reports it, before the other plugins handle it (some of them stop the event), and does not stop it.
|
|
43
|
+
*/
|
|
44
|
+
export default class ElementClickable extends Plugin {
|
|
45
|
+
static get pluginName(): 'ElementClickable';
|
|
46
|
+
init(): void;
|
|
47
|
+
/**
|
|
48
|
+
* Returns the definition and the element that a click on the view element is for: the innermost element, among the
|
|
49
|
+
* view element and its ancestors, that matches a definition for the event. `null` when there is none.
|
|
50
|
+
*/
|
|
51
|
+
find(viewElement: ViewElement, event?: ElementClickableEvent): {
|
|
52
|
+
element: ModelElement;
|
|
53
|
+
definition: ElementClickableDefinition;
|
|
54
|
+
} | null;
|
|
55
|
+
private _handle;
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=elementClickable.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"elementClickable.d.ts","sourceRoot":"","sources":["../src/elementClickable.ts"],"names":[],"mappings":"AAAA,OAAO,EAEN,MAAM,EACN,KAAK,MAAM,EACX,KAAK,YAAY,EAEjB,KAAK,WAAW,EAChB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAqC,KAAK,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAGjG;;GAEG;AACH,MAAM,MAAM,qBAAqB,GAAG,OAAO,GAAG,UAAU,CAAC;AAEzD;;GAEG;AACH,MAAM,WAAW,0BAA0B;IAC1C;;;OAGG;IACH,OAAO,EAAE,cAAc,CAAC;IAExB;;OAEG;IACH,KAAK,CAAC,EAAE,qBAAqB,CAAC;IAE9B;;OAEG;IACH,QAAQ,EAAE,CAAC,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,KAAK,IAAI,CAAC;CAChF;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,CAAC,OAAO,OAAO,gBAAiB,SAAQ,MAAM;IACnD,WAAkB,UAAU,IAAI,kBAAkB,CAEjD;IAEM,IAAI,IAAI,IAAI;IA0BnB;;;OAGG;IACI,IAAI,CACV,WAAW,EAAE,WAAW,EACxB,KAAK,GAAE,qBAA+B,GACpC;QAAE,OAAO,EAAE,YAAY,CAAC;QAAC,UAAU,EAAE,0BAA0B,CAAA;KAAE,GAAG,IAAI;IAiB3E,OAAO,CAAC,OAAO;CAKf"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { ClickObserver, Plugin } from 'ckeditor5';
|
|
2
|
+
import { getElementsAround, matchesElement } from 'ckeditor5-elements-core';
|
|
3
|
+
import DoubleClickObserver, {} from './doubleClickObserver.js';
|
|
4
|
+
/**
|
|
5
|
+
* Calls a function when an element is clicked or double-clicked, with the model element. Elements are matched by name or
|
|
6
|
+
* by a filter function, and the definitions are the `elementClickable` editor config option (see
|
|
7
|
+
* {@link ElementClickableDefinition}):
|
|
8
|
+
*
|
|
9
|
+
* ```ts
|
|
10
|
+
* elementClickable: [
|
|
11
|
+
* { element: 'imageBlock', callback: (element, editor) => console.log(element.getAttribute('src')) },
|
|
12
|
+
* { element: element => element.name.startsWith('heading'), event: 'dblclick', callback: edit }
|
|
13
|
+
* ];
|
|
14
|
+
* ```
|
|
15
|
+
*
|
|
16
|
+
* The click is matched against the element that was clicked and its ancestors, the innermost first: the first element
|
|
17
|
+
* that matches a definition for the event calls its callback, and the event goes no further. For two definitions that
|
|
18
|
+
* match the same element, the first one in the config wins.
|
|
19
|
+
*
|
|
20
|
+
* It works in read-only mode too. The click still does what it does in the editor, e.g. selects an image: the plugin
|
|
21
|
+
* only reports it, before the other plugins handle it (some of them stop the event), and does not stop it.
|
|
22
|
+
*/
|
|
23
|
+
export default class ElementClickable extends Plugin {
|
|
24
|
+
static get pluginName() {
|
|
25
|
+
return 'ElementClickable';
|
|
26
|
+
}
|
|
27
|
+
init() {
|
|
28
|
+
const view = this.editor.editing.view;
|
|
29
|
+
view.addObserver(ClickObserver);
|
|
30
|
+
view.addObserver(DoubleClickObserver);
|
|
31
|
+
// With the highest priority, before other plugins: some stop the click, e.g. CKEditor's link handling stops the
|
|
32
|
+
// click on a link, with a high priority. The plugin reports the click and does not stop it.
|
|
33
|
+
this.listenTo(view.document, 'click', (evt, data) => {
|
|
34
|
+
this._handle('click', data.target, data.domEvent);
|
|
35
|
+
}, { priority: 'highest' });
|
|
36
|
+
this.listenTo(view.document, 'dblclick', (evt, data) => {
|
|
37
|
+
this._handle('dblclick', data.target, data.domEvent);
|
|
38
|
+
}, { priority: 'highest' });
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Returns the definition and the element that a click on the view element is for: the innermost element, among the
|
|
42
|
+
* view element and its ancestors, that matches a definition for the event. `null` when there is none.
|
|
43
|
+
*/
|
|
44
|
+
find(viewElement, event = 'click') {
|
|
45
|
+
const mapper = this.editor.editing.mapper;
|
|
46
|
+
const definitions = (this.editor.config.get('elementClickable') ?? []).filter(definition => (definition.event ?? 'click') === event);
|
|
47
|
+
for (const { element } of getElementsAround(mapper, viewElement)) {
|
|
48
|
+
const definition = definitions.find(({ element: matcher }) => matchesElement(matcher, element));
|
|
49
|
+
if (definition) {
|
|
50
|
+
return { element, definition };
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
_handle(event, target, domEvent) {
|
|
56
|
+
const found = this.isEnabled ? this.find(target, event) : null;
|
|
57
|
+
found?.definition.callback(found.element, this.editor, domEvent);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=elementClickable.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"elementClickable.js","sourceRoot":"","sources":["../src/elementClickable.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,aAAa,EACb,MAAM,EAKN,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAuB,MAAM,yBAAyB,CAAC;AACjG,OAAO,mBAAmB,EAAE,EAAqC,MAAM,0BAA0B,CAAC;AA4BlG;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,CAAC,OAAO,OAAO,gBAAiB,SAAQ,MAAM;IAC5C,MAAM,KAAK,UAAU;QAC3B,OAAO,kBAA2B,CAAC;IACpC,CAAC;IAEM,IAAI;QACV,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC;QAEtC,IAAI,CAAC,WAAW,CAAC,aAAa,CAAC,CAAC;QAChC,IAAI,CAAC,WAAW,CAAC,mBAAmB,CAAC,CAAC;QAEtC,gHAAgH;QAChH,4FAA4F;QAC5F,IAAI,CAAC,QAAQ,CACZ,IAAI,CAAC,QAAQ,EACb,OAAO,EACP,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE;YACb,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;QACnD,CAAC,EACD,EAAE,QAAQ,EAAE,SAAS,EAAE,CACvB,CAAC;QACF,IAAI,CAAC,QAAQ,CACZ,IAAI,CAAC,QAAQ,EACb,UAAU,EACV,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE;YACb,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;QACtD,CAAC,EACD,EAAE,QAAQ,EAAE,SAAS,EAAE,CACvB,CAAC;IACH,CAAC;IAED;;;OAGG;IACI,IAAI,CACV,WAAwB,EACxB,QAA+B,OAAO;QAEtC,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC;QAC1C,MAAM,WAAW,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,kBAAkB,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAC5E,UAAU,CAAC,EAAE,CAAC,CAAC,UAAU,CAAC,KAAK,IAAI,OAAO,CAAC,KAAK,KAAK,CACrD,CAAC;QAEF,KAAK,MAAM,EAAE,OAAO,EAAE,IAAI,iBAAiB,CAAC,MAAM,EAAE,WAAW,CAAC,EAAE,CAAC;YAClE,MAAM,UAAU,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,cAAc,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;YAEhG,IAAI,UAAU,EAAE,CAAC;gBAChB,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC;YAChC,CAAC;QACF,CAAC;QAED,OAAO,IAAI,CAAC;IACb,CAAC;IAEO,OAAO,CAAC,KAA4B,EAAE,MAAmB,EAAE,QAAoB;QACtF,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAE/D,KAAK,EAAE,UAAU,CAAC,QAAQ,CAAC,KAAK,CAAC,OAAO,EAAE,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAClE,CAAC;CACD"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { Plugin, type ModelElement } from 'ckeditor5';
|
|
2
|
+
import { type ElementMatcher } from 'ckeditor5-elements-core';
|
|
3
|
+
/**
|
|
4
|
+
* Matches the elements that can be selected: the name of a model element (e.g. `'callout'`), or a function that returns
|
|
5
|
+
* whether it is for the given element (e.g. `element => element.hasAttribute('card')`).
|
|
6
|
+
*/
|
|
7
|
+
export type ElementSelectableMatcher = ElementMatcher;
|
|
8
|
+
/**
|
|
9
|
+
* Makes elements selectable as a whole, which is what CKEditor's widgets do, without them: it does not change the view
|
|
10
|
+
* of the element, and does not need the `Widget` plugin. The elements are matched by name or by a filter function, and
|
|
11
|
+
* they are the `elementSelectable` editor config option:
|
|
12
|
+
*
|
|
13
|
+
* ```ts
|
|
14
|
+
* elementSelectable: ['callout', 'divider', element => element.hasAttribute('card')];
|
|
15
|
+
* ```
|
|
16
|
+
*
|
|
17
|
+
* An element is selected (the model selection is *on* it, and the view has a fake selection and the
|
|
18
|
+
* `ck-elements-selected` class):
|
|
19
|
+
*
|
|
20
|
+
* * **By a click** on the element. An element that has content, e.g. a container, is selected by a click on the element
|
|
21
|
+
* itself, not on its content: a click in its text puts the cursor there, as it does without the plugin. An element
|
|
22
|
+
* with no content (an object) is selected by a click anywhere in it.
|
|
23
|
+
* * **By the left and right arrow keys**: when the next step of the cursor enters or passes a selectable element (at the
|
|
24
|
+
* end of a block, or before an inline element), the key selects it instead. With an element selected, the arrow keys
|
|
25
|
+
* move the cursor to the content after (right, down) or before (left, up) it, or select the next selectable element,
|
|
26
|
+
* if it is just there.
|
|
27
|
+
* * **By the Esc key**: with the cursor in an element, it selects the closest selectable one around the cursor, unless
|
|
28
|
+
* something else handles Esc first, e.g. closes a balloon.
|
|
29
|
+
*
|
|
30
|
+
* A selected element is deleted, replaced by typing and copied, as any selected content.
|
|
31
|
+
*
|
|
32
|
+
* Names are made selectable in the model schema (`isSelectable`), so the selection on them is kept: a filter cannot,
|
|
33
|
+
* so the elements it matches must be selectable already (e.g. objects). Features can add elements with
|
|
34
|
+
* {@link ElementSelectable#add}.
|
|
35
|
+
*/
|
|
36
|
+
export default class ElementSelectable extends Plugin {
|
|
37
|
+
private readonly _matchers;
|
|
38
|
+
private _selected;
|
|
39
|
+
static get pluginName(): 'ElementSelectable';
|
|
40
|
+
init(): void;
|
|
41
|
+
afterInit(): void;
|
|
42
|
+
/**
|
|
43
|
+
* Makes more elements selectable, as the `elementSelectable` option does: a name of a model element, or a filter.
|
|
44
|
+
* Used by features that add elements, e.g. the element schema.
|
|
45
|
+
*/
|
|
46
|
+
add(...matchers: ElementSelectableMatcher[]): void;
|
|
47
|
+
/**
|
|
48
|
+
* Returns whether the element can be selected by this plugin: it matches a name or a filter.
|
|
49
|
+
*/
|
|
50
|
+
isSelectable(element: ModelElement): boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Selects the element as a whole.
|
|
53
|
+
*/
|
|
54
|
+
private _select;
|
|
55
|
+
/**
|
|
56
|
+
* Makes the model elements named in the matchers selectable in the schema, for the ones that are registered.
|
|
57
|
+
*/
|
|
58
|
+
private _makeSelectable;
|
|
59
|
+
/**
|
|
60
|
+
* Returns the element a mouse down on the view element selects: the closest selectable element around it (or it), if
|
|
61
|
+
* the click is on the element itself, or the element has no content.
|
|
62
|
+
*/
|
|
63
|
+
private _findToSelect;
|
|
64
|
+
/**
|
|
65
|
+
* Handles an arrow key. Returns whether the key moved the selection, and the browser must not.
|
|
66
|
+
*/
|
|
67
|
+
private _handleArrowKey;
|
|
68
|
+
/**
|
|
69
|
+
* Returns the closest selectable element around the selection, not the selected one.
|
|
70
|
+
*/
|
|
71
|
+
private _findAround;
|
|
72
|
+
/**
|
|
73
|
+
* Marks the selected view element with the class, and unmarks the one that was selected.
|
|
74
|
+
*/
|
|
75
|
+
private _mark;
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=elementSelectable.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"elementSelectable.d.ts","sourceRoot":"","sources":["../src/elementSelectable.ts"],"names":[],"mappings":"AAAA,OAAO,EAEN,MAAM,EAKN,KAAK,YAAY,EAKjB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAyD,KAAK,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAGrH;;;GAGG;AACH,MAAM,MAAM,wBAAwB,GAAG,cAAc,CAAC;AAOtD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,CAAC,OAAO,OAAO,iBAAkB,SAAQ,MAAM;IACpD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAkC;IAC5D,OAAO,CAAC,SAAS,CAA4B;IAE7C,WAAkB,UAAU,IAAI,mBAAmB,CAElD;IAEM,IAAI,IAAI,IAAI;IAoFZ,SAAS,IAAI,IAAI;IAaxB;;;OAGG;IACI,GAAG,CAAC,GAAG,QAAQ,EAAE,wBAAwB,EAAE,GAAG,IAAI;IAKzD;;OAEG;IACI,YAAY,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO;IAInD;;OAEG;IACH,OAAO,CAAC,OAAO;IAMf;;OAEG;IACH,OAAO,CAAC,eAAe;IAUvB;;;OAGG;IACH,OAAO,CAAC,aAAa;IAUrB;;OAEG;IACH,OAAO,CAAC,eAAe;IAiCvB;;OAEG;IACH,OAAO,CAAC,WAAW;IAInB;;OAEG;IACH,OAAO,CAAC,KAAK;CAeb"}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { MouseObserver, Plugin, keyCodes, isArrowKeyCode, isForwardArrowKeyCode } from 'ckeditor5';
|
|
2
|
+
import { getElementsAround, keyboardPriorities, matchesElement } from 'ckeditor5-elements-core';
|
|
3
|
+
import { findLeaveTarget, findSelectableAfterStep, findSelectableAround } from './navigation.js';
|
|
4
|
+
/**
|
|
5
|
+
* The class of the selected element in the editing view.
|
|
6
|
+
*/
|
|
7
|
+
const SELECTED_CLASS = 'ck-elements-selected';
|
|
8
|
+
/**
|
|
9
|
+
* Makes elements selectable as a whole, which is what CKEditor's widgets do, without them: it does not change the view
|
|
10
|
+
* of the element, and does not need the `Widget` plugin. The elements are matched by name or by a filter function, and
|
|
11
|
+
* they are the `elementSelectable` editor config option:
|
|
12
|
+
*
|
|
13
|
+
* ```ts
|
|
14
|
+
* elementSelectable: ['callout', 'divider', element => element.hasAttribute('card')];
|
|
15
|
+
* ```
|
|
16
|
+
*
|
|
17
|
+
* An element is selected (the model selection is *on* it, and the view has a fake selection and the
|
|
18
|
+
* `ck-elements-selected` class):
|
|
19
|
+
*
|
|
20
|
+
* * **By a click** on the element. An element that has content, e.g. a container, is selected by a click on the element
|
|
21
|
+
* itself, not on its content: a click in its text puts the cursor there, as it does without the plugin. An element
|
|
22
|
+
* with no content (an object) is selected by a click anywhere in it.
|
|
23
|
+
* * **By the left and right arrow keys**: when the next step of the cursor enters or passes a selectable element (at the
|
|
24
|
+
* end of a block, or before an inline element), the key selects it instead. With an element selected, the arrow keys
|
|
25
|
+
* move the cursor to the content after (right, down) or before (left, up) it, or select the next selectable element,
|
|
26
|
+
* if it is just there.
|
|
27
|
+
* * **By the Esc key**: with the cursor in an element, it selects the closest selectable one around the cursor, unless
|
|
28
|
+
* something else handles Esc first, e.g. closes a balloon.
|
|
29
|
+
*
|
|
30
|
+
* A selected element is deleted, replaced by typing and copied, as any selected content.
|
|
31
|
+
*
|
|
32
|
+
* Names are made selectable in the model schema (`isSelectable`), so the selection on them is kept: a filter cannot,
|
|
33
|
+
* so the elements it matches must be selectable already (e.g. objects). Features can add elements with
|
|
34
|
+
* {@link ElementSelectable#add}.
|
|
35
|
+
*/
|
|
36
|
+
export default class ElementSelectable extends Plugin {
|
|
37
|
+
_matchers = [];
|
|
38
|
+
_selected = null;
|
|
39
|
+
static get pluginName() {
|
|
40
|
+
return 'ElementSelectable';
|
|
41
|
+
}
|
|
42
|
+
init() {
|
|
43
|
+
const editor = this.editor;
|
|
44
|
+
const view = editor.editing.view;
|
|
45
|
+
const viewDocument = view.document;
|
|
46
|
+
this._matchers.push(...(editor.config.get('elementSelectable') ?? []));
|
|
47
|
+
// The selection of an element is a fake one, as for widgets: the browser does not draw the selection on its own.
|
|
48
|
+
editor.editing.downcastDispatcher.on('selection', (evt, data, conversionApi) => {
|
|
49
|
+
const element = data.selection.getSelectedElement();
|
|
50
|
+
const viewElement = element && this.isSelectable(element) ? editor.editing.mapper.toViewElement(element) : null;
|
|
51
|
+
if (element && viewElement && conversionApi.consumable.consume(data.selection, 'selection')) {
|
|
52
|
+
conversionApi.writer.setSelection(conversionApi.writer.createRangeOn(viewElement), {
|
|
53
|
+
fake: true,
|
|
54
|
+
label: `<${element.name}>`
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
editor.editing.downcastDispatcher.on('selection', (evt, data, conversionApi) => {
|
|
59
|
+
this._mark(conversionApi.writer, viewDocument.selection.getSelectedElement());
|
|
60
|
+
}, { priority: 'low' });
|
|
61
|
+
view.addObserver(MouseObserver);
|
|
62
|
+
this.listenTo(viewDocument, 'mousedown', (evt, data) => {
|
|
63
|
+
const element = this.isEnabled ? this._findToSelect(data.target) : null;
|
|
64
|
+
if (element) {
|
|
65
|
+
// The browser would put the cursor where the click is, and move the focus.
|
|
66
|
+
data.preventDefault();
|
|
67
|
+
view.focus();
|
|
68
|
+
this._select(element);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
// The arrow keys are handled on `keydown`, before CKEditor's widgets, and not as the `arrowKey` event: that one
|
|
72
|
+
// bubbles from the selection, and CKEditor's widgets (loaded by other features, e.g. the clipboard) handle it for
|
|
73
|
+
// every object, in a context that is reached before this plugin could.
|
|
74
|
+
this.listenTo(viewDocument, 'keydown', (evt, data) => {
|
|
75
|
+
const isPlain = !data.shiftKey && !data.ctrlKey && !data.altKey && !data.metaKey;
|
|
76
|
+
if (this.isEnabled &&
|
|
77
|
+
isPlain &&
|
|
78
|
+
isArrowKeyCode(data.keyCode) &&
|
|
79
|
+
this._handleArrowKey(data.keyCode, isForwardArrowKeyCode(data.keyCode, editor.locale.contentLanguageDirection))) {
|
|
80
|
+
data.preventDefault();
|
|
81
|
+
evt.stop();
|
|
82
|
+
}
|
|
83
|
+
}, { priority: keyboardPriorities.selectable });
|
|
84
|
+
// Esc goes after what else uses it, e.g. closing a balloon. Editor keystrokes (`cancel()`)
|
|
85
|
+
// do not stop this event, they prevent the default action of the DOM event: then Esc is handled already.
|
|
86
|
+
this.listenTo(viewDocument, 'keydown', (evt, data) => {
|
|
87
|
+
const isFree = data.keystroke === keyCodes.esc && !data.domEvent.defaultPrevented;
|
|
88
|
+
const element = this.isEnabled && isFree ? this._findAround() : null;
|
|
89
|
+
if (element) {
|
|
90
|
+
this._select(element);
|
|
91
|
+
data.preventDefault();
|
|
92
|
+
evt.stop();
|
|
93
|
+
}
|
|
94
|
+
}, { priority: keyboardPriorities.selectableEsc });
|
|
95
|
+
}
|
|
96
|
+
afterInit() {
|
|
97
|
+
// Before the data is loaded, once all the elements are registered: also the ones that other plugins register in
|
|
98
|
+
// `afterInit()`, e.g. the element schema, whichever plugin comes first.
|
|
99
|
+
this.listenTo(this.editor.data, 'init', () => {
|
|
100
|
+
this._makeSelectable(this._matchers);
|
|
101
|
+
}, { priority: 'high' });
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Makes more elements selectable, as the `elementSelectable` option does: a name of a model element, or a filter.
|
|
105
|
+
* Used by features that add elements, e.g. the element schema.
|
|
106
|
+
*/
|
|
107
|
+
add(...matchers) {
|
|
108
|
+
this._matchers.push(...matchers);
|
|
109
|
+
this._makeSelectable(matchers);
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Returns whether the element can be selected by this plugin: it matches a name or a filter.
|
|
113
|
+
*/
|
|
114
|
+
isSelectable(element) {
|
|
115
|
+
return !element.is('rootElement') && this._matchers.some(matcher => matchesElement(matcher, element));
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Selects the element as a whole.
|
|
119
|
+
*/
|
|
120
|
+
_select(element) {
|
|
121
|
+
this.editor.model.change(writer => {
|
|
122
|
+
writer.setSelection(element, 'on');
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Makes the model elements named in the matchers selectable in the schema, for the ones that are registered.
|
|
127
|
+
*/
|
|
128
|
+
_makeSelectable(matchers) {
|
|
129
|
+
const schema = this.editor.model.schema;
|
|
130
|
+
for (const matcher of matchers) {
|
|
131
|
+
if (typeof matcher === 'string' && schema.isRegistered(matcher) && !schema.isSelectable(matcher)) {
|
|
132
|
+
schema.extend(matcher, { isSelectable: true });
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Returns the element a mouse down on the view element selects: the closest selectable element around it (or it), if
|
|
138
|
+
* the click is on the element itself, or the element has no content.
|
|
139
|
+
*/
|
|
140
|
+
_findToSelect(target) {
|
|
141
|
+
for (const { element, viewElement } of getElementsAround(this.editor.editing.mapper, target)) {
|
|
142
|
+
if (this.isSelectable(element) && (viewElement === target || element.isEmpty)) {
|
|
143
|
+
return element;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Handles an arrow key. Returns whether the key moved the selection, and the browser must not.
|
|
150
|
+
*/
|
|
151
|
+
_handleArrowKey(keyCode, isForward) {
|
|
152
|
+
const model = this.editor.model;
|
|
153
|
+
const selection = model.document.selection;
|
|
154
|
+
const selected = selection.getSelectedElement();
|
|
155
|
+
const isSelectable = (element) => this.isSelectable(element);
|
|
156
|
+
if (selected && this.isSelectable(selected)) {
|
|
157
|
+
const target = findLeaveTarget(model, selected, isForward, isSelectable);
|
|
158
|
+
if (target) {
|
|
159
|
+
model.change(writer => {
|
|
160
|
+
writer.setSelection(target);
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
// Also when there is nowhere to go: the browser would move the cursor into the fake selection.
|
|
164
|
+
return true;
|
|
165
|
+
}
|
|
166
|
+
// The up and down keys move in the lines of the text: only the browser knows where.
|
|
167
|
+
const isHorizontal = keyCode === keyCodes.arrowleft || keyCode === keyCodes.arrowright;
|
|
168
|
+
const next = isHorizontal && selection.isCollapsed
|
|
169
|
+
? findSelectableAfterStep(model, selection, isForward, isSelectable)
|
|
170
|
+
: null;
|
|
171
|
+
if (next) {
|
|
172
|
+
this._select(next);
|
|
173
|
+
}
|
|
174
|
+
return !!next;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Returns the closest selectable element around the selection, not the selected one.
|
|
178
|
+
*/
|
|
179
|
+
_findAround() {
|
|
180
|
+
return findSelectableAround(this.editor.model.document.selection, element => this.isSelectable(element));
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Marks the selected view element with the class, and unmarks the one that was selected.
|
|
184
|
+
*/
|
|
185
|
+
_mark(writer, selected) {
|
|
186
|
+
const mapper = this.editor.editing.mapper;
|
|
187
|
+
const element = selected && mapper.toModelElement(selected);
|
|
188
|
+
const marked = element && this.isSelectable(element) ? selected : null;
|
|
189
|
+
if (this._selected && this._selected !== marked) {
|
|
190
|
+
writer.removeClass(SELECTED_CLASS, this._selected);
|
|
191
|
+
}
|
|
192
|
+
if (marked) {
|
|
193
|
+
writer.addClass(SELECTED_CLASS, marked);
|
|
194
|
+
}
|
|
195
|
+
this._selected = marked;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
//# sourceMappingURL=elementSelectable.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"elementSelectable.js","sourceRoot":"","sources":["../src/elementSelectable.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Element interaction, without CKEditor's widgets: select an element as a whole by a click or by the arrow keys, and call
|
|
3
|
+
* a function, with the model element, when an element that matches a name or a filter is clicked or double-clicked.
|
|
4
|
+
*
|
|
5
|
+
* @module ckeditor5-elements-interaction
|
|
6
|
+
*/
|
|
7
|
+
export { default as ElementSelectable, type ElementSelectableMatcher } from './elementSelectable.js';
|
|
8
|
+
export { default as ElementClickable, type ElementClickableDefinition, type ElementClickableEvent } from './elementClickable.js';
|
|
9
|
+
export { default as DoubleClickObserver, type ViewDocumentDoubleClickEvent } from './doubleClickObserver.js';
|
|
10
|
+
import './augmentation.js';
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,KAAK,wBAAwB,EAAE,MAAM,wBAAwB,CAAC;AACrG,OAAO,EACN,OAAO,IAAI,gBAAgB,EAC3B,KAAK,0BAA0B,EAC/B,KAAK,qBAAqB,EAC1B,MAAM,uBAAuB,CAAC;AAG/B,OAAO,EAAE,OAAO,IAAI,mBAAmB,EAAE,KAAK,4BAA4B,EAAE,MAAM,0BAA0B,CAAC;AAE7G,OAAO,mBAAmB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Element interaction, without CKEditor's widgets: select an element as a whole by a click or by the arrow keys, and call
|
|
3
|
+
* a function, with the model element, when an element that matches a name or a filter is clicked or double-clicked.
|
|
4
|
+
*
|
|
5
|
+
* @module ckeditor5-elements-interaction
|
|
6
|
+
*/
|
|
7
|
+
// Plugins.
|
|
8
|
+
export { default as ElementSelectable } from './elementSelectable.js';
|
|
9
|
+
export { default as ElementClickable } from './elementClickable.js';
|
|
10
|
+
// Observers.
|
|
11
|
+
export { default as DoubleClickObserver } from './doubleClickObserver.js';
|
|
12
|
+
import './augmentation.js';
|
|
13
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,WAAW;AACX,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAiC,MAAM,wBAAwB,CAAC;AACrG,OAAO,EACN,OAAO,IAAI,gBAAgB,EAG3B,MAAM,uBAAuB,CAAC;AAE/B,aAAa;AACb,OAAO,EAAE,OAAO,IAAI,mBAAmB,EAAqC,MAAM,0BAA0B,CAAC;AAE7G,OAAO,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Model, ModelDocumentSelection, ModelElement, ModelRange, ModelSelection } from 'ckeditor5';
|
|
2
|
+
/**
|
|
3
|
+
* Whether an element can be selected as a whole.
|
|
4
|
+
*/
|
|
5
|
+
export type IsSelectable = (element: ModelElement) => boolean;
|
|
6
|
+
/**
|
|
7
|
+
* Returns the selectable element that the cursor reaches with one step in the direction, as the arrow key moves it, or
|
|
8
|
+
* `null`: the next element is not selectable, or the step stays in the text. The step is CKEditor's: a character in the
|
|
9
|
+
* text, and the boundary between blocks, an inline object or an element at the end of a block. The first selectable
|
|
10
|
+
* element the step enters or passes is the one: the outermost, when it enters nested ones.
|
|
11
|
+
*/
|
|
12
|
+
export declare function findSelectableAfterStep(model: Model, selection: ModelSelection | ModelDocumentSelection, isForward: boolean, isSelectable: IsSelectable): ModelElement | null;
|
|
13
|
+
/**
|
|
14
|
+
* Returns the range the selection goes to from the selected element in the direction: the next selectable element
|
|
15
|
+
* (selected as a whole) when it is just there, otherwise the nearest place of the content, or `null` when there is none.
|
|
16
|
+
*/
|
|
17
|
+
export declare function findLeaveTarget(model: Model, element: ModelElement, isForward: boolean, isSelectable: IsSelectable): ModelRange | null;
|
|
18
|
+
/**
|
|
19
|
+
* Returns the closest selectable element around the selection, not the selected one, or `null`.
|
|
20
|
+
*/
|
|
21
|
+
export declare function findSelectableAround(selection: ModelSelection | ModelDocumentSelection, isSelectable: IsSelectable): ModelElement | null;
|
|
22
|
+
//# sourceMappingURL=navigation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"navigation.d.ts","sourceRoot":"","sources":["../src/navigation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,sBAAsB,EAAE,YAAY,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAGzG;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,EAAE,YAAY,KAAK,OAAO,CAAC;AAE9D;;;;;GAKG;AACH,wBAAgB,uBAAuB,CACtC,KAAK,EAAE,KAAK,EACZ,SAAS,EAAE,cAAc,GAAG,sBAAsB,EAClD,SAAS,EAAE,OAAO,EAClB,YAAY,EAAE,YAAY,GACxB,YAAY,GAAG,IAAI,CAuBrB;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC9B,KAAK,EAAE,KAAK,EACZ,OAAO,EAAE,YAAY,EACrB,SAAS,EAAE,OAAO,EAClB,YAAY,EAAE,YAAY,GACxB,UAAU,GAAG,IAAI,CAOnB;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CACnC,SAAS,EAAE,cAAc,GAAG,sBAAsB,EAClD,YAAY,EAAE,YAAY,GACxB,YAAY,GAAG,IAAI,CAUrB"}
|