Merge pull request #4061 from atom/bo-modal-panel

Modal panel API
This commit is contained in:
Ben Ogle
2014-11-04 15:34:43 -08:00
11 changed files with 113 additions and 21 deletions

View File

@@ -49,6 +49,7 @@ describe "PanelContainerElement", ->
panel1 = new Panel({viewRegistry, item: new TestPanelContainerItem(), location: 'left'})
container.addPanel(panel1)
expect(element.childNodes.length).toBe 1
expect(element.childNodes[0].getAttribute('location')).toBe 'left'
expect(element.childNodes[0].tagName).toBe 'ATOM-PANEL'
@@ -56,8 +57,34 @@ describe "PanelContainerElement", ->
container.addPanel(panel2)
expect(element.childNodes.length).toBe 2
expect(panel1.getView().style.display).not.toBe 'none'
expect(panel2.getView().style.display).not.toBe 'none'
panel1.destroy()
expect(element.childNodes.length).toBe 1
panel2.destroy()
expect(element.childNodes.length).toBe 0
describe "when the container is modal", ->
beforeEach ->
container = new PanelContainer({viewRegistry, location: 'modal'})
element = container.getView()
jasmineContent.appendChild(element)
it "allows only one panel to be visible at a time", ->
panel1 = new Panel({viewRegistry, item: new TestPanelContainerItem()})
container.addPanel(panel1)
expect(panel1.getView().style.display).not.toBe 'none'
panel2 = new Panel({viewRegistry, item: new TestPanelContainerItem()})
container.addPanel(panel2)
expect(panel1.getView().style.display).toBe 'none'
expect(panel2.getView().style.display).not.toBe 'none'
panel1.show()
expect(panel1.getView().style.display).not.toBe 'none'
expect(panel2.getView().style.display).toBe 'none'

View File

@@ -54,3 +54,12 @@ describe "PanelElement", ->
panel.show()
expect(element.style.display).not.toBe 'none'
describe "when a class name is specified", ->
it 'initially renders panel created with visibile: false', ->
panel = new Panel({viewRegistry, className: 'some classes', item: new TestPanelItem})
element = panel.getView()
jasmineContent.appendChild(element)
expect(element).toHaveClass 'some'
expect(element).toHaveClass 'classes'

View File

@@ -492,3 +492,12 @@ describe "Workspace", ->
expect(panel).toBeDefined()
expect(addPanelSpy).toHaveBeenCalledWith({panel, index: 0})
describe '::addModalPanel(model)', ->
it 'adds a panel to the correct panel container', ->
atom.workspace.panelContainers.modal.onDidAddPanel addPanelSpy = jasmine.createSpy()
panel = atom.workspace.addModalPanel(item: new TestPanel())
expect(panel).toBeDefined()
expect(addPanelSpy).toHaveBeenCalledWith({panel, index: 0})
expect(panel.getClassName()).toBe 'overlay from-top' # the default

View File

@@ -286,3 +286,6 @@ describe "WorkspaceView", ->
bottomContainer = workspaceElement.querySelector('atom-panel-container[location="bottom"]')
expect(topContainer.nextSibling).toBe workspaceElement.paneContainer
expect(bottomContainer.previousSibling).toBe workspaceElement.paneContainer
modalContainer = workspaceElement.querySelector('atom-panel-container[location="modal"]')
expect(modalContainer.parentNode).toBe workspaceElement

View File

@@ -14,11 +14,18 @@ class PanelContainerElement extends HTMLElement
@setAttribute('location', @model.getLocation())
panelAdded: ({panel, index}) ->
panelElement = panel.getView()
panelElement.setAttribute('location', @model.getLocation())
if index >= @childNodes.length
@appendChild(panel.getView())
@appendChild(panelElement)
else
referenceItem = @childNodes[index + 1]
@insertBefore(panel.getView(), referenceItem)
@insertBefore(panelElement, referenceItem)
if @model.isModal()
@hideAllPanelsExcept(panel)
@subscriptions.add panel.onDidChangeVisible (visible) =>
@hideAllPanelsExcept(panel) if visible
panelRemoved: ({panel, index}) ->
@removeChild(@childNodes[index])
@@ -27,4 +34,9 @@ class PanelContainerElement extends HTMLElement
@subscriptions.dispose()
@parentNode?.removeChild(this)
hideAllPanelsExcept: (excludedPanel) ->
for panel in @model.getPanels()
panel.hide() unless panel is excludedPanel
return
module.exports = PanelContainerElement = document.registerElement 'atom-panel-container', prototype: PanelContainerElement.prototype

View File

