Notes/src/public/app/widgets/tab_row.js

644 lines
33 KiB
JavaScript
Raw Normal View History

2019-04-30 22:31:12 +02:00
/*!
* Draggabilly PACKAGED v2.2.0
* Make that shiz draggable
* https://draggabilly.desandro.com
* MIT license
*/
2020-01-14 21:52:18 +01:00
import BasicWidget from "./basic_widget.js";
2020-02-29 13:03:05 +01:00
import contextMenu from "../services/context_menu.js";
2020-01-19 21:24:14 +01:00
import utils from "../services/utils.js";
2020-01-20 22:35:52 +01:00
import keyboardActionService from "../services/keyboard_actions.js";
import appContext from "../services/app_context.js";
2020-01-12 19:05:09 +01:00
2020-04-12 14:22:51 +02:00
!function(i, e){"function"==typeof define&&define.amd?define("jquery-bridget/jquery-bridget",["jquery"],function(t){return e(i,t)}):"object"==typeof module&&module.exports?module.exports=e(i,utils.dynamicRequire("jquery")):i.jQueryBridget=e(i,i.jQuery)}(window,function(t, i){"use strict";var c=Array.prototype.slice,e=t.console,p=void 0===e?function(){}:function(t){e.error(t)};function n(d, o, u){(u=u||i||t.jQuery)&&(o.prototype.option||(o.prototype.option=function(t){u.isPlainObject(t)&&(this.options=u.extend(!0,this.options,t))}),u.fn[d]=function(t){if("string"==typeof t){var i=c.call(arguments,1);return s=i,a="$()."+d+'("'+(r=t)+'")',(e=this).each(function(t, i){var e=u.data(i,d);if(e){var n=e[r];if(n&&"_"!=r.charAt(0)){var o=n.apply(e,s);h=void 0===h?o:h}else p(a+" is not a valid method")}else p(d+" not initialized. Cannot call methods, i.e. "+a)}),void 0!==h?h:e}var e,r,s,h,a,n;return n=t,this.each(function(t, i){var e=u.data(i,d);e?(e.option(n),e._init()):(e=new o(i,n),u.data(i,d,e))}),this},r(u))}function r(t){!t||t&&t.bridget||(t.bridget=n)}return r(i||t.jQuery),n}),function(t, i){"use strict";"function"==typeof define&&define.amd?define("get-size/get-size",[],function(){return i()}):"object"==typeof module&&module.exports?module.exports=i():t.getSize=i()}(window,function(){"use strict";function m(t){var i=parseFloat(t);return-1==t.indexOf("%")&&!isNaN(i)&&i}var e="undefined"==typeof console?function(){}:function(t){console.error(t)},y=["paddingLeft","paddingRight","paddingTop","paddingBottom","marginLeft","marginRight","marginTop","marginBottom","borderLeftWidth","borderRightWidth","borderTopWidth","borderBottomWidth"],b=y.length;function E(t){var i=getComputedStyle(t);return i||e("Style returned "+i+". Are you running this code in a hidden iframe on Firefox? See http://bit.ly/getsizebug1"),i}var _,x=!1;function P(t){if(function(){if(!x){x=!0;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 i=document.body||document.documentElement;i.appendChild(t);var e=E(t);P.isBoxSizeOuter=_=200==m(e.width),i.removeChild(t)}}(),"string"==typeof t&&(t=document.querySelector(t)),t&&"object"==typeof t&&t.nodeType){var i=E(t);if("none"==i.display)return function(){for(var t={width:0,height:0,innerWidth:0,innerHeight:0,outerWidth:0,outerHeight:0},i=0; i<b; i++)t[y[i]]=0;return t}();var e={};e.width=t.offsetWidth,e.height=t.offsetHeight;for(var n=e.isBorderBox="border-box"==i.boxSizing,o=0; o<b; o++){var r=y[o],s=i[r],h=parseFloat(s);e[r]=isNaN(h)?0:h}var a=e.paddingLeft+e.paddingRight,d=e.paddingTop+e.paddingBottom,u=e.marginLeft+e.marginRight,c=e.marginTop+e.marginBottom,p=e.borderLeftWidth+e.borderRightWidth,f=e.borderTopWidth+e.borderBottomWidth,g=n&&_,l=m(i.width);!1!==l&&(e.width=l+(g?0:a+p));var v=m(i.height);return!1!==v&&(e.height=v+(g?0:d+f)),e.innerWidth=e.width-(a+p),e.innerHeight=e.height-(d+f),e.outerWidth=e.width+u,e.outerHeight=e.height+c,e}}return P}),function(t, i){"function"==typeof define&&define.amd?define("ev-emitter/ev-emitter",i):"object"==typeof module&&module.exports?module.exports=i():t.EvEmitter=i()}("undefined"!=typeof window?window:this,function(){function t(){}var i=t.prototype;return i.on=function(t, i){if(t&&i){var e=this._events=this._events||{},n=e[t]=e[t]||[];return-1==n.indexOf(i)&&n.push(i),this}},i.once=function(t, i){if(t&&i){this.on(t,i);var e=this._onceEvents=this._onceEvents||{};return(e[t]=e[t]||{})[i]=!0,this}},i.off=function(t, i){var e=this._events&&this._events[t];if(e&&e.length){var n=e.indexOf(i);return-1!=n&&e.splice(n,1),this}},i.emitEvent=function(t, i){var e=this._events&&this._events[t];if(e&&e.length){e=e.slice(0),i=i||[];for(var n=this._onceEvents&&this._onceEvents[t],o=0; o<e.length; o++){var r=e[o];n&&n[r]&&(this.off(t,r),delete n[r]),r.apply(this,i)}return this}},i.allOff=function(){delete this._events,delete this._onceEvents},t}),function(i, e){"function"==typeof define&&define.amd?define("unipointer/unipointer",["ev-emitter/ev-em
2019-04-30 22:31:12 +02:00
2019-05-11 19:44:58 +02:00
const Draggabilly = window.Draggabilly;
2019-04-30 22:31:12 +02:00
2020-02-01 19:25:37 +01:00
const TAB_CONTAINER_MIN_WIDTH = 24;
const TAB_CONTAINER_MAX_WIDTH = 240;
2019-05-12 10:59:53 +02:00
const NEW_TAB_WIDTH = 32;
const MIN_FILLER_WIDTH = 50;
2019-04-30 22:31:12 +02:00
2019-05-11 19:44:58 +02:00
const TAB_SIZE_SMALL = 84;
const TAB_SIZE_SMALLER = 60;
const TAB_SIZE_MINI = 48;
2019-04-30 22:31:12 +02:00
2020-01-12 20:15:05 +01:00
const TAB_TPL = `
2019-05-11 19:44:58 +02:00
<div class="note-tab">
<div class="note-tab-wrapper">
<div class="note-tab-title"></div>
<div class="note-tab-drag-handle"></div>
<div class="note-tab-close" title="Close tab" data-trigger-command="closeActiveTab"><span>×</span></div>
2019-05-11 19:44:58 +02:00
</div>
2019-05-12 10:11:41 +02:00
</div>`;
2019-05-11 19:44:58 +02:00
const NEW_TAB_BUTTON_TPL = `<div class="note-new-tab" data-trigger-command="openNewTab" title="Add new tab">+</div>`;
2020-01-12 20:15:05 +01:00
const FILLER_TPL = `<div class="tab-row-filler">
<div class="tab-row-border"></div>
</div>`;
2019-05-12 10:59:53 +02:00
2020-01-12 20:15:05 +01:00
const TAB_ROW_TPL = `
2020-01-12 19:05:09 +01:00
<div class="note-tab-row">
2020-01-12 20:15:05 +01:00
<style>
.note-tab-row {
box-sizing: border-box;
position: relative;
height: 34px;
min-height: 34px;
width: 100%;
background: var(--main-background-color);
border-radius: 5px 5px 0 0;
overflow: hidden;
margin-top: 2px;
}
.note-tab-row * {
box-sizing: inherit;
font: inherit;
}
2020-02-01 19:25:37 +01:00
.note-tab-row .note-tab-row-container {
2020-01-12 20:15:05 +01:00
position: relative;
width: 100%;
height: 100%;
}
.note-tab-row .note-tab {
position: absolute;
left: 0;
height: 33px;
width: 240px;
border: 0;
margin: 0;
z-index: 1;
pointer-events: none;
}
.note-new-tab {
position: absolute;
left: 0;
height: 33px;
width: 32px;
border: 0;
margin: 0;
z-index: 1;
text-align: center;
font-size: 24px;
cursor: pointer;
border-bottom: 1px solid var(--main-border-color);
}
.note-new-tab:hover {
background-color: var(--accented-background-color);
border-radius: 5px;
}
.tab-row-filler {
-webkit-app-region: drag;
position: absolute;
left: 0;
height: 33px;
}
.tab-row-filler .tab-row-border {
position: relative;
background: linear-gradient(to right, var(--main-border-color), transparent);
height: 1px;
margin-top: 32px;
}
.note-tab-row .note-tab[active] {
z-index: 5;
}
.note-tab-row .note-tab,
.note-tab-row .note-tab * {
user-select: none;
cursor: default;
}
.note-tab-row .note-tab.note-tab-was-just-added {
top: 10px;
animation: note-tab-was-just-added 120ms forwards ease-in-out;
}
.note-tab-row .note-tab .note-tab-wrapper {
position: absolute;
display: flex;
top: 0;
bottom: 0;
left: 0;
right: 0;
padding: 5px 8px;
border-top-left-radius: 8px;
border-top-right-radius: 8px;
overflow: hidden;
pointer-events: all;
background-color: var(--accented-background-color);
border-bottom: 1px solid var(--main-border-color);
}
.note-tab-row .note-tab[active] .note-tab-wrapper {
background-color: var(--main-background-color);
border: 1px solid var(--main-border-color);
border-bottom: 0;
font-weight: bold;
}
.note-tab-row .note-tab[is-mini] .note-tab-wrapper {
padding-left: 2px;
padding-right: 2px;
}
.note-tab-row .note-tab .note-tab-title {
flex: 1;
vertical-align: top;
overflow: hidden;
white-space: nowrap;
color: var(--muted-text-color);
}
.note-tab-row .note-tab[is-small] .note-tab-title {
margin-left: 0;
}
.note-tab-row .note-tab[active] .note-tab-title {
color: var(--main-text-color);
}
.note-tab-row .note-tab .note-tab-drag-handle {
position: absolute;
top: 0;
bottom: 0;
right: 0;
left: 0;
border-top-left-radius: 8px;
border-top-right-radius: 8px;
}
.note-tab-row .note-tab .note-tab-close {
flex-grow: 0;
flex-shrink: 0;
border-radius: 50%;
z-index: 100;
width: 24px;
height: 24px;
text-align: center;
}
.note-tab-row .note-tab .note-tab-close span {
font-size: 24px;
position: relative;
top: -6px;
}
.note-tab-row .note-tab .note-tab-close:hover {
background-color: var(--hover-item-background-color);
color: var(--hover-item-text-color);
}
.note-tab-row .note-tab[is-smaller] .note-tab-close {
margin-left: auto;
}
.note-tab-row .note-tab[is-mini]:not([active]) .note-tab-close {
display: none;
}
.note-tab-row .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;
}
}
.note-tab-row.note-tab-row-is-sorting .note-tab:not(.note-tab-is-dragging),
.note-tab-row:not(.note-tab-row-is-sorting) .note-tab.note-tab-was-just-dragged {
transition: transform 120ms ease-in-out;
}
</style>
2020-02-01 19:25:37 +01:00
<div class="note-tab-row-container"></div>
2020-01-12 19:05:09 +01:00
</div>`;
export default class TabRowWidget extends BasicWidget {
2020-01-12 20:15:05 +01:00
doRender() {
2020-01-15 22:11:30 +01:00
this.$widget = $(TAB_ROW_TPL);
2020-01-12 19:05:09 +01:00
this.draggabillies = [];
this.eventListeners = {};
2019-04-30 22:31:12 +02:00
2020-01-15 22:11:30 +01:00
this.setupStyle();
2019-05-11 19:44:58 +02:00
this.setupEvents();
this.setupDraggabilly();
2019-05-12 10:59:53 +02:00
this.setupNewButton();
2019-11-17 10:22:26 +01:00
this.setupFiller();
this.layoutTabs();
2019-05-11 19:44:58 +02:00
this.setVisibility();
2020-01-15 22:11:30 +01:00
this.$widget.on('contextmenu', '.note-tab', e => {
2020-01-12 19:05:09 +01:00
e.preventDefault();
2020-01-20 22:35:52 +01:00
const tabId = $(e.target).closest(".note-tab").attr('data-tab-id');
2020-01-12 19:05:09 +01:00
2020-02-29 13:03:05 +01:00
contextMenu.show({
x: e.pageX,
y: e.pageY,
items: [
{title: "Move this tab to a new window", command: "moveTabToNewWindow", uiIcon: "window-open"},
{title: "Close all tabs", command: "removeAllTabs", uiIcon: "x"},
{title: "Close all tabs except for this", command: "removeAllTabsExceptForThis", uiIcon: "x"},
2020-02-29 13:03:05 +01:00
],
selectMenuItemHandler: ({command}) => {
this.triggerCommand(command, {tabId});
2020-01-12 19:05:09 +01:00
}
});
2020-01-12 20:15:05 +01:00
});
2020-01-15 22:11:30 +01:00
return this.$widget;
2019-04-30 22:31:12 +02:00
}
2020-01-15 22:11:30 +01:00
setupStyle() {
this.$style = $("<style>");
this.$widget.append(this.$style);
2019-04-30 22:31:12 +02:00
}
setupEvents() {
const resizeListener = _ => {
2019-05-11 19:44:58 +02:00
this.cleanUpPreviouslyDraggedTabs();
this.layoutTabs();
};
// ResizeObserver exists only in FF69
if (typeof ResizeObserver !== "undefined") {
2020-01-15 22:11:30 +01:00
new ResizeObserver(resizeListener).observe(this.$widget[0]);
}
else {
// for older firefox
2020-03-09 21:00:31 +01:00
window.addEventListener('resize', resizeListener);
}
2019-04-30 22:31:12 +02:00
this.tabEls.forEach((tabEl) => this.setTabCloseEvent(tabEl));
2019-04-30 22:31:12 +02:00
}
setVisibility() {
2020-01-15 22:11:30 +01:00
this.$widget.show();
}
2019-04-30 22:31:12 +02:00
get tabEls() {
2020-01-15 22:11:30 +01:00
return Array.prototype.slice.call(this.$widget.find('.note-tab'));
2019-04-30 22:31:12 +02:00
}
2020-02-01 19:25:37 +01:00
get $tabContainer() {
return this.$widget.find('.note-tab-row-container');
2019-04-30 22:31:12 +02:00
}
2020-02-01 19:25:37 +01:00
get tabWidths() {
2019-05-11 19:44:58 +02:00
const numberOfTabs = this.tabEls.length;
2020-02-01 19:25:37 +01:00
const tabsContainerWidth = this.$tabContainer[0].clientWidth - NEW_TAB_WIDTH - MIN_FILLER_WIDTH;
const targetWidth = tabsContainerWidth / numberOfTabs;
const clampedTargetWidth = Math.max(TAB_CONTAINER_MIN_WIDTH, Math.min(TAB_CONTAINER_MAX_WIDTH, targetWidth));
2019-05-11 19:44:58 +02:00
const flooredClampedTargetWidth = Math.floor(clampedTargetWidth);
2019-05-12 10:21:27 +02:00
const totalTabsWidthUsingTarget = flooredClampedTargetWidth * numberOfTabs;
2020-02-01 19:25:37 +01:00
const totalExtraWidthDueToFlooring = tabsContainerWidth - totalTabsWidthUsingTarget;
2019-05-11 19:44:58 +02:00
const widths = [];
let extraWidthRemaining = totalExtraWidthDueToFlooring;
2019-11-17 10:22:26 +01:00
2019-05-11 19:44:58 +02:00
for (let i = 0; i < numberOfTabs; i += 1) {
2020-02-01 19:25:37 +01:00
const extraWidth = flooredClampedTargetWidth < TAB_CONTAINER_MAX_WIDTH && extraWidthRemaining > 0 ? 1 : 0;
2019-05-11 19:44:58 +02:00
widths.push(flooredClampedTargetWidth + extraWidth);
if (extraWidthRemaining > 0) extraWidthRemaining -= 1;
}
2019-04-30 22:31:12 +02:00
2020-01-15 22:35:15 +01:00
if (this.$filler) {
this.$filler.css("width", (extraWidthRemaining + MIN_FILLER_WIDTH) + "px");
2019-11-17 10:22:26 +01:00
}
2019-05-12 10:11:41 +02:00
return widths;
2019-04-30 22:31:12 +02:00
}
2019-05-12 10:59:53 +02:00
getTabPositions() {
const tabPositions = [];
2019-05-11 19:44:58 +02:00
2019-05-12 10:11:41 +02:00
let position = 0;
2020-02-01 19:25:37 +01:00
this.tabWidths.forEach(width => {
2019-05-12 10:59:53 +02:00
tabPositions.push(position);
2019-05-11 19:44:58 +02:00
position += width;
});
2019-04-30 22:31:12 +02:00
2019-05-12 10:59:53 +02:00
const newTabPosition = position;
2019-11-17 10:22:26 +01:00
const fillerPosition = position + 32;
2019-04-30 22:31:12 +02:00
2019-11-17 10:22:26 +01:00
return {tabPositions, newTabPosition, fillerPosition};
2019-04-30 22:31:12 +02:00
}
layoutTabs() {
2020-02-01 19:25:37 +01:00
const tabContainerWidths = this.tabWidths;
2019-05-11 19:44:58 +02:00
this.tabEls.forEach((tabEl, i) => {
2020-02-01 19:25:37 +01:00
const width = tabContainerWidths[i];
2019-05-11 19:44:58 +02:00
tabEl.style.width = width + 'px';
tabEl.removeAttribute('is-small');
tabEl.removeAttribute('is-smaller');
tabEl.removeAttribute('is-mini');
2019-05-12 10:11:41 +02:00
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', '');
2019-05-11 19:44:58 +02:00
});
let styleHTML = '';
2019-05-12 10:59:53 +02:00
2019-11-17 10:22:26 +01:00
const {tabPositions, newTabPosition, fillerPosition} = this.getTabPositions();
2019-05-12 10:59:53 +02:00
tabPositions.forEach((position, i) => {
2019-05-13 23:08:59 +02:00
styleHTML += `.note-tab:nth-child(${ i + 1 }) { transform: translate3d(${ position }px, 0, 0)} `;
2019-05-11 19:44:58 +02:00
});
2019-05-13 23:08:59 +02:00
styleHTML += `.note-new-tab { transform: translate3d(${ newTabPosition }px, 0, 0) } `;
2019-11-17 10:22:26 +01:00
styleHTML += `.tab-row-filler { transform: translate3d(${ fillerPosition }px, 0, 0) } `;
2019-05-12 10:59:53 +02:00
2020-01-15 22:11:30 +01:00
this.$style.html(styleHTML);
2019-04-30 22:31:12 +02:00
}
2019-05-14 22:29:47 +02:00
addTab(tabId) {
2020-01-20 22:35:52 +01:00
const $tab = $(TAB_TPL).attr('data-tab-id', tabId);
2019-04-30 22:31:12 +02:00
2020-01-20 22:35:52 +01:00
keyboardActionService.updateDisplayedShortcuts($tab);
2019-04-30 22:31:12 +02:00
2020-01-20 22:35:52 +01:00
$tab.addClass('note-tab-was-just-added');
setTimeout(() => $tab.removeClass('note-tab-was-just-added'), 500);
this.$newTab.before($tab);
2019-05-11 19:44:58 +02:00
this.setVisibility();
this.setTabCloseEvent($tab);
2020-01-20 22:35:52 +01:00
this.updateTitle($tab, 'New tab');
2019-05-11 19:44:58 +02:00
this.cleanUpPreviouslyDraggedTabs();
this.layoutTabs();
this.setupDraggabilly();
2019-04-30 22:31:12 +02:00
}
closeActiveTabCommand({$el}) {
const tabId = $el.closest(".note-tab").attr('data-tab-id');
appContext.tabManager.removeTab(tabId);
}
setTabCloseEvent($tab) {
2020-01-20 22:35:52 +01:00
$tab.on('mousedown', e => {
if (e.which === 2) {
appContext.tabManager.removeTab($tab.attr('data-tab-id'));
return true; // event has been handled
}
});
2019-04-30 22:31:12 +02:00
}
get activeTabEl() {
2020-01-15 22:11:30 +01:00
return this.$widget.find('.note-tab[active]')[0];
2019-04-30 22:31:12 +02:00
}
2020-02-16 19:23:49 +01:00
activeTabChangedEvent() {
const activeTabContext = appContext.tabManager.getActiveTabContext();
2020-02-09 21:13:05 +01:00
if (!activeTabContext) {
return;
}
const tabEl = this.getTabById(activeTabContext.tabId)[0];
2019-05-11 19:44:58 +02:00
const activeTabEl = this.activeTabEl;
if (activeTabEl === tabEl) return;
if (activeTabEl) activeTabEl.removeAttribute('active');
2020-02-09 21:13:05 +01:00
if (tabEl) tabEl.setAttribute('active', '');
2019-04-30 22:31:12 +02:00
}
2020-03-06 22:17:07 +01:00
newTabOpenedEvent({tabContext}) {
this.addTab(tabContext.tabId);
2020-01-20 20:51:22 +01:00
}
2020-01-15 22:27:52 +01:00
removeTab(tabId) {
2020-01-19 21:12:53 +01:00
const tabEl = this.getTabById(tabId)[0];
2020-01-15 22:27:52 +01:00
if (tabEl) {
tabEl.parentNode.removeChild(tabEl);
this.cleanUpPreviouslyDraggedTabs();
this.layoutTabs();
this.setupDraggabilly();
this.setVisibility();
}
2019-04-30 22:31:12 +02:00
}
2020-01-19 21:12:53 +01:00
getTabIdsInOrder() {
return this.tabEls.map(el => el.getAttribute('data-tab-id'));
}
2020-01-20 22:35:52 +01:00
updateTitle($tab, title) {
$tab.find('.note-tab-title').text(title);
2019-04-30 22:31:12 +02:00
}
getTabById(tabId) {
return this.$widget.find(`[data-tab-id='${tabId}']`);
}
2020-02-16 19:23:49 +01:00
tabRemovedEvent({tabId}) {
2020-01-19 21:12:53 +01:00
this.removeTab(tabId);
}
2019-04-30 22:31:12 +02:00
cleanUpPreviouslyDraggedTabs() {
2019-05-11 19:44:58 +02:00
this.tabEls.forEach((tabEl) => tabEl.classList.remove('note-tab-was-just-dragged'));
2019-04-30 22:31:12 +02:00
}
setupDraggabilly() {
2019-05-11 19:44:58 +02:00
const tabEls = this.tabEls;
2019-05-12 10:59:53 +02:00
const {tabPositions} = this.getTabPositions();
2019-05-11 19:44:58 +02:00
if (this.isDragging) {
this.isDragging = false;
2020-01-15 22:11:30 +01:00
this.$widget.removeClass('note-tab-row-is-sorting');
2019-05-11 19:44:58 +02:00
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
2019-05-11 19:44:58 +02:00
this.draggabillyDragging.destroy();
this.draggabillyDragging = null;
}
2019-04-30 22:31:12 +02:00
2019-05-11 19:44:58 +02:00
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',
2020-02-01 19:25:37 +01:00
containment: this.$tabContainer[0]
2019-05-11 19:44:58 +02:00
});
this.draggabillies.push(draggabilly);
draggabilly.on('pointerDown', _ => {
appContext.tabManager.activateTab(tabEl.getAttribute('data-tab-id'));
2019-05-11 19:44:58 +02:00
});
draggabilly.on('dragStart', _ => {
this.isDragging = true;
this.draggabillyDragging = draggabilly;
tabEl.classList.add('note-tab-is-dragging');
2020-01-15 22:11:30 +01:00
this.$widget.addClass('note-tab-row-is-sorting');
2019-05-11 19:44:58 +02:00
});
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');
2020-01-15 22:11:30 +01:00
this.$widget.removeClass('note-tab-row-is-sorting');
2019-05-11 19:44:58 +02:00
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;
2019-05-12 10:59:53 +02:00
const destinationIndexTarget = this.closest(currentTabPositionX, tabPositions);
2019-05-11 19:44:58 +02:00
const destinationIndex = Math.max(0, Math.min(tabEls.length, destinationIndexTarget));
if (currentIndex !== destinationIndex) {
this.animateTabMove(tabEl, currentIndex, destinationIndex);
}
});
});
2019-04-30 22:31:12 +02:00
}
2020-01-15 22:27:52 +01:00
animateTabMove(tabEl, originIndex, destinationIndex) {
2019-05-11 19:44:58 +02:00
if (destinationIndex < originIndex) {
tabEl.parentNode.insertBefore(tabEl, this.tabEls[destinationIndex]);
} else {
2020-01-20 20:51:22 +01:00
const beforeEl = this.tabEls[destinationIndex + 1] || this.$newTab[0];
2019-05-22 21:59:14 +02:00
tabEl.parentNode.insertBefore(tabEl, beforeEl);
2019-05-11 19:44:58 +02:00
}
2020-02-16 19:21:17 +01:00
this.triggerEvent('tabReorder', {tabIdsInOrder: this.getTabIdsInOrder()});
2019-05-11 19:44:58 +02:00
this.layoutTabs();
2019-04-30 22:31:12 +02:00
}
2019-05-12 10:59:53 +02:00
setupNewButton() {
2020-01-15 22:35:15 +01:00
this.$newTab = $(NEW_TAB_BUTTON_TPL);
2019-05-12 10:59:53 +02:00
2020-02-01 19:25:37 +01:00
this.$tabContainer.append(this.$newTab);
2019-05-12 10:59:53 +02:00
}
2019-11-17 10:22:26 +01:00
setupFiller() {
2020-01-15 22:35:15 +01:00
this.$filler = $(FILLER_TPL);
2019-11-17 10:22:26 +01:00
2020-02-01 19:25:37 +01:00
this.$tabContainer.append(this.$filler);
2019-11-17 10:22:26 +01:00
}
2019-05-12 10:59:53 +02:00
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;
2019-05-12 10:59:53 +02:00
}
});
return closestIndex;
};
2020-01-19 21:24:14 +01:00
2020-03-07 13:40:46 +01:00
tabNoteSwitchedAndActivatedEvent({tabContext}) {
2020-02-28 11:46:35 +01:00
this.activeTabChangedEvent();
2020-03-07 13:40:46 +01:00
this.updateTabById(tabContext.tabId);
2020-02-28 00:31:12 +01:00
}
2020-03-07 13:40:46 +01:00
tabNoteSwitchedEvent({tabContext}) {
this.updateTabById(tabContext.tabId);
2020-02-28 00:31:12 +01:00
}
updateTabById(tabId) {
2020-01-19 21:24:14 +01:00
const $tab = this.getTabById(tabId);
const {note} = appContext.tabManager.getTabContextById(tabId);
2020-01-19 21:24:14 +01:00
2020-02-01 19:25:37 +01:00
this.updateTab($tab, note);
}
updateTab($tab, note) {
2020-05-12 12:45:32 +02:00
if (!$tab.length) {
2020-01-19 21:24:14 +01:00
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);
}
}
2020-05-12 12:45:32 +02:00
if (!note) {
this.updateTitle($tab, 'New tab');
}
this.updateTitle($tab, note.title);
$tab.addClass(note.getCssClass());
2020-01-19 21:24:14 +01:00
$tab.addClass(utils.getNoteTypeClass(note.type));
$tab.addClass(utils.getMimeTypeClass(note.mime));
}
2020-01-24 15:44:24 +01:00
2020-02-16 19:23:49 +01:00
async entitiesReloadedEvent({loadResults}) {
for (const tabContext of appContext.tabManager.tabContexts) {
2020-02-01 19:25:37 +01:00
if (loadResults.isNoteReloaded(tabContext.noteId)) {
const $tab = this.getTabById(tabContext.tabId);
this.updateTab($tab, tabContext.note);
}
}
}
2020-02-16 19:23:49 +01:00
treeCacheReloadedEvent() {
for (const tabContext of appContext.tabManager.tabContexts) {
const $tab = this.getTabById(tabContext.tabId);
this.updateTab($tab, tabContext.note);
}
2020-01-24 15:44:24 +01:00
}
2020-05-12 12:45:32 +02:00
}