diff --git a/spec/panel-container-element-spec.coffee b/spec/panel-container-element-spec.coffee index 3dd6b2b18..12e28e04d 100644 --- a/spec/panel-container-element-spec.coffee +++ b/spec/panel-container-element-spec.coffee @@ -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' diff --git a/spec/panel-element-spec.coffee b/spec/panel-element-spec.coffee index 7cf7f8d8b..c283e158d 100644 --- a/spec/panel-element-spec.coffee +++ b/spec/panel-element-spec.coffee @@ -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' diff --git a/spec/workspace-spec.coffee b/spec/workspace-spec.coffee index e844acecd..edd954acc 100644 --- a/spec/workspace-spec.coffee +++ b/spec/workspace-spec.coffee @@ -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 diff --git a/spec/workspace-view-spec.coffee b/spec/workspace-view-spec.coffee index ff0eff660..4b40b20c4 100644 --- a/spec/workspace-view-spec.coffee +++ b/spec/workspace-view-spec.coffee @@ -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 diff --git a/src/panel-container-element.coffee b/src/panel-container-element.coffee index fb5c6684e..d162686cf 100644 --- a/src/panel-container-element.coffee +++ b/src/panel-container-element.coffee @@ -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 diff --git a/src/panel-container.coffee b/src/panel-container.coffee index 4bf7e30ba..24d48eac1 100644 --- a/src/panel-container.coffee +++ b/src/panel-container.coffee @@ -34,6 +34,8 @@ class PanelContainer getLocation: -> @location + isModal: -> @location is 'modal' + getPanels: -> @panels addPanel: (panel) -> diff --git a/src/panel-element.coffee b/src/panel-element.coffee index 87d0fdafc..eec1d91b5 100644 --- a/src/panel-element.coffee +++ b/src/panel-element.coffee @@ -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)) diff --git a/src/panel.coffee b/src/panel.coffee index 048d6840e..365e4ad5e 100644 --- a/src/panel.coffee +++ b/src/panel.coffee @@ -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 diff --git a/src/workspace-element.coffee b/src/workspace-element.coffee index 73a9438af..3994ccfe9 100644 --- a/src/workspace-element.coffee +++ b/src/workspace-element.coffee @@ -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) -> diff --git a/src/workspace.coffee b/src/workspace.coffee index e4fa0ffb9..d88f9196a 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -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 diff --git a/static/overlay.less b/static/overlay.less index ee6f983ac..6bcd85ea7 100644 --- a/static/overlay.less +++ b/static/overlay.less @@ -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;