@ckeditor/ckeditor5-link 36.0.0 → 37.0.0-alpha.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.
@@ -2,79 +2,65 @@
2
2
  * @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
3
3
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
4
  */
5
-
6
5
  /**
7
6
  * @module link/unlinkcommand
8
7
  */
9
-
10
8
  import { Command } from 'ckeditor5/src/core';
11
9
  import { findAttributeRange } from 'ckeditor5/src/typing';
12
-
13
10
  import { isLinkableElement } from './utils';
14
-
15
11
  /**
16
12
  * The unlink command. It is used by the {@link module:link/link~Link link plugin}.
17
- *
18
- * @extends module:core/command~Command
19
13
  */
20
14
  export default class UnlinkCommand extends Command {
21
- /**
22
- * @inheritDoc
23
- */
24
- refresh() {
25
- const model = this.editor.model;
26
- const selection = model.document.selection;
27
- const selectedElement = selection.getSelectedElement();
28
-
29
- // A check for any integration that allows linking elements (e.g. `LinkImage`).
30
- // Currently the selection reads attributes from text nodes only. See #7429 and #7465.
31
- if ( isLinkableElement( selectedElement, model.schema ) ) {
32
- this.isEnabled = model.schema.checkAttribute( selectedElement, 'linkHref' );
33
- } else {
34
- this.isEnabled = model.schema.checkAttributeInSelection( selection, 'linkHref' );
35
- }
36
- }
37
-
38
- /**
39
- * Executes the command.
40
- *
41
- * When the selection is collapsed, it removes the `linkHref` attribute from each node with the same `linkHref` attribute value.
42
- * When the selection is non-collapsed, it removes the `linkHref` attribute from each node in selected ranges.
43
- *
44
- * # Decorators
45
- *
46
- * If {@link module:link/link~LinkConfig#decorators `config.link.decorators`} is specified,
47
- * all configured decorators are removed together with the `linkHref` attribute.
48
- *
49
- * @fires execute
50
- */
51
- execute() {
52
- const editor = this.editor;
53
- const model = this.editor.model;
54
- const selection = model.document.selection;
55
- const linkCommand = editor.commands.get( 'link' );
56
-
57
- model.change( writer => {
58
- // Get ranges to unlink.
59
- const rangesToUnlink = selection.isCollapsed ?
60
- [ findAttributeRange(
61
- selection.getFirstPosition(),
62
- 'linkHref',
63
- selection.getAttribute( 'linkHref' ),
64
- model
65
- ) ] :
66
- model.schema.getValidRanges( selection.getRanges(), 'linkHref' );
67
-
68
- // Remove `linkHref` attribute from specified ranges.
69
- for ( const range of rangesToUnlink ) {
70
- writer.removeAttribute( 'linkHref', range );
71
- // If there are registered custom attributes, then remove them during unlink.
72
- if ( linkCommand ) {
73
- for ( const manualDecorator of linkCommand.manualDecorators ) {
74
- writer.removeAttribute( manualDecorator.id, range );
75
- }
76
- }
77
- }
78
- } );
79
- }
15
+ /**
16
+ * @inheritDoc
17
+ */
18
+ refresh() {
19
+ const model = this.editor.model;
20
+ const selection = model.document.selection;
21
+ const selectedElement = selection.getSelectedElement();
22
+ // A check for any integration that allows linking elements (e.g. `LinkImage`).
23
+ // Currently the selection reads attributes from text nodes only. See #7429 and #7465.
24
+ if (isLinkableElement(selectedElement, model.schema)) {
25
+ this.isEnabled = model.schema.checkAttribute(selectedElement, 'linkHref');
26
+ }
27
+ else {
28
+ this.isEnabled = model.schema.checkAttributeInSelection(selection, 'linkHref');
29
+ }
30
+ }
31
+ /**
32
+ * Executes the command.
33
+ *
34
+ * When the selection is collapsed, it removes the `linkHref` attribute from each node with the same `linkHref` attribute value.
35
+ * When the selection is non-collapsed, it removes the `linkHref` attribute from each node in selected ranges.
36
+ *
37
+ * # Decorators
38
+ *
39
+ * If {@link module:link/link~LinkConfig#decorators `config.link.decorators`} is specified,
40
+ * all configured decorators are removed together with the `linkHref` attribute.
41
+ *
42
+ * @fires execute
43
+ */
44
+ execute() {
45
+ const editor = this.editor;
46
+ const model = this.editor.model;
47
+ const selection = model.document.selection;
48
+ const linkCommand = editor.commands.get('link');
49
+ model.change(writer => {
50
+ // Get ranges to unlink.
51
+ const rangesToUnlink = selection.isCollapsed ?
52
+ [findAttributeRange(selection.getFirstPosition(), 'linkHref', selection.getAttribute('linkHref'), model)] :
53
+ model.schema.getValidRanges(selection.getRanges(), 'linkHref');
54
+ // Remove `linkHref` attribute from specified ranges.
55
+ for (const range of rangesToUnlink) {
56
+ writer.removeAttribute('linkHref', range);
57
+ // If there are registered custom attributes, then remove them during unlink.
58
+ if (linkCommand) {
59
+ for (const manualDecorator of linkCommand.manualDecorators) {
60
+ writer.removeAttribute(manualDecorator.id, range);
61
+ }
62
+ }
63
+ }
64
+ });
65
+ }
80
66
  }