@@ -34,6 +34,8 @@ class PanelContainer
getLocation: -> @location
isModal: -> @location is 'modal'
getPanels: -> @panels
addPanel: (panel) ->

View File

@@ -12,6 +12,7 @@ class PanelElement extends HTMLElement
@appendChild(view)
callAttachHooks(view) # for backward compatibility with SpacePen views
@classList.add(@model.getClassName().split(' ')...) if @model.getClassName()?
@subscriptions.add @model.onDidChangeVisible(@visibleChanged.bind(this))
@subscriptions.add @model.onDidDestroy(@destroyed.bind(this))

View File

@@ -14,7 +14,7 @@ class Panel
Section: Construction and Destruction
###
constructor: ({@viewRegistry, @item, @visible, @priority}) ->
constructor: ({@viewRegistry, @item, @visible, @priority, @className}) ->
@emitter = new Emitter
@visible ?= true
@priority ?= 100
@@ -22,6 +22,7 @@ class Panel
# Public: Destroy and remove this panel from the UI.
destroy: ->
@emitter.emit 'did-destroy', this
@emitter.dispose()
###
Section: Event Subscription
@@ -62,6 +63,8 @@ class Panel
# Public: Returns a {Number} indicating this panel's priority.
getPriority: -> @priority
getClassName: -> @className
# Public: Returns a {Boolean} true when the panel is visible.
isVisible: -> @visible

View File

@@ -77,6 +77,7 @@ class WorkspaceElement extends HTMLElement
left: @model.panelContainers.left.getView()
right: @model.panelContainers.right.getView()
bottom: @model.panelContainers.bottom.getView()
modal: @model.panelContainers.modal.getView()
@horizontalAxis.insertBefore(@panelContainers.left, @verticalAxis)
@horizontalAxis.appendChild(@panelContainers.right)
@@ -84,6 +85,8 @@ class WorkspaceElement extends HTMLElement
@verticalAxis.insertBefore(@panelContainers.top, @paneContainer)
@verticalAxis.appendChild(@panelContainers.bottom)
@appendChild(@panelContainers.modal)
@__spacePenView.setModel(@model)
setTextEditorFontSize: (fontSize) ->

View File

@@ -54,6 +54,7 @@ class Workspace extends Model
left: new PanelContainer({viewRegistry, location: 'left'})
right: new PanelContainer({viewRegistry, location: 'right'})
bottom: new PanelContainer({viewRegistry, location: 'bottom'})
modal: new PanelContainer({viewRegistry, location: 'modal'})
@subscribeToActiveItem()
@@ -660,6 +661,23 @@ class Workspace extends Model
addTopPanel: (options) ->
@addPanel('top', options)
# Essential: Adds a panel item as a modal dialog.
#
# * `options` {Object}
# * `item` Your panel content. It can be DOM element, a jQuery element, or
# a model with a view registered via {ViewRegistry::addViewProvider}. We recommend the
# latter. See {ViewRegistry::addViewProvider} for more information.
# * `visible` (optional) {Boolean} false if you want the panel to initially be hidden
# (default: true)
# * `priority` (optional) {Number} Determines stacking order. Lower priority items are
# forced closer to the edges of the window. (default: 100)
#
# Returns a {Panel}
addModalPanel: (options={}) ->
# TODO: remove these default classes. They are to supoprt existing themes.
options.className ?= 'overlay from-top'
@addPanel('modal', options)
addPanel: (location, options) ->
options ?= {}
options.viewRegistry = atom.views

View File

@@ -1,6 +1,7 @@
@import "ui-variables";
.overlay {
.overlay,
atom-panel[location="modal"] {
position: absolute;
left: 50%;
width: 500px;
@@ -8,7 +9,9 @@
z-index: 9999;
box-sizing: border-box;
background-color: #fff;
color: @text-color;
background-color: @overlay-background-color;
padding: 10px;
h1 {
@@ -21,30 +24,32 @@
h2 {
font-size: 1.3em;
}
atom-text-editor.mini {
margin-bottom: 10px;
}
.message {
padding-top: 5px;
font-size: 11px;
}
&.mini {
width: 200px;
margin-left: -100px;
font-size: 12px;
}
}
.overlay atom-text-editor.mini {
margin-bottom: 10px;
}
.overlay .message {
padding-top: 5px;
font-size: 11px;
}
.overlay.mini {
width: 200px;
margin-left: -100px;
font-size: 12px;
}
.overlay.from-top {
.overlay.from-top, // TODO: Remove the .from-* classes
atom-panel[location="modal"] {
top: 0;
border-top: none;
border-top-left-radius: 0;
border-top-right-radius: 0;
}
// TODO: Remove these!
.overlay.from-bottom {
bottom: 0;
border-bottom: none;