UNPKG

comindware.core.ui

Version:

Comindware Core UI provides the basic components like editors, lists, dropdowns, popups that we so desperately need while creating Marionette-based single-page applications.

303 lines (254 loc) • 9.49 kB
import WindowService from 'services/WindowService'; import template from './popup.hbs'; import LayoutBehavior from '../behaviors/LayoutBehavior'; import GlobalEventService from '../../services/GlobalEventService'; import LoadingBehavior from '../../views/behaviors/LoadingBehavior'; import ButtonView from '../button/ButtonView'; import meta from 'Meta'; import MobileService from 'services/MobileService'; const classes = { CLASS_NAME: 'layout__popup-view', MOBILE: 'layout__popup-view_mobile', MOBILE_SYSTEM_TYPE: 'layout__popup-view_mobile-system-type', EXPAND: 'layout__popup-view-window_expand', CURSOR_AUTO: 'cur_aI' }; const iconsNames = meta.iconsNames; const defaultOptions = { expand: false }; const TRANSITION = 100; //ms const sizeVisibleChunk = 50; //px; export default Marionette.View.extend({ initialize() { _.defaults(this.options, defaultOptions); this.content = this.options.content; this.onClose = this.options.onClose; _.bindAll(this, '__drag', '__startDrag', '__stopDrag'); }, template: Handlebars.compile(template), templateContext() { return { windowClass: this.options.windowClass || '', headerText: this.options.header, ...iconsNames }; }, className() { if (MobileService.isMobile && !this.options.isSystemPopup) { return classes.MOBILE; } else if (MobileService.isMobile && this.options.isSystemPopup) { return classes.MOBILE_SYSTEM_TYPE; } return classes.CLASS_NAME; }, behaviors: { LayoutBehavior: { behaviorClass: LayoutBehavior }, LoadingBehavior: { behaviorClass: LoadingBehavior, region: 'loadingRegion' } }, ui: { button: '.js-button', header: '.js-header', window: '.js-window', close: '.js-close', newTab: '.js-new-tab', fullscreenToggle: '.js-fullscreen-toggle', headerText: '.js-header-text' }, events: { 'click @ui.button': '__onButtonClick', 'click @ui.close': 'close', 'click @ui.newTab': '__openInNewTab', 'click @ui.fullscreenToggle': '__fullscreenToggle', 'mousedown @ui.header': '__startDrag' }, regions: { contentRegion: '.js-content-region', buttonsRegion: '.js-buttons-region', loadingRegion: '.js-loading-region' }, onRender() { if (this.options.size && !MobileService.isMobile) { this.ui.window.css(this.options.size); } this.__setExpandState(this.options.expand); this.showChildView('contentRegion', this.options.content); if (this.options.buttons) { this.showChildView('buttonsRegion', this.__createButtonsView()); } this.__updateState(); if (!this.options.newTabUrl) { this.ui.newTab[0].setAttribute('hidden', ''); } if (this.options.fullscreenToggleDisabled) { this.ui.fullscreenToggle[0].setAttribute('hidden', ''); } this.__debounceOnResize = _.debounce(this.__onResize, 300); this.listenTo(GlobalEventService, 'window:resize', this.__debounceOnResize); }, update() { if (this.content.update) { this.content.update(); } this.__updateState(); }, validate() { const content = this.options.content; if (content.validate) { return content.validate(); } }, setHeader(eventTitle: string) { this.ui.headerText.text(eventTitle); }, __openInNewTab() { if (Array.isArray(this.options.newTabUrl)) { window.open(...this.options.newTabUrl); } else { window.open(this.options.newTabUrl); } this.close(); }, __fullscreenToggle() { this.__expanded = !this.__expanded; this.__callWithTransition(() => this.__setExpandState(this.__expanded)); }, __setExpandState(expandState: boolean) { this.__expanded = expandState; this.ui.window.toggleClass(classes.EXPAND, this.__expanded); this.ui.header.toggleClass(classes.CURSOR_AUTO, this.__expanded); this.ui.fullscreenToggle.toggleClass(`fa-${iconsNames.expand}`, !this.__expanded); this.ui.fullscreenToggle.toggleClass(`fa-${iconsNames.minimize}`, this.__expanded); }, __callWithTransition(callback: Function | undefined, callbackAfterTransition: Function | undefined) { this.ui.window.css('transition', `all ${TRANSITION}ms ease-in-out 0s`); typeof callback === 'function' && callback(); setTimeout(() => { this.ui.window.css('transition', 'unset'); typeof callbackAfterTransition === 'function' && callbackAfterTransition(); }, TRANSITION); }, async close() { if (WindowService.isPopupOnTop(this.popupId)) { if (this.onClose) { const closeResult = await this.onClose(); return closeResult && WindowService.closePopup(this.popupId); } return WindowService.closePopup(this.popupId); } }, __createButtonsView() { const buttons = this.options.buttons.map(item => new ButtonView({ context: this, ...item })); return new Core.layout.HorizontalLayout({ columns: buttons }); }, __startDrag(startEvent: MouseEvent) { if (this.__expanded) { return; } this.__setCurrentDragContext(startEvent); document.addEventListener('mousemove', this.__drag); document.addEventListener('mouseup', this.__stopDrag); }, __setCurrentDragContext(startEvent: MouseEvent) { const windowEl = this.ui.window.get(0); this.dragContext = { startEvent, startStyleLeft: parseFloat(windowEl.style.left || '0'), startStyleTop: parseFloat(windowEl.style.top || '0'), startOffsetTop: windowEl.offsetTop, startOffsetLeft: windowEl.offsetLeft, windowWidth: windowEl.offsetWidth, documentWidth: document.body.offsetWidth, documentHeight: document.body.offsetHeight }; }, __stopDrag() { document.removeEventListener('mousemove', this.__drag); document.removeEventListener('mouseup', this.__stopDrag); this.__checkPosition(); delete this.dragContext; }, __drag(e) { if (this.__eventOutOfClient(e)) { return; } const diffX = e.clientX - this.dragContext.startEvent.clientX; const diffY = e.clientY - this.dragContext.startEvent.clientY; this.ui.window[0].style.left = `${this.dragContext.startStyleLeft + diffX}px`; this.ui.window[0].style.top = `${this.dragContext.startStyleTop + diffY}px`; }, __eventOutOfClient(e) { return e.clientX > this.dragContext.documentWidth || e.clientY > this.dragContext.documentHeight || e.clientX < 0 || e.clientY < 0; }, __getCorrectPopupDiffs(assumedDiffX, assumedDiffY) { return { diffX: this.__checkLeftContainment(this.__checkRightContainment(assumedDiffX)), diffY: this.__checkTopContainment(this.__checkBottomContainment(assumedDiffY)) }; }, __checkPosition() { this.__setCurrentDragContext(); const { diffX, diffY } = this.__getCorrectPopupDiffs(0, 0); if (diffX !== 0) { this.ui.window[0].style.left = `${this.dragContext.startStyleLeft + diffX}px`; } if (diffY !== 0) { this.ui.window[0].style.top = `${this.dragContext.startStyleTop + diffY}px`; } delete this.dragContext; }, __checkTopContainment(diffY) { const offsetTop = this.dragContext.startOffsetTop + diffY; if (offsetTop < 0) { return -this.dragContext.startOffsetTop; } return diffY; }, __checkBottomContainment(diffY) { const offsetTop = diffY + this.dragContext.startOffsetTop; const heightExcess = offsetTop + sizeVisibleChunk - this.dragContext.documentHeight; if (heightExcess > 0) { return diffY - heightExcess; } return diffY; }, __checkLeftContainment(diffX) { const offsetLeft = diffX + this.dragContext.startOffsetLeft; const offsetWidth = this.dragContext.windowWidth; const visibleChunk = offsetLeft + offsetWidth; const lack = sizeVisibleChunk - visibleChunk; if (lack > 0) { return diffX + lack; } return diffX; }, __checkRightContainment(diffX) { const offsetLeft = diffX + this.dragContext.startOffsetLeft; const widthExcess = offsetLeft + sizeVisibleChunk - document.body.offsetWidth; if (widthExcess > 0) { return diffX - widthExcess; } return diffX; }, __onResize() { if (this.isDestroyed()) { return; } this.__checkPosition(); }, isNeedToPrevent() { return document.querySelector('.dev-codemirror-maximized') !== null || document.querySelector('.CodeMirror-hints') !== null; }, setLoading(loading: boolean) { if (!this.isDestroyed()) { this.loading.setLoading(loading); } } });