@@ -0,0 +1,44 @@
1
+ /**
2
+ * @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
3
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
+ */
5
+ /**
6
+ * @module link/utils/automaticdecorators
7
+ */
8
+ import { type ArrayOrItem } from 'ckeditor5/src/utils';
9
+ import type { DowncastDispatcher } from 'ckeditor5/src/engine';
10
+ import type { NormalizedLinkDecoratorAutomaticDefinition } from '../utils';
11
+ /**
12
+ * Helper class that ties together all {@link module:link/link~LinkDecoratorAutomaticDefinition} and provides
13
+ * the {@link module:engine/conversion/downcasthelpers~DowncastHelpers#attributeToElement downcast dispatchers} for them.
14
+ */
15
+ export default class AutomaticDecorators {
16
+ /**
17
+ * Stores the definition of {@link module:link/link~LinkDecoratorAutomaticDefinition automatic decorators}.
18
+ * This data is used as a source for a downcast dispatcher to create a proper conversion to output data.
19
+ */
20
+ private _definitions;
21
+ /**
22
+ * Gives information about the number of decorators stored in the {@link module:link/utils~AutomaticDecorators} instance.
23
+ */
24
+ get length(): number;
25
+ /**
26
+ * Adds automatic decorator objects or an array with them to be used during downcasting.
27
+ *
28
+ * @param item A configuration object of automatic rules for decorating links. It might also be an array of such objects.
29
+ */
30
+ add(item: ArrayOrItem<NormalizedLinkDecoratorAutomaticDefinition>): void;
31
+ /**
32
+ * Provides the conversion helper used in the {@link module:engine/conversion/downcasthelpers~DowncastHelpers#add} method.
33
+ *
34
+ * @returns A dispatcher function used as conversion helper in {@link module:engine/conversion/downcasthelpers~DowncastHelpers#add}.
35
+ */
36
+ getDispatcher(): (dispatcher: DowncastDispatcher) => void;
37
+ /**
38
+ * Provides the conversion helper used in the {@link module:engine/conversion/downcasthelpers~DowncastHelpers#add} method
39
+ * when linking images.
40
+ *
41
+ * @returns A dispatcher function used as conversion helper in {@link module:engine/conversion/downcasthelpers~DowncastHelpers#add}.
42
+ */
43
+ getDispatcherForLinkedImage(): (dispatcher: DowncastDispatcher) => void;
44
+ }
@@ -2,160 +2,138 @@
2
2
  * @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
3
3
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
4
  */
5
-
6
5
  /**
7
- * @module link/utils
6
+ * @module link/utils/automaticdecorators
8
7
  */
9
-
10
8
  import { toMap } from 'ckeditor5/src/utils';
11
-
12
9
  /**
13
10
  * Helper class that ties together all {@link module:link/link~LinkDecoratorAutomaticDefinition} and provides
14
11
  * the {@link module:engine/conversion/downcasthelpers~DowncastHelpers#attributeToElement downcast dispatchers} for them.
15
12
  */
