Skip to content

Commit

Permalink
feat!: refactor of Panel & PanelStack
Browse files Browse the repository at this point in the history
Panel Stack now works on panel events.
Bugfixing of animation process.
Panel Stack is responsible for group management.

BREAKING CHANGE:
Panel Stack uses self group management.
Group attribute should be removed to reach collapsible behavior.
Panel Stack & Panel inner API changes.
  • Loading branch information
julia-murashko committed Feb 2, 2021
1 parent ca46a33 commit d819259
Show file tree
Hide file tree
Showing 5 changed files with 336 additions and 70 deletions.
2 changes: 1 addition & 1 deletion src/modules/esl-panel/core/esl-panel-stack.less
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@ esl-panel-stack {
}
}
}
}
}
67 changes: 46 additions & 21 deletions src/modules/esl-panel/core/esl-panel-stack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@ export class ESLPanelStack extends ESLBaseElement {
public static is = 'esl-panel-stack';

@attr() public accordionTransformation: string;
@attr({defaultValue: 'animate'}) public animateClass: string;
@attr({defaultValue: 'animate'}) public animationClass: string;
@attr({defaultValue: 'accordion'}) public accordionClass: string;
@attr({defaultValue: 'auto'}) public fallbackDuration: number;

protected previousHeight: number;
protected _previousHeight: number = 0;
protected _transformationQuery: ESLMediaQuery;

protected connectedCallback() {
Expand All @@ -30,65 +31,90 @@ export class ESLPanelStack extends ESLBaseElement {
}

protected bindEvents() {
this.addEventListener('esl:show', this._onShowPanel);
this.addEventListener('esl:before:show', this._onBeforeShow);
this.addEventListener('esl:show', this._onShow);
this.addEventListener('esl:before:hide', this._onBeforeHide);

this.addEventListener('transitionend', this._onTransitionEnd);
}

protected unbindEvents() {
this.removeEventListener('esl:show', this._onShowPanel);
this.removeEventListener('esl:before:show', this._onBeforeShow);
this.removeEventListener('esl:show', this._onShow);
this.removeEventListener('esl:before:hide', this._onBeforeHide);

this.removeEventListener('transitionend', this._onTransitionEnd);
}

/** Get all panels for which there is no specified group */
public get panels(): ESLPanel[] {
const els = Array.from(this.children);
return els.filter((el) => el instanceof ESLPanel) as ESLPanel[];
return els.filter((el) => (el instanceof ESLPanel) && !el.groupName) as ESLPanel[];
}

public get current(): ESLPanel | undefined {
/** Get panel that is opened or undefined if all panels are closed */
public get activePanel(): ESLPanel | undefined {
return this.panels.find((el: ESLPanel) => el.open);
}

/** Condition-guard to check if the target is controlled panel */
public includesPanel(target: any): target is ESLPanel {
return this.panels.indexOf(target) !== -1;
}

/** Hide opened panel before a new one will be shown */
@bind
protected _onShowPanel(e: CustomEvent) {
if (!e.detail.open) return; // TODO check
protected _onBeforeShow(e: CustomEvent) {
if (!this.includesPanel(e.target)) return;
this.activePanel?.hide();
}

@bind
protected _onShow(e: CustomEvent) {
const panel = e.target;
if (!this.includesPanel(panel)) return;
if (this.isAccordion) return;
const panel = e.target as ESLPanel;

this.beforeAnimate();
this.onAnimate(this.previousHeight, panel.initialHeight);
// TODO: fallbackDuration
this.onAnimate(this._previousHeight, panel.initialHeight);
this.fallbackDuration >= 0 && setTimeout(this.afterAnimate, this.fallbackDuration);
}

@bind
protected _onBeforeHide(e: CustomEvent) {
if (!(e.target as ESLPanel).open) return;
this.previousHeight = this.offsetHeight;
const target = e.target;
if (!this.includesPanel(target)) return;
if (!target.open) return;
this._previousHeight = this.scrollHeight;
}

/** Animate height of component */
protected onAnimate(from?: number, to?: number) {
// set initial height
if (!this.style.height || this.style.height === 'auto') {
this.style.height = `${from}px`;
}
// make sure that browser apply initial height for animation
// make sure that browser apply initial height to animate
afterNextRender(() => {
this.style.height = `${to}px`;
});
}

/** Set animation class */
protected beforeAnimate() {
CSSUtil.addCls(this, this.animateClass);
CSSUtil.addCls(this, this.animationClass);
}

/** Remove animation class */
protected afterAnimate() {
CSSUtil.removeCls(this, this.animateClass);
this.style.removeProperty('height');
CSSUtil.removeCls(this, this.animationClass);
}

/** Clean up the bits of animation */
@bind
protected _onTransitionEnd(e?: TransitionEvent) {
if (!e || e.propertyName === 'height') {
this.style.removeProperty('height');
this.afterAnimate();
}
}
Expand All @@ -100,7 +126,6 @@ export class ESLPanelStack extends ESLBaseElement {
}
return this._transformationQuery;
}

set transformationQuery(query) {
if (this._transformationQuery) {
this._transformationQuery.removeListener(this.onModeChange);
Expand All @@ -109,19 +134,19 @@ export class ESLPanelStack extends ESLBaseElement {
this._transformationQuery.addListener(this.onModeChange);
}

/** Check if mode is accordion */
get isAccordion() {
return this.transformationQuery.matches;
}

/**
* config that is used to form result panel action params
*/
/** Get config that is used to form result panel action params */
get panelConfig() {
return {
noCollapse: !this.isAccordion
};
}

/** Toggle accordion class according to mode */
protected onModeChange = () => {
CSSUtil.toggleClsTo(this, this.accordionClass, this.isAccordion);
};
Expand Down
66 changes: 33 additions & 33 deletions src/modules/esl-panel/core/esl-panel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,18 @@ export class ESLPanel extends ESLBasePopup {
@boolAttr() public isAccordion: boolean;
@boolAttr() public startAnimation: boolean;

@jsonAttr<PanelActionParams>({defaultValue: {silent: false, force: true, initiator: 'init', noAnimation: true}})
@jsonAttr<PanelActionParams>({defaultValue: {force: true, initiator: 'init', noAnimation: true}})
public initialParams: PopupActionParams;

public initialHeight: number;
protected _initialHeight: number;

public get initialHeight() {
return this._initialHeight;
}

public get stack(): ESLPanelStack | null {
return this.closest(ESLPanelStack.is);
}

protected bindEvents() {
super.bindEvents();
Expand All @@ -41,61 +49,57 @@ export class ESLPanel extends ESLBasePopup {

protected onShow(params: PanelActionParams) {
super.onShow(params);
this.initialHeight = this.scrollHeight;
this._initialHeight = this.scrollHeight;

if (params.noAnimation) return;

this.beforeAnimate(params);
this.beforeAnimate();
if (!params.noCollapse) {
this.onHeightAnimate('show', params);
this.fallbackDuration >= 0 && setTimeout(this._onTransitionEnd, this.fallbackDuration);
this.onAnimate('show');
}
this.afterAnimate(params);
this.fallbackDuration >= 0 && setTimeout(this.afterAnimate, this.fallbackDuration);
}

protected onHide(params: PanelActionParams) {
this.initialHeight = this.scrollHeight;
this._initialHeight = this.scrollHeight;
super.onHide(params);

if (params.noAnimation) return;

this.beforeAnimate(params);
this.beforeAnimate();
if (!params.noCollapse) {
this.onHeightAnimate('hide', params);
this.fallbackDuration >= 0 && setTimeout(this._onTransitionEnd, this.fallbackDuration);
}
this.afterAnimate(params);
}

@bind
protected _onTransitionEnd(e?: TransitionEvent) {
if (!e || e.propertyName === 'max-height') {
// TODO: state is not 'safe' as we can not be sure in caller
this.style.removeProperty('max-height');
// TODO: move to afterAnimate!
this.$$fire(this.open ? 'after:show' : 'after:hide');
this.onAnimate('hide');
}
this.fallbackDuration >= 0 && setTimeout(this.afterAnimate, this.fallbackDuration);
}

protected beforeAnimate(params: PanelActionParams) {
protected beforeAnimate() {
CSSUtil.addCls(this, this.animateClass);
this.postAnimateClass && afterNextRender(() => CSSUtil.addCls(this, this.postAnimateClass));
}

protected onHeightAnimate(action: string, params: PanelActionParams) {
protected onAnimate(action: string) {
// set initial height
this.style.setProperty('max-height', `${action === 'hide' ? this.initialHeight : 0}px`);
this.style.setProperty('max-height', `${action === 'hide' ? this._initialHeight : 0}px`);
// make sure that browser apply initial height for animation
afterNextRender(() => {
this.style.setProperty('max-height', `${action === 'hide' ? 0 : this.initialHeight}px`);
this.style.setProperty('max-height', `${action === 'hide' ? 0 : this._initialHeight}px`);
});
}

protected afterAnimate(params: PanelActionParams) {
// FIXME: that is not working!!!
params.noCollapse && this.style.removeProperty('max-height');
protected afterAnimate() {
this.style.removeProperty('max-height');
CSSUtil.removeCls(this, this.animateClass);
CSSUtil.removeCls(this, this.postAnimateClass);

this.$$fire(this.open ? 'after:show' : 'after:hide');
}

@bind
protected _onTransitionEnd(e?: TransitionEvent) {
if (!e || e.propertyName === 'max-height') {
this.afterAnimate();
}
}

/**
Expand All @@ -105,10 +109,6 @@ export class ESLPanel extends ESLBasePopup {
const stackConfig = this.stack?.panelConfig || {};
return Object.assign({}, stackConfig, this.defaultParams, params || {});
}

public get stack(): ESLPanelStack | null {
return this.closest(ESLPanelStack.is);
}
}

export default ESLPanel;
7 changes: 0 additions & 7 deletions test-server/views/pages/esl-scrollable-tab.html
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,6 @@ <h5 class="mb-0">Tab #7 Lorem ipsum dolor sit amet, consectetur adipiscing elit<
<h5 class="mb-0">Toggle Accordion #1</h5>
</esl-trigger>
<esl-panel class="tab tab-1"
group="tab-group-1"
tabindex="0">
<div class="tab-body">
<p>1 Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry richardson
Expand All @@ -143,7 +142,6 @@ <h5 class="mb-0">Toggle Accordion #1</h5>
<h5 class="mb-0">Toggle Accordion #2</h5>
</esl-trigger>
<esl-panel open class="tab tab-2"
group="tab-group-1"
tabindex="0">
<div class="tab-body">
<p> 2 Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry richardson
Expand All @@ -164,7 +162,6 @@ <h5 class="mb-0">Toggle Accordion #2</h5>
<h5 class="mb-0">Toggle Accordion #3</h5>
</esl-trigger>
<esl-panel class="tab tab-3"
group="tab-group-1"
tabindex="0">
<div class="tab-body">
<p> 3 Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry richardson
Expand All @@ -191,7 +188,6 @@ <h5 class="mb-0">Toggle Accordion #3</h5>
<h5 class="mb-0">Toggle Accordion #4</h5>
</esl-trigger>
<esl-panel class="tab tab-4"
group="tab-group-1"
tabindex="0">
<div class="tab-body">
<p>4 Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry richardson
Expand All @@ -212,7 +208,6 @@ <h5 class="mb-0">Toggle Accordion #4</h5>
<h5 class="mb-0">Toggle Accordion #5</h5>
</esl-trigger>
<esl-panel class="tab tab-5"
group="tab-group-1"
tabindex="0">
<div class="tab-body">
<p>5 Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry richardson
Expand All @@ -233,7 +228,6 @@ <h5 class="mb-0">Toggle Accordion #5</h5>
<h5 class="mb-0">Toggle Accordion #6</h5>
</esl-trigger>
<esl-panel class="tab tab-6"
group="tab-group-1"
tabindex="0">
<div class="tab-body">
<p>6 Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry richardson
Expand All @@ -254,7 +248,6 @@ <h5 class="mb-0">Toggle Accordion #6</h5>
<h5 class="mb-0">Toggle Accordion #7</h5>
</esl-trigger>
<esl-panel class="tab tab-7"
group="tab-group-1"
tabindex="0">
<div class="tab-body">
<p>7 Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry richardson
Expand Down
Loading

0 comments on commit d819259

Please sign in to comment.