mirror of
https://github.com/zadam/trilium.git
synced 2025-03-01 14:22:32 +01:00
719 lines
36 KiB
JavaScript
719 lines
36 KiB
JavaScript
import BasicWidget from "./basic_widget.js";
|
|
import contextMenu from "../menus/context_menu.js";
|
|
import utils from "../services/utils.js";
|
|
import keyboardActionService from "../services/keyboard_actions.js";
|
|
import appContext from "../components/app_context.js";
|
|
import froca from "../services/froca.js";
|
|
import attributeService from "../services/attributes.js";
|
|
|
|
/*!
|
|
* Draggabilly v2.3.0
|
|
* Make that shiz draggable
|
|
* https://draggabilly.desandro.com
|
|
* MIT license
|
|
*/
|
|
(function(e,i){e.jQueryBridget=i(e,e.jQuery)})(window,function t(e,r){"use strict";var s=Array.prototype.slice;var i=e.console;var f=typeof i=="undefined"?function(){}:function(t){i.error(t)};function n(h,o,d){d=d||r||e.jQuery;if(!d){return}if(!o.prototype.option){o.prototype.option=function(t){if(!d.isPlainObject(t)){return}this.options=d.extend(true,this.options,t)}}d.fn[h]=function(t){if(typeof t=="string"){var e=s.call(arguments,1);return i(this,t,e)}n(this,t);return this};function i(t,r,s){var a;var u=`$().${h}("${r}")`;t.each(function(t, e){var i=d.data(e,h);if(!i){f(`${h} not initialized. Cannot call methods, i.e. ${u}`);return}var n=i[r];if(!n||r.charAt(0)=="_"){f(`${u} is not a valid method`);return}var o=n.apply(i,s);a=a===undefined?o:a});return a!==undefined?a:t}function n(t, n){t.each(function(t, e){var i=d.data(e,h);if(i){i.option(n);i._init()}else{i=new o(e,n);d.data(e,h,i)}})}a(d)}function a(t){if(!t||t&&t.bridget){return}t.bridget=n}a(r||e.jQuery);return n});
|
|
(function(t,e){"use strict";t.getSize=e()})(window,function t(){"use strict";function m(t){var e=parseFloat(t);var i=t.indexOf("%")==-1&&!isNaN(e);return i&&e}function e(){}var i=typeof console=="undefined"?e:function(t){console.error(t)};var y=["paddingLeft","paddingRight","paddingTop","paddingBottom","marginLeft","marginRight","marginTop","marginBottom","borderLeftWidth","borderRightWidth","borderTopWidth","borderBottomWidth"];var b=y.length;function E(){var t={width:0,height:0,innerWidth:0,innerHeight:0,outerWidth:0,outerHeight:0};for(var e=0;e<b;e++){var i=y[e];t[i]=0}return t}function _(t){var e=getComputedStyle(t);if(!e){i(`Style returned ${e}. Are you running this code in a hidden iframe on Firefox? See http://bit.ly/getsizebug1`)}return e}var n=false;var x;function P(){if(n){return}n=true;var t=document.createElement("div");t.style.width="200px";t.style.padding="1px 2px 3px 4px";t.style.borderStyle="solid";t.style.borderWidth="1px 2px 3px 4px";t.style.boxSizing="border-box";var e=document.body||document.documentElement;e.appendChild(t);var i=_(t);o.isBoxSizeOuter=x=m(i.width)==200;e.removeChild(t)}function o(t){P();if(typeof t=="string"){t=document.querySelector(t)}if(!t||typeof t!="object"||!t.nodeType){return}var e=_(t);if(e.display=="none"){return E()}var i={};i.width=t.offsetWidth;i.height=t.offsetHeight;var n=i.isBorderBox=e.boxSizing=="border-box";for(var o=0; o<b; o++){var r=y[o];var s=e[r];var a=parseFloat(s);i[r]=!isNaN(a)?a:0}var u=i.paddingLeft+i.paddingRight;var h=i.paddingTop+i.paddingBottom;var d=i.marginLeft+i.marginRight;var f=i.marginTop+i.marginBottom;var p=i.borderLeftWidth+i.borderRightWidth;var c=i.borderTopWidth+i.borderBottomWidth;var v=n&&x;var l=m(e.width);if(l!==false){i.width=l+(v?0:u+p)}var g=m(e.height);if(g!==false){i.height=g+(v?0:h+c)}i.innerWidth=i.width-(u+p);i.innerHeight=i.height-(h+c);i.outerWidth=i.width+d;i.outerHeight=i.height+f;return i}return o});(function(t, e){t.EvEmitter=e()})(typeof window!="undefined"?window:this,function(){function t(){}var e=t.prototype;e.on=function(t, e){if(!t||!e){return}var i=this._events=this._events||{};var n=i[t]=i[t]||[];if(n.indexOf(e)==-1){n.push(e)}return this};e.once=function(t, e){if(!t||!e){return}this.on(t,e);var i=this._onceEvents=this._onceEvents||{};var n=i[t]=i[t]||{};n[e]=true;return this};e.off=function(t, e){var i=this._events&&this._events[t];if(!i||!i.length){return}var n=i.indexOf(e);if(n!=-1){i.splice(n,1)}return this};e.emitEvent=function(t, e){var i=this._events&&this._events[t];if(!i||!i.length){return}i=i.slice(0);e=e||[];var n=this._onceEvents&&this._onceEvents[t];for(var o=0; o<i.length; o++){var r=i[o];var s=n&&n[r];if(s){this.off(t,r);delete n[r]}r.apply(this,e)}return this};e.allOff=function(){delete this._events;delete this._onceEvents};return t});
|
|
(function(e,i){e.Unipointer=i(e,e.EvEmitter)})(window,function t(o,e){function i(){}function n(){}var r=n.prototype=Object.create(e.prototype);r.bindStartEvent=function(t){this._bindStartEvent(t,true)};r.unbindStartEvent=function(t){this._bindStartEvent(t,false)};r._bindStartEvent=function(t,e){e=e===undefined?true:e;var i=e?"addEventListener":"removeEventListener";var n="mousedown";if(o.PointerEvent){n="pointerdown"}else if("ontouchstart"in o){n="touchstart"}t[i](n,this)};r.handleEvent=function(t){var e=`on${t.type}`;if(this[e]){this[e](t)}};r.getTouch=function(t){for(var e=0; e<t.length; e++){var i=t[e];if(i.identifier==this.pointerIdentifier){return i}}};r.onmousedown=function(t){var e=t.button;if(e&&(e!==0&&e!==1)){return}this._pointerDown(t,t)};r.ontouchstart=function(t){this._pointerDown(t,t.changedTouches[0])};r.onpointerdown=function(t){this._pointerDown(t,t)};r._pointerDown=function(t, e){if(t.button||this.isPointerDown){return}this.isPointerDown=true;this.pointerIdentifier=e.pointerId!==undefined?e.pointerId:e.identifier;this.pointerDown(t,e)};r.pointerDown=function(t, e){this._bindPostStartEvents(t);this.emitEvent("pointerDown",[t,e])};var s={mousedown:["mousemove","mouseup"],touchstart:["touchmove","touchend","touchcancel"],pointerdown:["pointermove","pointerup","pointercancel"]};r._bindPostStartEvents=function(t){if(!t){return}var e=s[t.type];e.forEach(function(t){o.addEventListener(t,this)},this);this._boundPointerEvents=e};r._unbindPostStartEvents=function(){if(!this._boundPointerEvents){return}this._boundPointerEvents.forEach(function(t){o.removeEventListener(t,this)},this);delete this._boundPointerEvents};r.onmousemove=function(t){this._pointerMove(t,t)};r.onpointermove=function(t){if(t.pointerId==this.pointerIdentifier){this._pointerMove(t,t)}};r.ontouchmove=function(t){var e=this.getTouch(t.changedTouches);if(e){this._pointerMove(t,e)}};r._pointerMove=function(t, e){this.pointerMove(t,e)};r.pointerMove=function(t, e){this.emitEvent("pointerMove",[t,e])};r.onmouseup=function(t){this._pointerUp(t,t)};r.onpointerup=function(t){if(t.pointerId==this.pointerIdentifier){this._pointerUp(t,t)}};r.ontouchend=function(t){var e=this.getTouch(t.changedTouches);if(e){this._pointerUp(t,e)}};r._pointerUp=function(t, e){this._pointerDone();this.pointerUp(t,e)};r.pointerUp=function(t, e){this.emitEvent("pointerUp",[t,e])};r._pointerDone=function(){this._pointerReset();this._unbindPostStartEvents();this.pointerDone()};r._pointerReset=function(){this.isPointerDown=false;delete this.pointerIdentifier};r.pointerDone=i;r.onpointercancel=function(t){if(t.pointerId==this.pointerIdentifier){this._pointerCancel(t,t)}};r.ontouchcancel=function(t){var e=this.getTouch(t.changedTouches);if(e){this._pointerCancel(t,e)}};r._pointerCancel=function(t, e){this._pointerDone();this.pointerCancel(t,e)};r.pointerCancel=function(t, e){this.emitEvent("pointerCancel",[t,e])};n.getPointerPoint=function(t){return{x:t.pageX,y:t.pageY}};return n});
|
|
(function(e,i){e.Unidragger=i(e,e.Unipointer)})(window,function t(r,e){function i(){}var n=i.prototype=Object.create(e.prototype);n.bindHandles=function(){this._bindHandles(true)};n.unbindHandles=function(){this._bindHandles(false)};n._bindHandles=function(t){t=t===undefined?true:t;var e=t?"addEventListener":"removeEventListener";var i=t?this._touchActionValue:"";for(var n=0;n<this.handles.length;n++){var o=this.handles[n];this._bindStartEvent(o,t);o[e]("click",this);if(r.PointerEvent){o.style.touchAction=i}}};n._touchActionValue="none";n.pointerDown=function(t,e){var i=this.okayPointerDown(t);if(!i){return}this.pointerDownPointer=e;t.preventDefault();this.pointerDownBlur();this._bindPostStartEvents(t);this.emitEvent("pointerDown",[t,e])};var o={TEXTAREA:true,INPUT:true,SELECT:true,OPTION:true};var s={radio:true,checkbox:true,button:true,submit:true,image:true,file:true};n.okayPointerDown=function(t){var e=o[t.target.nodeName];var i=s[t.target.type];var n=!e||i;if(!n){this._pointerReset()}return n};n.pointerDownBlur=function(){var t=document.activeElement;var e=t&&t.blur&&t!=document.body;if(e){t.blur()}};n.pointerMove=function(t,e){var i=this._dragPointerMove(t,e);this.emitEvent("pointerMove",[t,e,i]);this._dragMove(t,e,i)};n._dragPointerMove=function(t,e){var i={x:e.pageX-this.pointerDownPointer.pageX,y:e.pageY-this.pointerDownPointer.pageY};if(!this.isDragging&&this.hasDragStarted(i)){this._dragStart(t,e)}return i};n.hasDragStarted=function(t){return Math.abs(t.x)>3||Math.abs(t.y)>3};n.pointerUp=function(t,e){this.emitEvent("pointerUp",[t,e]);this._dragPointerUp(t,e)};n._dragPointerUp=function(t,e){if(this.isDragging){this._dragEnd(t,e)}else{this._staticClick(t,e)}};n._dragStart=function(t,e){this.isDragging=true;this.isPreventingClicks=true;this.dragStart(t,e)};n.dragStart=function(t,e){this.emitEvent("dragStart",[t,e])};n._dragMove=function(t,e,i){if(!this.isDragging){return}this.dragMove(t,e,i)};n.dragMove=function(t,e,i){t.preventDefault();this.emitEvent("dragMove",[t,e,i])};n._dragEnd=function(t,e){this.isDragging=false;setTimeout(function(){delete this.isPreventingClicks}.bind(this));this.dragEnd(t,e)};n.dragEnd=function(t,e){this.emitEvent("dragEnd",[t,e])};n.onclick=function(t){if(this.isPreventingClicks){t.preventDefault()}};n._staticClick=function(t,e){if(this.isIgnoringMouseUp&&t.type=="mouseup"){return}this.staticClick(t,e);if(t.type!="mouseup"){this.isIgnoringMouseUp=true;setTimeout(function(){delete this.isIgnoringMouseUp}.bind(this),400)}};n.staticClick=function(t,e){this.emitEvent("staticClick",[t,e])};i.getPointerPoint=e.getPointerPoint;return i});
|
|
(function(i,n){i.Draggabilly=n(i,i.getSize,i.Unidragger)})(window,function t(r,u,e){function i(t,e){for(var i in e){t[i]=e[i]}return t}function n(){}var o=r.jQuery;function s(t,e){this.element=typeof t=="string"?document.querySelector(t):t;if(o){this.$element=o(this.element)}this.options=i({},this.constructor.defaults);this.option(e);this._create()}var a=s.prototype=Object.create(e.prototype);s.defaults={};a.option=function(t){i(this.options,t)};var h={relative:true,absolute:true,fixed:true};a._create=function(){this.position={};this._getPosition();this.startPoint={x:0,y:0};this.dragPoint={x:0,y:0};this.startPosition=i({},this.position);var t=getComputedStyle(this.element);if(!h[t.position]){this.element.style.position="relative"}this.on("pointerMove",this.onPointerMove);this.on("pointerUp",this.onPointerUp);this.enable();this.setHandles()};a.setHandles=function(){this.handles=this.options.handle?this.element.querySelectorAll(this.options.handle):[this.element];this.bindHandles()};a.dispatchEvent=function(t,e,i){var n=[e].concat(i);this.emitEvent(t,n);this.dispatchJQueryEvent(t,e,i)};a.dispatchJQueryEvent=function(t,e,i){var n=r.jQuery;if(!n||!this.$element){return}var o=n.Event(e);o.type=t;this.$element.trigger(o,i)};a._getPosition=function(){var t=getComputedStyle(this.element);var e=this._getPositionCoord(t.left,"width");var i=this._getPositionCoord(t.top,"height");this.position.x=isNaN(e)?0:e;this.position.y=isNaN(i)?0:i;this._addTransformPosition(t)};a._getPositionCoord=function(t,e){if(t.indexOf("%")!=-1){var i=u(this.element.parentNode);return!i?0:parseFloat(t)/100*i[e]}return parseInt(t,10)};a._addTransformPosition=function(t){var e=t.transform;if(e.indexOf("matrix")!==0){return}var i=e.split(",");var n=e.indexOf("matrix3d")===0?12:4;var o=parseInt(i[n],10);var r=parseInt(i[n+1],10);this.position.x+=o;this.position.y+=r};a.onPointerDown=function(t,e){this.element.classList.add("is-pointer-down");this.dispatchJQueryEvent("pointerDown",t,[e])};a.pointerDown=function(t,e){var i=this.okayPointerDown(t);if(!i||!this.isEnabled){this._pointerReset();return}this.pointerDownPointer={pageX:e.pageX,pageY:e.pageY};t.preventDefault();this.pointerDownBlur();this._bindPostStartEvents(t);this.element.classList.add("is-pointer-down");this.dispatchEvent("pointerDown",t,[e])};a.dragStart=function(t,e){if(!this.isEnabled){return}this._getPosition();this.measureContainment();this.startPosition.x=this.position.x;this.startPosition.y=this.position.y;this.setLeftTop();this.dragPoint.x=0;this.dragPoint.y=0;this.element.classList.add("is-dragging");this.dispatchEvent("dragStart",t,[e]);this.animate()};a.measureContainment=function(){var t=this.getContainer();if(!t){return}var e=u(this.element);var i=u(t);var n=this.element.getBoundingClientRect();var o=t.getBoundingClientRect();var r=i.borderLeftWidth+i.borderRightWidth;var s=i.borderTopWidth+i.borderBottomWidth;var a=this.relativeStartPosition={x:n.left-(o.left+i.borderLeftWidth),y:n.top-(o.top+i.borderTopWidth)};this.containSize={width:i.width-r-a.x-e.width,height:i.height-s-a.y-e.height}};a.getContainer=function(){var t=this.options.containment;if(!t){return}var e=t instanceof HTMLElement;if(e){return t}if(typeof t=="string"){return document.querySelector(t)}return this.element.parentNode};a.onPointerMove=function(t,e,i){this.dispatchJQueryEvent("pointerMove",t,[e,i])};a.dragMove=function(t,e,i){if(!this.isEnabled){return}var n=i.x;var o=i.y;var r=this.options.grid;var s=r&&r[0];var a=r&&r[1];n=d(n,s);o=d(o,a);n=this.containDrag("x",n,s);o=this.containDrag("y",o,a);n=this.options.axis=="y"?0:n;o=this.options.axis=="x"?0:o;this.position.x=this.startPosition.x+n;this.position.y=this.startPosition.y+o;this.dragPoint.x=n;this.dragPoint.y=o;this.dispatchEvent("dragMove",t,[e,i])};function d(t,e,i){i=i||"round";return e?Math[i](t/e)*e:t}a.containDrag=function(t,e,i){if(!this.options.containment){return e}var n=t=="x"?"width":"height";var o=this.relativeStartPosition[t];var r=d(-o,i,"ceil");var s=this.containSize[n];s=d(s,i,"floor");return Math.max(r,Math.min(s,e))};a.onPointerUp=function(t,e){this.element.classList.remove("is-pointer-down");this.dispatchJQueryEvent("pointerUp",t,[e])};a.dragEnd=function(t,e){if(!this.isEnabled){return}this.element.style.transform="";this.setLeftTop();this.element.classList.remove("is-dragging");this.dispatchEvent("dragEnd",t,[e])};a.animate=function(){if(!this.isDragging){return}this.positionDrag();var e=this;requestAnimationFrame(function t(){e.animate()})};a.setLeftTop=function(){this.element.style.left=`${this.position.x}px`;this.element.style.top=`${this.position.y}px`};a.positionDrag=function(){this.element.style.transform=`translate3d( ${this.dragPoint.x}px, ${this.dragPoint.y}px, 0)`};a.staticClick=function(t, e){this.dispatchEvent("staticClick",t,[e])};a.setPosition=function(t, e){this.position.x=t;this.position.y=e;this.setLeftTop()};a.enable=function(){this.isEnabled=true};a.disable=function(){this.isEnabled=false;if(this.isDragging){this.dragEnd()}};a.destroy=function(){this.disable();this.element.style.transform="";this.element.style.left="";this.element.style.top="";this.element.style.position="";this.unbindHandles();if(this.$element){this.$element.removeData("draggabilly")}};a._init=n;if(o&&o.bridget){o.bridget("draggabilly",s)}return s});
|
|
|
|
const Draggabilly = window.Draggabilly;
|
|
|
|
const TAB_CONTAINER_MIN_WIDTH = 24;
|
|
const TAB_CONTAINER_MAX_WIDTH = 240;
|
|
const NEW_TAB_WIDTH = 32;
|
|
const MIN_FILLER_WIDTH = 50;
|
|
const MARGIN_WIDTH = 5;
|
|
|
|
const TAB_SIZE_SMALL = 84;
|
|
const TAB_SIZE_SMALLER = 60;
|
|
const TAB_SIZE_MINI = 48;
|
|
|
|
const TAB_TPL = `
|
|
<div class="note-tab">
|
|
<div class="note-tab-wrapper">
|
|
<div class="note-tab-drag-handle"></div>
|
|
<div class="note-tab-icon"></div>
|
|
<div class="note-tab-title"></div>
|
|
<div class="note-tab-close bx bx-x" title="Close tab" data-trigger-command="closeActiveTab"></div>
|
|
</div>
|
|
</div>`;
|
|
|
|
const NEW_TAB_BUTTON_TPL = `<div class="note-new-tab" data-trigger-command="openNewTab" title="Add new tab">+</div>`;
|
|
const FILLER_TPL = `<div class="tab-row-filler"></div>`;
|
|
|
|
const TAB_ROW_TPL = `
|
|
<div class="tab-row-widget">
|
|
<style>
|
|
.tab-row-widget {
|
|
box-sizing: border-box;
|
|
position: relative;
|
|
width: 100%;
|
|
background: var(--main-background-color);
|
|
overflow: hidden;
|
|
}
|
|
|
|
.tab-row-widget * {
|
|
box-sizing: inherit;
|
|
font: inherit;
|
|
}
|
|
|
|
.tab-row-widget .tab-row-widget-container {
|
|
box-sizing: border-box;
|
|
position: relative;
|
|
width: 100%;
|
|
height: 100%;
|
|
}
|
|
|
|
.tab-row-widget .note-tab {
|
|
position: absolute;
|
|
left: 0;
|
|
width: 240px;
|
|
border: 0;
|
|
margin: 0;
|
|
z-index: 1;
|
|
pointer-events: none;
|
|
}
|
|
|
|
.note-new-tab {
|
|
position: absolute;
|
|
left: 0;
|
|
width: 36px;
|
|
height: 36px;
|
|
padding: 1px;
|
|
border: 0;
|
|
margin: 0;
|
|
z-index: 1;
|
|
text-align: center;
|
|
font-size: 24px;
|
|
cursor: pointer;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
.note-new-tab:hover {
|
|
background-color: var(--accented-background-color);
|
|
border-radius: var(--button-border-radius);
|
|
}
|
|
|
|
.tab-row-filler {
|
|
box-sizing: border-box;
|
|
-webkit-app-region: drag;
|
|
position: absolute;
|
|
left: 0;
|
|
height: 100%;
|
|
}
|
|
|
|
.tab-row-widget .note-tab[active] {
|
|
z-index: 5;
|
|
}
|
|
|
|
.tab-row-widget .note-tab,
|
|
.tab-row-widget .note-tab * {
|
|
user-select: none;
|
|
cursor: default;
|
|
}
|
|
|
|
.tab-row-widget .note-tab.note-tab-was-just-added {
|
|
top: 10px;
|
|
animation: note-tab-was-just-added 120ms forwards ease-in-out;
|
|
}
|
|
|
|
.tab-row-widget .note-tab .note-tab-wrapper {
|
|
position: absolute;
|
|
display: flex;
|
|
align-items: center;
|
|
top: 0;
|
|
bottom: 0;
|
|
left: 0;
|
|
right: 0;
|
|
height: 36px;
|
|
padding: 7px 5px 7px 11px;
|
|
border-radius: 8px;
|
|
overflow: hidden;
|
|
pointer-events: all;
|
|
color: var(--inactive-tab-text-color);
|
|
background-color: var(--inactive-tab-background-color);
|
|
}
|
|
|
|
.tab-row-widget .note-tab[active] .note-tab-wrapper {
|
|
font-weight: bold;
|
|
color: var(--active-tab-text-color);
|
|
background-color : var(--active-tab-background-color);
|
|
}
|
|
|
|
.tab-row-widget .note-tab[is-mini] .note-tab-wrapper {
|
|
padding-left: 2px;
|
|
padding-right: 2px;
|
|
}
|
|
|
|
.tab-row-widget .note-tab .note-tab-title {
|
|
flex: 1;
|
|
vertical-align: top;
|
|
overflow: hidden;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.tab-row-widget .note-tab .note-tab-icon {
|
|
position: relative;
|
|
top: -1px;
|
|
padding-right: 3px;
|
|
}
|
|
|
|
.tab-row-widget .note-tab[is-small] .note-tab-title {
|
|
margin-left: 0;
|
|
}
|
|
|
|
.tab-row-widget .note-tab .note-tab-drag-handle {
|
|
position: absolute;
|
|
top: 0;
|
|
bottom: 0;
|
|
right: 0;
|
|
left: 0;
|
|
}
|
|
|
|
.tab-row-widget .note-tab .note-tab-close {
|
|
flex: 0 0 22px;
|
|
border-radius: 50%;
|
|
z-index: 100;
|
|
width: 22px;
|
|
height: 22px;
|
|
cursor: pointer;
|
|
text-align: center;
|
|
}
|
|
|
|
.tab-row-widget .note-tab:hover .note-tab-wrapper {
|
|
background-color: var(--inactive-tab-hover-background-color);
|
|
}
|
|
|
|
.tab-row-widget .note-tab[active]:hover .note-tab-wrapper {
|
|
background-color: var(--active-tab-hover-background-color);
|
|
}
|
|
|
|
.tab-row-widget .note-tab .note-tab-close:hover {
|
|
background-color: var(--hover-item-background-color);
|
|
color: var(--hover-item-text-color);
|
|
}
|
|
|
|
.tab-row-widget .note-tab[is-smaller] .note-tab-close {
|
|
margin-left: auto;
|
|
}
|
|
.tab-row-widget .note-tab[is-mini]:not([active]) .note-tab-close {
|
|
display: none;
|
|
}
|
|
.tab-row-widget .note-tab[is-mini][active] .note-tab-close {
|
|
margin-left: auto;
|
|
margin-right: auto;
|
|
}
|
|
@-moz-keyframes note-tab-was-just-added {
|
|
to {
|
|
top: 0;
|
|
}
|
|
}
|
|
@-webkit-keyframes note-tab-was-just-added {
|
|
to {
|
|
top: 0;
|
|
}
|
|
}
|
|
@-o-keyframes note-tab-was-just-added {
|
|
to {
|
|
top: 0;
|
|
}
|
|
}
|
|
@keyframes note-tab-was-just-added {
|
|
to {
|
|
top: 0;
|
|
}
|
|
}
|
|
.tab-row-widget.tab-row-widget-is-sorting .note-tab:not(.note-tab-is-dragging),
|
|
.tab-row-widget:not(.tab-row-widget-is-sorting) .note-tab.note-tab-was-just-dragged {
|
|
transition: transform 120ms ease-in-out;
|
|
}
|
|
</style>
|
|
|
|
<div class="tab-row-widget-container"></div>
|
|
</div>`;
|
|
|
|
export default class TabRowWidget extends BasicWidget {
|
|
doRender() {
|
|
this.$widget = $(TAB_ROW_TPL);
|
|
|
|
this.draggabillies = [];
|
|
|
|
this.setupStyle();
|
|
this.setupEvents();
|
|
this.setupDraggabilly();
|
|
this.setupNewButton();
|
|
this.setupFiller();
|
|
this.layoutTabs();
|
|
this.setVisibility();
|
|
|
|
this.$widget.on('contextmenu', '.note-tab', e => {
|
|
e.preventDefault();
|
|
|
|
const ntxId = $(e.target).closest(".note-tab").attr('data-ntx-id');
|
|
|
|
contextMenu.show({
|
|
x: e.pageX,
|
|
y: e.pageY,
|
|
items: [
|
|
{title: "Close", command: "closeTab", uiIcon: "bx bx-x"},
|
|
{title: "Close other tabs", command: "closeOtherTabs", uiIcon: "bx bx-x"},
|
|
{title: "Close all tabs", command: "closeAllTabs", uiIcon: "bx bx-x"},
|
|
{title: "Move this tab to a new window", command: "moveTabToNewWindow", uiIcon: "bx bx-window-open"}
|
|
],
|
|
selectMenuItemHandler: ({command}) => {
|
|
this.triggerCommand(command, {ntxId});
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
setupStyle() {
|
|
this.$style = $("<style>");
|
|
this.$widget.append(this.$style);
|
|
}
|
|
|
|
setupEvents() {
|
|
const resizeListener = _ => {
|
|
this.cleanUpPreviouslyDraggedTabs();
|
|
this.layoutTabs();
|
|
};
|
|
|
|
new ResizeObserver(resizeListener).observe(this.$widget[0]);
|
|
|
|
this.tabEls.forEach((tabEl) => this.setTabCloseEvent(tabEl));
|
|
}
|
|
|
|
setVisibility() {
|
|
this.$widget.show();
|
|
}
|
|
|
|
get tabEls() {
|
|
return Array.prototype.slice.call(this.$widget.find('.note-tab'));
|
|
}
|
|
|
|
get $tabContainer() {
|
|
return this.$widget.find('.tab-row-widget-container');
|
|
}
|
|
|
|
get tabWidths() {
|
|
const numberOfTabs = this.tabEls.length;
|
|
const tabsContainerWidth = this.$tabContainer[0].clientWidth - NEW_TAB_WIDTH - MIN_FILLER_WIDTH;
|
|
const marginWidth = (numberOfTabs - 1) * MARGIN_WIDTH;
|
|
const targetWidth = (tabsContainerWidth - marginWidth) / numberOfTabs;
|
|
const clampedTargetWidth = Math.max(TAB_CONTAINER_MIN_WIDTH, Math.min(TAB_CONTAINER_MAX_WIDTH, targetWidth));
|
|
const flooredClampedTargetWidth = Math.floor(clampedTargetWidth);
|
|
const totalTabsWidthUsingTarget = flooredClampedTargetWidth * numberOfTabs + marginWidth;
|
|
const totalExtraWidthDueToFlooring = tabsContainerWidth - totalTabsWidthUsingTarget;
|
|
|
|
const widths = [];
|
|
let extraWidthRemaining = totalExtraWidthDueToFlooring;
|
|
|
|
for (let i = 0; i < numberOfTabs; i += 1) {
|
|
const extraWidth = flooredClampedTargetWidth < TAB_CONTAINER_MAX_WIDTH && extraWidthRemaining > 0 ? 1 : 0;
|
|
|
|
widths.push(flooredClampedTargetWidth + extraWidth);
|
|
|
|
if (extraWidthRemaining > 0) {
|
|
extraWidthRemaining -= 1;
|
|
}
|
|
}
|
|
|
|
if (this.$filler) {
|
|
this.$filler.css("width", `${extraWidthRemaining + MIN_FILLER_WIDTH}px`);
|
|
}
|
|
|
|
return widths;
|
|
}
|
|
|
|
getTabPositions() {
|
|
const tabPositions = [];
|
|
|
|
let position = 0;
|
|
this.tabWidths.forEach(width => {
|
|
tabPositions.push(position);
|
|
position += width + MARGIN_WIDTH;
|
|
});
|
|
|
|
position -= MARGIN_WIDTH; // last margin should not be applied
|
|
|
|
const newTabPosition = position;
|
|
const fillerPosition = position + 32;
|
|
|
|
return {tabPositions, newTabPosition, fillerPosition};
|
|
}
|
|
|
|
layoutTabs() {
|
|
const tabContainerWidths = this.tabWidths;
|
|
|
|
this.tabEls.forEach((tabEl, i) => {
|
|
const width = tabContainerWidths[i];
|
|
|
|
tabEl.style.width = `${width}px`;
|
|
tabEl.removeAttribute('is-small');
|
|
tabEl.removeAttribute('is-smaller');
|
|
tabEl.removeAttribute('is-mini');
|
|
|
|
if (width < TAB_SIZE_SMALL) tabEl.setAttribute('is-small', '');
|
|
if (width < TAB_SIZE_SMALLER) tabEl.setAttribute('is-smaller', '');
|
|
if (width < TAB_SIZE_MINI) tabEl.setAttribute('is-mini', '');
|
|
});
|
|
|
|
let styleHTML = '';
|
|
|
|
const {tabPositions, newTabPosition, fillerPosition} = this.getTabPositions();
|
|
|
|
tabPositions.forEach((position, i) => {
|
|
styleHTML += `.note-tab:nth-child(${ i + 1 }) { transform: translate3d(${ position }px, 0, 0)} `;
|
|
});
|
|
|
|
styleHTML += `.note-new-tab { transform: translate3d(${ newTabPosition }px, 0, 0) } `;
|
|
styleHTML += `.tab-row-filler { transform: translate3d(${ fillerPosition }px, 0, 0) } `;
|
|
|
|
this.$style.html(styleHTML);
|
|
}
|
|
|
|
addTab(ntxId) {
|
|
const $tab = $(TAB_TPL).attr('data-ntx-id', ntxId);
|
|
|
|
keyboardActionService.updateDisplayedShortcuts($tab);
|
|
|
|
$tab.addClass('note-tab-was-just-added');
|
|
|
|
setTimeout(() => $tab.removeClass('note-tab-was-just-added'), 500);
|
|
|
|
this.$newTab.before($tab);
|
|
this.setVisibility();
|
|
this.setTabCloseEvent($tab);
|
|
this.updateTitle($tab, 'New tab');
|
|
this.cleanUpPreviouslyDraggedTabs();
|
|
this.layoutTabs();
|
|
this.setupDraggabilly();
|
|
}
|
|
|
|
closeActiveTabCommand({$el}) {
|
|
const ntxId = $el.closest(".note-tab").attr('data-ntx-id');
|
|
|
|
appContext.tabManager.removeNoteContext(ntxId);
|
|
}
|
|
|
|
setTabCloseEvent($tab) {
|
|
$tab.on('mousedown', e => {
|
|
if (e.which === 2) {
|
|
appContext.tabManager.removeNoteContext($tab.attr('data-ntx-id'));
|
|
|
|
return true; // event has been handled
|
|
}
|
|
});
|
|
}
|
|
|
|
get activeTabEl() {
|
|
return this.$widget.find('.note-tab[active]')[0];
|
|
}
|
|
|
|
activeContextChangedEvent() {
|
|
let activeNoteContext = appContext.tabManager.getActiveContext();
|
|
|
|
if (!activeNoteContext) {
|
|
return;
|
|
}
|
|
|
|
if (activeNoteContext.mainNtxId) {
|
|
activeNoteContext = appContext.tabManager.getNoteContextById(activeNoteContext.mainNtxId);
|
|
}
|
|
|
|
const tabEl = this.getTabById(activeNoteContext.ntxId)[0];
|
|
const activeTabEl = this.activeTabEl;
|
|
if (activeTabEl === tabEl) return;
|
|
if (activeTabEl) activeTabEl.removeAttribute('active');
|
|
if (tabEl) tabEl.setAttribute('active', '');
|
|
}
|
|
|
|
newNoteContextCreatedEvent({noteContext}) {
|
|
if (!noteContext.mainNtxId) {
|
|
this.addTab(noteContext.ntxId);
|
|
}
|
|
}
|
|
|
|
removeTab(ntxId) {
|
|
const tabEl = this.getTabById(ntxId)[0];
|
|
|
|
if (tabEl) {
|
|
tabEl.parentNode.removeChild(tabEl);
|
|
this.cleanUpPreviouslyDraggedTabs();
|
|
this.layoutTabs();
|
|
this.setupDraggabilly();
|
|
this.setVisibility();
|
|
}
|
|
}
|
|
|
|
getNtxIdsInOrder() {
|
|
return this.tabEls.map(el => el.getAttribute('data-ntx-id'));
|
|
}
|
|
|
|
updateTitle($tab, title) {
|
|
$tab.attr("title", title)
|
|
$tab.find('.note-tab-title')
|
|
.text(title);
|
|
}
|
|
|
|
getTabById(ntxId) {
|
|
return this.$widget.find(`[data-ntx-id='${ntxId}']`);
|
|
}
|
|
|
|
getTabId($tab) {
|
|
return $tab.attr('data-ntx-id');
|
|
}
|
|
|
|
noteContextRemovedEvent({ntxIds}) {
|
|
for (const ntxId of ntxIds) {
|
|
this.removeTab(ntxId);
|
|
}
|
|
}
|
|
|
|
cleanUpPreviouslyDraggedTabs() {
|
|
this.tabEls.forEach((tabEl) => tabEl.classList.remove('note-tab-was-just-dragged'));
|
|
}
|
|
|
|
setupDraggabilly() {
|
|
const tabEls = this.tabEls;
|
|
const {tabPositions} = this.getTabPositions();
|
|
|
|
if (this.isDragging) {
|
|
this.isDragging = false;
|
|
this.$widget.removeClass('tab-row-widget-is-sorting');
|
|
this.draggabillyDragging.element.classList.remove('note-tab-is-dragging');
|
|
this.draggabillyDragging.element.style.transform = '';
|
|
this.draggabillyDragging.dragEnd();
|
|
this.draggabillyDragging.isDragging = false;
|
|
this.draggabillyDragging.positionDrag = _ => {}; // Prevent Draggabilly from updating tabEl.style.transform in later frames
|
|
this.draggabillyDragging.destroy();
|
|
this.draggabillyDragging = null;
|
|
}
|
|
|
|
this.draggabillies.forEach(d => d.destroy());
|
|
|
|
tabEls.forEach((tabEl, originalIndex) => {
|
|
const originalTabPositionX = tabPositions[originalIndex];
|
|
const draggabilly = new Draggabilly(tabEl, {
|
|
axis: 'x',
|
|
handle: '.note-tab-drag-handle',
|
|
containment: this.$tabContainer[0]
|
|
});
|
|
|
|
this.draggabillies.push(draggabilly);
|
|
|
|
draggabilly.on('pointerDown', _ => {
|
|
appContext.tabManager.activateNoteContext(tabEl.getAttribute('data-ntx-id'));
|
|
});
|
|
|
|
draggabilly.on('dragStart', _ => {
|
|
this.isDragging = true;
|
|
this.draggabillyDragging = draggabilly;
|
|
tabEl.classList.add('note-tab-is-dragging');
|
|
this.$widget.addClass('tab-row-widget-is-sorting');
|
|
});
|
|
|
|
draggabilly.on('dragEnd', _ => {
|
|
this.isDragging = false;
|
|
const finalTranslateX = parseFloat(tabEl.style.left, 10);
|
|
tabEl.style.transform = `translate3d(0, 0, 0)`;
|
|
|
|
// Animate dragged tab back into its place
|
|
requestAnimationFrame(_ => {
|
|
tabEl.style.left = '0';
|
|
tabEl.style.transform = `translate3d(${ finalTranslateX }px, 0, 0)`;
|
|
|
|
requestAnimationFrame(_ => {
|
|
tabEl.classList.remove('note-tab-is-dragging');
|
|
this.$widget.removeClass('tab-row-widget-is-sorting');
|
|
|
|
tabEl.classList.add('note-tab-was-just-dragged');
|
|
|
|
requestAnimationFrame(_ => {
|
|
tabEl.style.transform = '';
|
|
|
|
this.layoutTabs();
|
|
this.setupDraggabilly();
|
|
})
|
|
})
|
|
})
|
|
});
|
|
|
|
draggabilly.on('dragMove', (event, pointer, moveVector) => {
|
|
// Current index be computed within the event since it can change during the dragMove
|
|
const tabEls = this.tabEls;
|
|
const currentIndex = tabEls.indexOf(tabEl);
|
|
|
|
const currentTabPositionX = originalTabPositionX + moveVector.x;
|
|
const destinationIndexTarget = this.closest(currentTabPositionX, tabPositions);
|
|
const destinationIndex = Math.max(0, Math.min(tabEls.length, destinationIndexTarget));
|
|
|
|
if (currentIndex !== destinationIndex) {
|
|
this.animateTabMove(tabEl, currentIndex, destinationIndex);
|
|
}
|
|
|
|
if (Math.abs(moveVector.y) > 100) {
|
|
this.triggerCommand('moveTabToNewWindow', {ntxId: this.getTabId($(tabEl))});
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
animateTabMove(tabEl, originIndex, destinationIndex) {
|
|
if (destinationIndex < originIndex) {
|
|
tabEl.parentNode.insertBefore(tabEl, this.tabEls[destinationIndex]);
|
|
} else {
|
|
const beforeEl = this.tabEls[destinationIndex + 1] || this.$newTab[0];
|
|
|
|
tabEl.parentNode.insertBefore(tabEl, beforeEl);
|
|
}
|
|
this.triggerEvent('tabReorder', {ntxIdsInOrder: this.getNtxIdsInOrder()});
|
|
this.layoutTabs();
|
|
}
|
|
|
|
setupNewButton() {
|
|
this.$newTab = $(NEW_TAB_BUTTON_TPL);
|
|
|
|
this.$tabContainer.append(this.$newTab);
|
|
}
|
|
|
|
setupFiller() {
|
|
this.$filler = $(FILLER_TPL);
|
|
|
|
this.$tabContainer.append(this.$filler);
|
|
}
|
|
|
|
closest(value, array) {
|
|
let closest = Infinity;
|
|
let closestIndex = -1;
|
|
|
|
array.forEach((v, i) => {
|
|
if (Math.abs(value - v) < closest) {
|
|
closest = Math.abs(value - v);
|
|
closestIndex = i;
|
|
}
|
|
});
|
|
|
|
return closestIndex;
|
|
};
|
|
|
|
noteSwitchedAndActivatedEvent({noteContext}) {
|
|
this.activeContextChangedEvent();
|
|
|
|
this.updateTabById(noteContext.mainNtxId || noteContext.ntxId);
|
|
}
|
|
|
|
noteSwitchedEvent({noteContext}) {
|
|
this.updateTabById(noteContext.mainNtxId || noteContext.ntxId);
|
|
}
|
|
|
|
noteContextReorderEvent({ntxIdsInOrder, oldNtxIdsInOrder, mainNtxIdsInOrder}) {
|
|
if (!oldNtxIdsInOrder || !mainNtxIdsInOrder
|
|
|| ntxIdsInOrder.length !== oldNtxIdsInOrder.length
|
|
|| ntxIdsInOrder.length !== mainNtxIdsInOrder.length
|
|
)
|
|
return;
|
|
|
|
ntxIdsInOrder.forEach((id, i) => {
|
|
// update main context that is not a tab
|
|
if (!mainNtxIdsInOrder[i] && this.getTabById(id).length === 0) {
|
|
this.getTabById(oldNtxIdsInOrder[i]).attr("data-ntx-id", id);
|
|
this.updateTabById(id);
|
|
}
|
|
});
|
|
}
|
|
|
|
updateTabById(ntxId) {
|
|
const $tab = this.getTabById(ntxId);
|
|
|
|
const noteContext = appContext.tabManager.getNoteContextById(ntxId);
|
|
|
|
this.updateTab($tab, noteContext);
|
|
}
|
|
|
|
/** @param {NoteContext} noteContext */
|
|
updateTab($tab, noteContext) {
|
|
if (!$tab.length) {
|
|
return;
|
|
}
|
|
|
|
for (const clazz of Array.from($tab[0].classList)) { // create copy to safely iterate over while removing classes
|
|
if (clazz !== 'note-tab') {
|
|
$tab.removeClass(clazz);
|
|
}
|
|
}
|
|
|
|
if (noteContext) {
|
|
const hoistedNote = froca.getNoteFromCache(noteContext.hoistedNoteId);
|
|
|
|
if (hoistedNote) {
|
|
$tab.find('.note-tab-icon')
|
|
.removeClass()
|
|
.addClass("note-tab-icon")
|
|
.addClass(hoistedNote.getWorkspaceIconClass());
|
|
|
|
$tab.find('.note-tab-wrapper').css("background", hoistedNote.getWorkspaceTabBackgroundColor());
|
|
}
|
|
else {
|
|
$tab.find('.note-tab-wrapper').removeAttr("style");
|
|
}
|
|
}
|
|
|
|
const {note} = noteContext;
|
|
|
|
if (!note) {
|
|
this.updateTitle($tab, 'New tab');
|
|
return;
|
|
}
|
|
|
|
const viewMode = noteContext.viewScope?.viewMode;
|
|
const title = (viewMode && viewMode !== 'default')
|
|
? `${viewMode}: ${note.title}`
|
|
: note.title;
|
|
|
|
this.updateTitle($tab, title);
|
|
|
|
$tab.addClass(note.getCssClass());
|
|
$tab.addClass(utils.getNoteTypeClass(note.type));
|
|
$tab.addClass(utils.getMimeTypeClass(note.mime));
|
|
}
|
|
|
|
async entitiesReloadedEvent({loadResults}) {
|
|
for (const noteContext of appContext.tabManager.noteContexts) {
|
|
if (!noteContext.noteId) {
|
|
continue;
|
|
}
|
|
|
|
if (loadResults.isNoteReloaded(noteContext.noteId) ||
|
|
loadResults.getAttributes().find(attr =>
|
|
['workspace', 'workspaceIconClass', 'workspaceTabBackgroundColor'].includes(attr.name)
|
|
&& attributeService.isAffecting(attr, noteContext.note))
|
|
) {
|
|
const $tab = this.getTabById(noteContext.ntxId);
|
|
|
|
this.updateTab($tab, noteContext);
|
|
}
|
|
}
|
|
}
|
|
|
|
frocaReloadedEvent() {
|
|
for (const noteContext of appContext.tabManager.noteContexts) {
|
|
const $tab = this.getTabById(noteContext.ntxId);
|
|
|
|
this.updateTab($tab, noteContext);
|
|
}
|
|
}
|
|
|
|
hoistedNoteChangedEvent({ntxId}) {
|
|
const $tab = this.getTabById(ntxId);
|
|
|
|
if ($tab) {
|
|
const noteContext = appContext.tabManager.getNoteContextById(ntxId);
|
|
|
|
this.updateTab($tab, noteContext);
|
|
}
|
|
}
|
|
}
|