| OLD | NEW |
| 1 // Copyright 2017 The Chromium Authors. All rights reserved. | 1 // Copyright 2017 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 UI.GlassPane = class { | 5 UI.GlassPane = class extends UI.Widget { |
| 6 /** | 6 constructor() { |
| 7 * @param {!Document} document | 7 super(true); |
| 8 * @param {boolean} dimmed | 8 this.markAsRoot(); |
| 9 * @param {boolean} blockPointerEvents | 9 this.registerRequiredCSS('ui/glassPane.css'); |
| 10 * @param {function(!Event)} onClickOutside | 10 this.element.classList.add('no-pointer-events'); |
| 11 */ | 11 this._onMouseDownBound = this._onMouseDown.bind(this); |
| 12 constructor(document, dimmed, blockPointerEvents, onClickOutside) { | 12 /** @type {?function(!Event)} */ |
| 13 this._element = createElementWithClass('div', 'glass-pane'); | 13 this._onClickOutsideCallback = null; |
| 14 this._element.style.backgroundColor = dimmed ? 'rgba(255, 255, 255, 0.5)' :
'transparent'; | |
| 15 if (!blockPointerEvents) | |
| 16 this._element.style.pointerEvents = 'none'; | |
| 17 this._onMouseDown = event => { | |
| 18 if (!this.contentElement.isSelfOrAncestor(/** @type {?Node} */ (event.targ
et))) | |
| 19 onClickOutside.call(null, event); | |
| 20 }; | |
| 21 | |
| 22 this.contentElement = this._element.createChild('div', 'glass-pane-content')
; | |
| 23 this._document = document; | |
| 24 this._visible = false; | |
| 25 /** @type {?UI.Size} */ | 14 /** @type {?UI.Size} */ |
| 26 this._maxSize = null; | 15 this._maxSize = null; |
| 27 /** @type {?number} */ | 16 /** @type {?number} */ |
| 28 this._positionX = null; | 17 this._positionX = null; |
| 29 /** @type {?number} */ | 18 /** @type {?number} */ |
| 30 this._positionY = null; | 19 this._positionY = null; |
| 31 /** @type {?AnchorBox} */ | 20 /** @type {?AnchorBox} */ |
| 32 this._anchorBox = null; | 21 this._anchorBox = null; |
| 33 this._anchorBehavior = UI.GlassPane.AnchorBehavior.PreferTop; | 22 this._anchorBehavior = UI.GlassPane.AnchorBehavior.PreferTop; |
| 34 this._fixedHeight = true; | 23 this._sizeBehavior = UI.GlassPane.SizeBehavior.SetHeight; |
| 35 } | 24 } |
| 36 | 25 |
| 37 /** | 26 /** |
| 27 * @param {boolean} dimmed |
| 28 */ |
| 29 setDimmed(dimmed) { |
| 30 this.element.classList.toggle('dimmed-pane', dimmed); |
| 31 } |
| 32 |
| 33 /** |
| 34 * @param {boolean} blockPointerEvents |
| 35 */ |
| 36 setBlockPointerEvents(blockPointerEvents) { |
| 37 this.element.classList.toggle('no-pointer-events', !blockPointerEvents); |
| 38 } |
| 39 |
| 40 /** |
| 41 * @param {?function(!Event)} callback |
| 42 */ |
| 43 setSetOutsideClickCallback(callback) { |
| 44 this._onClickOutsideCallback = callback; |
| 45 } |
| 46 |
| 47 /** |
| 38 * @param {?UI.Size} size | 48 * @param {?UI.Size} size |
| 39 */ | 49 */ |
| 40 setMaxContentSize(size) { | 50 setMaxContentSize(size) { |
| 41 this._maxSize = size; | 51 this._maxSize = size; |
| 42 this._positionContent(); | 52 this._positionContent(); |
| 43 } | 53 } |
| 44 | 54 |
| 45 /** | 55 /** |
| 46 * @param {boolean} fixedHeight | 56 * @param {!UI.GlassPane.SizeBehavior} sizeBehavior |
| 47 */ | 57 */ |
| 48 setFixedHeight(fixedHeight) { | 58 setSizeBehavior(sizeBehavior) { |
| 49 this._fixedHeight = fixedHeight; | 59 this._sizeBehavior = sizeBehavior; |
| 60 this._positionContent(); |
| 50 } | 61 } |
| 51 | 62 |
| 52 /** | 63 /** |
| 53 * @param {?number} x | 64 * @param {?number} x |
| 54 * @param {?number} y | 65 * @param {?number} y |
| 55 * Position is relative to root element. | 66 * Position is relative to root element. |
| 56 */ | 67 */ |
| 57 setContentPosition(x, y) { | 68 setContentPosition(x, y) { |
| 58 this._positionX = x; | 69 this._positionX = x; |
| 59 this._positionY = y; | 70 this._positionY = y; |
| 60 this._positionContent(); | 71 this._positionContent(); |
| 61 } | 72 } |
| 62 | 73 |
| 63 /** | 74 /** |
| 64 * @param {?AnchorBox} anchorBox | 75 * @param {?AnchorBox} anchorBox |
| 65 * Anchor box is relative to the document. | 76 * Anchor box is relative to the document. |
| 66 */ | 77 */ |
| 67 setContentAnchorBox(anchorBox) { | 78 setContentAnchorBox(anchorBox) { |
| 68 this._anchorBox = anchorBox; | 79 this._anchorBox = anchorBox; |
| 69 this._positionContent(); | 80 this._positionContent(); |
| 70 } | 81 } |
| 71 | 82 |
| 72 /** | 83 /** |
| 73 * @param {!UI.GlassPane.AnchorBehavior} behavior | 84 * @param {!UI.GlassPane.AnchorBehavior} behavior |
| 74 */ | 85 */ |
| 75 setAnchorBehavior(behavior) { | 86 setAnchorBehavior(behavior) { |
| 76 this._anchorBehavior = behavior; | 87 this._anchorBehavior = behavior; |
| 77 } | 88 } |
| 78 | 89 |
| 79 show() { | 90 /** |
| 80 if (this._visible) | 91 * @param {!Document} document |
| 92 */ |
| 93 showGlassPane(document) { |
| 94 if (this.isShowing()) |
| 81 return; | 95 return; |
| 82 this._visible = true; | |
| 83 // Deliberately starts with 3000 to hide other z-indexed elements below. | 96 // Deliberately starts with 3000 to hide other z-indexed elements below. |
| 84 this._element.style.zIndex = 3000 + 1000 * UI.GlassPane._panes.size; | 97 this.element.style.zIndex = 3000 + 1000 * UI.GlassPane._panes.size; |
| 85 this._document.body.appendChild(this._element); | 98 document.body.addEventListener('mousedown', this._onMouseDownBound, true); |
| 86 this._document.body.addEventListener('mousedown', this._onMouseDown, true); | 99 this.show(document.body); |
| 87 UI.GlassPane._panes.add(this); | 100 UI.GlassPane._panes.add(this); |
| 101 this._positionContent(); |
| 88 } | 102 } |
| 89 | 103 |
| 90 hide() { | 104 hideGlassPane() { |
| 91 if (!this._visible) | 105 if (!this.isShowing()) |
| 92 return; | 106 return; |
| 93 UI.GlassPane._panes.delete(this); | 107 UI.GlassPane._panes.delete(this); |
| 94 this._document.body.removeEventListener('mousedown', this._onMouseDown, true
); | 108 this.element.ownerDocument.body.removeEventListener('mousedown', this._onMou
seDownBound, true); |
| 95 this._document.body.removeChild(this._element); | 109 this.detach(); |
| 96 this._visible = false; | |
| 97 } | 110 } |
| 98 | 111 |
| 99 /** | 112 /** |
| 100 * @return {boolean} | 113 * @param {!Event} event |
| 101 */ | 114 */ |
| 102 visible() { | 115 _onMouseDown(event) { |
| 103 return this._visible; | 116 if (!this._onClickOutsideCallback) |
| 117 return; |
| 118 if (this.contentElement.isSelfOrAncestor(/** @type {?Node} */ (event.deepEle
mentFromPoint()))) |
| 119 return; |
| 120 this._onClickOutsideCallback.call(null, event); |
| 104 } | 121 } |
| 105 | 122 |
| 106 _positionContent() { | 123 _positionContent() { |
| 107 if (!this._visible) | 124 if (!this.isShowing()) |
| 108 return; | 125 return; |
| 109 | 126 |
| 110 var gutterSize = 5; | 127 var gutterSize = 5; |
| 111 var container = UI.GlassPane._containers.get(this._document); | 128 var container = UI.GlassPane._containers.get(/** @type {!Document} */ (this.
element.ownerDocument)); |
| 129 if (this._sizeBehavior === UI.GlassPane.SizeBehavior.MeasureContent) { |
| 130 this.contentElement.positionAt(0, 0); |
| 131 this.contentElement.style.width = ''; |
| 132 this.contentElement.style.height = ''; |
| 133 this.contentElement.style.maxHeight = ''; |
| 134 } |
| 135 |
| 112 var containerWidth = container.offsetWidth; | 136 var containerWidth = container.offsetWidth; |
| 113 var containerHeight = container.offsetHeight; | 137 var containerHeight = container.offsetHeight; |
| 114 | 138 |
| 115 var width = containerWidth - gutterSize * 2; | 139 var width = containerWidth - gutterSize * 2; |
| 116 var height = containerHeight - gutterSize * 2; | 140 var height = containerHeight - gutterSize * 2; |
| 117 var positionX = gutterSize; | 141 var positionX = gutterSize; |
| 118 var positionY = gutterSize; | 142 var positionY = gutterSize; |
| 119 | 143 |
| 120 if (this._maxSize) { | 144 if (this._maxSize) { |
| 121 width = Math.min(width, this._maxSize.width); | 145 width = Math.min(width, this._maxSize.width); |
| 122 height = Math.min(height, this._maxSize.height); | 146 height = Math.min(height, this._maxSize.height); |
| 123 } | 147 } |
| 124 | 148 |
| 149 if (this._sizeBehavior === UI.GlassPane.SizeBehavior.MeasureContent) { |
| 150 width = Math.min(width, this.contentElement.offsetWidth); |
| 151 height = Math.min(height, this.contentElement.offsetHeight); |
| 152 } |
| 153 |
| 125 if (this._anchorBox) { | 154 if (this._anchorBox) { |
| 126 var anchorBox = this._anchorBox.relativeToElement(container); | 155 var anchorBox = this._anchorBox.relativeToElement(container); |
| 127 var behavior = this._anchorBehavior; | 156 var behavior = this._anchorBehavior; |
| 128 | 157 |
| 129 if (behavior === UI.GlassPane.AnchorBehavior.PreferTop || behavior === UI.
GlassPane.AnchorBehavior.PreferBottom) { | 158 if (behavior === UI.GlassPane.AnchorBehavior.PreferTop || behavior === UI.
GlassPane.AnchorBehavior.PreferBottom) { |
| 130 var top = anchorBox.y - gutterSize; | 159 var top = anchorBox.y - gutterSize; |
| 131 var bottom = containerHeight - anchorBox.y - anchorBox.height - gutterSi
ze; | 160 var bottom = containerHeight - anchorBox.y - anchorBox.height - gutterSi
ze; |
| 132 if (behavior === UI.GlassPane.AnchorBehavior.PreferTop && top < height &
& bottom > top) | 161 if (behavior === UI.GlassPane.AnchorBehavior.PreferTop && top < height &
& bottom > top) |
| 133 behavior = UI.GlassPane.AnchorBehavior.PreferBottom; | 162 behavior = UI.GlassPane.AnchorBehavior.PreferBottom; |
| 134 if (behavior === UI.GlassPane.AnchorBehavior.PreferBottom && bottom < he
ight && top > bottom) | 163 if (behavior === UI.GlassPane.AnchorBehavior.PreferBottom && bottom < he
ight && top > bottom) |
| (...skipping 27 matching lines...) Expand all Loading... |
| 162 } | 191 } |
| 163 } | 192 } |
| 164 } else { | 193 } else { |
| 165 positionX = this._positionX !== null ? this._positionX : (containerWidth -
width) / 2; | 194 positionX = this._positionX !== null ? this._positionX : (containerWidth -
width) / 2; |
| 166 positionY = this._positionY !== null ? this._positionY : (containerHeight
- height) / 2; | 195 positionY = this._positionY !== null ? this._positionY : (containerHeight
- height) / 2; |
| 167 width = Math.min(width, containerWidth - positionX - gutterSize); | 196 width = Math.min(width, containerWidth - positionX - gutterSize); |
| 168 height = Math.min(height, containerHeight - positionY - gutterSize); | 197 height = Math.min(height, containerHeight - positionY - gutterSize); |
| 169 } | 198 } |
| 170 | 199 |
| 171 this.contentElement.style.width = width + 'px'; | 200 this.contentElement.style.width = width + 'px'; |
| 172 if (this._fixedHeight) | 201 if (this._sizeBehavior === UI.GlassPane.SizeBehavior.SetMaxHeight) |
| 202 this.contentElement.style.maxHeight = height + 'px'; |
| 203 else |
| 173 this.contentElement.style.height = height + 'px'; | 204 this.contentElement.style.height = height + 'px'; |
| 174 else | |
| 175 this.contentElement.style.maxHeight = height + 'px'; | |
| 176 this.contentElement.positionAt(positionX, positionY, container); | 205 this.contentElement.positionAt(positionX, positionY, container); |
| 177 } | 206 } |
| 178 | 207 |
| 179 /** | 208 /** |
| 180 * @param {!Element} element | 209 * @param {!Element} element |
| 181 */ | 210 */ |
| 182 static setContainer(element) { | 211 static setContainer(element) { |
| 183 UI.GlassPane._containers.set(/** @type {!Document} */ (element.ownerDocument
), element); | 212 UI.GlassPane._containers.set(/** @type {!Document} */ (element.ownerDocument
), element); |
| 184 UI.GlassPane.containerMoved(element); | 213 UI.GlassPane.containerMoved(element); |
| 185 } | 214 } |
| 186 | 215 |
| 187 /** | 216 /** |
| 188 * @param {!Document} document | 217 * @param {!Document} document |
| 189 * @return {!Element} | 218 * @return {!Element} |
| 190 */ | 219 */ |
| 191 static container(document) { | 220 static container(document) { |
| 192 return UI.GlassPane._containers.get(document); | 221 return UI.GlassPane._containers.get(document); |
| 193 } | 222 } |
| 194 | 223 |
| 195 /** | 224 /** |
| 196 * @param {!Element} element | 225 * @param {!Element} element |
| 197 */ | 226 */ |
| 198 static containerMoved(element) { | 227 static containerMoved(element) { |
| 199 for (var pane of UI.GlassPane._panes) { | 228 for (var pane of UI.GlassPane._panes) { |
| 200 if (pane._document === element.ownerDocument) | 229 if (pane.isShowing() && pane.element.ownerDocument === element.ownerDocume
nt) { |
| 201 pane._positionContent(); | 230 pane._positionContent(); |
| 231 pane.doResize(); |
| 232 } |
| 202 } | 233 } |
| 203 } | 234 } |
| 204 }; | 235 }; |
| 205 | 236 |
| 206 /** | 237 /** |
| 207 * @enum {symbol} | 238 * @enum {symbol} |
| 208 */ | 239 */ |
| 209 UI.GlassPane.AnchorBehavior = { | 240 UI.GlassPane.AnchorBehavior = { |
| 210 PreferTop: Symbol('PreferTop'), | 241 PreferTop: Symbol('PreferTop'), |
| 211 PreferBottom: Symbol('PreferBottom'), | 242 PreferBottom: Symbol('PreferBottom'), |
| 212 PreferLeft: Symbol('PreferLeft'), | 243 PreferLeft: Symbol('PreferLeft'), |
| 213 PreferRight: Symbol('PreferRight'), | 244 PreferRight: Symbol('PreferRight'), |
| 214 }; | 245 }; |
| 215 | 246 |
| 247 /** |
| 248 * @enum {symbol} |
| 249 */ |
| 250 UI.GlassPane.SizeBehavior = { |
| 251 SetHeight: Symbol('SetHeight'), |
| 252 SetMaxHeight: Symbol('SetMaxHeight'), |
| 253 MeasureContent: Symbol('MeasureContent') |
| 254 }; |
| 255 |
| 216 /** @type {!Map<!Document, !Element>} */ | 256 /** @type {!Map<!Document, !Element>} */ |
| 217 UI.GlassPane._containers = new Map(); | 257 UI.GlassPane._containers = new Map(); |
| 218 /** @type {!Set<!UI.GlassPane>} */ | 258 /** @type {!Set<!UI.GlassPane>} */ |
| 219 UI.GlassPane._panes = new Set(); | 259 UI.GlassPane._panes = new Set(); |
| OLD | NEW |