16
13
  export default class AutomaticDecorators {
17
- constructor() {
18
- /**
19
- * Stores the definition of {@link module:link/link~LinkDecoratorAutomaticDefinition automatic decorators}.
20
- * This data is used as a source for a downcast dispatcher to create a proper conversion to output data.
21
- *
22
- * @private
23
- * @type {Set}
24
- */
25
- this._definitions = new Set();
26
- }
27
-
28
- /**
29
- * Gives information about the number of decorators stored in the {@link module:link/utils~AutomaticDecorators} instance.
30
- *
31
- * @readonly
32
- * @protected
33
- * @type {Number}
34
- */
35
- get length() {
36
- return this._definitions.size;
37
- }
38
-
39
- /**
40
- * Adds automatic decorator objects or an array with them to be used during downcasting.
41
- *
42
- * @param {module:link/link~LinkDecoratorAutomaticDefinition|Array.<module:link/link~LinkDecoratorAutomaticDefinition>} item
43
- * A configuration object of automatic rules for decorating links. It might also be an array of such objects.
44
- */
45
- add( item ) {
46
- if ( Array.isArray( item ) ) {
47
- item.forEach( item => this._definitions.add( item ) );
48
- } else {
49
- this._definitions.add( item );
50
- }
51
- }
52
-
53
- /**
54
- * Provides the conversion helper used in the {@link module:engine/conversion/downcasthelpers~DowncastHelpers#add} method.
55
- *
56
- * @returns {Function} A dispatcher function used as conversion helper
57
- * in {@link module:engine/conversion/downcasthelpers~DowncastHelpers#add}.
58
- */
59
- getDispatcher() {
60
- return dispatcher => {
61
- dispatcher.on( 'attribute:linkHref', ( evt, data, conversionApi ) => {
62
- // There is only test as this behavior decorates links and
63
- // it is run before dispatcher which actually consumes this node.
64
- // This allows on writing own dispatcher with highest priority,
65
- // which blocks both native converter and this additional decoration.
66
- if ( !conversionApi.consumable.test( data.item, 'attribute:linkHref' ) ) {
67
- return;
68
- }
69
-
70
- // Automatic decorators for block links are handled e.g. in LinkImageEditing.
71
- if ( !( data.item.is( 'selection' ) || conversionApi.schema.isInline( data.item ) ) ) {
72
- return;
73
- }
74
-
75
- const viewWriter = conversionApi.writer;
76
- const viewSelection = viewWriter.document.selection;
77
-
78
- for ( const item of this._definitions ) {
79
- const viewElement = viewWriter.createAttributeElement( 'a', item.attributes, {
80
- priority: 5
81
- } );
82
-
83
- if ( item.classes ) {
84
- viewWriter.addClass( item.classes, viewElement );
85
- }
86
-
87
- for ( const key in item.styles ) {
88
- viewWriter.setStyle( key, item.styles[ key ], viewElement );
89
- }
90
-
91
- viewWriter.setCustomProperty( 'link', true, viewElement );
92
- if ( item.callback( data.attributeNewValue ) ) {
93
- if ( data.item.is( 'selection' ) ) {
94
- viewWriter.wrap( viewSelection.getFirstRange(), viewElement );
95
- } else {
96
- viewWriter.wrap( conversionApi.mapper.toViewRange( data.range ), viewElement );
97
- }
98
- } else {
99
- viewWriter.unwrap( conversionApi.mapper.toViewRange( data.range ), viewElement );
100
- }
101
- }
102
- }, { priority: 'high' } );
103
- };
104
- }
105
-
106
- /**
107
- * Provides the conversion helper used in the {@link module:engine/conversion/downcasthelpers~DowncastHelpers#add} method
108
- * when linking images.
109
- *
110
- * @returns {Function} A dispatcher function used as conversion helper
111
- * in {@link module:engine/conversion/downcasthelpers~DowncastHelpers#add}.
112
- */
113
- getDispatcherForLinkedImage() {
114
- return dispatcher => {
115
- dispatcher.on( 'attribute:linkHref:imageBlock', ( evt, data, { writer, mapper } ) => {
116
- const viewFigure = mapper.toViewElement( data.item );
117
- const linkInImage = Array.from( viewFigure.getChildren() ).find( child => child.name === 'a' );
118
-
119
- for ( const item of this._definitions ) {
120
- const attributes = toMap( item.attributes );
121
-
122
- if ( item.callback( data.attributeNewValue ) ) {
123
- for ( const [ key, val ] of attributes ) {
124
- // Left for backward compatibility. Since v30 decorator should
125
- // accept `classes` and `styles` separately from `attributes`.
126
- if ( key === 'class' ) {
127
- writer.addClass( val, linkInImage );
128
- } else {
129
- writer.setAttribute( key, val, linkInImage );
130
- }
131
- }
132
-
133
- if ( item.classes ) {
134
- writer.addClass( item.classes, linkInImage );
135
- }
136
-
137
- for ( const key in item.styles ) {
138
- writer.setStyle( key, item.styles[ key ], linkInImage );
139
- }
140
- } else {
141
- for ( const [ key, val ] of attributes ) {
142
- if ( key === 'class' ) {
143
- writer.removeClass( val, linkInImage );
144
- } else {
145
- writer.removeAttribute( key, linkInImage );
146
- }
147
- }
148
-
149
- if ( item.classes ) {
150
- writer.removeClass( item.classes, linkInImage );
151
- }
152
-
153
- for ( const key in item.styles ) {
154
- writer.removeStyle( key, linkInImage );
155
- }
156
- }
157
- }
158
- } );
159
- };
160
- }
14
+ constructor() {
15
+ /**
16
+ * Stores the definition of {@link module:link/link~LinkDecoratorAutomaticDefinition automatic decorators}.
17
+ * This data is used as a source for a downcast dispatcher to create a proper conversion to output data.
18
+ */
19
+ this._definitions = new Set();
20
+ }
21
+ /**
22
+ * Gives information about the number of decorators stored in the {@link module:link/utils~AutomaticDecorators} instance.
23
+ */
24
+ get length() {
25
+ return this._definitions.size;
26
+ }
27
+ /**
28
+ * Adds automatic decorator objects or an array with them to be used during downcasting.
29
+ *
30
+ * @param item A configuration object of automatic rules for decorating links. It might also be an array of such objects.
31
+ */
32
+ add(item) {
33
+ if (Array.isArray(item)) {
34
+ item.forEach(item => this._definitions.add(item));
35
+ }
36
+ else {
37
+ this._definitions.add(item);
38
+ }
39
+ }
40
+ /**
41
+ * Provides the conversion helper used in the {@link module:engine/conversion/downcasthelpers~DowncastHelpers#add} method.
42
+ *
43
+ * @returns A dispatcher function used as conversion helper in {@link module:engine/conversion/downcasthelpers~DowncastHelpers#add}.
44
+ */
45
+ getDispatcher() {
46
+ return dispatcher => {
47
+ dispatcher.on('attribute:linkHref', (evt, data, conversionApi) => {
48
+ // There is only test as this behavior decorates links and
49
+ // it is run before dispatcher which actually consumes this node.
50
+ // This allows on writing own dispatcher with highest priority,
51
+ // which blocks both native converter and this additional decoration.
52
+ if (!conversionApi.consumable.test(data.item, 'attribute:linkHref')) {
53
+ return;
54
+ }
55
+ // Automatic decorators for block links are handled e.g. in LinkImageEditing.
56
+ if (!(data.item.is('selection') || conversionApi.schema.isInline(data.item))) {
57
+ return;
58
+ }
59
+ const viewWriter = conversionApi.writer;
60
+ const viewSelection = viewWriter.document.selection;
61
+ for (const item of this._definitions) {
62
+ const viewElement = viewWriter.createAttributeElement('a', item.attributes, {
63
+ priority: 5
64
+ });
65
+ if (item.classes) {
66
+ viewWriter.addClass(item.classes, viewElement);
67
+ }
68
+ for (const key in item.styles) {
69
+ viewWriter.setStyle(key, item.styles[key], viewElement);
70
+ }
71
+ viewWriter.setCustomProperty('link', true, viewElement);
72
+ if (item.callback(data.attributeNewValue)) {
73
+ if (data.item.is('selection')) {
74
+ viewWriter.wrap(viewSelection.getFirstRange(), viewElement);
75
+ }
76
+ else {
77
+ viewWriter.wrap(conversionApi.mapper.toViewRange(data.range), viewElement);
78
+ }
79
+ }
80
+ else {
81
+ viewWriter.unwrap(conversionApi.mapper.toViewRange(data.range), viewElement);
82
+ }
83
+ }
84
+ }, { priority: 'high' });
85
+ };
86
+ }
87
+ /**
88
+ * Provides the conversion helper used in the {@link module:engine/conversion/downcasthelpers~DowncastHelpers#add} method
89
+ * when linking images.
90
+ *
91
+ * @returns A dispatcher function used as conversion helper in {@link module:engine/conversion/downcasthelpers~DowncastHelpers#add}.
92
+ */
93
+ getDispatcherForLinkedImage() {
94
+ return dispatcher => {
95
+ dispatcher.on('attribute:linkHref:imageBlock', (evt, data, { writer, mapper }) => {
96
+ const viewFigure = mapper.toViewElement(data.item);
97
+ const linkInImage = Array.from(viewFigure.getChildren())
98
+ .find((child) => child.is('element', 'a'));
99
+ for (const item of this._definitions) {
100
+ const attributes = toMap(item.attributes);
101
+ if (item.callback(data.attributeNewValue)) {
102
+ for (const [key, val] of attributes) {
103
+ // Left for backward compatibility. Since v30 decorator should
104
+ // accept `classes` and `styles` separately from `attributes`.
105
+ if (key === 'class') {
106
+ writer.addClass(val, linkInImage);
107
+ }
108
+ else {
109
+ writer.setAttribute(key, val, linkInImage);
110
+ }
111
+ }
112
+ if (item.classes) {
113
+ writer.addClass(item.classes, linkInImage);
114
+ }
115
+ for (const key in item.styles) {
116
+ writer.setStyle(key, item.styles[key], linkInImage);
117
+ }
118
+ }
119
+ else {
120
+ for (const [key, val] of attributes) {
121
+ if (key === 'class') {
122
+ writer.removeClass(val, linkInImage);
123
+ }
124
+ else {
125
+ writer.removeAttribute(key, linkInImage);
126
+ }
127
+ }
128
+ if (item.classes) {
129
+ writer.removeClass(item.classes, linkInImage);
130
+ }
131
+ for (const key in item.styles) {
132
+ writer.removeStyle(key, linkInImage);
133
+ }
134
+ }
135
+ }
136
+ });
137
+ };
138
+ }
161
139
  }
