@ckeditor/ckeditor5-minimap 0.0.0-nightly-20230629.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/CHANGELOG.md +4 -0
- package/LICENSE.md +17 -0
- package/README.md +21 -0
- package/build/minimap.js +4 -0
- package/ckeditor5-metadata.json +11 -0
- package/package.json +40 -0
- package/src/augmentation.d.ts +18 -0
- package/src/augmentation.js +5 -0
- package/src/index.d.ts +10 -0
- package/src/index.js +9 -0
- package/src/minimap.d.ts +54 -0
- package/src/minimap.js +154 -0
- package/src/minimapconfig.d.ts +85 -0
- package/src/minimapconfig.js +5 -0
- package/src/minimapiframeview.d.ts +54 -0
- package/src/minimapiframeview.js +130 -0
- package/src/minimappositiontrackerview.d.ts +58 -0
- package/src/minimappositiontrackerview.js +78 -0
- package/src/minimapview.d.ts +109 -0
- package/src/minimapview.js +137 -0
- package/src/utils.d.ts +61 -0
- package/src/utils.js +97 -0
- package/theme/minimap.css +70 -0
package/CHANGELOG.md
ADDED
package/LICENSE.md
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
Software License Agreement
|
|
2
|
+
==========================
|
|
3
|
+
|
|
4
|
+
**CKEditor 5 Minimap Feature** – https://github.com/ckeditor/ckeditor5-minimap <br>
|
|
5
|
+
Copyright (c) 2003-2023, [CKSource Holding sp. z o.o.](https://cksource.com) All rights reserved.
|
|
6
|
+
|
|
7
|
+
Licensed under the terms of [GNU General Public License Version 2 or later](http://www.gnu.org/licenses/gpl.html).
|
|
8
|
+
|
|
9
|
+
Sources of Intellectual Property Included in CKEditor
|
|
10
|
+
-----------------------------------------------------
|
|
11
|
+
|
|
12
|
+
Where not otherwise indicated, all CKEditor content is authored by CKSource engineers and consists of CKSource-owned intellectual property. In some specific instances, CKEditor will incorporate work done by developers outside of CKSource with their express permission.
|
|
13
|
+
|
|
14
|
+
Trademarks
|
|
15
|
+
----------
|
|
16
|
+
|
|
17
|
+
**CKEditor** is a trademark of [CKSource Holding sp. z o.o.](https://cksource.com) All other brand and product names are trademarks, registered trademarks or service marks of their respective holders.
|
package/README.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
CKEditor 5 Minimap Feature
|
|
2
|
+
===========================
|
|
3
|
+
|
|
4
|
+
[](https://www.npmjs.com/package/@ckeditor/ckeditor5-minimap)
|
|
5
|
+
[](https://coveralls.io/github/ckeditor/ckeditor5?branch=master)
|
|
6
|
+
[](https://app.travis-ci.com/github/ckeditor/ckeditor5)
|
|
7
|
+

|
|
8
|
+
|
|
9
|
+
This package implements the content minimap feature for CKEditor 5.
|
|
10
|
+
|
|
11
|
+
## Demo
|
|
12
|
+
|
|
13
|
+
Check out the demo in the [content minimap feature guide](https://ckeditor.com/docs/ckeditor5/latest/features/minimap.html#demo).
|
|
14
|
+
|
|
15
|
+
## Documentation
|
|
16
|
+
|
|
17
|
+
See the [`@ckeditor/ckeditor5-minimap` package](https://ckeditor.com/docs/ckeditor5/latest/api/minimap.html) page as well as the [Content minimap feature](https://ckeditor.com/docs/ckeditor5/latest/features/minimap.html) guide in [CKEditor 5 documentation](https://ckeditor.com/docs/ckeditor5/latest/).
|
|
18
|
+
|
|
19
|
+
## License
|
|
20
|
+
|
|
21
|
+
Licensed under the terms of [GNU General Public License Version 2 or later](http://www.gnu.org/licenses/gpl.html). For full details about the license, please check the `LICENSE.md` file or [https://ckeditor.com/legal/ckeditor-oss-license](https://ckeditor.com/legal/ckeditor-oss-license).
|
package/build/minimap.js
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
|
|
3
|
+
* For licensing, see LICENSE.md.
|
|
4
|
+
*/(()=>{var t={544:(t,e,i)=>{"use strict";i.d(e,{Z:()=>r});var n=i(609),o=i.n(n)()((function(t){return t[1]}));o.push([t.id,':root{--ck-color-minimap-tracker-background:208,0%,51%;--ck-color-minimap-iframe-outline:#bfbfbf;--ck-color-minimap-iframe-shadow:rgba(0,0,0,.11);--ck-color-minimap-progress-background:#666}.ck.ck-minimap{background:var(--ck-color-base-background);position:absolute;user-select:none}.ck.ck-minimap,.ck.ck-minimap iframe{height:100%;width:100%}.ck.ck-minimap iframe{border:0;box-shadow:0 2px 5px var(--ck-color-minimap-iframe-shadow);margin:0;outline:1px solid var(--ck-color-minimap-iframe-outline);pointer-events:none;position:relative}.ck.ck-minimap .ck.ck-minimap__position-tracker{background:hsla(var(--ck-color-minimap-tracker-background),.2);position:absolute;top:0;transition:background .1s ease-in-out;width:100%;z-index:1}.ck.ck-minimap .ck.ck-minimap__position-tracker:hover{background:hsla(var(--ck-color-minimap-tracker-background),.3)}.ck.ck-minimap .ck.ck-minimap__position-tracker.ck-minimap__position-tracker_dragging,.ck.ck-minimap .ck.ck-minimap__position-tracker.ck-minimap__position-tracker_dragging:hover{background:hsla(var(--ck-color-minimap-tracker-background),.4)}.ck.ck-minimap .ck.ck-minimap__position-tracker.ck-minimap__position-tracker_dragging:after,.ck.ck-minimap .ck.ck-minimap__position-tracker.ck-minimap__position-tracker_dragging:hover:after{opacity:1}.ck.ck-minimap .ck.ck-minimap__position-tracker:after{background:var(--ck-color-minimap-progress-background);border:1px solid var(--ck-color-base-background);border-radius:3px;color:var(--ck-color-base-background);content:attr(data-progress) "%";font-size:10px;opacity:0;padding:2px 4px;position:absolute;right:5px;top:5px;transition:opacity .1s ease-in-out}',""]);const r=o},609:t=>{"use strict";t.exports=function(t){var e=[];return e.toString=function(){return this.map((function(e){var i=t(e);return e[2]?"@media ".concat(e[2]," {").concat(i,"}"):i})).join("")},e.i=function(t,i,n){"string"==typeof t&&(t=[[null,t,""]]);var o={};if(n)for(var r=0;r<this.length;r++){var s=this[r][0];null!=s&&(o[s]=!0)}for(var a=0;a<t.length;a++){var c=[].concat(t[a]);n&&o[c[0]]||(i&&(c[2]?c[2]="".concat(i," and ").concat(c[2]):c[2]=i),e.push(c))}},e}},62:(t,e,i)=>{"use strict";var n,o=function(){return void 0===n&&(n=Boolean(window&&document&&document.all&&!window.atob)),n},r=function(){var t={};return function(e){if(void 0===t[e]){var i=document.querySelector(e);if(window.HTMLIFrameElement&&i instanceof window.HTMLIFrameElement)try{i=i.contentDocument.head}catch(t){i=null}t[e]=i}return t[e]}}(),s=[];function a(t){for(var e=-1,i=0;i<s.length;i++)if(s[i].identifier===t){e=i;break}return e}function c(t,e){for(var i={},n=[],o=0;o<t.length;o++){var r=t[o],c=e.base?r[0]+e.base:r[0],l=i[c]||0,h="".concat(c," ").concat(l);i[c]=l+1;var m=a(h),d={css:r[1],media:r[2],sourceMap:r[3]};-1!==m?(s[m].references++,s[m].updater(d)):s.push({identifier:h,updater:f(d,e),references:1}),n.push(h)}return n}function l(t){var e=document.createElement("style"),n=t.attributes||{};if(void 0===n.nonce){var o=i.nc;o&&(n.nonce=o)}if(Object.keys(n).forEach((function(t){e.setAttribute(t,n[t])})),"function"==typeof t.insert)t.insert(e);else{var s=r(t.insert||"head");if(!s)throw new Error("Couldn't find a style target. This probably means that the value for the 'insert' parameter is invalid.");s.appendChild(e)}return e}var h,m=(h=[],function(t,e){return h[t]=e,h.filter(Boolean).join("\n")});function d(t,e,i,n){var o=i?"":n.media?"@media ".concat(n.media," {").concat(n.css,"}"):n.css;if(t.styleSheet)t.styleSheet.cssText=m(e,o);else{var r=document.createTextNode(o),s=t.childNodes;s[e]&&t.removeChild(s[e]),s.length?t.insertBefore(r,s[e]):t.appendChild(r)}}function p(t,e,i){var n=i.css,o=i.media,r=i.sourceMap;if(o?t.setAttribute("media",o):t.removeAttribute("media"),r&&"undefined"!=typeof btoa&&(n+="\n/*# sourceMappingURL=data:application/json;base64,".concat(btoa(unescape(encodeURIComponent(JSON.stringify(r))))," */")),t.styleSheet)t.styleSheet.cssText=n;else{for(;t.firstChild;)t.removeChild(t.firstChild);t.appendChild(document.createTextNode(n))}}var g=null,u=0;function f(t,e){var i,n,o;if(e.singleton){var r=u++;i=g||(g=l(e)),n=d.bind(null,i,r,!1),o=d.bind(null,i,r,!0)}else i=l(e),n=p.bind(null,i,e),o=function(){!function(t){if(null===t.parentNode)return!1;t.parentNode.removeChild(t)}(i)};return n(t),function(e){if(e){if(e.css===t.css&&e.media===t.media&&e.sourceMap===t.sourceMap)return;n(t=e)}else o()}}t.exports=function(t,e){(e=e||{}).singleton||"boolean"==typeof e.singleton||(e.singleton=o());var i=c(t=t||[],e);return function(t){if(t=t||[],"[object Array]"===Object.prototype.toString.call(t)){for(var n=0;n<i.length;n++){var o=a(i[n]);s[o].references--}for(var r=c(t,e),l=0;l<i.length;l++){var h=a(i[l]);0===s[h].references&&(s[h].updater(),s.splice(h,1))}i=r}}}},704:(t,e,i)=>{t.exports=i(79)("./src/core.js")},492:(t,e,i)=>{t.exports=i(79)("./src/engine.js")},273:(t,e,i)=>{t.exports=i(79)("./src/ui.js")},209:(t,e,i)=>{t.exports=i(79)("./src/utils.js")},79:t=>{"use strict";t.exports=CKEditor5.dll}},e={};function i(n){var o=e[n];if(void 0!==o)return o.exports;var r=e[n]={id:n,exports:{}};return t[n](r,r.exports,i),r.exports}i.n=t=>{var e=t&&t.__esModule?()=>t.default:()=>t;return i.d(e,{a:e}),e},i.d=(t,e)=>{for(var n in e)i.o(e,n)&&!i.o(t,n)&&Object.defineProperty(t,n,{enumerable:!0,get:e[n]})},i.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),i.r=t=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},i.nc=void 0;var n={};(()=>{"use strict";i.r(n),i.d(n,{Minimap:()=>b});var t=i(704),e=i(209),o=i(273);const r=(0,e.toUnit)("px");class s extends o.IframeView{constructor(t,e){super(t);const i=this.bindTemplate;this.set("top",0),this.set("height",0),this._options=e,this.extendTemplate({attributes:{class:["ck-minimap__iframe"],style:{top:i.to("top",(t=>r(t))),height:i.to("height",(t=>r(t)))}}})}render(){return super.render().then((()=>{this._prepareDocument()}))}setHeight(t){this.height=t}setTopOffset(t){this.top=t}_prepareDocument(){const t=this.element.contentWindow.document,e=t.adoptNode(this._options.domRootClone),i=this._options.useSimplePreview?"\n\t\t\t.ck.ck-editor__editable_inline img {\n\t\t\t\tfilter: contrast( 0 );\n\t\t\t}\n\n\t\t\tp, li, a, figcaption, span {\n\t\t\t\tbackground: hsl(0, 0%, 80%) !important;\n\t\t\t\tcolor: hsl(0, 0%, 80%) !important;\n\t\t\t}\n\n\t\t\th1, h2, h3, h4 {\n\t\t\t\tbackground: hsl(0, 0%, 60%) !important;\n\t\t\t\tcolor: hsl(0, 0%, 60%) !important;\n\t\t\t}\n\t\t":"",n=`<!DOCTYPE html><html lang="en">\n\t\t\t<head>\n\t\t\t\t<meta charset="utf-8">\n\t\t\t\t<meta name="viewport" content="width=device-width, initial-scale=1">\n\t\t\t\t${this._options.pageStyles.map((t=>"string"==typeof t?`<style>${t}</style>`:`<link rel="stylesheet" type="text/css" href="${t.href}">`)).join("\n")}\n\t\t\t\t<style>\n\t\t\t\t\thtml, body {\n\t\t\t\t\t\tmargin: 0 !important;\n\t\t\t\t\t\tpadding: 0 !important;\n\t\t\t\t\t}\n\n\t\t\t\t\thtml {\n\t\t\t\t\t\toverflow: hidden;\n\t\t\t\t\t}\n\n\t\t\t\t\tbody {\n\t\t\t\t\t\ttransform: scale( ${this._options.scaleRatio} );\n\t\t\t\t\t\ttransform-origin: 0 0;\n\t\t\t\t\t\toverflow: visible;\n\t\t\t\t\t}\n\n\t\t\t\t\t.ck.ck-editor__editable_inline {\n\t\t\t\t\t\tmargin: 0 !important;\n\t\t\t\t\t\tborder-color: transparent !important;\n\t\t\t\t\t\toutline-color: transparent !important;\n\t\t\t\t\t\tbox-shadow: none !important;\n\t\t\t\t\t}\n\n\t\t\t\t\t.ck.ck-content {\n\t\t\t\t\t\tbackground: white;\n\t\t\t\t\t}\n\n\t\t\t\t\t${i}\n\t\t\t\t</style>\n\t\t\t</head>\n\t\t\t<body class="${this._options.extraClasses||""}"></body>\n\t\t</html>`;t.open(),t.write(n),t.close(),t.body.appendChild(e)}}const a=(0,e.toUnit)("px");class c extends o.View{constructor(t){super(t);const e=this.bindTemplate;this.set("height",0),this.set("top",0),this.set("scrollProgress",0),this.set("_isDragging",!1),this.setTemplate({tag:"div",attributes:{class:["ck","ck-minimap__position-tracker",e.if("_isDragging","ck-minimap__position-tracker_dragging")],style:{top:e.to("top",(t=>a(t))),height:e.to("height",(t=>a(t)))},"data-progress":e.to("scrollProgress")},on:{mousedown:e.to((()=>{this._isDragging=!0}))}})}render(){super.render(),this.listenTo(e.global.document,"mousemove",((t,e)=>{this._isDragging&&this.fire("drag",e.movementY)}),{useCapture:!0}),this.listenTo(e.global.document,"mouseup",(()=>{this._isDragging=!1}),{useCapture:!0})}setHeight(t){this.height=t}setTopOffset(t){this.top=t}setScrollProgress(t){this.scrollProgress=t}}class l extends o.View{constructor({locale:t,scaleRatio:e,pageStyles:i,extraClasses:n,useSimplePreview:o,domRootClone:r}){super(t);const a=this.bindTemplate;this._positionTrackerView=new c(t),this._positionTrackerView.delegate("drag").to(this),this._scaleRatio=e,this._minimapIframeView=new s(t,{useSimplePreview:o,pageStyles:i,extraClasses:n,scaleRatio:e,domRootClone:r}),this.setTemplate({tag:"div",attributes:{class:["ck","ck-minimap"]},children:[this._positionTrackerView],on:{click:a.to(this._handleMinimapClick.bind(this)),wheel:a.to(this._handleMinimapMouseWheel.bind(this))}})}destroy(){this._minimapIframeView.destroy(),super.destroy()}get height(){return new e.Rect(this.element).height}get scrollHeight(){return Math.max(0,Math.min(this.height,this._minimapIframeView.height)-this._positionTrackerView.height)}render(){super.render(),this._minimapIframeView.render(),this.element.appendChild(this._minimapIframeView.element)}setContentHeight(t){this._minimapIframeView.setHeight(t*this._scaleRatio)}setScrollProgress(t){const e=this._minimapIframeView,i=this._positionTrackerView;if(e.height<this.height)e.setTopOffset(0),i.setTopOffset((e.height-i.height)*t);else{const n=e.height-this.height;e.setTopOffset(-n*t),i.setTopOffset((this.height-i.height)*t)}i.setScrollProgress(Math.round(100*t))}setPositionTrackerHeight(t){this._positionTrackerView.setHeight(t*this._scaleRatio)}_handleMinimapClick(t){const i=this._positionTrackerView;if(t.target===i.element)return;const n=new e.Rect(i.element),o=(t.clientY-n.top-n.height/2)/this._minimapIframeView.height;this.fire("click",o)}_handleMinimapMouseWheel(t){this.fire("drag",t.deltaY*this._scaleRatio)}}var h=i(492);function m(t,e){const i=t.editing.view.document,n=i.getRoot(e),o=new h.DomConverter(i),r=new h.Renderer(o,i.selection),s=t.editing.view.getDomRoot().cloneNode();return o.bindElements(s,n),r.markToSync("children",n),r.markToSync("attributes",n),n.on("change:children",((t,e)=>r.markToSync("children",e))),n.on("change:attributes",((t,e)=>r.markToSync("attributes",e))),n.on("change:text",((t,e)=>r.markToSync("text",e))),r.render(),t.editing.view.on("render",(()=>r.render())),t.on("destroy",(()=>{o.unbindDomElement(s)})),s}function d(t){return new e.Rect(t===e.global.document.body?e.global.window:t)}function p(t){return t===e.global.document.body?e.global.window:t}var g=i(62),u=i.n(g),f=i(544),k={injectType:"singletonStyleTag",attributes:{"data-cke":!0},insert:"head",singleton:!0};u()(f.Z,k);f.Z.locals;class b extends t.Plugin{static get pluginName(){return"Minimap"}init(){const t=this.editor;this._minimapView=null,this._scrollableRootAncestor=null,this.listenTo(t.ui,"ready",this._onUiReady.bind(this))}destroy(){this._minimapView.destroy(),this._minimapView.element.remove()}_onUiReady(){const t=this.editor,i=this._editingRootElement=t.ui.getEditableElement();this._scrollableRootAncestor=(0,e.findClosestScrollableAncestor)(i),i.ownerDocument.body.contains(i)?(this._initializeMinimapView(),this.listenTo(t.editing.view,"render",(()=>{this._syncMinimapToEditingRootScrollPosition()})),this._syncMinimapToEditingRootScrollPosition()):t.ui.once("update",this._onUiReady.bind(this))}_initializeMinimapView(){const t=this.editor,i=t.locale,n=t.config.get("minimap.useSimplePreview"),o=t.config.get("minimap.container"),r=this._scrollableRootAncestor,s=d(this._editingRootElement).width,a=d(o).width/s,c=this._minimapView=new l({locale:i,scaleRatio:a,pageStyles:Array.from(e.global.document.styleSheets).map((t=>t.href&&!t.href.startsWith(e.global.window.location.origin)?{href:t.href}:Array.from(t.cssRules).filter((t=>!(t instanceof CSSMediaRule))).map((t=>t.cssText)).join(" \n"))),extraClasses:t.config.get("minimap.extraClasses"),useSimplePreview:n,domRootClone:m(t)});c.render(),c.listenTo(e.global.document,"scroll",((t,i)=>{if(r===e.global.document.body){if(i.target!==e.global.document)return}else if(i.target!==r)return;this._syncMinimapToEditingRootScrollPosition()}),{useCapture:!0,usePassive:!0}),c.listenTo(e.global.window,"resize",(()=>{this._syncMinimapToEditingRootScrollPosition()})),c.on("drag",((t,i)=>{let n;n=0===c.scrollHeight?0:i/c.scrollHeight;const o=n*(r.scrollHeight-((s=r)===e.global.document.body?e.global.window.innerHeight:s.clientHeight));var s;p(r).scrollBy(0,Math.round(o))})),c.on("click",((t,e)=>{const i=e*r.scrollHeight;p(r).scrollBy(0,Math.round(i))})),o.appendChild(c.element)}_syncMinimapToEditingRootScrollPosition(){const t=this._editingRootElement,e=this._minimapView;e.setContentHeight(t.offsetHeight);const i=d(t),n=d(this._scrollableRootAncestor);let o;n.contains(i)||i.top>n.top?o=0:(o=(i.top-n.top)/(n.height-i.height),o=Math.max(0,Math.min(o,1))),e.setPositionTrackerHeight(n.getIntersection(i).height),e.setScrollProgress(o)}}})(),(window.CKEditor5=window.CKEditor5||{}).minimap=n})();
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ckeditor/ckeditor5-minimap",
|
|
3
|
+
"version": "0.0.0-nightly-20230629.0",
|
|
4
|
+
"description": "Content minimap feature for CKEditor 5.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"ckeditor",
|
|
7
|
+
"ckeditor5",
|
|
8
|
+
"ckeditor 5",
|
|
9
|
+
"ckeditor5-feature",
|
|
10
|
+
"ckeditor5-plugin",
|
|
11
|
+
"ckeditor5-dll"
|
|
12
|
+
],
|
|
13
|
+
"main": "src/index.js",
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"ckeditor5": "0.0.0-nightly-20230629.0"
|
|
16
|
+
},
|
|
17
|
+
"engines": {
|
|
18
|
+
"node": ">=16.0.0",
|
|
19
|
+
"npm": ">=5.7.1"
|
|
20
|
+
},
|
|
21
|
+
"author": "CKSource (http://cksource.com/)",
|
|
22
|
+
"license": "GPL-2.0-or-later",
|
|
23
|
+
"homepage": "https://ckeditor.com/ckeditor-5",
|
|
24
|
+
"bugs": "https://github.com/ckeditor/ckeditor5/issues",
|
|
25
|
+
"repository": {
|
|
26
|
+
"type": "git",
|
|
27
|
+
"url": "https://github.com/ckeditor/ckeditor5.git",
|
|
28
|
+
"directory": "packages/ckeditor5-minimap"
|
|
29
|
+
},
|
|
30
|
+
"files": [
|
|
31
|
+
"lang",
|
|
32
|
+
"src/**/*.js",
|
|
33
|
+
"src/**/*.d.ts",
|
|
34
|
+
"theme",
|
|
35
|
+
"build",
|
|
36
|
+
"ckeditor5-metadata.json",
|
|
37
|
+
"CHANGELOG.md"
|
|
38
|
+
],
|
|
39
|
+
"types": "src/index.d.ts"
|
|
40
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
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
|
+
import type { MinimapConfig, Minimap } from './index';
|
|
6
|
+
declare module '@ckeditor/ckeditor5-core' {
|
|
7
|
+
interface EditorConfig {
|
|
8
|
+
/**
|
|
9
|
+
* The configuration of the minimap feature. Introduced by the {@link module:minimap/minimap~Minimap} feature.
|
|
10
|
+
*
|
|
11
|
+
* Read more in {@link module:minimap/minimapconfig~MinimapConfig}.
|
|
12
|
+
*/
|
|
13
|
+
minimap?: MinimapConfig;
|
|
14
|
+
}
|
|
15
|
+
interface PluginsMap {
|
|
16
|
+
[Minimap.pluginName]: Minimap;
|
|
17
|
+
}
|
|
18
|
+
}
|
package/src/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
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 minimap
|
|
7
|
+
*/
|
|
8
|
+
export { default as Minimap } from './minimap';
|
|
9
|
+
export type { MinimapConfig } from './minimapconfig';
|
|
10
|
+
import './augmentation';
|
package/src/index.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
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 minimap
|
|
7
|
+
*/
|
|
8
|
+
export { default as Minimap } from './minimap';
|
|
9
|
+
import './augmentation';
|
package/src/minimap.d.ts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
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 minimap/minimap
|
|
7
|
+
*/
|
|
8
|
+
import { Plugin } from 'ckeditor5/src/core';
|
|
9
|
+
import '../theme/minimap.css';
|
|
10
|
+
/**
|
|
11
|
+
* The content minimap feature.
|
|
12
|
+
*/
|
|
13
|
+
export default class Minimap extends Plugin {
|
|
14
|
+
/**
|
|
15
|
+
* @inheritDoc
|
|
16
|
+
*/
|
|
17
|
+
static get pluginName(): "Minimap";
|
|
18
|
+
/**
|
|
19
|
+
* The reference to the view of the minimap.
|
|
20
|
+
*/
|
|
21
|
+
private _minimapView;
|
|
22
|
+
/**
|
|
23
|
+
* The DOM element closest to the editable element of the editor as returned
|
|
24
|
+
* by {@link module:ui/editorui/editorui~EditorUI#getEditableElement}.
|
|
25
|
+
*/
|
|
26
|
+
private _scrollableRootAncestor;
|
|
27
|
+
/**
|
|
28
|
+
* The DOM element closest to the editable element of the editor as returned
|
|
29
|
+
* by {@link module:ui/editorui/editorui~EditorUI#getEditableElement}.
|
|
30
|
+
*/
|
|
31
|
+
private _editingRootElement?;
|
|
32
|
+
/**
|
|
33
|
+
* @inheritDoc
|
|
34
|
+
*/
|
|
35
|
+
init(): void;
|
|
36
|
+
/**
|
|
37
|
+
* @inheritDoc
|
|
38
|
+
*/
|
|
39
|
+
destroy(): void;
|
|
40
|
+
/**
|
|
41
|
+
* Initializes the minimap view element and starts the layout synchronization
|
|
42
|
+
* on the editing view `render` event.
|
|
43
|
+
*/
|
|
44
|
+
private _onUiReady;
|
|
45
|
+
/**
|
|
46
|
+
* Initializes the minimap view and attaches listeners that make it responsive to the environment (document)
|
|
47
|
+
* but also allow the minimap to control the document (scroll position).
|
|
48
|
+
*/
|
|
49
|
+
private _initializeMinimapView;
|
|
50
|
+
/**
|
|
51
|
+
* @private
|
|
52
|
+
*/
|
|
53
|
+
private _syncMinimapToEditingRootScrollPosition;
|
|
54
|
+
}
|
package/src/minimap.js
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
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 minimap/minimap
|
|
7
|
+
*/
|
|
8
|
+
import { Plugin } from 'ckeditor5/src/core';
|
|
9
|
+
import { findClosestScrollableAncestor, global } from 'ckeditor5/src/utils';
|
|
10
|
+
import MinimapView from './minimapview';
|
|
11
|
+
import { cloneEditingViewDomRoot, getClientHeight, getDomElementRect, getPageStyles, getScrollable } from './utils';
|
|
12
|
+
// @if CK_DEBUG_MINIMAP // const RectDrawer = require( '@ckeditor/ckeditor5-utils/tests/_utils/rectdrawer' ).default;
|
|
13
|
+
import '../theme/minimap.css';
|
|
14
|
+
/**
|
|
15
|
+
* The content minimap feature.
|
|
16
|
+
*/
|
|
17
|
+
export default class Minimap extends Plugin {
|
|
18
|
+
/**
|
|
19
|
+
* @inheritDoc
|
|
20
|
+
*/
|
|
21
|
+
static get pluginName() {
|
|
22
|
+
return 'Minimap';
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* @inheritDoc
|
|
26
|
+
*/
|
|
27
|
+
init() {
|
|
28
|
+
const editor = this.editor;
|
|
29
|
+
this._minimapView = null;
|
|
30
|
+
this._scrollableRootAncestor = null;
|
|
31
|
+
this.listenTo(editor.ui, 'ready', this._onUiReady.bind(this));
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* @inheritDoc
|
|
35
|
+
*/
|
|
36
|
+
destroy() {
|
|
37
|
+
this._minimapView.destroy();
|
|
38
|
+
this._minimapView.element.remove();
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Initializes the minimap view element and starts the layout synchronization
|
|
42
|
+
* on the editing view `render` event.
|
|
43
|
+
*/
|
|
44
|
+
_onUiReady() {
|
|
45
|
+
const editor = this.editor;
|
|
46
|
+
// TODO: This will not work with the multi-root editor.
|
|
47
|
+
const editingRootElement = this._editingRootElement = editor.ui.getEditableElement();
|
|
48
|
+
this._scrollableRootAncestor = findClosestScrollableAncestor(editingRootElement);
|
|
49
|
+
// DOM root element is not yet attached to the document.
|
|
50
|
+
if (!editingRootElement.ownerDocument.body.contains(editingRootElement)) {
|
|
51
|
+
editor.ui.once('update', this._onUiReady.bind(this));
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
this._initializeMinimapView();
|
|
55
|
+
this.listenTo(editor.editing.view, 'render', () => {
|
|
56
|
+
this._syncMinimapToEditingRootScrollPosition();
|
|
57
|
+
});
|
|
58
|
+
this._syncMinimapToEditingRootScrollPosition();
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Initializes the minimap view and attaches listeners that make it responsive to the environment (document)
|
|
62
|
+
* but also allow the minimap to control the document (scroll position).
|
|
63
|
+
*/
|
|
64
|
+
_initializeMinimapView() {
|
|
65
|
+
const editor = this.editor;
|
|
66
|
+
const locale = editor.locale;
|
|
67
|
+
const useSimplePreview = editor.config.get('minimap.useSimplePreview');
|
|
68
|
+
// TODO: Throw an error if there is no `minimap` in config.
|
|
69
|
+
const minimapContainerElement = editor.config.get('minimap.container');
|
|
70
|
+
const scrollableRootAncestor = this._scrollableRootAncestor;
|
|
71
|
+
// TODO: This should be dynamic, the root width could change as the viewport scales if not fixed unit.
|
|
72
|
+
const editingRootElementWidth = getDomElementRect(this._editingRootElement).width;
|
|
73
|
+
const minimapContainerWidth = getDomElementRect(minimapContainerElement).width;
|
|
74
|
+
const minimapScaleRatio = minimapContainerWidth / editingRootElementWidth;
|
|
75
|
+
const minimapView = this._minimapView = new MinimapView({
|
|
76
|
+
locale,
|
|
77
|
+
scaleRatio: minimapScaleRatio,
|
|
78
|
+
pageStyles: getPageStyles(),
|
|
79
|
+
extraClasses: editor.config.get('minimap.extraClasses'),
|
|
80
|
+
useSimplePreview,
|
|
81
|
+
domRootClone: cloneEditingViewDomRoot(editor)
|
|
82
|
+
});
|
|
83
|
+
minimapView.render();
|
|
84
|
+
// Scrollable ancestor scroll -> minimap position update.
|
|
85
|
+
minimapView.listenTo(global.document, 'scroll', (evt, data) => {
|
|
86
|
+
if (scrollableRootAncestor === global.document.body) {
|
|
87
|
+
if (data.target !== global.document) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
else if (data.target !== scrollableRootAncestor) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
this._syncMinimapToEditingRootScrollPosition();
|
|
95
|
+
}, { useCapture: true, usePassive: true });
|
|
96
|
+
// Viewport resize -> minimap position update.
|
|
97
|
+
minimapView.listenTo(global.window, 'resize', () => {
|
|
98
|
+
this._syncMinimapToEditingRootScrollPosition();
|
|
99
|
+
});
|
|
100
|
+
// Dragging the visible content area -> document (scrollable) position update.
|
|
101
|
+
minimapView.on('drag', (evt, movementY) => {
|
|
102
|
+
let movementYPercentage;
|
|
103
|
+
if (minimapView.scrollHeight === 0) {
|
|
104
|
+
movementYPercentage = 0;
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
movementYPercentage = movementY / minimapView.scrollHeight;
|
|
108
|
+
}
|
|
109
|
+
const absoluteScrollProgress = movementYPercentage *
|
|
110
|
+
(scrollableRootAncestor.scrollHeight - getClientHeight(scrollableRootAncestor));
|
|
111
|
+
const scrollable = getScrollable(scrollableRootAncestor);
|
|
112
|
+
scrollable.scrollBy(0, Math.round(absoluteScrollProgress));
|
|
113
|
+
});
|
|
114
|
+
// Clicking the minimap -> center the document (scrollable) to the corresponding position.
|
|
115
|
+
minimapView.on('click', (evt, percentage) => {
|
|
116
|
+
const absoluteScrollProgress = percentage * scrollableRootAncestor.scrollHeight;
|
|
117
|
+
const scrollable = getScrollable(scrollableRootAncestor);
|
|
118
|
+
scrollable.scrollBy(0, Math.round(absoluteScrollProgress));
|
|
119
|
+
});
|
|
120
|
+
minimapContainerElement.appendChild(minimapView.element);
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* @private
|
|
124
|
+
*/
|
|
125
|
+
_syncMinimapToEditingRootScrollPosition() {
|
|
126
|
+
const editingRootElement = this._editingRootElement;
|
|
127
|
+
const minimapView = this._minimapView;
|
|
128
|
+
minimapView.setContentHeight(editingRootElement.offsetHeight);
|
|
129
|
+
const editingRootRect = getDomElementRect(editingRootElement);
|
|
130
|
+
const scrollableRootAncestorRect = getDomElementRect(this._scrollableRootAncestor);
|
|
131
|
+
let scrollProgress;
|
|
132
|
+
// @if CK_DEBUG_MINIMAP // RectDrawer.clear();
|
|
133
|
+
// @if CK_DEBUG_MINIMAP // RectDrawer.draw( scrollableRootAncestorRect, { outlineColor: 'red' }, 'scrollableRootAncestor' );
|
|
134
|
+
// @if CK_DEBUG_MINIMAP // RectDrawer.draw( editingRootRect, { outlineColor: 'green' }, 'editingRoot' );
|
|
135
|
+
// The root is completely visible in the scrollable ancestor.
|
|
136
|
+
if (scrollableRootAncestorRect.contains(editingRootRect)) {
|
|
137
|
+
scrollProgress = 0;
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
if (editingRootRect.top > scrollableRootAncestorRect.top) {
|
|
141
|
+
scrollProgress = 0;
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
scrollProgress = (editingRootRect.top - scrollableRootAncestorRect.top) /
|
|
145
|
+
(scrollableRootAncestorRect.height - editingRootRect.height);
|
|
146
|
+
scrollProgress = Math.max(0, Math.min(scrollProgress, 1));
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
// The intersection helps to change the tracker height when there is a lot of padding around the root.
|
|
150
|
+
// Note: It is **essential** that the height is set first because the progress depends on the correct tracker height.
|
|
151
|
+
minimapView.setPositionTrackerHeight(scrollableRootAncestorRect.getIntersection(editingRootRect).height);
|
|
152
|
+
minimapView.setScrollProgress(scrollProgress);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
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 minimap/minimapconfig
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* The configuration of the {@link module:minimap/minimap~Minimap} feature.
|
|
10
|
+
*
|
|
11
|
+
* ```ts
|
|
12
|
+
* ClassicEditor
|
|
13
|
+
* .create( {
|
|
14
|
+
* minimap: ... // Minimap feature config.
|
|
15
|
+
* } )
|
|
16
|
+
* .then( ... )
|
|
17
|
+
* .catch( ... );
|
|
18
|
+
* ```
|
|
19
|
+
*
|
|
20
|
+
* See {@link module:core/editor/editorconfig~EditorConfig all editor options}.
|
|
21
|
+
*/
|
|
22
|
+
export interface MinimapConfig {
|
|
23
|
+
/**
|
|
24
|
+
* The DOM element container for the minimap.
|
|
25
|
+
*
|
|
26
|
+
* **Note**: The container must have a fixed `width` and `overflow: hidden` for the minimap to work correctly.
|
|
27
|
+
*/
|
|
28
|
+
container: HTMLElement;
|
|
29
|
+
/**
|
|
30
|
+
* When set to `true`, the minimap will render content as simple boxes instead of replicating the look of the content (default).
|
|
31
|
+
*/
|
|
32
|
+
useSimplePreview?: boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Extra CSS class (or classes) that will be set internally on the `<body>` element of the `<iframe>` enclosing the minimap.
|
|
35
|
+
*
|
|
36
|
+
* By default, the minimap feature will attempt to clone all website styles and re-apply them in the `<iframe>` for the best accuracy.
|
|
37
|
+
* However, this may not work if the content of your editor inherits the styles from parent containers, resulting in inconsistent
|
|
38
|
+
* look and imprecise scrolling of the minimap.
|
|
39
|
+
*
|
|
40
|
+
* This optional configuration can address these issues by ensuring the same CSS rules apply to the content of the minimap
|
|
41
|
+
* and the original content of the editor.
|
|
42
|
+
*
|
|
43
|
+
* For instance, consider the following DOM structure:
|
|
44
|
+
*
|
|
45
|
+
* ```html
|
|
46
|
+
* <div class="website">
|
|
47
|
+
* <!-- ... -->
|
|
48
|
+
* <div class="styled-container">
|
|
49
|
+
* <!-- ... -->
|
|
50
|
+
* <div id="editor">
|
|
51
|
+
* <!-- content of the editor -->
|
|
52
|
+
* </div>
|
|
53
|
+
* </div>
|
|
54
|
+
* <!-- ... -->
|
|
55
|
+
* </div>
|
|
56
|
+
* ```
|
|
57
|
+
*
|
|
58
|
+
* and the following CSS styles:
|
|
59
|
+
*
|
|
60
|
+
* ```css
|
|
61
|
+
* .website p {
|
|
62
|
+
* font-size: 13px;
|
|
63
|
+
* }
|
|
64
|
+
*
|
|
65
|
+
* .styled-container p {
|
|
66
|
+
* color: #ccc;
|
|
67
|
+
* }
|
|
68
|
+
* ```
|
|
69
|
+
*
|
|
70
|
+
* To maintain the consistency of styling (`font-size` and `color` of paragraphs), you will need to pass the CSS class names
|
|
71
|
+
* of these containers:
|
|
72
|
+
*
|
|
73
|
+
* ```ts
|
|
74
|
+
* ClassicEditor
|
|
75
|
+
* .create( document.getElementById( 'editor' ), {
|
|
76
|
+
* minimap: {
|
|
77
|
+
* extraClasses: 'website styled-container'
|
|
78
|
+
* }
|
|
79
|
+
* } )
|
|
80
|
+
* .then( ... )
|
|
81
|
+
* .catch( ... );
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
84
|
+
extraClasses?: string;
|
|
85
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
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 minimap/minimapiframeview
|
|
7
|
+
*/
|
|
8
|
+
import { IframeView } from 'ckeditor5/src/ui';
|
|
9
|
+
import { type Locale } from 'ckeditor5/src/utils';
|
|
10
|
+
import type { MinimapViewOptions } from './minimapview';
|
|
11
|
+
/**
|
|
12
|
+
* The internal `<iframe>` view that hosts the minimap content.
|
|
13
|
+
*
|
|
14
|
+
* @internal
|
|
15
|
+
*/
|
|
16
|
+
export default class MinimapIframeView extends IframeView {
|
|
17
|
+
/**
|
|
18
|
+
* The CSS `top` used to scroll the minimap.
|
|
19
|
+
*
|
|
20
|
+
* @readonly
|
|
21
|
+
*/
|
|
22
|
+
top: number;
|
|
23
|
+
/**
|
|
24
|
+
* The CSS `height` of the iframe.
|
|
25
|
+
*
|
|
26
|
+
* @readonly
|
|
27
|
+
*/
|
|
28
|
+
height: number;
|
|
29
|
+
/**
|
|
30
|
+
* Cached view constructor options for re-use in other methods.
|
|
31
|
+
*/
|
|
32
|
+
private readonly _options;
|
|
33
|
+
/**
|
|
34
|
+
* Creates an instance of the internal minimap iframe.
|
|
35
|
+
*/
|
|
36
|
+
constructor(locale: Locale, options: MinimapViewOptions);
|
|
37
|
+
/**
|
|
38
|
+
* @inheritDoc
|
|
39
|
+
*/
|
|
40
|
+
render(): Promise<unknown>;
|
|
41
|
+
/**
|
|
42
|
+
* Sets the new height of the iframe.
|
|
43
|
+
*/
|
|
44
|
+
setHeight(newHeight: number): void;
|
|
45
|
+
/**
|
|
46
|
+
* Sets the top offset of the iframe to move it around vertically.
|
|
47
|
+
*/
|
|
48
|
+
setTopOffset(newOffset: number): void;
|
|
49
|
+
/**
|
|
50
|
+
* Sets the internal structure of the `<iframe>` readying it to display the
|
|
51
|
+
* minimap element.
|
|
52
|
+
*/
|
|
53
|
+
private _prepareDocument;
|
|
54
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
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 minimap/minimapiframeview
|
|
7
|
+
*/
|
|
8
|
+
import { IframeView } from 'ckeditor5/src/ui';
|
|
9
|
+
import { toUnit } from 'ckeditor5/src/utils';
|
|
10
|
+
const toPx = toUnit('px');
|
|
11
|
+
/**
|
|
12
|
+
* The internal `<iframe>` view that hosts the minimap content.
|
|
13
|
+
*
|
|
14
|
+
* @internal
|
|
15
|
+
*/
|
|
16
|
+
export default class MinimapIframeView extends IframeView {
|
|
17
|
+
/**
|
|
18
|
+
* Creates an instance of the internal minimap iframe.
|
|
19
|
+
*/
|
|
20
|
+
constructor(locale, options) {
|
|
21
|
+
super(locale);
|
|
22
|
+
const bind = this.bindTemplate;
|
|
23
|
+
this.set('top', 0);
|
|
24
|
+
this.set('height', 0);
|
|
25
|
+
this._options = options;
|
|
26
|
+
this.extendTemplate({
|
|
27
|
+
attributes: {
|
|
28
|
+
class: [
|
|
29
|
+
'ck-minimap__iframe'
|
|
30
|
+
],
|
|
31
|
+
style: {
|
|
32
|
+
top: bind.to('top', top => toPx(top)),
|
|
33
|
+
height: bind.to('height', height => toPx(height))
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* @inheritDoc
|
|
40
|
+
*/
|
|
41
|
+
render() {
|
|
42
|
+
return super.render().then(() => {
|
|
43
|
+
this._prepareDocument();
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Sets the new height of the iframe.
|
|
48
|
+
*/
|
|
49
|
+
setHeight(newHeight) {
|
|
50
|
+
this.height = newHeight;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Sets the top offset of the iframe to move it around vertically.
|
|
54
|
+
*/
|
|
55
|
+
setTopOffset(newOffset) {
|
|
56
|
+
this.top = newOffset;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Sets the internal structure of the `<iframe>` readying it to display the
|
|
60
|
+
* minimap element.
|
|
61
|
+
*/
|
|
62
|
+
_prepareDocument() {
|
|
63
|
+
const iframeDocument = this.element.contentWindow.document;
|
|
64
|
+
const domRootClone = iframeDocument.adoptNode(this._options.domRootClone);
|
|
65
|
+
const boxStyles = this._options.useSimplePreview ? `
|
|
66
|
+
.ck.ck-editor__editable_inline img {
|
|
67
|
+
filter: contrast( 0 );
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
p, li, a, figcaption, span {
|
|
71
|
+
background: hsl(0, 0%, 80%) !important;
|
|
72
|
+
color: hsl(0, 0%, 80%) !important;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
h1, h2, h3, h4 {
|
|
76
|
+
background: hsl(0, 0%, 60%) !important;
|
|
77
|
+
color: hsl(0, 0%, 60%) !important;
|
|
78
|
+
}
|
|
79
|
+
` : '';
|
|
80
|
+
const pageStyles = this._options.pageStyles.map(definition => {
|
|
81
|
+
if (typeof definition === 'string') {
|
|
82
|
+
return `<style>${definition}</style>`;
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
return `<link rel="stylesheet" type="text/css" href="${definition.href}">`;
|
|
86
|
+
}
|
|
87
|
+
}).join('\n');
|
|
88
|
+
const html = `<!DOCTYPE html><html lang="en">
|
|
89
|
+
<head>
|
|
90
|
+
<meta charset="utf-8">
|
|
91
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
92
|
+
${pageStyles}
|
|
93
|
+
<style>
|
|
94
|
+
html, body {
|
|
95
|
+
margin: 0 !important;
|
|
96
|
+
padding: 0 !important;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
html {
|
|
100
|
+
overflow: hidden;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
body {
|
|
104
|
+
transform: scale( ${this._options.scaleRatio} );
|
|
105
|
+
transform-origin: 0 0;
|
|
106
|
+
overflow: visible;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.ck.ck-editor__editable_inline {
|
|
110
|
+
margin: 0 !important;
|
|
111
|
+
border-color: transparent !important;
|
|
112
|
+
outline-color: transparent !important;
|
|
113
|
+
box-shadow: none !important;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.ck.ck-content {
|
|
117
|
+
background: white;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
${boxStyles}
|
|
121
|
+
</style>
|
|
122
|
+
</head>
|
|
123
|
+
<body class="${this._options.extraClasses || ''}"></body>
|
|
124
|
+
</html>`;
|
|
125
|
+
iframeDocument.open();
|
|
126
|
+
iframeDocument.write(html);
|
|
127
|
+
iframeDocument.close();
|
|
128
|
+
iframeDocument.body.appendChild(domRootClone);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
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 minimap/minimappositiontrackerview
|
|
7
|
+
*/
|
|
8
|
+
import { View } from 'ckeditor5/src/ui';
|
|
9
|
+
import { type Locale } from 'ckeditor5/src/utils';
|
|
10
|
+
/**
|
|
11
|
+
* The position tracker visualizing the visible subset of the content. Displayed over the minimap.
|
|
12
|
+
*
|
|
13
|
+
* @internal
|
|
14
|
+
*/
|
|
15
|
+
export default class MinimapPositionTrackerView extends View {
|
|
16
|
+
/**
|
|
17
|
+
* The CSS `height` of the tracker visualizing the subset of the content visible to the user.
|
|
18
|
+
*
|
|
19
|
+
* @readonly
|
|
20
|
+
*/
|
|
21
|
+
height: number;
|
|
22
|
+
/**
|
|
23
|
+
* The CSS `top` of the tracker, used to move it vertically over the minimap.
|
|
24
|
+
*
|
|
25
|
+
* @readonly
|
|
26
|
+
*/
|
|
27
|
+
top: number;
|
|
28
|
+
/**
|
|
29
|
+
* The scroll progress (in %) displayed over the tracker when being dragged by the user.
|
|
30
|
+
*
|
|
31
|
+
* @readonly
|
|
32
|
+
*/
|
|
33
|
+
scrollProgress: number;
|
|
34
|
+
/**
|
|
35
|
+
* Indicates whether the tracker is being dragged by the user (e.g. using the mouse).
|
|
36
|
+
*
|
|
37
|
+
* @internal
|
|
38
|
+
* @readonly
|
|
39
|
+
*/
|
|
40
|
+
_isDragging: boolean;
|
|
41
|
+
constructor(locale: Locale);
|
|
42
|
+
/**
|
|
43
|
+
* @inheritDoc
|
|
44
|
+
*/
|
|
45
|
+
render(): void;
|
|
46
|
+
/**
|
|
47
|
+
* Sets the new height of the tracker to visualize the subset of the content visible to the user.
|
|
48
|
+
*/
|
|
49
|
+
setHeight(newHeight: number): void;
|
|
50
|
+
/**
|
|
51
|
+
* Sets the top offset of the tracker to move it around vertically.
|
|
52
|
+
*/
|
|
53
|
+
setTopOffset(newOffset: number): void;
|
|
54
|
+
/**
|
|
55
|
+
* Sets the scroll progress (in %) to inform the user using a label when the tracker is being dragged.
|
|
56
|
+
*/
|
|
57
|
+
setScrollProgress(newProgress: number): void;
|
|
58
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
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 minimap/minimappositiontrackerview
|
|
7
|
+
*/
|
|
8
|
+
import { View } from 'ckeditor5/src/ui';
|
|
9
|
+
import { toUnit, global } from 'ckeditor5/src/utils';
|
|
10
|
+
const toPx = toUnit('px');
|
|
11
|
+
/**
|
|
12
|
+
* The position tracker visualizing the visible subset of the content. Displayed over the minimap.
|
|
13
|
+
*
|
|
14
|
+
* @internal
|
|
15
|
+
*/
|
|
16
|
+
export default class MinimapPositionTrackerView extends View {
|
|
17
|
+
constructor(locale) {
|
|
18
|
+
super(locale);
|
|
19
|
+
const bind = this.bindTemplate;
|
|
20
|
+
this.set('height', 0);
|
|
21
|
+
this.set('top', 0);
|
|
22
|
+
this.set('scrollProgress', 0);
|
|
23
|
+
this.set('_isDragging', false);
|
|
24
|
+
this.setTemplate({
|
|
25
|
+
tag: 'div',
|
|
26
|
+
attributes: {
|
|
27
|
+
class: [
|
|
28
|
+
'ck',
|
|
29
|
+
'ck-minimap__position-tracker',
|
|
30
|
+
bind.if('_isDragging', 'ck-minimap__position-tracker_dragging')
|
|
31
|
+
],
|
|
32
|
+
style: {
|
|
33
|
+
top: bind.to('top', top => toPx(top)),
|
|
34
|
+
height: bind.to('height', height => toPx(height))
|
|
35
|
+
},
|
|
36
|
+
'data-progress': bind.to('scrollProgress')
|
|
37
|
+
},
|
|
38
|
+
on: {
|
|
39
|
+
mousedown: bind.to(() => {
|
|
40
|
+
this._isDragging = true;
|
|
41
|
+
})
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* @inheritDoc
|
|
47
|
+
*/
|
|
48
|
+
render() {
|
|
49
|
+
super.render();
|
|
50
|
+
this.listenTo(global.document, 'mousemove', (evt, data) => {
|
|
51
|
+
if (!this._isDragging) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
this.fire('drag', data.movementY);
|
|
55
|
+
}, { useCapture: true });
|
|
56
|
+
this.listenTo(global.document, 'mouseup', () => {
|
|
57
|
+
this._isDragging = false;
|
|
58
|
+
}, { useCapture: true });
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Sets the new height of the tracker to visualize the subset of the content visible to the user.
|
|
62
|
+
*/
|
|
63
|
+
setHeight(newHeight) {
|
|
64
|
+
this.height = newHeight;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Sets the top offset of the tracker to move it around vertically.
|
|
68
|
+
*/
|
|
69
|
+
setTopOffset(newOffset) {
|
|
70
|
+
this.top = newOffset;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Sets the scroll progress (in %) to inform the user using a label when the tracker is being dragged.
|
|
74
|
+
*/
|
|
75
|
+
setScrollProgress(newProgress) {
|
|
76
|
+
this.scrollProgress = newProgress;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
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 minimap/minimapview
|
|
7
|
+
*/
|
|
8
|
+
import { View } from 'ckeditor5/src/ui';
|
|
9
|
+
import { type Locale } from 'ckeditor5/src/utils';
|
|
10
|
+
export type MinimapViewOptions = {
|
|
11
|
+
domRootClone: HTMLElement;
|
|
12
|
+
pageStyles: Array<string | {
|
|
13
|
+
href: string;
|
|
14
|
+
}>;
|
|
15
|
+
scaleRatio: number;
|
|
16
|
+
useSimplePreview?: boolean;
|
|
17
|
+
extraClasses?: string;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* The main view of the minimap. It renders the original content but scaled down with a tracker element
|
|
21
|
+
* visualizing the subset of the content visible to the user and allowing interactions (scrolling, dragging).
|
|
22
|
+
*
|
|
23
|
+
* @internal
|
|
24
|
+
*/
|
|
25
|
+
export default class MinimapView extends View {
|
|
26
|
+
/**
|
|
27
|
+
* An instance of the tracker view displayed over the minimap.
|
|
28
|
+
*/
|
|
29
|
+
private readonly _positionTrackerView;
|
|
30
|
+
/**
|
|
31
|
+
* The scale ratio of the minimap relative to the original editing DOM root with the content.
|
|
32
|
+
*/
|
|
33
|
+
private readonly _scaleRatio;
|
|
34
|
+
/**
|
|
35
|
+
* An instance of the iframe view that hosts the minimap.
|
|
36
|
+
*/
|
|
37
|
+
private readonly _minimapIframeView;
|
|
38
|
+
/**
|
|
39
|
+
* Creates an instance of the minimap view.
|
|
40
|
+
*/
|
|
41
|
+
constructor({ locale, scaleRatio, pageStyles, extraClasses, useSimplePreview, domRootClone }: {
|
|
42
|
+
locale: Locale;
|
|
43
|
+
} & MinimapViewOptions);
|
|
44
|
+
/**
|
|
45
|
+
* @inheritDoc
|
|
46
|
+
*/
|
|
47
|
+
destroy(): void;
|
|
48
|
+
/**
|
|
49
|
+
* Returns the DOM {@link module:utils/dom/rect~Rect} height of the minimap.
|
|
50
|
+
*/
|
|
51
|
+
get height(): number;
|
|
52
|
+
/**
|
|
53
|
+
* Returns the number of available space (pixels) the position tracker (visible subset of the content) can use to scroll vertically.
|
|
54
|
+
*/
|
|
55
|
+
get scrollHeight(): number;
|
|
56
|
+
/**
|
|
57
|
+
* @inheritDoc
|
|
58
|
+
*/
|
|
59
|
+
render(): void;
|
|
60
|
+
/**
|
|
61
|
+
* Sets the new height of the minimap (in px) to respond to the changes in the original editing DOM root.
|
|
62
|
+
*
|
|
63
|
+
* **Note**:The provided value should be the `offsetHeight` of the original editing DOM root.
|
|
64
|
+
*/
|
|
65
|
+
setContentHeight(newHeight: number): void;
|
|
66
|
+
/**
|
|
67
|
+
* Sets the minimap scroll progress.
|
|
68
|
+
*
|
|
69
|
+
* The minimap scroll progress is linked to the original editing DOM root and its scrollable container (ancestor).
|
|
70
|
+
* Changing the progress will alter the vertical position of the minimap (and its position tracker) and give the user an accurate
|
|
71
|
+
* overview of the visible document.
|
|
72
|
+
*
|
|
73
|
+
* **Note**: The value should be between 0 and 1. 0 when the DOM root has not been scrolled, 1 when the
|
|
74
|
+
* scrolling has reached the end.
|
|
75
|
+
*/
|
|
76
|
+
setScrollProgress(newScrollProgress: number): void;
|
|
77
|
+
/**
|
|
78
|
+
* Sets the new height of the tracker (in px) to visualize the subset of the content visible to the user.
|
|
79
|
+
*/
|
|
80
|
+
setPositionTrackerHeight(trackerHeight: number): void;
|
|
81
|
+
/**
|
|
82
|
+
* @param data DOM event data
|
|
83
|
+
*/
|
|
84
|
+
private _handleMinimapClick;
|
|
85
|
+
/**
|
|
86
|
+
* @param data DOM event data
|
|
87
|
+
*/
|
|
88
|
+
private _handleMinimapMouseWheel;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Fired when the minimap view is clicked.
|
|
92
|
+
*
|
|
93
|
+
* @eventName ~MinimapView#click
|
|
94
|
+
* @param percentage The number between 0 and 1 representing a place in the minimap (its height) that was clicked.
|
|
95
|
+
*/
|
|
96
|
+
export type MinimapClickEvent = {
|
|
97
|
+
name: 'click';
|
|
98
|
+
args: [percentage: number];
|
|
99
|
+
};
|
|
100
|
+
/**
|
|
101
|
+
* Fired when the position tracker is dragged or the minimap is scrolled via mouse wheel.
|
|
102
|
+
*
|
|
103
|
+
* @eventName ~MinimapView#drag
|
|
104
|
+
* @param movementY The vertical movement of the minimap as a result of dragging or scrolling.
|
|
105
|
+
*/
|
|
106
|
+
export type MinimapDragEvent = {
|
|
107
|
+
name: 'drag';
|
|
108
|
+
args: [movementY: number];
|
|
109
|
+
};
|
|
@@ -0,0 +1,137 @@
|
|
|
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 minimap/minimapview
|
|
7
|
+
*/
|
|
8
|
+
import { View } from 'ckeditor5/src/ui';
|
|
9
|
+
import { Rect } from 'ckeditor5/src/utils';
|
|
10
|
+
import MinimapIframeView from './minimapiframeview';
|
|
11
|
+
import MinimapPositionTrackerView from './minimappositiontrackerview';
|
|
12
|
+
/**
|
|
13
|
+
* The main view of the minimap. It renders the original content but scaled down with a tracker element
|
|
14
|
+
* visualizing the subset of the content visible to the user and allowing interactions (scrolling, dragging).
|
|
15
|
+
*
|
|
16
|
+
* @internal
|
|
17
|
+
*/
|
|
18
|
+
export default class MinimapView extends View {
|
|
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
|
+
}
|
|
137
|
+
}
|
package/src/utils.d.ts
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
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 minimap/utils
|
|
7
|
+
*/
|
|
8
|
+
import { Rect } from 'ckeditor5/src/utils';
|
|
9
|
+
import type { Editor } from 'ckeditor5/src/core';
|
|
10
|
+
/**
|
|
11
|
+
* Clones the editing view DOM root by using a dedicated pair of {@link module:engine/view/renderer~Renderer} and
|
|
12
|
+
* {@link module:engine/view/domconverter~DomConverter}. The DOM root clone updates incrementally to stay in sync with the
|
|
13
|
+
* source root.
|
|
14
|
+
*
|
|
15
|
+
* @internal
|
|
16
|
+
* @param editor The editor instance the original editing root belongs to.
|
|
17
|
+
* @param rootName The name of the root to clone.
|
|
18
|
+
* @returns The editing root DOM clone element.
|
|
19
|
+
*/
|
|
20
|
+
export declare function cloneEditingViewDomRoot(editor: Editor, rootName?: string): HTMLElement;
|
|
21
|
+
/**
|
|
22
|
+
* Harvests all web page styles, for instance, to allow re-using them in an `<iframe>` preserving the look of the content.
|
|
23
|
+
*
|
|
24
|
+
* The returned data format is as follows:
|
|
25
|
+
*
|
|
26
|
+
* ```ts
|
|
27
|
+
* [
|
|
28
|
+
* 'p { color: red; ... } h2 { font-size: 2em; ... } ...',
|
|
29
|
+
* '.spacing { padding: 1em; ... }; ...',
|
|
30
|
+
* '...',
|
|
31
|
+
* { href: 'http://link.to.external.stylesheet' },
|
|
32
|
+
* { href: '...' }
|
|
33
|
+
* ]
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* **Note**: For stylesheets with `href` different than window origin, an object is returned because
|
|
37
|
+
* accessing rules of these styles may cause CORS errors (depending on the configuration of the web page).
|
|
38
|
+
*
|
|
39
|
+
* @internal
|
|
40
|
+
*/
|
|
41
|
+
export declare function getPageStyles(): Array<string | {
|
|
42
|
+
href: string;
|
|
43
|
+
}>;
|
|
44
|
+
/**
|
|
45
|
+
* Gets dimensions rectangle according to passed DOM element. Returns whole window's size for `body` element.
|
|
46
|
+
*
|
|
47
|
+
* @internal
|
|
48
|
+
*/
|
|
49
|
+
export declare function getDomElementRect(domElement: HTMLElement): Rect;
|
|
50
|
+
/**
|
|
51
|
+
* Gets client height according to passed DOM element. Returns window's height for `body` element.
|
|
52
|
+
*
|
|
53
|
+
* @internal
|
|
54
|
+
*/
|
|
55
|
+
export declare function getClientHeight(domElement: HTMLElement): number;
|
|
56
|
+
/**
|
|
57
|
+
* Returns the DOM element itself if it's not a `body` element, whole window otherwise.
|
|
58
|
+
*
|
|
59
|
+
* @internal
|
|
60
|
+
*/
|
|
61
|
+
export declare function getScrollable(domElement: HTMLElement): Window | HTMLElement;
|
package/src/utils.js
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
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
|
+
/* global CSSMediaRule */
|
|
6
|
+
/**
|
|
7
|
+
* @module minimap/utils
|
|
8
|
+
*/
|
|
9
|
+
import { Rect, global } from 'ckeditor5/src/utils';
|
|
10
|
+
import { DomConverter, Renderer } from 'ckeditor5/src/engine';
|
|
11
|
+
/**
|
|
12
|
+
* Clones the editing view DOM root by using a dedicated pair of {@link module:engine/view/renderer~Renderer} and
|
|
13
|
+
* {@link module:engine/view/domconverter~DomConverter}. The DOM root clone updates incrementally to stay in sync with the
|
|
14
|
+
* source root.
|
|
15
|
+
*
|
|
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.
|
|
20
|
+
*/
|
|
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;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Harvests all web page styles, for instance, to allow re-using them in an `<iframe>` preserving the look of the content.
|
|
43
|
+
*
|
|
44
|
+
* The returned data format is as follows:
|
|
45
|
+
*
|
|
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
|
+
* ```
|
|
55
|
+
*
|
|
56
|
+
* **Note**: For stylesheets with `href` different than window origin, an object is returned because
|
|
57
|
+
* accessing rules of these styles may cause CORS errors (depending on the configuration of the web page).
|
|
58
|
+
*
|
|
59
|
+
* @internal
|
|
60
|
+
*/
|
|
61
|
+
export function getPageStyles() {
|
|
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
|
+
});
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Gets dimensions rectangle according to passed DOM element. Returns whole window's size for `body` element.
|
|
76
|
+
*
|
|
77
|
+
* @internal
|
|
78
|
+
*/
|
|
79
|
+
export function getDomElementRect(domElement) {
|
|
80
|
+
return new Rect(domElement === global.document.body ? global.window : domElement);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Gets client height according to passed DOM element. Returns window's height for `body` element.
|
|
84
|
+
*
|
|
85
|
+
* @internal
|
|
86
|
+
*/
|
|
87
|
+
export function getClientHeight(domElement) {
|
|
88
|
+
return domElement === global.document.body ? global.window.innerHeight : domElement.clientHeight;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Returns the DOM element itself if it's not a `body` element, whole window otherwise.
|
|
92
|
+
*
|
|
93
|
+
* @internal
|
|
94
|
+
*/
|
|
95
|
+
export function getScrollable(domElement) {
|
|
96
|
+
return domElement === global.document.body ? global.window : domElement;
|
|
97
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* 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
|
+
:root {
|
|
7
|
+
--ck-color-minimap-tracker-background: 208, 0%, 51%;
|
|
8
|
+
--ck-color-minimap-iframe-outline: hsl(0deg 0% 75%);
|
|
9
|
+
--ck-color-minimap-iframe-shadow: hsl(0deg 0% 0% / 11%);
|
|
10
|
+
--ck-color-minimap-progress-background: hsl(0,0%,40%);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.ck.ck-minimap {
|
|
14
|
+
position: absolute;
|
|
15
|
+
user-select: none;
|
|
16
|
+
background: var(--ck-color-base-background);
|
|
17
|
+
|
|
18
|
+
&,
|
|
19
|
+
& iframe {
|
|
20
|
+
width: 100%;
|
|
21
|
+
height: 100%;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
& iframe {
|
|
25
|
+
border: 0;
|
|
26
|
+
pointer-events: none;
|
|
27
|
+
position: relative;
|
|
28
|
+
outline: 1px solid var(--ck-color-minimap-iframe-outline);
|
|
29
|
+
box-shadow: 0 2px 5px var(--ck-color-minimap-iframe-shadow);
|
|
30
|
+
margin: 0;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
& .ck.ck-minimap__position-tracker {
|
|
34
|
+
position: absolute;
|
|
35
|
+
width: 100%;
|
|
36
|
+
top: 0;
|
|
37
|
+
background: hsla( var(--ck-color-minimap-tracker-background), .2 );
|
|
38
|
+
z-index: 1;
|
|
39
|
+
transition: background 100ms ease-in-out;
|
|
40
|
+
|
|
41
|
+
&:hover {
|
|
42
|
+
background:hsla( var(--ck-color-minimap-tracker-background), .3 );
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
&.ck-minimap__position-tracker_dragging,
|
|
46
|
+
&.ck-minimap__position-tracker_dragging:hover {
|
|
47
|
+
background:hsla( var(--ck-color-minimap-tracker-background), .4 );
|
|
48
|
+
|
|
49
|
+
&::after {
|
|
50
|
+
opacity: 1;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
&::after {
|
|
55
|
+
content: attr(data-progress) "%";
|
|
56
|
+
position: absolute;
|
|
57
|
+
top: 5px;
|
|
58
|
+
right: 5px;
|
|
59
|
+
background: var(--ck-color-minimap-progress-background);
|
|
60
|
+
color: var(--ck-color-base-background);
|
|
61
|
+
border: 1px solid var(--ck-color-base-background);
|
|
62
|
+
padding: 2px 4px;
|
|
63
|
+
font-size: 10px;
|
|
64
|
+
border-radius: 3px;
|
|
65
|
+
opacity: 0;
|
|
66
|
+
transition: opacity 100ms ease-in-out;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|