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" ;
2020-02-16 18:11:32 +01:00
import appContext from "../services/app_context.js" ;
2021-04-16 23:01:56 +02:00
import froca from "../services/froca.js" ;
2020-01-12 19:05:09 +01:00
2021-03-21 23:35:13 +01:00
/ * !
* Draggabilly v2 . 3.0
* Make that shiz draggable
* https : //draggabilly.desandro.com
* MIT license
* /
2021-03-29 22:01:01 +02:00
( 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 ,
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 ;
2019-12-23 11:52:45 +01:00
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" >
2020-11-25 20:25:55 +01:00
< div class = "note-tab-icon" > < / d i v >
2019-05-11 19:44:58 +02:00
< div class = "note-tab-title" > < / d i v >
< div class = "note-tab-drag-handle" > < / d i v >
2020-05-05 23:58:52 +02:00
< div class = "note-tab-close" title = "Close tab" data - trigger - command = "closeActiveTab" > < span > × < / s p a n > < / d i v >
2019-05-11 19:44:58 +02:00
< / d i v >
2019-05-12 10:11:41 +02:00
< / d i v > ` ;
2019-05-11 19:44:58 +02:00
2020-05-05 23:58:52 +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">
2019-12-23 11:52:45 +01:00
< div class = "tab-row-border" > < / d i v >
< / d i v > ` ;
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 ;
2020-08-27 22:03:56 +02:00
height : 36 px ;
2020-01-12 20:15:05 +01:00
width : 100 % ;
background : var ( -- main - background - color ) ;
overflow : hidden ;
margin - top : 2 px ;
}
2020-08-27 22:03:56 +02:00
2020-01-12 20:15:05 +01:00
. note - tab - row * {
box - sizing : inherit ;
font : inherit ;
}
2020-08-27 22:03:56 +02:00
2020-02-01 19:25:37 +01:00
. note - tab - row . note - tab - row - container {
2020-08-27 22:03:56 +02:00
box - sizing : border - box ;
2020-01-12 20:15:05 +01:00
position : relative ;
width : 100 % ;
height : 100 % ;
}
2020-08-27 22:03:56 +02:00
2020-01-12 20:15:05 +01:00
. note - tab - row . note - tab {
position : absolute ;
left : 0 ;
height : 33 px ;
width : 240 px ;
border : 0 ;
margin : 0 ;
z - index : 1 ;
pointer - events : none ;
}
. note - new - tab {
position : absolute ;
left : 0 ;
height : 33 px ;
width : 32 px ;
border : 0 ;
margin : 0 ;
z - index : 1 ;
text - align : center ;
font - size : 24 px ;
cursor : pointer ;
border - bottom : 1 px solid var ( -- main - border - color ) ;
2020-08-27 22:03:56 +02:00
box - sizing : border - box ;
2020-01-12 20:15:05 +01:00
}
. note - new - tab : hover {
background - color : var ( -- accented - background - color ) ;
border - radius : 5 px ;
}
. tab - row - filler {
2020-08-27 22:03:56 +02:00
box - sizing : border - box ;
2020-01-12 20:15:05 +01:00
- webkit - app - region : drag ;
position : absolute ;
left : 0 ;
2020-08-27 22:03:56 +02:00
height : 36 px ;
2020-01-12 20:15:05 +01:00
}
. tab - row - filler . tab - row - border {
background : linear - gradient ( to right , var ( -- main - border - color ) , transparent ) ;
height : 1 px ;
margin - top : 32 px ;
}
. 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 : 10 px ;
animation : note - tab - was - just - added 120 ms 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 : 5 px 8 px ;
border - top - left - radius : 8 px ;
border - top - right - radius : 8 px ;
overflow : hidden ;
pointer - events : all ;
background - color : var ( -- accented - background - color ) ;
border - bottom : 1 px solid var ( -- main - border - color ) ;
}
. note - tab - row . note - tab [ active ] . note - tab - wrapper {
background - color : var ( -- main - background - color ) ;
border : 1 px solid var ( -- main - border - color ) ;
border - bottom : 0 ;
font - weight : bold ;
}
. note - tab - row . note - tab [ is - mini ] . note - tab - wrapper {
padding - left : 2 px ;
padding - right : 2 px ;
}
. note - tab - row . note - tab . note - tab - title {
flex : 1 ;
vertical - align : top ;
overflow : hidden ;
white - space : nowrap ;
color : var ( -- muted - text - color ) ;
}
2020-11-25 20:25:55 +01:00
. note - tab - row . note - tab . note - tab - icon {
position : relative ;
top : - 1 px ;
padding - right : 3 px ;
}
2020-01-12 20:15:05 +01:00
. 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 : 8 px ;
border - top - right - radius : 8 px ;
}
. note - tab - row . note - tab . note - tab - close {
flex - grow : 0 ;
flex - shrink : 0 ;
border - radius : 50 % ;
z - index : 100 ;
width : 24 px ;
height : 24 px ;
text - align : center ;
}
. note - tab - row . note - tab . note - tab - close span {
font - size : 24 px ;
position : relative ;
top : - 6 px ;
}
. 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 120 ms ease - in - out ;
}
< / s t y l e >
2020-02-01 19:25:37 +01:00
< div class = "note-tab-row-container" > < / d i v >
2020-01-12 19:05:09 +01:00
< / d i v > ` ;
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
2019-05-11 21:27:27 +02: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 ( ) ;
2019-05-11 21:27:27 +02:00
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 ( ) ;
2021-05-22 12:26:45 +02:00
const ntxId = $ ( 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 : [
2020-05-05 19:30:03 +02:00
{ 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 } ) => {
2021-05-22 12:26:45 +02:00
this . triggerCommand ( command , { ntxId } ) ;
2020-01-12 19:05:09 +01:00
}
} ) ;
2020-01-12 20:15:05 +01:00
} ) ;
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 ( ) {
2019-07-20 22:01:50 +02:00
const resizeListener = _ => {
2019-05-11 19:44:58 +02:00
this . cleanUpPreviouslyDraggedTabs ( ) ;
this . layoutTabs ( ) ;
2019-07-20 22:01:50 +02:00
} ;
// ResizeObserver exists only in FF69
if ( typeof ResizeObserver !== "undefined" ) {
2020-01-15 22:11:30 +01:00
new ResizeObserver ( resizeListener ) . observe ( this . $widget [ 0 ] ) ;
2019-07-20 22:01:50 +02:00
}
else {
// for older firefox
2020-03-09 21:00:31 +01:00
window . addEventListener ( 'resize' , resizeListener ) ;
2019-07-20 22:01:50 +02:00
}
2019-04-30 22:31:12 +02:00
2020-02-16 19:54:11 +01:00
this . tabEls . forEach ( ( tabEl ) => this . setTabCloseEvent ( tabEl ) ) ;
2019-04-30 22:31:12 +02:00
}
2019-05-07 22:14:35 +02:00
setVisibility ( ) {
2020-01-15 22:11:30 +01:00
this . $widget . show ( ) ;
2019-05-07 22:14:35 +02:00
}
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
}
2021-05-22 12:26:45 +02:00
addTab ( ntxId ) {
const $tab = $ ( TAB _TPL ) . attr ( 'data-tab-id' , ntxId ) ;
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 ( ) ;
2020-02-16 19:54:11 +01:00
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
}
2020-05-05 23:58:52 +02:00
closeActiveTabCommand ( { $el } ) {
2021-05-22 12:26:45 +02:00
const ntxId = $el . closest ( ".note-tab" ) . attr ( 'data-tab-id' ) ;
2020-05-05 23:58:52 +02:00
2021-05-22 12:35:41 +02:00
appContext . tabManager . removeNoteContext ( ntxId ) ;
2020-05-05 23:58:52 +02:00
}
2019-05-15 21:50:27 +02:00
2020-05-05 23:58:52 +02:00
setTabCloseEvent ( $tab ) {
2020-01-20 22:35:52 +01:00
$tab . on ( 'mousedown' , e => {
2019-05-15 21:50:27 +02:00
if ( e . which === 2 ) {
2021-05-22 12:35:41 +02:00
appContext . tabManager . removeNoteContext ( $tab . attr ( 'data-tab-id' ) ) ;
2019-05-15 21:50:27 +02:00
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
}
2021-05-22 17:58:46 +02:00
activeContextChangedEvent ( ) {
2021-05-22 12:35:41 +02:00
let activeNoteContext = appContext . tabManager . getActiveContext ( ) ;
2020-02-09 21:13:05 +01:00
2021-05-22 12:26:45 +02:00
if ( ! activeNoteContext ) {
2020-03-07 20:41:03 +01:00
return ;
}
2021-05-22 12:26:45 +02:00
if ( activeNoteContext . mainNtxId ) {
activeNoteContext = appContext . tabManager . getNoteContextById ( activeNoteContext . mainNtxId ) ;
2021-05-19 23:00:03 +02:00
}
2021-05-22 12:26:45 +02:00
const tabEl = this . getTabById ( activeNoteContext . ntxId ) [ 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
}
2021-05-22 12:35:41 +02:00
newNoteContextCreatedEvent ( { noteContext } ) {
2021-05-22 12:26:45 +02:00
if ( ! noteContext . mainNtxId ) {
this . addTab ( noteContext . ntxId ) ;
2021-05-19 23:00:03 +02:00
}
2020-01-20 20:51:22 +01:00
}
2021-05-22 12:26:45 +02:00
removeTab ( ntxId ) {
const tabEl = this . getTabById ( ntxId ) [ 0 ] ;
2020-01-15 22:27:52 +01:00
2020-03-10 22:25:57 +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' ) ) ;
2019-05-07 21:34:01 +02:00
}
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
}
2021-05-22 12:26:45 +02:00
getTabById ( ntxId ) {
return this . $widget . find ( ` [data-tab-id=' ${ ntxId } '] ` ) ;
2020-01-19 18:05:06 +01:00
}
2020-11-24 23:24:05 +01:00
getTabId ( $tab ) {
return $tab . attr ( 'data-tab-id' ) ;
}
2021-05-22 12:35:41 +02:00
noteContextRemovedEvent ( { ntxIds } ) {
2021-05-22 12:26:45 +02:00
for ( const ntxId of ntxIds ) {
this . removeTab ( ntxId ) ;
2021-05-20 23:13:34 +02:00
}
2020-01-19 21:12:53 +01:00
}
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 ;
2019-05-11 21:27:27 +02:00
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' , _ => {
2021-05-22 12:35:41 +02:00
appContext . tabManager . activateNoteContext ( 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 ) ;
}
2021-03-21 23:35:13 +01:00
if ( Math . abs ( moveVector . y ) > 100 ) {
2021-05-22 12:26:45 +02:00
this . triggerCommand ( 'moveTabToNewWindow' , { ntxId : this . getTabId ( $ ( tabEl ) ) } ) ;
2021-03-21 23:35:13 +01:00
}
2020-02-01 22:29:32 +01:00
} ) ;
} ) ;
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
}
2021-05-22 12:26:45 +02:00
this . triggerEvent ( 'tabReorder' , { ntxIdsInOrder : 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 ) ;
2020-02-01 22:29:32 +01:00
closestIndex = i ;
2019-05-12 10:59:53 +02:00
}
} ) ;
return closestIndex ;
} ;
2020-01-19 21:24:14 +01:00
2021-05-22 12:35:41 +02:00
noteSwitchedAndActivatedEvent ( { noteContext } ) {
2021-05-22 17:58:46 +02:00
this . activeContextChangedEvent ( ) ;
2020-02-28 11:46:35 +01:00
2021-05-22 12:26:45 +02:00
this . updateTabById ( noteContext . mainNtxId || noteContext . ntxId ) ;
2020-02-28 00:31:12 +01:00
}
2021-05-22 12:35:41 +02:00
noteSwitchedEvent ( { noteContext } ) {
2021-05-22 12:26:45 +02:00
this . updateTabById ( noteContext . mainNtxId || noteContext . ntxId ) ;
2020-02-28 00:31:12 +01:00
}
2021-05-22 12:26:45 +02:00
updateTabById ( ntxId ) {
const $tab = this . getTabById ( ntxId ) ;
2020-01-19 21:24:14 +01:00
2021-05-22 12:26:45 +02:00
const { note } = appContext . tabManager . getNoteContextById ( ntxId ) ;
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 ) ;
}
}
2021-05-22 12:26:45 +02:00
const noteContext = appContext . tabManager . getNoteContextById ( this . getTabId ( $tab ) ) ;
2020-11-24 23:24:05 +01:00
2021-05-22 12:26:45 +02:00
if ( noteContext ) {
const hoistedNote = froca . getNoteFromCache ( noteContext . hoistedNoteId ) ;
2020-11-24 23:24:05 +01:00
if ( hoistedNote ) {
2020-11-25 20:25:55 +01:00
$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" ) ;
2020-11-24 23:24:05 +01:00
}
}
2020-05-12 12:45:32 +02:00
if ( ! note ) {
this . updateTitle ( $tab , 'New tab' ) ;
2020-05-12 13:40:42 +02:00
return ;
2020-05-12 12:45:32 +02:00
}
this . updateTitle ( $tab , note . title ) ;
2020-03-18 22:42:29 +01:00
$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 } ) {
2021-05-22 12:26:45 +02:00
for ( const noteContext of appContext . tabManager . noteContexts ) {
if ( ! noteContext . noteId ) {
2021-02-07 20:55:49 +01:00
continue ;
}
2021-05-22 12:26:45 +02:00
if ( loadResults . isNoteReloaded ( noteContext . noteId ) ||
2021-02-07 20:55:49 +01:00
loadResults . getAttributes ( ) . find ( attr =>
[ 'workspace' , 'workspaceIconClass' , 'workspaceTabBackgroundColor' ] . includes ( attr . name )
2021-05-22 12:26:45 +02:00
&& attr . isAffecting ( noteContext . note ) )
2021-02-07 20:55:49 +01:00
) {
2021-05-22 12:26:45 +02:00
const $tab = this . getTabById ( noteContext . ntxId ) ;
2020-02-01 19:25:37 +01:00
2021-05-22 12:26:45 +02:00
this . updateTab ( $tab , noteContext . note ) ;
2020-02-01 19:25:37 +01:00
}
}
}
2021-04-16 22:57:37 +02:00
frocaReloadedEvent ( ) {
2021-05-22 12:26:45 +02:00
for ( const noteContext of appContext . tabManager . noteContexts ) {
const $tab = this . getTabById ( noteContext . ntxId ) ;
2020-02-01 22:29:32 +01:00
2021-05-22 12:26:45 +02:00
this . updateTab ( $tab , noteContext . note ) ;
2020-02-01 22:29:32 +01:00
}
2020-01-24 15:44:24 +01:00
}
2021-02-07 21:27:09 +01:00
2021-05-22 12:26:45 +02:00
hoistedNoteChangedEvent ( { ntxId } ) {
const $tab = this . getTabById ( ntxId ) ;
2021-02-07 21:27:09 +01:00
if ( $tab ) {
2021-05-22 12:26:45 +02:00
const noteContext = appContext . tabManager . getNoteContextById ( ntxId ) ;
2021-02-07 21:27:09 +01:00
2021-05-22 12:26:45 +02:00
this . updateTab ( $tab , noteContext . note ) ;
2021-02-07 21:27:09 +01:00
}
}
2020-05-12 12:45:32 +02:00
}