@@ -0,0 +1,72 @@
1
+ /**
2
+ * @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
3
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
+ */
5
+ /**
6
+ * @module link/utils/manualdecorator
7
+ */
8
+ import { type ArrayOrItem } from 'ckeditor5/src/utils';
9
+ import type { MatcherObjectPattern } from 'ckeditor5/src/engine';
10
+ import type { NormalizedLinkDecoratorManualDefinition } from '../utils';
11
+ declare const ManualDecorator_base: {
12
+ new (): import("ckeditor5/src/utils").Observable;
13
+ prototype: import("ckeditor5/src/utils").Observable;
14
+ };
15
+ /**
16
+ * Helper class that stores manual decorators with observable {@link module:link/utils~ManualDecorator#value}
17
+ * to support integration with the UI state. An instance of this class is a model with the state of individual manual decorators.
18
+ * These decorators are kept as collections in {@link module:link/linkcommand~LinkCommand#manualDecorators}.
19
+ */
20
+ export default class ManualDecorator extends ManualDecorator_base {
21
+ /**
22
+ * An ID of a manual decorator which is the name of the attribute in the model, for example: 'linkManualDecorator0'.
23
+ */
24
+ id: string;
25
+ /**
26
+ * The value of the current manual decorator. It reflects its state from the UI.
27
+ *
28
+ * @observable
29
+ */
30
+ value: boolean | undefined;
31
+ /**
32
+ * The default value of manual decorator.
33
+ */
34
+ defaultValue?: boolean;
35
+ /**
36
+ * The label used in the user interface to toggle the manual decorator.
37
+ */
38
+ label: string;
39
+ /**
40
+ * A set of attributes added to downcasted data when the decorator is activated for a specific link.
41
+ * Attributes should be added in a form of attributes defined in {@link module:engine/view/elementdefinition~ElementDefinition}.
42
+ */
43
+ attributes?: Record<string, string>;
44
+ /**
45
+ * A set of classes added to downcasted data when the decorator is activated for a specific link.
46
+ * Classes should be added in a form of classes defined in {@link module:engine/view/elementdefinition~ElementDefinition}.
47
+ */
48
+ classes?: ArrayOrItem<string>;
49
+ /**
50
+ * A set of styles added to downcasted data when the decorator is activated for a specific link.
51
+ * Styles should be added in a form of styles defined in {@link module:engine/view/elementdefinition~ElementDefinition}.
52
+ */
53
+ styles?: Record<string, string>;
54
+ /**
55
+ * Creates a new instance of {@link module:link/utils~ManualDecorator}.
56
+ *
57
+ * @param config.id The name of the attribute used in the model that represents a given manual decorator.
58
+ * For example: `'linkIsExternal'`.
59
+ * @param config.label The label used in the user interface to toggle the manual decorator.
60
+ * @param config.attributes A set of attributes added to output data when the decorator is active for a specific link.
61
+ * Attributes should keep the format of attributes defined in {@link module:engine/view/elementdefinition~ElementDefinition}.
62
+ * @param [config.defaultValue] Controls whether the decorator is "on" by default.
63
+ */
64
+ constructor({ id, label, attributes, classes, styles, defaultValue }: NormalizedLinkDecoratorManualDefinition);
65
+ /**
66
+ * Returns {@link module:engine/view/matcher~MatcherPattern} with decorator attributes.
67
+ *
68
+ * @internal
69
+ */
70
+ _createPattern(): MatcherObjectPattern;
71
+ }
72
+ export {};