@ckeditor/ckeditor5-minimap 35.4.0 → 36.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.
@@ -1,221 +1,137 @@
1
1
  /**
2
- * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
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 minimap/minimapview
8
7
  */
9
-
10
8
  import { View } from 'ckeditor5/src/ui';
11
9
  import { Rect } from 'ckeditor5/src/utils';
12
-
13
10
  import MinimapIframeView from './minimapiframeview';
14
11
  import MinimapPositionTrackerView from './minimappositiontrackerview';
15
-
16
12
  /**
17
13
  * The main view of the minimap. It renders the original content but scaled down with a tracker element
18
14
  * visualizing the subset of the content visible to the user and allowing interactions (scrolling, dragging).
19
15
  *
20
- * @private
21
- * @extends module:ui/view~View
16
+ * @internal
22
17
  */
23
18
  export default class MinimapView extends View {
24
- /**
25
- * Creates an instance of the minimap view.
26
- *
27
- * @param {module:utils/locale~Locale} locale
28
- * @param {Object} options
29
- * @param {HTMLElement} options.domRootClone
30
- * @param {Array} options.pageStyles
31
- * @param {Number} options.scaleRatio
32
- * @param {Boolean} [options.useSimplePreview]
33
- * @param {String} [options.extraClasses]
34
- */
35
- constructor( { locale, scaleRatio, pageStyles, extraClasses, useSimplePreview, domRootClone } ) {
36
- super( locale );
37
-
38
- const bind = this.bindTemplate;
39
-
40
- /**
41
- * An instance of the tracker view displayed over the minimap.
42
- *
43
- * @protected
44
- * @readonly
45
- * @member {module:minimap/minimappositiontrackerview~MinimapPositionTrackerView}
46
- */
47
- this._positionTrackerView = new MinimapPositionTrackerView( locale );
48
- this._positionTrackerView.delegate( 'drag' ).to( this );
49
-
50
- /**
51
- * The scale ratio of the minimap relative to the original editing DOM root with the content.
52
- *
53
- * @protected
54
- * @readonly
55
- * @member {Number}
56
- */
57
- this._scaleRatio = scaleRatio;
58
-
59
- /**
60
- * An instance of the iframe view that hosts the minimap.
61
- *
62
- * @protected
63
- * @readonly
64
- * @member {module:minimap/minimapiframeview~MinimapIframeView}
65
- */
66
- this._minimapIframeView = new MinimapIframeView( locale, {
67
- useSimplePreview,
68
- pageStyles,
69
- extraClasses,
70
- scaleRatio,
71
- domRootClone
72
- } );
73
-
74
- this.setTemplate( {
75
- tag: 'div',
76
- attributes: {
77
- class: [
78
- 'ck',
79
- 'ck-minimap'
80
- ]
81
- },
82
- children: [
83
- this._positionTrackerView
84
- ],
85
- on: {
86
- click: bind.to( this._handleMinimapClick.bind( this ) ),
87
- wheel: bind.to( this._handleMinimapMouseWheel.bind( this ) )
88
- }
89
- } );
90
-
91
- /**
92
- * Fired when the minimap view is clicked.
93
- *
94
- * @event click
95
- * @param {Number} progress The number between 0 and 1 representing a place in the minimap (its height) that was clicked.
96
- */
97
-
98
- /**
99
- * Fired when the position tracker is dragged or the minimap is scrolled via mouse wheel.
100
- *
101
- * @event drag
102
- * @param {Number} movementY The vertical movement of the minimap as a result of dragging or scrolling.
103
- */
104
- }
105
-
106
- /**
107
- * @inheritDoc
108
- */
109
- destroy() {
110
- this._minimapIframeView.destroy();
111
-
112
- super.destroy();
113
- }
114
-
115
- /**
116
- * Returns the DOM {@link module:utils/dom/rect~Rect} height of the minimap.
117
- *
118
- * @readonly
119
- * @member {Number}
120
- */
121
- get height() {
122
- return new Rect( this.element ).height;
123
- }
124
-
125
- /**
126
- * Returns the number of available space (pixels) the position tracker (visible subset of the content) can use to scroll vertically.
127
- *
128
- * @readonly
129
- * @member {Number}
130
- */
131
- get scrollHeight() {
132
- return Math.max( 0, Math.min( this.height, this._minimapIframeView.height ) - this._positionTrackerView.height );
133
- }
134
-
135
- /**
136
- * @inheritDoc
137
- */
138
- render() {
139
- super.render();
140
-
141
- this._minimapIframeView.render();
142
-
143
- this.element.appendChild( this._minimapIframeView.element );
144
- }
145
-
146
- /**
147
- * Sets the new height of the minimap (in px) to respond to the changes in the original editing DOM root.
148
- *
149
- * **Note**:The provided value should be the `offsetHeight` of the original editing DOM root.
150
- *
151
- * @param {Number} newHeight
152
- */
153
- setContentHeight( newHeight ) {
154
- this._minimapIframeView.setHeight( newHeight * this._scaleRatio );
155
- }
156
-
157
- /**
158
- * Sets the minimap scroll progress.
159
- *
160
- * The minimap scroll progress is linked to the original editing DOM root and its scrollable container (ancestor).
161
- * Changing the progress will alter the vertical position of the minimap (and its position tracker) and give the user an accurate
162
- * overview of the visible document.
163
- *
164
- * **Note**: The value should be between 0 and 1. 0 when the DOM root has not been scrolled, 1 when the
165
- * scrolling has reached the end.
166
- *
167
- * @param {Number} newScrollProgress
168
- */
169
- setScrollProgress( newScrollProgress ) {
170
- const iframeView = this._minimapIframeView;
171
- const positionTrackerView = this._positionTrackerView;
172
-
173
- // The scrolling should end when the bottom edge of the iframe touches the bottom edge of the minimap.
174
- if ( iframeView.height < this.height ) {
175
- iframeView.setTopOffset( 0 );
176
- positionTrackerView.setTopOffset( ( iframeView.height - positionTrackerView.height ) * newScrollProgress );
177
- } else {
178
- const totalOffset = iframeView.height - this.height;
179
-
180
- iframeView.setTopOffset( -totalOffset * newScrollProgress );
181
- positionTrackerView.setTopOffset( ( this.height - positionTrackerView.height ) * newScrollProgress );
182
- }
183
-
184
- positionTrackerView.setScrollProgress( Math.round( newScrollProgress * 100 ) );
185
- }
186
-
187
- /**
188
- * Sets the new height of the tracker (in px) to visualize the subset of the content visible to the user.
189
- *
190
- * @param {Number} trackerHeight
191
- */
192
- setPositionTrackerHeight( trackerHeight ) {
193
- this._positionTrackerView.setHeight( trackerHeight * this._scaleRatio );
194
- }
195
-
196
- /**
197
- * @private
198
- * @param {Event} data DOM event data
199
- */
200
- _handleMinimapClick( data ) {
201
- const positionTrackerView = this._positionTrackerView;
202
-
203
- if ( data.target === positionTrackerView.element ) {
204
- return;
205
- }
206
-
207
- const trackerViewRect = new Rect( positionTrackerView.element );
208
- const diff = data.clientY - trackerViewRect.top - trackerViewRect.height / 2;
209
- const percentage = diff / this._minimapIframeView.height;
210
-
211
- this.fire( 'click', percentage );
212
- }
213
-
214
- /**
215
- * @private
216
- * @param {Event} data DOM event data
217
- */
218
- _handleMinimapMouseWheel( data ) {
219
- this.fire( 'drag', data.deltaY * this._scaleRatio );
220
- }
19
+ /**
20
+ * Creates an instance of the minimap view.
21
+ */
22
+ constructor({ locale, scaleRatio, pageStyles, extraClasses, useSimplePreview, domRootClone }) {
23
+ super(locale);
24
+ const bind = this.bindTemplate;
25
+ this._positionTrackerView = new MinimapPositionTrackerView(locale);
26
+ this._positionTrackerView.delegate('drag').to(this);
27
+ this._scaleRatio = scaleRatio;
28
+ this._minimapIframeView = new MinimapIframeView(locale, {
29
+ useSimplePreview,
30
+ pageStyles,
31
+ extraClasses,
32
+ scaleRatio,
33
+ domRootClone
34
+ });
35
+ this.setTemplate({
36
+ tag: 'div',
37
+ attributes: {
38
+ class: [
39
+ 'ck',
40
+ 'ck-minimap'
41
+ ]
42
+ },
43
+ children: [
44
+ this._positionTrackerView
45
+ ],
46
+ on: {
47
+ click: bind.to(this._handleMinimapClick.bind(this)),
48
+ wheel: bind.to(this._handleMinimapMouseWheel.bind(this))
49
+ }
50
+ });
51
+ }
52
+ /**
53
+ * @inheritDoc
54
+ */
55
+ destroy() {
56
+ this._minimapIframeView.destroy();
57
+ super.destroy();
58
+ }
59
+ /**
60
+ * Returns the DOM {@link module:utils/dom/rect~Rect} height of the minimap.
61
+ */
62
+ get height() {
63
+ return new Rect(this.element).height;
64
+ }
65
+ /**
66
+ * Returns the number of available space (pixels) the position tracker (visible subset of the content) can use to scroll vertically.
67
+ */
68
+ get scrollHeight() {
69
+ return Math.max(0, Math.min(this.height, this._minimapIframeView.height) - this._positionTrackerView.height);
70
+ }
71
+ /**
72
+ * @inheritDoc
73
+ */
74
+ render() {
75
+ super.render();
76
+ this._minimapIframeView.render();
77
+ this.element.appendChild(this._minimapIframeView.element);
78
+ }
79
+ /**
80
+ * Sets the new height of the minimap (in px) to respond to the changes in the original editing DOM root.
81
+ *
82
+ * **Note**:The provided value should be the `offsetHeight` of the original editing DOM root.
83
+ */
84
+ setContentHeight(newHeight) {
85
+ this._minimapIframeView.setHeight(newHeight * this._scaleRatio);
86
+ }
87
+ /**
88
+ * Sets the minimap scroll progress.
89
+ *
90
+ * The minimap scroll progress is linked to the original editing DOM root and its scrollable container (ancestor).
91
+ * Changing the progress will alter the vertical position of the minimap (and its position tracker) and give the user an accurate
92
+ * overview of the visible document.
93
+ *
94
+ * **Note**: The value should be between 0 and 1. 0 when the DOM root has not been scrolled, 1 when the
95
+ * scrolling has reached the end.
96
+ */
97
+ setScrollProgress(newScrollProgress) {
98
+ const iframeView = this._minimapIframeView;
99
+ const positionTrackerView = this._positionTrackerView;
100
+ // The scrolling should end when the bottom edge of the iframe touches the bottom edge of the minimap.
101
+ if (iframeView.height < this.height) {
102
+ iframeView.setTopOffset(0);
103
+ positionTrackerView.setTopOffset((iframeView.height - positionTrackerView.height) * newScrollProgress);
104
+ }
105
+ else {
106
+ const totalOffset = iframeView.height - this.height;
107
+ iframeView.setTopOffset(-totalOffset * newScrollProgress);
108
+ positionTrackerView.setTopOffset((this.height - positionTrackerView.height) * newScrollProgress);
109
+ }
110
+ positionTrackerView.setScrollProgress(Math.round(newScrollProgress * 100));
111
+ }
112
+ /**
113
+ * Sets the new height of the tracker (in px) to visualize the subset of the content visible to the user.
114
+ */
115
+ setPositionTrackerHeight(trackerHeight) {
116
+ this._positionTrackerView.setHeight(trackerHeight * this._scaleRatio);
117
+ }
118
+ /**
119
+ * @param data DOM event data
120
+ */
121
+ _handleMinimapClick(data) {
122
+ const positionTrackerView = this._positionTrackerView;
123
+ if (data.target === positionTrackerView.element) {
124
+ return;
125
+ }
126
+ const trackerViewRect = new Rect(positionTrackerView.element);
127
+ const diff = data.clientY - trackerViewRect.top - trackerViewRect.height / 2;
128
+ const percentage = diff / this._minimapIframeView.height;
129
+ this.fire('click', percentage);
130
+ }
131
+ /**
132
+ * @param data DOM event data
133
+ */
134
+ _handleMinimapMouseWheel(data) {
135
+ this.fire('drag', data.deltaY * this._scaleRatio);
136
+ }
221
137
  }
