UNPKG

@exadel/esl

Version:

Exadel Smart Library (ESL) is the lightweight custom elements library that provide a set of super-flexible components

146 lines (145 loc) 5.83 kB
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; import { ExportNs } from '../../esl-utils/environment/export-ns'; import { CSSClassUtils } from '../../esl-utils/dom/class'; import { attr, boolAttr, jsonAttr, listen } from '../../esl-utils/decorators'; import { afterNextRender, skipOneRender } from '../../esl-utils/async/raf'; import { ESLToggleable } from '../../esl-toggleable/core'; /** * ESLPanel component * @author Julia Murashko * * ESLPanel is a custom element that is used as a wrapper for content that can be shown or hidden. * Can use collapsing/expanding animation (smooth height change). * Can be used in conjunction with {@link ESLPanelGroup} to control a group of ESLPopups */ let ESLPanel = class ESLPanel extends ESLToggleable { constructor() { super(...arguments); /** Inner height state that updates after show/hide actions but before show/hide events triggered */ this._initialHeight = 0; } /** @returns Previous active panel height at the start of the animation */ get initialHeight() { return this._initialHeight; } /** @returns Closest panel group or null if not presented */ get $group() { if (this.groupName === 'none' || this.groupName) return null; return this.closest(this.panelGroupSel); } /** Process show action */ onShow(params) { this._initialHeight = this.scrollHeight; super.onShow(params); this.beforeAnimate(); if (params.noAnimate) return this.postAnimate(params.capturedBy); this.onAnimate(0, this._initialHeight); } /** Process hide action */ onHide(params) { this._initialHeight = this.scrollHeight; super.onHide(params); this.beforeAnimate(); if (params.noAnimate) return this.postAnimate(null); this.onAnimate(this._initialHeight, 0); } /** Pre-processing animation action */ beforeAnimate() { this.toggleAttribute('animating', true); CSSClassUtils.add(this, this.animateClass); this.postAnimateClass && afterNextRender(() => CSSClassUtils.add(this, this.postAnimateClass)); } /** Handles post animation process to initiate after animate step */ postAnimate(capturedBy) { if (capturedBy && capturedBy.animating) { capturedBy.$$on({ event: capturedBy.AFTER_ANIMATE_EVENT, once: true }, () => this.afterAnimate()); } else { skipOneRender(() => this.afterAnimate()); } } /** Process animation */ onAnimate(from, to) { // set initial height this.style.setProperty('max-height', `${from}px`); // make sure that browser applies initial height for animation afterNextRender(() => { this.style.setProperty('max-height', `${to}px`); this.fallbackAnimate(); }); } /** Checks if transition happens and runs afterAnimate step if transition is not presented*/ fallbackAnimate() { afterNextRender(() => { const distance = parseFloat(this.style.maxHeight) - this.clientHeight; if (Math.abs(distance) <= 1) this.afterAnimate(); }); } /** Post-processing animation action */ afterAnimate() { const { animating } = this; this.clearAnimation(); // Prevent fallback calls from being tracked if (!animating) return; this.$$fire(this.open ? this.AFTER_SHOW_EVENT : this.AFTER_HIDE_EVENT); } /** Clear animation properties */ clearAnimation() { this.toggleAttribute('animating', false); this.style.removeProperty('max-height'); CSSClassUtils.remove(this, this.animateClass); CSSClassUtils.remove(this, this.postAnimateClass); } /** Catching CSS transition end event to start post-animate processing */ _onTransitionEnd(e) { if (!e || (e.propertyName === 'max-height' && e.target === this)) { this.afterAnimate(); } } /** Merge params that are used by panel group for actions */ mergeDefaultParams(params) { var _a; const type = this.constructor; const stackConfig = ((_a = this.$group) === null || _a === void 0 ? void 0 : _a.panelConfig) || {}; return Object.assign({}, stackConfig, type.DEFAULT_PARAMS, this.defaultParams, params || {}); } }; ESLPanel.is = 'esl-panel'; __decorate([ attr({ defaultValue: 'open' }) ], ESLPanel.prototype, "activeClass", void 0); __decorate([ attr({ defaultValue: 'animate' }) ], ESLPanel.prototype, "animateClass", void 0); __decorate([ attr({ defaultValue: 'post-animate' }) ], ESLPanel.prototype, "postAnimateClass", void 0); __decorate([ attr({ defaultValue: 'esl-panel-group' }) ], ESLPanel.prototype, "panelGroupSel", void 0); __decorate([ jsonAttr({ defaultValue: { force: true, initiator: 'init' } }) ], ESLPanel.prototype, "initialParams", void 0); __decorate([ boolAttr({ readonly: true }) ], ESLPanel.prototype, "animating", void 0); __decorate([ listen('transitionend') ], ESLPanel.prototype, "_onTransitionEnd", null); ESLPanel = __decorate([ ExportNs('Panel') ], ESLPanel); export { ESLPanel };