lion/packages/collapsible/demo/CustomCollapsible.js
Konstantinos Norgias dbacafa539 fix: remove @ts-expect-error & add @type {any} to static get props
Co-authored-by: jorenbroekema <Joren.Broekema@ing.com>
2021-03-04 10:32:39 +01:00

115 lines
3.3 KiB
JavaScript

import { LionCollapsible } from '../index.js';
const EVENT = {
TRANSITION_END: 'transitionend',
TRANSITION_START: 'transitionstart',
};
/**
* `CustomCollapsible` is a class for custom collapsible element (`<custom-collapsible>` web component).
* @customElement custom-collapsible
*/
export class CustomCollapsible extends LionCollapsible {
/** @type {any} */
static get properties() {
return {
transitioning: {
type: Boolean,
reflect: true,
},
};
}
constructor() {
super();
this.transitioning = false;
}
connectedCallback() {
super.connectedCallback();
this._contentNode?.style.setProperty(
'transition',
'max-height 0.35s, padding 0.35s, opacity 0.35s',
);
if (this.opened) {
this._contentNode?.style.setProperty('padding', '12px 0');
}
}
/**
* Wait until transition is finished.
* @override
*/
toggle() {
if (!this.transitioning) {
super.toggle();
}
}
/**
* Trigger show animation and wait for transition to be finished.
* @param {Object} options - element node and its options
* @param {HTMLElement} options.contentNode
* @override
*/
async _showAnimation({ contentNode }) {
const expectedHeight = await this.__calculateHeight(contentNode);
contentNode.style.setProperty('opacity', '1');
contentNode.style.setProperty('padding', '12px 0');
contentNode.style.setProperty('max-height', '0px');
await /** @type {Promise<void>} */ (new Promise(resolve =>
requestAnimationFrame(() => resolve()),
));
contentNode.style.setProperty('max-height', expectedHeight);
await this._waitForTransition({ contentNode });
}
/**
* Trigger hide animation and wait for transition to be finished.
* @param {Object} options - element node and its options
* @param {HTMLElement} options.contentNode
* @override
*/
async _hideAnimation({ contentNode }) {
if (this._contentHeight === '0px') {
return;
}
['opacity', 'padding', 'max-height'].map(prop => contentNode.style.setProperty(prop, '0'));
await this._waitForTransition({ contentNode });
}
/**
* Wait until the transition event is finished.
* @param {Object} options - element node and its options
* @param {HTMLElement} options.contentNode
* @returns {Promise<void>} transition event
*/
_waitForTransition({ contentNode }) {
return new Promise(resolve => {
const transitionStarted = () => {
contentNode.removeEventListener(EVENT.TRANSITION_START, transitionStarted);
this.transitioning = true;
};
contentNode.addEventListener(EVENT.TRANSITION_START, transitionStarted);
const transitionEnded = () => {
contentNode.removeEventListener(EVENT.TRANSITION_END, transitionEnded);
this.transitioning = false;
resolve();
};
contentNode.addEventListener(EVENT.TRANSITION_END, transitionEnded);
});
}
/**
* Calculate total content height after collapsible opens
* @param {HTMLElement} contentNode content node
* @private
*/
async __calculateHeight(contentNode) {
contentNode.style.setProperty('max-height', '');
await /** @type {Promise<void>} */ (new Promise(resolve =>
requestAnimationFrame(() => resolve()),
));
return this._contentHeight; // Expected height i.e. actual size once collapsed after animation
}
}