111 lines
3.2 KiB
JavaScript
111 lines
3.2 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
|
|
* @extends LionCollapsible
|
|
*/
|
|
export class CustomCollapsible extends LionCollapsible {
|
|
static get properties() {
|
|
return {
|
|
transitioning: {
|
|
type: Boolean,
|
|
reflect: true,
|
|
},
|
|
};
|
|
}
|
|
|
|
constructor() {
|
|
super();
|
|
this.transitioning = false;
|
|
}
|
|
|
|
connectedCallback() {
|
|
if (super.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
|
|
* @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 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
|
|
* @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
|
|
* @returns {Promise} 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 {Object} contentNode content node
|
|
* @private
|
|
*/
|
|
async __calculateHeight(contentNode) {
|
|
contentNode.style.setProperty('max-height', '');
|
|
await new Promise(resolve => requestAnimationFrame(() => resolve()));
|
|
return this._contentHeight; // Expected height i.e. actual size once collapsed after animation
|
|
}
|
|
}
|