mirror of
https://github.com/atom/atom.git
synced 2026-01-24 14:28:14 -05:00
Allow tabs to be moved to the beginning and end of the tab bar
Closes #409 (409, puts grease on the run! http://www.dailymotion.com/video/xxsbbb_formula-409-1990-puts-grease-on-the-run-commercial-2_tv#.UVocOZbF30Y)
This commit is contained in:
@@ -7,38 +7,40 @@ class SortableList extends View
|
||||
|
||||
initialize: ->
|
||||
@on 'dragstart', '.sortable', @onDragStart
|
||||
@on 'dragend', '.sortable', @onDragEnd
|
||||
@on 'dragover', '.sortable', @onDragOver
|
||||
@on 'dragenter', '.sortable', @onDragEnter
|
||||
@on 'dragleave', '.sortable', @onDragLeave
|
||||
@on 'drop', '.sortable', @onDrop
|
||||
@on 'dragend', '.sortable', @onDragEnd
|
||||
@on 'dragover', @onDragOver
|
||||
@on 'drop', @onDrop
|
||||
|
||||
onDragStart: (event) =>
|
||||
unless @shouldAllowDrag(event)
|
||||
event.preventDefault()
|
||||
return
|
||||
|
||||
el = @getSortableElement(event)
|
||||
el = $(event.target).closest('.sortable')
|
||||
el.addClass 'is-dragging'
|
||||
event.originalEvent.dataTransfer.setData 'sortable-index', el.index()
|
||||
|
||||
onDragEnd: (event) =>
|
||||
@getSortableElement(event).removeClass 'is-dragging'
|
||||
|
||||
onDragEnter: (event) =>
|
||||
event.preventDefault()
|
||||
@find(".is-dragging").removeClass 'is-dragging'
|
||||
|
||||
onDragOver: (event) =>
|
||||
event.preventDefault()
|
||||
@getSortableElement(event).addClass 'is-drop-target'
|
||||
currentDropTargetIndex = @find(".is-drop-target").index()
|
||||
newDropTargetIndex = @getDropTargetIndex(event)
|
||||
|
||||
onDragLeave: (event) =>
|
||||
@getSortableElement(event).removeClass 'is-drop-target'
|
||||
if newDropTargetIndex != currentDropTargetIndex
|
||||
@children().removeClass 'is-drop-target drop-target-is-after'
|
||||
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) =>
|
||||
return false if !@shouldAllowDrop(event)
|
||||
event.stopPropagation()
|
||||
@find('.is-drop-target').removeClass 'is-drop-target'
|
||||
@children('.is-drop-target').removeClass 'is-drop-target'
|
||||
@children('.drop-target-is-after').removeClass 'drop-target-is-after'
|
||||
|
||||
shouldAllowDrag: (event) ->
|
||||
true
|
||||
@@ -46,9 +48,15 @@ class SortableList extends View
|
||||
shouldAllowDrop: (event) ->
|
||||
true
|
||||
|
||||
getDroppedElement: (event) ->
|
||||
index = event.originalEvent.dataTransfer.getData('sortable-index')
|
||||
@find(".sortable:eq(#{index})")
|
||||
getDropTargetIndex: (event) ->
|
||||
el = $(event.target).closest('.sortable')
|
||||
el = $(event.target).find('.sortable').last() if el.length == 0
|
||||
|
||||
getSortableElement: (event) ->
|
||||
$(event.target).closest('.sortable')
|
||||
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
|
||||
|
||||
@@ -86,15 +86,15 @@ class TabBarView extends SortableList
|
||||
fromIndex = parseInt(dataTransfer.getData('sortable-index'))
|
||||
fromPaneIndex = parseInt(dataTransfer.getData('from-pane-index'))
|
||||
fromPane = @paneContainer.paneAtIndex(fromPaneIndex)
|
||||
toIndex = @getSortableElement(event).index()
|
||||
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
|
||||
toIndex-- if fromIndex < toIndex
|
||||
toPane.moveItem(item, toIndex)
|
||||
else
|
||||
fromPane.moveItemToPane(item, toPane, toIndex)
|
||||
fromPane.moveItemToPane(item, toPane, toIndex--)
|
||||
toPane.showItem(item)
|
||||
toPane.focus()
|
||||
|
||||
@@ -235,6 +235,20 @@ describe "TabBarView", ->
|
||||
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] = []
|
||||
|
||||
|
||||
@@ -83,11 +83,10 @@
|
||||
|
||||
}
|
||||
|
||||
.tab.is-drop-target:after {
|
||||
.tab.is-drop-target:before, .tab.drop-target-is-after:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: -2px;
|
||||
content: "";
|
||||
z-index: 999;
|
||||
display: inline-block;
|
||||
width: 2px;
|
||||
@@ -96,15 +95,30 @@
|
||||
background: #0098ff;
|
||||
}
|
||||
|
||||
.tab.is-drop-target:before {
|
||||
.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;
|
||||
right: -4px;
|
||||
top: 30px;
|
||||
border-radius: 4px;
|
||||
z-index: 9999;
|
||||
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