mirror of
https://github.com/atom/atom.git
synced 2026-01-23 13:58:08 -05:00
Pull out tabs package into a separate repo
This commit is contained in:
@@ -83,6 +83,7 @@
|
||||
"package-generator": "0.1.0",
|
||||
"spell-check": "0.1.0",
|
||||
"symbols-view": "0.1.0",
|
||||
"tabs": "0.1.0",
|
||||
"terminal": "0.3.0",
|
||||
"toml": "0.1.0",
|
||||
"whitespace": "0.1.0",
|
||||
|
||||
@@ -1,160 +0,0 @@
|
||||
$ = require 'jquery'
|
||||
{View} = require 'space-pen'
|
||||
_ = require 'underscore'
|
||||
TabView = require './tab-view'
|
||||
|
||||
module.exports =
|
||||
class TabBarView extends View
|
||||
@content: ->
|
||||
@ul tabindex: -1, class: "list-inline tabs"
|
||||
|
||||
initialize: (@pane) ->
|
||||
@on 'dragstart', '.sortable', @onDragStart
|
||||
@on 'dragend', '.sortable', @onDragEnd
|
||||
@on 'dragover', @onDragOver
|
||||
@on 'drop', @onDrop
|
||||
|
||||
@paneContainer = @pane.getContainer()
|
||||
@addTabForItem(item) for item in @pane.getItems()
|
||||
|
||||
@pane.on 'pane:item-added', (e, item, index) => @addTabForItem(item, index)
|
||||
@pane.on 'pane:item-moved', (e, item, index) => @moveItemTabToIndex(item, index)
|
||||
@pane.on 'pane:item-removed', (e, item) => @removeTabForItem(item)
|
||||
@pane.on 'pane:active-item-changed', => @updateActiveTab()
|
||||
|
||||
@updateActiveTab()
|
||||
|
||||
@on 'click', '.tab', (e) =>
|
||||
tab = $(e.target).closest('.tab').view()
|
||||
@pane.showItem(tab.item)
|
||||
@pane.focus()
|
||||
|
||||
@on 'click', '.tab .close-icon', (e) =>
|
||||
tab = $(e.target).closest('.tab').view()
|
||||
@pane.destroyItem(tab.item)
|
||||
false
|
||||
|
||||
@pane.prepend(this)
|
||||
|
||||
addTabForItem: (item, index) ->
|
||||
@insertTabAtIndex(new TabView(item, @pane), index)
|
||||
|
||||
moveItemTabToIndex: (item, index) ->
|
||||
tab = @tabForItem(item)
|
||||
tab.detach()
|
||||
@insertTabAtIndex(tab, index)
|
||||
|
||||
insertTabAtIndex: (tab, index) ->
|
||||
followingTab = @tabAtIndex(index) if index?
|
||||
if followingTab
|
||||
tab.insertBefore(followingTab)
|
||||
else
|
||||
@append(tab)
|
||||
tab.updateTitle()
|
||||
|
||||
removeTabForItem: (item) ->
|
||||
@tabForItem(item).remove()
|
||||
tab.updateTitle() for tab in @getTabs()
|
||||
|
||||
getTabs: ->
|
||||
@children('.tab').toArray().map (elt) -> $(elt).view()
|
||||
|
||||
tabAtIndex: (index) ->
|
||||
@children(".tab:eq(#{index})").view()
|
||||
|
||||
tabForItem: (item) ->
|
||||
_.detect @getTabs(), (tab) -> tab.item is item
|
||||
|
||||
setActiveTab: (tabView) ->
|
||||
unless tabView.hasClass('active')
|
||||
@find(".tab.active").removeClass('active')
|
||||
tabView.addClass('active')
|
||||
|
||||
updateActiveTab: ->
|
||||
@setActiveTab(@tabForItem(@pane.activeItem))
|
||||
|
||||
shouldAllowDrag: ->
|
||||
(@paneContainer.getPanes().length > 1) or (@pane.getItems().length > 1)
|
||||
|
||||
onDragStart: (event) =>
|
||||
if @shouldAllowDrag()
|
||||
event.originalEvent.dataTransfer.setData 'atom-event', 'true'
|
||||
|
||||
el = $(event.target).closest('.sortable')
|
||||
el.addClass 'is-dragging'
|
||||
event.originalEvent.dataTransfer.setData 'sortable-index', el.index()
|
||||
|
||||
pane = $(event.target).closest('.pane')
|
||||
paneIndex = @paneContainer.indexOfPane(pane)
|
||||
event.originalEvent.dataTransfer.setData 'from-pane-index', paneIndex
|
||||
|
||||
item = @pane.getItems()[el.index()]
|
||||
if item.getPath?
|
||||
event.originalEvent.dataTransfer.setData 'text/uri-list', 'file://' + item.getPath()
|
||||
event.originalEvent.dataTransfer.setData 'text/plain', item.getPath()
|
||||
|
||||
onDragEnd: (event) =>
|
||||
@find(".is-dragging").removeClass 'is-dragging'
|
||||
@removeDropTargetClasses()
|
||||
|
||||
onDragOver: (event) =>
|
||||
unless event.originalEvent.dataTransfer.getData('atom-event') is 'true'
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
return
|
||||
|
||||
@removeDropTargetClasses()
|
||||
|
||||
event.preventDefault()
|
||||
newDropTargetIndex = @getDropTargetIndex(event)
|
||||
|
||||
sortableObjects = @find(".sortable")
|
||||
if newDropTargetIndex < sortableObjects.length
|
||||
sortableObjects.eq(newDropTargetIndex).addClass 'is-drop-target'
|
||||
else
|
||||
sortableObjects.eq(newDropTargetIndex - 1).addClass 'drop-target-is-after'
|
||||
|
||||
onDrop: (event) =>
|
||||
unless event.originalEvent.dataTransfer.getData('atom-event') is 'true'
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
return
|
||||
|
||||
@find(".is-dragging").removeClass 'is-dragging'
|
||||
@removeDropTargetClasses()
|
||||
|
||||
event.stopPropagation()
|
||||
|
||||
dataTransfer = event.originalEvent.dataTransfer
|
||||
fromIndex = parseInt(dataTransfer.getData('sortable-index'))
|
||||
fromPaneIndex = parseInt(dataTransfer.getData('from-pane-index'))
|
||||
fromPane = @paneContainer.paneAtIndex(fromPaneIndex)
|
||||
toIndex = @getDropTargetIndex(event)
|
||||
toPane = $(event.target).closest('.pane').view()
|
||||
draggedTab = fromPane.find(".tabs .sortable:eq(#{fromIndex})").view()
|
||||
item = draggedTab.item
|
||||
|
||||
if toPane is fromPane
|
||||
toIndex-- if fromIndex < toIndex
|
||||
toPane.moveItem(item, toIndex)
|
||||
else
|
||||
fromPane.moveItemToPane(item, toPane, toIndex--)
|
||||
toPane.showItem(item)
|
||||
toPane.focus()
|
||||
|
||||
removeDropTargetClasses: ->
|
||||
rootView.find('.tabs .is-drop-target').removeClass 'is-drop-target'
|
||||
rootView.find('.tabs .drop-target-is-after').removeClass 'drop-target-is-after'
|
||||
|
||||
getDropTargetIndex: (event) ->
|
||||
el = $(event.target).closest('.sortable')
|
||||
el = $(event.target).find('.sortable').last() if el.length == 0
|
||||
|
||||
elementCenter = el.offset().left + el.width() / 2
|
||||
|
||||
if event.originalEvent.pageX < elementCenter
|
||||
el.index()
|
||||
else if el.next().length > 0
|
||||
el.next().index()
|
||||
else
|
||||
el.index() + 1
|
||||
@@ -1,56 +0,0 @@
|
||||
$ = require 'jquery'
|
||||
{View} = require 'space-pen'
|
||||
fsUtils = require 'fs-utils'
|
||||
path = require 'path'
|
||||
|
||||
module.exports =
|
||||
class TabView extends View
|
||||
@content: ->
|
||||
@li class: 'tab sortable', =>
|
||||
@div class: 'title', outlet: 'title'
|
||||
@div class: 'close-icon'
|
||||
|
||||
initialize: (@item, @pane) ->
|
||||
@item.on? 'title-changed', => @updateTitle()
|
||||
@item.on? 'modified-status-changed', => @updateModifiedStatus()
|
||||
@updateTitle()
|
||||
@updateModifiedStatus()
|
||||
|
||||
updateTitle: ->
|
||||
return if @updatingTitle
|
||||
@updatingTitle = true
|
||||
|
||||
title = @item.getTitle()
|
||||
useLongTitle = false
|
||||
for tab in @getSiblingTabs()
|
||||
if tab.item.getTitle() is title
|
||||
tab.updateTitle()
|
||||
useLongTitle = true
|
||||
title = @item.getLongTitle?() ? title if useLongTitle
|
||||
|
||||
@title.text(title)
|
||||
@updatingTitle = false
|
||||
|
||||
getSiblingTabs: ->
|
||||
@siblings('.tab').views()
|
||||
|
||||
updateModifiedStatus: ->
|
||||
if @item.isModified?()
|
||||
@addClass('modified') unless @isModified
|
||||
@isModified = true
|
||||
else
|
||||
@removeClass('modified') if @isModified
|
||||
@isModified = false
|
||||
|
||||
updateFileName: ->
|
||||
fileNameText = @editSession.buffer.getBaseName()
|
||||
if fileNameText?
|
||||
duplicates = @editor.getEditSessions().filter (session) -> fileNameText is session.buffer.getBaseName()
|
||||
if duplicates.length > 1
|
||||
directory = path.basename(path.dirname(@editSession.getPath()))
|
||||
fileNameText = "#{fileNameText} - #{directory}" if directory
|
||||
else
|
||||
fileNameText = 'untitled'
|
||||
|
||||
@fileName.text(fileNameText)
|
||||
@fileName.attr('title', @editSession.getPath())
|
||||
@@ -1,5 +0,0 @@
|
||||
TabBarView = require './tab-bar-view'
|
||||
|
||||
module.exports =
|
||||
activate: ->
|
||||
rootView.eachPane (pane) => new TabBarView(pane)
|
||||
@@ -1,2 +0,0 @@
|
||||
'main': './lib/tabs'
|
||||
'description': 'Display a selectable tab for each editor open.'
|
||||
@@ -1,304 +0,0 @@
|
||||
$ = require 'jquery'
|
||||
_ = require 'underscore'
|
||||
RootView = require 'root-view'
|
||||
Pane = require 'pane'
|
||||
PaneContainer = require 'pane-container'
|
||||
TabBarView = require 'tabs/lib/tab-bar-view'
|
||||
{View} = require 'space-pen'
|
||||
|
||||
describe "Tabs package main", ->
|
||||
beforeEach ->
|
||||
window.rootView = new RootView
|
||||
rootView.open('sample.js')
|
||||
atom.activatePackage("tabs")
|
||||
|
||||
describe ".activate()", ->
|
||||
it "appends a tab bar all existing and new panes", ->
|
||||
expect(rootView.panes.find('.pane').length).toBe 1
|
||||
expect(rootView.panes.find('.pane > .tabs').length).toBe 1
|
||||
rootView.getActivePane().splitRight()
|
||||
expect(rootView.find('.pane').length).toBe 2
|
||||
expect(rootView.panes.find('.pane > .tabs').length).toBe 2
|
||||
|
||||
describe "TabBarView", ->
|
||||
[item1, item2, editSession1, pane, tabBar] = []
|
||||
|
||||
class TestView extends View
|
||||
@deserialize: ({title, longTitle}) -> new TestView(title, longTitle)
|
||||
@content: (title) -> @div title
|
||||
initialize: (@title, @longTitle) ->
|
||||
getTitle: -> @title
|
||||
getLongTitle: -> @longTitle
|
||||
serialize: -> { deserializer: 'TestView', @title, @longTitle }
|
||||
|
||||
beforeEach ->
|
||||
window.rootView = new RootView
|
||||
registerDeserializer(TestView)
|
||||
item1 = new TestView('Item 1')
|
||||
item2 = new TestView('Item 2')
|
||||
editSession1 = project.open('sample.js')
|
||||
paneContainer = new PaneContainer
|
||||
pane = new Pane(item1, editSession1, item2)
|
||||
pane.showItem(item2)
|
||||
paneContainer.append(pane)
|
||||
tabBar = new TabBarView(pane)
|
||||
|
||||
afterEach ->
|
||||
unregisterDeserializer(TestView)
|
||||
|
||||
describe ".initialize(pane)", ->
|
||||
it "creates a tab for each item on the tab bar's parent pane", ->
|
||||
expect(pane.getItems().length).toBe 3
|
||||
expect(tabBar.find('.tab').length).toBe 3
|
||||
|
||||
expect(tabBar.find('.tab:eq(0) .title').text()).toBe item1.getTitle()
|
||||
expect(tabBar.find('.tab:eq(1) .title').text()).toBe editSession1.getTitle()
|
||||
expect(tabBar.find('.tab:eq(2) .title').text()).toBe item2.getTitle()
|
||||
|
||||
it "highlights the tab for the active pane item", ->
|
||||
expect(tabBar.find('.tab:eq(2)')).toHaveClass 'active'
|
||||
|
||||
describe "when the active pane item changes", ->
|
||||
it "highlights the tab for the new active pane item", ->
|
||||
pane.showItem(item1)
|
||||
expect(tabBar.find('.active').length).toBe 1
|
||||
expect(tabBar.find('.tab:eq(0)')).toHaveClass 'active'
|
||||
|
||||
pane.showItem(item2)
|
||||
expect(tabBar.find('.active').length).toBe 1
|
||||
expect(tabBar.find('.tab:eq(2)')).toHaveClass 'active'
|
||||
|
||||
describe "when a new item is added to the pane", ->
|
||||
it "adds a tab for the new item at the same index as the item in the pane", ->
|
||||
pane.showItem(item1)
|
||||
item3 = new TestView('Item 3')
|
||||
pane.showItem(item3)
|
||||
expect(tabBar.find('.tab').length).toBe 4
|
||||
expect(tabBar.tabAtIndex(1).find('.title')).toHaveText 'Item 3'
|
||||
|
||||
it "adds the 'modified' class to the new tab if the item is initially modified", ->
|
||||
editSession2 = project.open('sample.txt')
|
||||
editSession2.insertText('x')
|
||||
pane.showItem(editSession2)
|
||||
expect(tabBar.tabForItem(editSession2)).toHaveClass 'modified'
|
||||
|
||||
describe "when an item is removed from the pane", ->
|
||||
it "removes the item's tab from the tab bar", ->
|
||||
pane.removeItem(item2)
|
||||
expect(tabBar.getTabs().length).toBe 2
|
||||
expect(tabBar.find('.tab:contains(Item 2)')).not.toExist()
|
||||
|
||||
it "updates the titles of the remaining tabs", ->
|
||||
expect(tabBar.tabForItem(item2)).toHaveText 'Item 2'
|
||||
item2.longTitle = '2'
|
||||
item2a = new TestView('Item 2')
|
||||
item2a.longTitle = '2a'
|
||||
pane.showItem(item2a)
|
||||
expect(tabBar.tabForItem(item2)).toHaveText '2'
|
||||
expect(tabBar.tabForItem(item2a)).toHaveText '2a'
|
||||
pane.removeItem(item2a)
|
||||
expect(tabBar.tabForItem(item2)).toHaveText 'Item 2'
|
||||
|
||||
describe "when a tab is clicked", ->
|
||||
it "shows the associated item on the pane and focuses the pane", ->
|
||||
spyOn(pane, 'focus')
|
||||
|
||||
tabBar.tabAtIndex(0).click()
|
||||
expect(pane.activeItem).toBe pane.getItems()[0]
|
||||
|
||||
tabBar.tabAtIndex(2).click()
|
||||
expect(pane.activeItem).toBe pane.getItems()[2]
|
||||
|
||||
expect(pane.focus.callCount).toBe 2
|
||||
|
||||
describe "when a tab's close icon is clicked", ->
|
||||
it "destroys the tab's item on the pane", ->
|
||||
tabBar.tabForItem(editSession1).find('.close-icon').click()
|
||||
expect(pane.getItems().length).toBe 2
|
||||
expect(pane.getItems().indexOf(editSession1)).toBe -1
|
||||
expect(editSession1.destroyed).toBeTruthy()
|
||||
expect(tabBar.getTabs().length).toBe 2
|
||||
expect(tabBar.find('.tab:contains(sample.js)')).not.toExist()
|
||||
|
||||
describe "when a tab item's title changes", ->
|
||||
it "updates the title of the item's tab", ->
|
||||
editSession1.buffer.setPath('/this/is-a/test.txt')
|
||||
expect(tabBar.tabForItem(editSession1)).toHaveText 'test.txt'
|
||||
|
||||
describe "when two tabs have the same title", ->
|
||||
it "displays the long title on the tab if it's available from the item", ->
|
||||
item1.title = "Old Man"
|
||||
item1.longTitle = "Grumpy Old Man"
|
||||
item1.trigger 'title-changed'
|
||||
item2.title = "Old Man"
|
||||
item2.longTitle = "Jolly Old Man"
|
||||
item2.trigger 'title-changed'
|
||||
|
||||
expect(tabBar.tabForItem(item1)).toHaveText "Grumpy Old Man"
|
||||
expect(tabBar.tabForItem(item2)).toHaveText "Jolly Old Man"
|
||||
|
||||
item2.longTitle = undefined
|
||||
item2.trigger 'title-changed'
|
||||
|
||||
expect(tabBar.tabForItem(item1)).toHaveText "Grumpy Old Man"
|
||||
expect(tabBar.tabForItem(item2)).toHaveText "Old Man"
|
||||
|
||||
describe "when a tab item's modified status changes", ->
|
||||
it "adds or removes the 'modified' class to the tab based on the status", ->
|
||||
tab = tabBar.tabForItem(editSession1)
|
||||
expect(editSession1.isModified()).toBeFalsy()
|
||||
expect(tab).not.toHaveClass 'modified'
|
||||
|
||||
editSession1.insertText('x')
|
||||
advanceClock(editSession1.buffer.stoppedChangingDelay)
|
||||
expect(editSession1.isModified()).toBeTruthy()
|
||||
expect(tab).toHaveClass 'modified'
|
||||
|
||||
editSession1.undo()
|
||||
advanceClock(editSession1.buffer.stoppedChangingDelay)
|
||||
expect(editSession1.isModified()).toBeFalsy()
|
||||
expect(tab).not.toHaveClass 'modified'
|
||||
|
||||
describe "when a pane item moves to a new index", ->
|
||||
it "updates the order of the tabs to match the new item order", ->
|
||||
expect(tabBar.getTabs().map (tab) -> tab.text()).toEqual ["Item 1", "sample.js", "Item 2"]
|
||||
pane.moveItem(item2, 1)
|
||||
expect(tabBar.getTabs().map (tab) -> tab.text()).toEqual ["Item 1", "Item 2", "sample.js"]
|
||||
pane.moveItem(editSession1, 0)
|
||||
expect(tabBar.getTabs().map (tab) -> tab.text()).toEqual ["sample.js", "Item 1", "Item 2"]
|
||||
pane.moveItem(item1, 2)
|
||||
expect(tabBar.getTabs().map (tab) -> tab.text()).toEqual ["sample.js", "Item 2", "Item 1"]
|
||||
|
||||
describe "dragging and dropping tabs", ->
|
||||
buildDragEvents = (dragged, dropTarget) ->
|
||||
dataTransfer =
|
||||
data: {}
|
||||
setData: (key, value) -> @data[key] = value
|
||||
getData: (key) -> @data[key]
|
||||
|
||||
dragStartEvent = $.Event()
|
||||
dragStartEvent.target = dragged[0]
|
||||
dragStartEvent.originalEvent = { dataTransfer }
|
||||
|
||||
dropEvent = $.Event()
|
||||
dropEvent.target = dropTarget[0]
|
||||
dropEvent.originalEvent = { dataTransfer }
|
||||
|
||||
[dragStartEvent, dropEvent]
|
||||
|
||||
describe "when a tab is dragged within the same pane", ->
|
||||
describe "when it is dropped on tab that's later in the list", ->
|
||||
it "moves the tab and its item, shows the tab's item, and focuses the pane", ->
|
||||
expect(tabBar.getTabs().map (tab) -> tab.text()).toEqual ["Item 1", "sample.js", "Item 2"]
|
||||
expect(pane.getItems()).toEqual [item1, editSession1, item2]
|
||||
expect(pane.activeItem).toBe item2
|
||||
spyOn(pane, 'focus')
|
||||
|
||||
[dragStartEvent, dropEvent] = buildDragEvents(tabBar.tabAtIndex(0), tabBar.tabAtIndex(1))
|
||||
tabBar.onDragStart(dragStartEvent)
|
||||
tabBar.onDrop(dropEvent)
|
||||
|
||||
expect(tabBar.getTabs().map (tab) -> tab.text()).toEqual ["sample.js", "Item 1", "Item 2"]
|
||||
expect(pane.getItems()).toEqual [editSession1, item1, item2]
|
||||
expect(pane.activeItem).toBe item1
|
||||
expect(pane.focus).toHaveBeenCalled()
|
||||
|
||||
describe "when it is dropped on a tab that's earlier in the list", ->
|
||||
it "moves the tab and its item, shows the tab's item, and focuses the pane", ->
|
||||
expect(tabBar.getTabs().map (tab) -> tab.text()).toEqual ["Item 1", "sample.js", "Item 2"]
|
||||
expect(pane.getItems()).toEqual [item1, editSession1, item2]
|
||||
expect(pane.activeItem).toBe item2
|
||||
spyOn(pane, 'focus')
|
||||
|
||||
[dragStartEvent, dropEvent] = buildDragEvents(tabBar.tabAtIndex(2), tabBar.tabAtIndex(0))
|
||||
tabBar.onDragStart(dragStartEvent)
|
||||
tabBar.onDrop(dropEvent)
|
||||
|
||||
expect(tabBar.getTabs().map (tab) -> tab.text()).toEqual ["Item 1", "Item 2", "sample.js"]
|
||||
expect(pane.getItems()).toEqual [item1, item2, editSession1]
|
||||
expect(pane.activeItem).toBe item2
|
||||
expect(pane.focus).toHaveBeenCalled()
|
||||
|
||||
describe "when it is dropped on itself", ->
|
||||
it "doesn't move the tab or item, but does make it the active item and focuses the pane", ->
|
||||
expect(tabBar.getTabs().map (tab) -> tab.text()).toEqual ["Item 1", "sample.js", "Item 2"]
|
||||
expect(pane.getItems()).toEqual [item1, editSession1, item2]
|
||||
expect(pane.activeItem).toBe item2
|
||||
spyOn(pane, 'focus')
|
||||
|
||||
[dragStartEvent, dropEvent] = buildDragEvents(tabBar.tabAtIndex(0), tabBar.tabAtIndex(0))
|
||||
tabBar.onDragStart(dragStartEvent)
|
||||
tabBar.onDrop(dropEvent)
|
||||
|
||||
expect(tabBar.getTabs().map (tab) -> tab.text()).toEqual ["Item 1", "sample.js", "Item 2"]
|
||||
expect(pane.getItems()).toEqual [item1, editSession1, item2]
|
||||
expect(pane.activeItem).toBe item1
|
||||
expect(pane.focus).toHaveBeenCalled()
|
||||
|
||||
describe "when it is dropped on the tab bar", ->
|
||||
it "moves the tab and its item to the end", ->
|
||||
expect(tabBar.getTabs().map (tab) -> tab.text()).toEqual ["Item 1", "sample.js", "Item 2"]
|
||||
expect(pane.getItems()).toEqual [item1, editSession1, item2]
|
||||
expect(pane.activeItem).toBe item2
|
||||
spyOn(pane, 'focus')
|
||||
|
||||
[dragStartEvent, dropEvent] = buildDragEvents(tabBar.tabAtIndex(0), tabBar)
|
||||
tabBar.onDragStart(dragStartEvent)
|
||||
tabBar.onDrop(dropEvent)
|
||||
|
||||
expect(tabBar.getTabs().map (tab) -> tab.text()).toEqual ["sample.js", "Item 2", "Item 1"]
|
||||
expect(pane.getItems()).toEqual [editSession1, item2, item1]
|
||||
|
||||
describe "when a tab is dragged to a different pane", ->
|
||||
[pane2, tabBar2, item2b] = []
|
||||
|
||||
beforeEach ->
|
||||
pane2 = pane.splitRight()
|
||||
[item2b] = pane2.getItems()
|
||||
tabBar2 = new TabBarView(pane2)
|
||||
|
||||
it "removes the tab and item from their original pane and moves them to the target pane", ->
|
||||
expect(tabBar.getTabs().map (tab) -> tab.text()).toEqual ["Item 1", "sample.js", "Item 2"]
|
||||
expect(pane.getItems()).toEqual [item1, editSession1, item2]
|
||||
expect(pane.activeItem).toBe item2
|
||||
|
||||
expect(tabBar2.getTabs().map (tab) -> tab.text()).toEqual ["Item 2"]
|
||||
expect(pane2.getItems()).toEqual [item2b]
|
||||
expect(pane2.activeItem).toBe item2b
|
||||
spyOn(pane2, 'focus')
|
||||
|
||||
[dragStartEvent, dropEvent] = buildDragEvents(tabBar.tabAtIndex(0), tabBar2.tabAtIndex(0))
|
||||
tabBar.onDragStart(dragStartEvent)
|
||||
tabBar.onDrop(dropEvent)
|
||||
|
||||
expect(tabBar.getTabs().map (tab) -> tab.text()).toEqual ["sample.js", "Item 2"]
|
||||
expect(pane.getItems()).toEqual [editSession1, item2]
|
||||
expect(pane.activeItem).toBe item2
|
||||
|
||||
expect(tabBar2.getTabs().map (tab) -> tab.text()).toEqual ["Item 2", "Item 1"]
|
||||
expect(pane2.getItems()).toEqual [item2b, item1]
|
||||
expect(pane2.activeItem).toBe item1
|
||||
expect(pane2.focus).toHaveBeenCalled()
|
||||
|
||||
describe "when a non-tab is dragged to pane", ->
|
||||
it "has no effect", ->
|
||||
expect(tabBar.getTabs().map (tab) -> tab.text()).toEqual ["Item 1", "sample.js", "Item 2"]
|
||||
expect(pane.getItems()).toEqual [item1, editSession1, item2]
|
||||
expect(pane.activeItem).toBe item2
|
||||
spyOn(pane, 'focus')
|
||||
|
||||
[dragStartEvent, dropEvent] = buildDragEvents(tabBar.tabAtIndex(0), tabBar.tabAtIndex(0))
|
||||
tabBar.onDrop(dropEvent)
|
||||
|
||||
expect(tabBar.getTabs().map (tab) -> tab.text()).toEqual ["Item 1", "sample.js", "Item 2"]
|
||||
expect(pane.getItems()).toEqual [item1, editSession1, item2]
|
||||
expect(pane.activeItem).toBe item2
|
||||
expect(pane.focus).not.toHaveBeenCalled()
|
||||
|
||||
describe "when a tab is dragged out of application", ->
|
||||
it "should carry file's information", ->
|
||||
[dragStartEvent, dropEvent] = buildDragEvents(tabBar.tabAtIndex(1), tabBar.tabAtIndex(1))
|
||||
tabBar.onDragStart(dragStartEvent)
|
||||
|
||||
expect(dragStartEvent.originalEvent.dataTransfer.getData("text/plain")).toEqual editSession1.getPath()
|
||||
expect(dragStartEvent.originalEvent.dataTransfer.getData("text/uri-list")).toEqual 'file://' + editSession1.getPath()
|
||||
@@ -1,96 +0,0 @@
|
||||
@import "bootstrap/less/variables.less";
|
||||
@import "octicon-mixins.less";
|
||||
|
||||
@close-icon-size: 11px;
|
||||
|
||||
.tabs {
|
||||
display: -webkit-flex;
|
||||
-webkit-user-select: none;
|
||||
margin: 0;
|
||||
|
||||
.tab {
|
||||
line-height: 30px;
|
||||
font-size: 11px;
|
||||
position: relative;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px + @close-icon-size + 2px;
|
||||
-webkit-user-drag: element;
|
||||
-webkit-flex: 1;
|
||||
max-width: 175px;
|
||||
min-width: 40px;
|
||||
|
||||
&.active {
|
||||
-webkit-flex: 2;
|
||||
}
|
||||
|
||||
.title {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.close-icon {
|
||||
.octicon(x, @close-icon-size);
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&.modified:hover .close-icon {
|
||||
color: #66a6ff;
|
||||
}
|
||||
|
||||
&.modified:not(:hover) .close-icon {
|
||||
&:before { content: "" }
|
||||
top: 11px;
|
||||
right: 11px;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border: 2px solid #66a6ff;
|
||||
border-radius: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Drag and Drop */
|
||||
|
||||
.tab.is-drop-target:before, .tab.drop-target-is-after:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
z-index: 999;
|
||||
display: inline-block;
|
||||
width: 2px;
|
||||
height: 30px;
|
||||
display: inline-block;
|
||||
background: #0098ff;
|
||||
}
|
||||
|
||||
.tab.is-drop-target:after, .tab.drop-target-is-after:before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 30px;
|
||||
z-index: 9999;
|
||||
width: 4px;
|
||||
height: 4px;
|
||||
background: #0098ff;
|
||||
border-radius: 4px;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
.tab.is-drop-target:before {
|
||||
left: -2px;
|
||||
}
|
||||
|
||||
.tab.is-drop-target:after {
|
||||
left: -4px;
|
||||
}
|
||||
|
||||
.tab.drop-target-is-after:before {
|
||||
right: -4px;
|
||||
}
|
||||
|
||||
.tab.drop-target-is-after:after {
|
||||
right: -2px;
|
||||
}
|
||||
Reference in New Issue
Block a user