123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463 |
- /**
- * Panel.js
- *
- * @author realor
- */
- import { I18N } from '../i18n/I18N.js'
- class Panel {
- static LARGE_SCREEN_WIDTH = 768
- static POSITIONS = ['left', 'right', 'bottom']
- static HEADER_HEIGHT = 24
- static MARGIN = 6
- constructor(application) {
- this.application = application
- this.panelManager = null
- this.minimumHeight = 100 // greater than HEADER_HEIGHT
- this.preferredHeight = 0 // 0: all available space, > 0: height in pixels
- this.element = document.createElement('div')
- this.element.className = 'panel'
- this.element.style.margin = Panel.MARGIN + 'px'
- this.headerElem = document.createElement('div')
- this.headerElem.className = 'header'
- this.element.appendChild(this.headerElem)
- this.bodyElem = document.createElement('div')
- this.bodyElem.className = 'body'
- this.element.appendChild(this.bodyElem)
- this.minimizeButtonElem = document.createElement('button')
- this.minimizeButtonElem.className = 'minimize'
- I18N.set(this.minimizeButtonElem, 'aria-label', 'button.minimize')
- I18N.set(this.minimizeButtonElem, 'alt', 'button.minimize')
- I18N.set(this.minimizeButtonElem, 'title', 'button.minimize')
- this.headerElem.appendChild(this.minimizeButtonElem)
- this.maximizeButtonElem = document.createElement('button')
- this.maximizeButtonElem.className = 'maximize'
- I18N.set(this.maximizeButtonElem, 'aria-label', 'button.maximize')
- I18N.set(this.maximizeButtonElem, 'alt', 'button.maximize')
- I18N.set(this.maximizeButtonElem, 'title', 'button.maximize')
- this.headerElem.appendChild(this.maximizeButtonElem)
- this.titleElem = document.createElement('a')
- this.titleElem.className = 'title'
- this.titleElem.href = '#'
- this.headerElem.appendChild(this.titleElem)
- this.closeButtonElem = document.createElement('button')
- this.closeButtonElem.className = 'close'
- I18N.set(this.closeButtonElem, 'aria-label', 'button.close')
- I18N.set(this.closeButtonElem, 'alt', 'button.close')
- I18N.set(this.closeButtonElem, 'title', 'button.close')
- this.headerElem.appendChild(this.closeButtonElem)
- this.titleElem.addEventListener(
- 'click',
- event => {
- event.preventDefault()
- this.zoom()
- },
- false
- )
- this.minimizeButtonElem.addEventListener('click', event => (this.minimized = true), false)
- this.maximizeButtonElem.addEventListener('click', event => (this.minimized = false), false)
- this.closeButtonElem.addEventListener('click', event => (this.visible = false), false)
- this._position = null
- this.position = 'left'
- this.opacity = application.panelOpacity
- }
- get title() {
- return this.titleElem.innerHTML
- }
- set title(title) {
- this.titleElem.innerHTML = title
- I18N.set(this.titleElem, 'innerHTML', title)
- }
- get position() {
- return this._position
- }
- set position(position) {
- if (position !== this._position) {
- var element = this.element
- if (this._position) {
- element.classList.remove(this._position)
- }
- this._position = position
- element.classList.add(position)
- if (this.panelManager) this.panelManager.updateLayout()
- }
- }
- get visible() {
- return this.element.classList.contains('show')
- }
- set visible(visible) {
- if (this.panelManager) this.panelManager.setAnimationEnabled(false)
- let prevVisible = this.visible
- if (visible && !prevVisible) {
- this.element.classList.add('show')
- if (this.panelManager) {
- this.element.classList.remove('minimized')
- this.panelManager.preferredPanel = this
- this.panelManager.updateLayout()
- }
- this.onShow()
- } else if (!visible && prevVisible) {
- this.element.classList.remove('show')
- if (this.panelManager) this.panelManager.updateLayout()
- this.onHide()
- }
- }
- get minimized() {
- return this.element.classList.contains('minimized')
- }
- set minimized(minimized) {
- if (this.panelManager) {
- this.panelManager.setAnimationEnabled(true)
- if (minimized === false) {
- this.panelManager.preferredPanel = this
- }
- }
- let prevMinimized = this.minimized
- if (minimized && !prevMinimized) {
- this.element.classList.add('minimized')
- if (this.panelManager) this.panelManager.updateLayout()
- this.onMinimize()
- } else if (!minimized && prevMinimized) {
- this.element.classList.remove('minimized')
- if (this.panelManager) this.panelManager.updateLayout()
- this.onMaximize()
- }
- }
- setTitle(title) {
- this.title = title
- return this
- }
- setPosition(position) {
- this.position = position
- return this
- }
- setClassName(className) {
- this.element.classList.add(className)
- return this
- }
- zoom() {
- if (this.panelManager) {
- let position = this.panelManager.isLargeScreen() ? this.position : null
- let panels = this.panelManager.getPanels(position, true)
- for (let panel of panels) {
- if (panel !== this) {
- panel.element.classList.add('minimized')
- }
- }
- this.element.classList.remove('minimized')
- this.panelManager.setAnimationEnabled(true)
- this.panelManager.updateLayout()
- }
- }
- get opacity() {
- return this._opacity
- }
- set opacity(opacity) {
- this._opacity = opacity
- this.element.style.background = 'rgba(255,255,255,' + opacity + ')'
- let headerOpacity = Math.min(opacity + 0.2, 1)
- this.headerElem.style.background = 'rgba(255,255,255,' + headerOpacity + ')'
- }
- onShow() {}
- onHide() {}
- onMinimize() {}
- onMaximize() {}
- }
- class PanelManager {
- constructor(container) {
- this.container = container || document.body
- this.margin = 0
- this.headerHeight = Panel.HEADER_HEIGHT
- this.panels = []
- this.preferredPanel = null
- this.resizers = {}
- this.resizers.left = new PanelResizer(this, 'left')
- this.resizers.right = new PanelResizer(this, 'right')
- window.addEventListener(
- 'resize',
- event => {
- this.setAnimationEnabled(false)
- this.updateLayout()
- },
- false
- )
- }
- addPanel(panel) {
- let index = this.panels.indexOf(panel)
- if (index === -1) {
- panel.panelManager = this
- this.panels.push(panel)
- this.container.appendChild(panel.element)
- }
- }
- removePanel(panel) {
- let index = this.panels.indexOf(panel)
- if (index !== -1) {
- panel.panelManager = null
- this.panels.splice(index, 1)
- this.container.removeChild(panel.element)
- }
- }
- getPanels(position = null, visible = null) {
- let selection = []
- for (let i = 0; i < this.panels.length; i++) {
- let panel = this.panels[i]
- if (position === null || panel.position === position) {
- if (visible === null || panel.visible === visible) {
- selection.push(panel)
- }
- }
- }
- return selection
- }
- isAnimationEnabled() {
- return this.container.classList.contains('animate')
- }
- setAnimationEnabled(enabled) {
- if (enabled) {
- this.container.classList.add('animate')
- } else {
- this.container.classList.remove('animate')
- }
- }
- updateLayout() {
- const container = this.container
- const height = container.clientHeight - Panel.MARGIN - 1
- if (this.isLargeScreen()) {
- this.resizers.left.enabled = true
- this.resizers.right.enabled = true
- let positions = Panel.POSITIONS
- for (var i = 0; i < positions.length; i++) {
- let position = positions[i]
- let panels = this.getPanels(position, true)
- let maxHeight = this.layoutElements(panels, height)
- let resizer = this.resizers[position]
- if (resizer) {
- resizer.height = maxHeight
- resizer.updateBar()
- resizer.saveWidth()
- }
- }
- } else {
- this.resizers.left.enabled = false
- this.resizers.right.enabled = false
- let panels = this.getPanels(null, true)
- this.layoutElements(panels, Math.floor(height / 2))
- }
- }
- layoutElements(panels, height) {
- let minimumHeight = 0
- // calculate minimumHeight
- for (let panel of panels) {
- if (panel.minimized) {
- minimumHeight += this.headerHeight
- } else {
- minimumHeight += panel.minimumHeight
- }
- minimumHeight += Panel.MARGIN
- }
- // minimize required panels to fit all
- let total = panels.length
- let remainingHeight = height - minimumHeight
- let i = 0
- while (remainingHeight < 0 && i < total) {
- let j = total - i - 1
- let panel = panels[j]
- if (!panel.minimized && panel !== this.preferredPanel) {
- panel.element.classList.add('minimized')
- remainingHeight += panel.minimumHeight - this.headerHeight
- }
- i++
- }
- // calculate largePanelExtraHeight
- let largePanelCount = 0
- let smallPanelRequiredHeight = 0
- for (let panel of panels) {
- if (!panel.minimized) {
- if (panel.preferredHeight === 0) {
- largePanelCount++
- } else if (panel.preferredHeight > panel.minimumHeight) {
- smallPanelRequiredHeight += panel.preferredHeight - panel.minimumHeight
- }
- }
- }
- let largePanelExtraHeight = 0
- let largePanelRemainingHeight = remainingHeight - smallPanelRequiredHeight
- if (largePanelRemainingHeight > 0 && largePanelCount > 0) {
- largePanelExtraHeight = Math.floor(largePanelRemainingHeight / largePanelCount)
- }
- // layout panels
- let bottom = this.margin
- for (let i = 0; i < total; i++) {
- let j = total - i - 1
- let panel = panels[j]
- // set panel bottom
- panel.element.style.bottom = bottom + 'px'
- // set panel width
- if (this.isLargeScreen()) {
- let resizer = this.resizers[panel.position]
- if (resizer) {
- panel.element.style.width = resizer.width - Panel.MARGIN + 'px'
- }
- } else {
- panel.element.style.width = ''
- }
- // set panel height
- let currentPanelHeight
- if (panel.minimized) {
- currentPanelHeight = this.headerHeight
- } else if (panel.preferredHeight === 0) {
- // large panel
- currentPanelHeight = panel.minimumHeight + largePanelExtraHeight
- remainingHeight -= largePanelExtraHeight
- } // small panel
- else {
- currentPanelHeight = panel.minimumHeight
- let requiredHeight = panel.preferredHeight - panel.minimumHeight
- if (requiredHeight > 0) {
- let extraHeight = Math.min(remainingHeight, requiredHeight)
- currentPanelHeight += extraHeight
- remainingHeight -= extraHeight
- }
- }
- panel.element.style.height = currentPanelHeight + 'px'
- bottom += currentPanelHeight + Panel.MARGIN
- }
- return bottom
- }
- isLargeScreen() {
- return this.container.clientWidth > Panel.LARGE_SCREEN_WIDTH
- }
- }
- class PanelResizer {
- constructor(panelManager, side) {
- this.panelManager = panelManager
- this.side = side
- this.height = 0
- this.width = 0
- this.element = document.createElement('div')
- const element = this.element
- const container = panelManager.container
- element.className = 'resizer'
- container.appendChild(element)
- this.restoreWidth()
- this.updateBar()
- const move = event => {
- this.width = this.getCurrentWidth(event)
- this.updateBar()
- this.panelManager.updateLayout()
- }
- const reset = event => {
- element.removeEventListener('pointermove', move, false)
- element.removeEventListener('pointerup', reset, false)
- element.releasePointerCapture(event.pointerId)
- }
- element.addEventListener('pointerdown', event => {
- element.addEventListener('pointermove', move, false)
- element.addEventListener('pointerup', reset, false)
- element.setPointerCapture(event.pointerId)
- })
- }
- updateBar() {
- this.element.style.height = this.height + 'px'
- this.element.style[this.side] = this.width + 'px'
- }
- get enabled() {
- return this.element.style.display === ''
- }
- set enabled(enabled) {
- this.element.style.display = enabled ? '' : 'none'
- }
- restoreWidth() {
- let value = window.localStorage.getItem('bimrocket.resizer.' + this.side)
- this.width = parseInt(value) || 250
- }
- saveWidth() {
- window.localStorage.setItem('bimrocket.resizer.' + this.side, this.width)
- }
- getCurrentWidth(event) {
- const rect = this.panelManager.container.getBoundingClientRect()
- let curWidth = 0
- if (this.side === 'left') {
- curWidth = event.clientX - rect.left
- } else if (this.side === 'right') {
- curWidth = rect.left + rect.width - event.clientX
- }
- return curWidth
- }
- }
- export { Panel, PanelManager }
|