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" ;
import contextMenuService 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" ;
2020-01-12 19:05:09 +01:00
2020-02-16 20:09:59 +01: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 , require ( "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-emitter" ] , funct
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" >
< div class = "note-tab-title" > < / d i v >
< div class = "note-tab-drag-handle" > < / d i v >
2020-02-27 23:12:22 +01:00
< div class = "note-tab-close kb-in-title" title = "Close tab" data - 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-02-27 23:12:22 +01:00
const NEW _TAB _BUTTON _TPL = ` <div class="note-new-tab kb-in-title" data-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 ;
height : 34 px ;
min - height : 34 px ;
width : 100 % ;
background : var ( -- main - background - color ) ;
border - radius : 5 px 5 px 0 0 ;
overflow : hidden ;
margin - top : 2 px ;
}
. 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 : 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 ) ;
}
. note - new - tab : hover {
background - color : var ( -- accented - background - color ) ;
border - radius : 5 px ;
}
. tab - row - filler {
- webkit - app - region : drag ;
position : absolute ;
left : 0 ;
height : 33 px ;
}
. tab - row - filler . tab - row - border {
position : relative ;
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 ) ;
}
. 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 ( ) ;
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
contextMenuService . initContextMenu ( e , {
getContextMenuItems : ( ) => {
return [
{ title : "Close all tabs" , cmd : "removeAllTabs" , uiIcon : "empty" } ,
{ title : "Close all tabs except for this" , cmd : "removeAllTabsExceptForThis" , uiIcon : "empty" }
] ;
} ,
selectContextMenuItem : ( e , cmd ) => {
2020-02-16 19:54:11 +01:00
this . triggerCommand ( cmd , { 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 ( ) {
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-02-16 19:23:49 +01:00
window . addEventEvent ( '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
}
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 ( ) ;
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-02-16 19:54:11 +01:00
setTabCloseEvent ( $tab ) {
2020-01-20 22:35:52 +01:00
$tab . find ( '.note-tab-close' )
2020-02-16 18:11:32 +01:00
. on ( 'click' , _ => appContext . tabManager . removeTab ( $tab . attr ( 'data-tab-id' ) ) ) ;
2019-05-15 21:50:27 +02:00
2020-01-20 22:35:52 +01:00
$tab . on ( 'mousedown' , e => {
2019-05-15 21:50:27 +02:00
if ( e . which === 2 ) {
2020-02-16 18:11:32 +01:00
appContext . tabManager . removeTab ( $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
}
2020-02-16 19:23:49 +01:00
activeTabChangedEvent ( ) {
2020-02-16 18:11:32 +01:00
const newActiveTabId = appContext . tabManager . activeTabId ;
2020-02-09 21:13:05 +01:00
2020-01-24 22:30:17 +01:00
const tabEl = this . getTabById ( newActiveTabId ) [ 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-02-16 19:23:49 +01:00
newTabOpenedEvent ( { tabId } ) {
2020-01-20 20:51:22 +01:00
this . addTab ( tabId ) ;
}
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
2019-05-11 19:44:58 +02:00
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
}
2020-01-19 18:05:06 +01: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 ;
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' , _ => {
2020-02-16 18:11:32 +01:00
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 ) ;
}
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
}
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 12:58:55 +02:00
2020-02-16 19:54:11 +01:00
this . $newTab . on ( 'click' , _ => this . triggerCommand ( 'openNewTab' ) ) ;
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
2020-02-16 19:23:49 +01:00
tabNoteSwitchedEvent ( { tabId } ) {
2020-01-19 21:24:14 +01:00
const $tab = this . getTabById ( tabId ) ;
2020-02-16 18:11:32 +01:00
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-01-19 22:05:45 +01:00
if ( ! note || ! $tab . length ) {
2020-01-19 21:24:14 +01:00
return ;
}
2020-01-20 22:35:52 +01:00
this . updateTitle ( $tab , note . title ) ;
2020-01-19 21:24:14 +01:00
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-02-25 09:40:49 +01:00
note . getCssClass ( ) . then ( cssClass => $tab . addClass ( cssClass ) ) ;
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 } ) {
2020-02-16 18:11:32 +01:00
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 ( ) {
2020-02-16 18:11:32 +01:00
for ( const tabContext of appContext . tabManager . tabContexts ) {
2020-02-01 22:29:32 +01:00
const $tab = this . getTabById ( tabContext . tabId ) ;
this . updateTab ( $tab , tabContext . note ) ;
}
2020-01-24 15:44:24 +01:00
}
2020-01-12 19:05:09 +01:00
}