diff --git a/src/pane-container-element.coffee b/src/pane-container-element.coffee deleted file mode 100644 index 78e2fbad3..000000000 --- a/src/pane-container-element.coffee +++ /dev/null @@ -1,28 +0,0 @@ -{CompositeDisposable} = require 'event-kit' -_ = require 'underscore-plus' - -module.exports = -class PaneContainerElement extends HTMLElement - createdCallback: -> - @subscriptions = new CompositeDisposable - @classList.add 'panes' - - initialize: (@model, {@views}) -> - throw new Error("Must pass a views parameter when initializing PaneContainerElements") unless @views? - - @subscriptions.add @model.observeRoot(@rootChanged.bind(this)) - this - - rootChanged: (root) -> - focusedElement = document.activeElement if @hasFocus() - @firstChild?.remove() - if root? - view = @views.getView(root) - @appendChild(view) - focusedElement?.focus() - - hasFocus: -> - this is document.activeElement or @contains(document.activeElement) - - -module.exports = PaneContainerElement = document.registerElement 'atom-pane-container', prototype: PaneContainerElement.prototype diff --git a/src/pane-container-element.js b/src/pane-container-element.js new file mode 100644 index 000000000..7a2a88463 --- /dev/null +++ b/src/pane-container-element.js @@ -0,0 +1,40 @@ +const {CompositeDisposable} = require('event-kit') + +class PaneContainerElement extends HTMLElement { + createdCallback () { + this.subscriptions = new CompositeDisposable() + this.classList.add('panes') + } + + initialize (model, {views}) { + this.model = model + this.views = views + if (this.views == null) { + throw new Error('Must pass a views parameter when initializing PaneContainerElements') + } + this.subscriptions.add(this.model.observeRoot(this.rootChanged.bind(this))) + return this + } + + rootChanged (root) { + const focusedElement = this.hasFocus() ? document.activeElement : null + if (this.firstChild != null) { + this.firstChild.remove() + } + if (root != null) { + const view = this.views.getView(root) + this.appendChild(view) + if (focusedElement != null) { + focusedElement.focus() + } + } + } + + hasFocus () { + return this === document.activeElement || this.contains(document.activeElement) + } +} + +module.exports = document.registerElement('atom-pane-container', { + prototype: PaneContainerElement.prototype +}) diff --git a/src/pane-element.coffee b/src/pane-element.coffee deleted file mode 100644 index d68b3b834..000000000 --- a/src/pane-element.coffee +++ /dev/null @@ -1,139 +0,0 @@ -path = require 'path' -{CompositeDisposable} = require 'event-kit' - -class PaneElement extends HTMLElement - attached: false - - createdCallback: -> - @attached = false - @subscriptions = new CompositeDisposable - @inlineDisplayStyles = new WeakMap - - @initializeContent() - @subscribeToDOMEvents() - - attachedCallback: -> - @attached = true - @focus() if @model.isFocused() - - detachedCallback: -> - @attached = false - - initializeContent: -> - @setAttribute 'class', 'pane' - @setAttribute 'tabindex', -1 - @appendChild @itemViews = document.createElement('div') - @itemViews.setAttribute 'class', 'item-views' - - subscribeToDOMEvents: -> - handleFocus = (event) => - @model.focus() unless @isActivating or @model.isDestroyed() or @contains(event.relatedTarget) - if event.target is this and view = @getActiveView() - view.focus() - event.stopPropagation() - - handleBlur = (event) => - @model.blur() unless @contains(event.relatedTarget) - - handleDragOver = (event) -> - event.preventDefault() - event.stopPropagation() - - handleDrop = (event) => - event.preventDefault() - event.stopPropagation() - @getModel().activate() - pathsToOpen = Array::map.call event.dataTransfer.files, (file) -> file.path - @applicationDelegate.open({pathsToOpen}) if pathsToOpen.length > 0 - - @addEventListener 'focus', handleFocus, true - @addEventListener 'blur', handleBlur, true - @addEventListener 'dragover', handleDragOver - @addEventListener 'drop', handleDrop - - initialize: (@model, {@views, @applicationDelegate}) -> - throw new Error("Must pass a views parameter when initializing PaneElements") unless @views? - throw new Error("Must pass an applicationDelegate parameter when initializing PaneElements") unless @applicationDelegate? - - @subscriptions.add @model.onDidActivate(@activated.bind(this)) - @subscriptions.add @model.observeActive(@activeStatusChanged.bind(this)) - @subscriptions.add @model.observeActiveItem(@activeItemChanged.bind(this)) - @subscriptions.add @model.onDidRemoveItem(@itemRemoved.bind(this)) - @subscriptions.add @model.onDidDestroy(@paneDestroyed.bind(this)) - @subscriptions.add @model.observeFlexScale(@flexScaleChanged.bind(this)) - this - - getModel: -> @model - - activated: -> - @isActivating = true - @focus() unless @hasFocus() # Don't steal focus from children. - @isActivating = false - - activeStatusChanged: (active) -> - if active - @classList.add('active') - else - @classList.remove('active') - - activeItemChanged: (item) -> - delete @dataset.activeItemName - delete @dataset.activeItemPath - @changePathDisposable?.dispose() - - return unless item? - - hasFocus = @hasFocus() - itemView = @views.getView(item) - - if itemPath = item.getPath?() - @dataset.activeItemName = path.basename(itemPath) - @dataset.activeItemPath = itemPath - - if item.onDidChangePath? - @changePathDisposable = item.onDidChangePath => - itemPath = item.getPath() - @dataset.activeItemName = path.basename(itemPath) - @dataset.activeItemPath = itemPath - - unless @itemViews.contains(itemView) - @itemViews.appendChild(itemView) - - for child in @itemViews.children - if child is itemView - @showItemView(child) if @attached - else - @hideItemView(child) - - itemView.focus() if hasFocus - - showItemView: (itemView) -> - inlineDisplayStyle = @inlineDisplayStyles.get(itemView) - if inlineDisplayStyle? - itemView.style.display = inlineDisplayStyle - else - itemView.style.display = '' - - hideItemView: (itemView) -> - inlineDisplayStyle = itemView.style.display - unless inlineDisplayStyle is 'none' - @inlineDisplayStyles.set(itemView, inlineDisplayStyle) if inlineDisplayStyle? - itemView.style.display = 'none' - - itemRemoved: ({item, index, destroyed}) -> - if viewToRemove = @views.getView(item) - viewToRemove.remove() - - paneDestroyed: -> - @subscriptions.dispose() - @changePathDisposable?.dispose() - - flexScaleChanged: (flexScale) -> - @style.flexGrow = flexScale - - getActiveView: -> @views.getView(@model.getActiveItem()) - - hasFocus: -> - this is document.activeElement or @contains(document.activeElement) - -module.exports = PaneElement = document.registerElement 'atom-pane', prototype: PaneElement.prototype diff --git a/src/pane-element.js b/src/pane-element.js new file mode 100644 index 000000000..2c9f4eeef --- /dev/null +++ b/src/pane-element.js @@ -0,0 +1,218 @@ +const path = require('path') +const {CompositeDisposable} = require('event-kit') + +class PaneElement extends HTMLElement { + createdCallback () { + this.attached = false + this.subscriptions = new CompositeDisposable() + this.inlineDisplayStyles = new WeakMap() + this.initializeContent() + this.subscribeToDOMEvents() + } + + attachedCallback () { + this.attached = true + if (this.model.isFocused()) { + this.focus() + } + } + + detachedCallback () { + this.attached = false + } + + initializeContent () { + this.setAttribute('class', 'pane') + this.setAttribute('tabindex', -1) + this.itemViews = document.createElement('div') + this.appendChild(this.itemViews) + this.itemViews.setAttribute('class', 'item-views') + } + + subscribeToDOMEvents () { + const handleFocus = event => { + if ( + !( + this.isActivating || + this.model.isDestroyed() || + this.contains(event.relatedTarget) + ) + ) { + this.model.focus() + } + if (event.target !== this) return + const view = this.getActiveView() + if (view) { + view.focus() + event.stopPropagation() + } + } + const handleBlur = event => { + if (!this.contains(event.relatedTarget)) { + this.model.blur() + } + } + const handleDragOver = event => { + event.preventDefault() + event.stopPropagation() + } + const handleDrop = event => { + event.preventDefault() + event.stopPropagation() + this.getModel().activate() + const pathsToOpen = [...event.dataTransfer.files].map(file => file.path) + if (pathsToOpen.length > 0) { + this.applicationDelegate.open({pathsToOpen}) + } + } + this.addEventListener('focus', handleFocus, true) + this.addEventListener('blur', handleBlur, true) + this.addEventListener('dragover', handleDragOver) + this.addEventListener('drop', handleDrop) + } + + initialize (model, {views, applicationDelegate}) { + this.model = model + this.views = views + this.applicationDelegate = applicationDelegate + if (this.views == null) { + throw new Error( + 'Must pass a views parameter when initializing PaneElements' + ) + } + if (this.applicationDelegate == null) { + throw new Error( + 'Must pass an applicationDelegate parameter when initializing PaneElements' + ) + } + this.subscriptions.add(this.model.onDidActivate(this.activated.bind(this))) + this.subscriptions.add( + this.model.observeActive(this.activeStatusChanged.bind(this)) + ) + this.subscriptions.add( + this.model.observeActiveItem(this.activeItemChanged.bind(this)) + ) + this.subscriptions.add( + this.model.onDidRemoveItem(this.itemRemoved.bind(this)) + ) + this.subscriptions.add( + this.model.onDidDestroy(this.paneDestroyed.bind(this)) + ) + this.subscriptions.add( + this.model.observeFlexScale(this.flexScaleChanged.bind(this)) + ) + return this + } + + getModel () { + return this.model + } + + activated () { + this.isActivating = true + if (!this.hasFocus()) { + // Don't steal focus from children. + this.focus() + } + this.isActivating = false + } + + activeStatusChanged (active) { + if (active) { + this.classList.add('active') + } else { + this.classList.remove('active') + } + } + + activeItemChanged (item) { + delete this.dataset.activeItemName + delete this.dataset.activeItemPath + if (this.changePathDisposable != null) { + this.changePathDisposable.dispose() + } + if (item == null) { + return + } + const hasFocus = this.hasFocus() + const itemView = this.views.getView(item) + const itemPath = typeof item.getPath === 'function' ? item.getPath() : null + if (itemPath) { + this.dataset.activeItemName = path.basename(itemPath) + this.dataset.activeItemPath = itemPath + if (item.onDidChangePath != null) { + this.changePathDisposable = item.onDidChangePath(() => { + const itemPath = item.getPath() + this.dataset.activeItemName = path.basename(itemPath) + this.dataset.activeItemPath = itemPath + }) + } + } + if (!this.itemViews.contains(itemView)) { + this.itemViews.appendChild(itemView) + } + for (const child of this.itemViews.children) { + if (child === itemView) { + if (this.attached) { + this.showItemView(child) + } + } else { + this.hideItemView(child) + } + } + if (hasFocus) { + itemView.focus() + } + } + + showItemView (itemView) { + const inlineDisplayStyle = this.inlineDisplayStyles.get(itemView) + if (inlineDisplayStyle != null) { + itemView.style.display = inlineDisplayStyle + } else { + itemView.style.display = '' + } + } + + hideItemView (itemView) { + const inlineDisplayStyle = itemView.style.display + if (inlineDisplayStyle !== 'none') { + if (inlineDisplayStyle != null) { + this.inlineDisplayStyles.set(itemView, inlineDisplayStyle) + } + itemView.style.display = 'none' + } + } + + itemRemoved ({item, index, destroyed}) { + const viewToRemove = this.views.getView(item) + if (viewToRemove) { + viewToRemove.remove() + } + } + + paneDestroyed () { + this.subscriptions.dispose() + if (this.changePathDisposable != null) { + this.changePathDisposable.dispose() + } + } + + flexScaleChanged (flexScale) { + this.style.flexGrow = flexScale + } + + getActiveView () { + return this.views.getView(this.model.getActiveItem()) + } + + hasFocus () { + return ( + this === document.activeElement || this.contains(document.activeElement) + ) + } +} + +module.exports = document.registerElement('atom-pane', { + prototype: PaneElement.prototype +})