pane-element.js
1 const path = require('path'); 2 const { CompositeDisposable } = require('event-kit'); 3 4 class PaneElement extends HTMLElement { 5 createdCallback() { 6 this.attached = false; 7 this.subscriptions = new CompositeDisposable(); 8 this.inlineDisplayStyles = new WeakMap(); 9 this.initializeContent(); 10 this.subscribeToDOMEvents(); 11 } 12 13 attachedCallback() { 14 this.attached = true; 15 if (this.model.isFocused()) { 16 this.focus(); 17 } 18 } 19 20 detachedCallback() { 21 this.attached = false; 22 } 23 24 initializeContent() { 25 this.setAttribute('class', 'pane'); 26 this.setAttribute('tabindex', -1); 27 this.itemViews = document.createElement('div'); 28 this.appendChild(this.itemViews); 29 this.itemViews.setAttribute('class', 'item-views'); 30 } 31 32 subscribeToDOMEvents() { 33 const handleFocus = event => { 34 if ( 35 !( 36 this.isActivating || 37 this.model.isDestroyed() || 38 this.contains(event.relatedTarget) 39 ) 40 ) { 41 this.model.focus(); 42 } 43 if (event.target !== this) return; 44 const view = this.getActiveView(); 45 if (view) { 46 view.focus(); 47 event.stopPropagation(); 48 } 49 }; 50 const handleBlur = event => { 51 if (!this.contains(event.relatedTarget)) { 52 this.model.blur(); 53 } 54 }; 55 const handleDragOver = event => { 56 event.preventDefault(); 57 event.stopPropagation(); 58 }; 59 const handleDrop = event => { 60 event.preventDefault(); 61 event.stopPropagation(); 62 this.getModel().activate(); 63 const pathsToOpen = [...event.dataTransfer.files].map(file => file.path); 64 if (pathsToOpen.length > 0) { 65 this.applicationDelegate.open({ pathsToOpen, here: true }); 66 } 67 }; 68 this.addEventListener('focus', handleFocus, true); 69 this.addEventListener('blur', handleBlur, true); 70 this.addEventListener('dragover', handleDragOver); 71 this.addEventListener('drop', handleDrop); 72 } 73 74 initialize(model, { views, applicationDelegate }) { 75 this.model = model; 76 this.views = views; 77 this.applicationDelegate = applicationDelegate; 78 if (this.views == null) { 79 throw new Error( 80 'Must pass a views parameter when initializing PaneElements' 81 ); 82 } 83 if (this.applicationDelegate == null) { 84 throw new Error( 85 'Must pass an applicationDelegate parameter when initializing PaneElements' 86 ); 87 } 88 this.subscriptions.add(this.model.onDidActivate(this.activated.bind(this))); 89 this.subscriptions.add( 90 this.model.observeActive(this.activeStatusChanged.bind(this)) 91 ); 92 this.subscriptions.add( 93 this.model.observeActiveItem(this.activeItemChanged.bind(this)) 94 ); 95 this.subscriptions.add( 96 this.model.onDidRemoveItem(this.itemRemoved.bind(this)) 97 ); 98 this.subscriptions.add( 99 this.model.onDidDestroy(this.paneDestroyed.bind(this)) 100 ); 101 this.subscriptions.add( 102 this.model.observeFlexScale(this.flexScaleChanged.bind(this)) 103 ); 104 return this; 105 } 106 107 getModel() { 108 return this.model; 109 } 110 111 activated() { 112 this.isActivating = true; 113 if (!this.hasFocus()) { 114 // Don't steal focus from children. 115 this.focus(); 116 } 117 this.isActivating = false; 118 } 119 120 activeStatusChanged(active) { 121 if (active) { 122 this.classList.add('active'); 123 } else { 124 this.classList.remove('active'); 125 } 126 } 127 128 activeItemChanged(item) { 129 delete this.dataset.activeItemName; 130 delete this.dataset.activeItemPath; 131 if (this.changePathDisposable != null) { 132 this.changePathDisposable.dispose(); 133 } 134 if (item == null) { 135 return; 136 } 137 const hasFocus = this.hasFocus(); 138 const itemView = this.views.getView(item); 139 const itemPath = typeof item.getPath === 'function' ? item.getPath() : null; 140 if (itemPath) { 141 this.dataset.activeItemName = path.basename(itemPath); 142 this.dataset.activeItemPath = itemPath; 143 if (item.onDidChangePath != null) { 144 this.changePathDisposable = item.onDidChangePath(() => { 145 const itemPath = item.getPath(); 146 this.dataset.activeItemName = path.basename(itemPath); 147 this.dataset.activeItemPath = itemPath; 148 }); 149 } 150 } 151 if (!this.itemViews.contains(itemView)) { 152 this.itemViews.appendChild(itemView); 153 } 154 for (const child of this.itemViews.children) { 155 if (child === itemView) { 156 if (this.attached) { 157 this.showItemView(child); 158 } 159 } else { 160 this.hideItemView(child); 161 } 162 } 163 if (hasFocus) { 164 itemView.focus(); 165 } 166 } 167 168 showItemView(itemView) { 169 const inlineDisplayStyle = this.inlineDisplayStyles.get(itemView); 170 if (inlineDisplayStyle != null) { 171 itemView.style.display = inlineDisplayStyle; 172 } else { 173 itemView.style.display = ''; 174 } 175 } 176 177 hideItemView(itemView) { 178 const inlineDisplayStyle = itemView.style.display; 179 if (inlineDisplayStyle !== 'none') { 180 if (inlineDisplayStyle != null) { 181 this.inlineDisplayStyles.set(itemView, inlineDisplayStyle); 182 } 183 itemView.style.display = 'none'; 184 } 185 } 186 187 itemRemoved({ item, index, destroyed }) { 188 const viewToRemove = this.views.getView(item); 189 if (viewToRemove) { 190 viewToRemove.remove(); 191 } 192 } 193 194 paneDestroyed() { 195 this.subscriptions.dispose(); 196 if (this.changePathDisposable != null) { 197 this.changePathDisposable.dispose(); 198 } 199 } 200 201 flexScaleChanged(flexScale) { 202 this.style.flexGrow = flexScale; 203 } 204 205 getActiveView() { 206 return this.views.getView(this.model.getActiveItem()); 207 } 208 209 hasFocus() { 210 return ( 211 this === document.activeElement || this.contains(document.activeElement) 212 ); 213 } 214 } 215 216 module.exports = document.registerElement('atom-pane', { 217 prototype: PaneElement.prototype 218 });