package/src/utils.js CHANGED
@@ -1,142 +1,97 @@
1
1
  /**
2
- * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
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
  /* global CSSMediaRule */
7
-
8
6
  /**
9
7
  * @module minimap/utils
10
8
  */
11
-
12
9
  import { Rect, global } from 'ckeditor5/src/utils';
13
10
  import { DomConverter, Renderer } from 'ckeditor5/src/engine';
14
-
15
11
  /**
16
12
  * Clones the editing view DOM root by using a dedicated pair of {@link module:engine/view/renderer~Renderer} and
17
13
  * {@link module:engine/view/domconverter~DomConverter}. The DOM root clone updates incrementally to stay in sync with the
18
14
  * source root.
19
15
  *
20
- * @protected
21
- * @param {module:core/editor/editor~Editor} editor The editor instance the original editing root belongs to.
22
- * @param {String} rootName The name of the root to clone.
23
- * @returns {HTMLElement} The editing root DOM clone element.
16
+ * @internal
17
+ * @param editor The editor instance the original editing root belongs to.
18
+ * @param rootName The name of the root to clone.
19
+ * @returns The editing root DOM clone element.
24
20
  */
25
- export function cloneEditingViewDomRoot( editor, rootName ) {
26
- const viewDocument = editor.editing.view.document;
27
- const viewRoot = viewDocument.getRoot( rootName );
28
- const domConverter = new DomConverter( viewDocument );
29
- const renderer = new Renderer( domConverter, viewDocument.selection );
30
- const domRootClone = editor.editing.view.getDomRoot().cloneNode();
31
-
32
- domConverter.bindElements( domRootClone, viewRoot );
33
-
34
- renderer.markToSync( 'children', viewRoot );
35
- renderer.markToSync( 'attributes', viewRoot );
36
-
37
- viewRoot.on( 'change:children', ( evt, node ) => renderer.markToSync( 'children', node ) );
38
- viewRoot.on( 'change:attributes', ( evt, node ) => renderer.markToSync( 'attributes', node ) );
39
- viewRoot.on( 'change:text', ( evt, node ) => renderer.markToSync( 'text', node ) );
40
-
41
- renderer.render();
42
-
43
- editor.editing.view.on( 'render', () => renderer.render() );
44
-
45
- // TODO: Cleanup after destruction.
46
- editor.on( 'destroy', () => {
47
- domConverter.unbindDomElement( domRootClone );
48
- } );
49
-
50
- return domRootClone;
21
+ export function cloneEditingViewDomRoot(editor, rootName) {
22
+ const viewDocument = editor.editing.view.document;
23
+ const viewRoot = viewDocument.getRoot(rootName);
24
+ const domConverter = new DomConverter(viewDocument);
25
+ const renderer = new Renderer(domConverter, viewDocument.selection);
26
+ const domRootClone = editor.editing.view.getDomRoot().cloneNode();
27
+ domConverter.bindElements(domRootClone, viewRoot);
28
+ renderer.markToSync('children', viewRoot);
29
+ renderer.markToSync('attributes', viewRoot);
30
+ viewRoot.on('change:children', (evt, node) => renderer.markToSync('children', node));
31
+ viewRoot.on('change:attributes', (evt, node) => renderer.markToSync('attributes', node));
32
+ viewRoot.on('change:text', (evt, node) => renderer.markToSync('text', node));
33
+ renderer.render();
34
+ editor.editing.view.on('render', () => renderer.render());
35
+ // TODO: Cleanup after destruction.
36
+ editor.on('destroy', () => {
37
+ domConverter.unbindDomElement(domRootClone);
38
+ });
39
+ return domRootClone;
51
40
  }
52
-
53
41
  /**
54
42
  * Harvests all web page styles, for instance, to allow re-using them in an `<iframe>` preserving the look of the content.
55
43
  *
56
44
  * The returned data format is as follows:
57
45
  *
58
- * [
59
- * 'p { color: red; ... } h2 { font-size: 2em; ... } ...',
60
- * '.spacing { padding: 1em; ... }; ...',
61
- * '...',
62
- * { href: 'http://link.to.external.stylesheet' },
63
- * { href: '...' }
64
- * ]
46
+ * ```ts
47
+ * [
48
+ * 'p { color: red; ... } h2 { font-size: 2em; ... } ...',
49
+ * '.spacing { padding: 1em; ... }; ...',
50
+ * '...',
51
+ * { href: 'http://link.to.external.stylesheet' },
52
+ * { href: '...' }
53
+ * ]
54
+ * ```
65
55
  *
66
56
  * **Note**: For stylesheets with `href` different than window origin, an object is returned because
67
57
  * accessing rules of these styles may cause CORS errors (depending on the configuration of the web page).
68
58
  *
69
- * @protected
70
- * @returns {Array.<String|Object>}
59
+ * @internal
71
60
  */
72
61
  export function getPageStyles() {
73
- return Array.from( global.document.styleSheets )
74
- .map( styleSheet => {
75
- // CORS
76
- if ( styleSheet.href && !styleSheet.href.startsWith( global.window.location.origin ) ) {
77
- return { href: styleSheet.href };
78
- }
79
-
80
- return Array.from( styleSheet.cssRules )
81
- .filter( rule => !( rule instanceof CSSMediaRule ) )
82
- .map( rule => rule.cssText )
83
- .join( ' \n' );
84
- } );
62
+ return Array.from(global.document.styleSheets)
63
+ .map(styleSheet => {
64
+ // CORS
65
+ if (styleSheet.href && !styleSheet.href.startsWith(global.window.location.origin)) {
66
+ return { href: styleSheet.href };
67
+ }
68
+ return Array.from(styleSheet.cssRules)
69
+ .filter(rule => !(rule instanceof CSSMediaRule))
70
+ .map(rule => rule.cssText)
71
+ .join(' \n');
72
+ });
85
73
  }
86
-
87
74
  /**
88
- * TODO
75
+ * Gets dimensions rectangle according to passed DOM element. Returns whole window's size for `body` element.
89
76
  *
90
- * @protected
91
- * @returns {module:utils/dom/rect~Rect}
77
+ * @internal
92
78
  */
93
- export function getDomElementRect( domElement ) {
94
- return new Rect( domElement === global.document.body ? global.window : domElement );
79
+ export function getDomElementRect(domElement) {
80
+ return new Rect(domElement === global.document.body ? global.window : domElement);
95
81
  }
96
-
97
82
  /**
98
- * TODO
83
+ * Gets client height according to passed DOM element. Returns window's height for `body` element.
99
84
  *
100
- * @protected
101
- * @returns {Number}
85
+ * @internal
102
86
  */
103
- export function getClientHeight( domElement ) {
104
- return domElement === global.document.body ? global.window.innerHeight : domElement.clientHeight;
87
+ export function getClientHeight(domElement) {
88
+ return domElement === global.document.body ? global.window.innerHeight : domElement.clientHeight;
105
89
  }
106
-
107
90
  /**
108
- * TODO
91
+ * Returns the DOM element itself if it's not a `body` element, whole window otherwise.
109
92
  *
110
- * @protected
111
- * @returns {HTMLElement}
93
+ * @internal
112
94
  */
113
- export function getScrollable( domElement ) {
114
- return domElement === global.document.body ? global.window : domElement;
115
- }
116
-
117
- /**
118
- * Returns the closest scrollable ancestor of a DOM element.
119
- *
120
- * TODO: Move to shared utils.
121
- *
122
- * @protected
123
- * @param {HTMLElement} domElement
124
- * @returns {HTMLElement|null}
125
- */
126
- export function findClosestScrollableAncestor( domElement ) {
127
- do {
128
- domElement = domElement.parentElement;
129
-
130
- if ( !domElement ) {
131
- return null;
132
- }
133
-
134
- const overflow = global.window.getComputedStyle( domElement ).overflowY;
135
-
136
- if ( overflow === 'auto' || overflow === 'scroll' ) {
137
- break;
138
- }
139
- } while ( domElement.tagName != 'BODY' );
140
-
141
- return domElement;
95
+ export function getScrollable(domElement) {
96
+ return domElement === global.document.body ? global.window : domElement;
142
97
  }
package/theme/minimap.css CHANGED
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
2
+ * 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
5