Skip to content

Commit

Permalink
fix(core): request animation depending on zonejs; reduce global events (
Browse files Browse the repository at this point in the history
  • Loading branch information
danielleroux committed Oct 20, 2023
1 parent a9d919b commit 98a615c
Show file tree
Hide file tree
Showing 7 changed files with 151 additions and 50 deletions.
30 changes: 2 additions & 28 deletions packages/core/component-doc.json
Original file line number Diff line number Diff line change
Expand Up @@ -3490,20 +3490,7 @@
"styles": [],
"slots": [],
"parts": [],
"listeners": [
{
"event": "click",
"target": "window",
"capture": false,
"passive": false
},
{
"event": "keydown",
"target": "window",
"capture": false,
"passive": false
}
]
"listeners": []
},
{
"dirPath": "./src/components/dropdown-button",
Expand Down Expand Up @@ -9055,12 +9042,6 @@
"event": "ix-select-item:labelChange",
"capture": false,
"passive": false
},
{
"event": "keydown",
"target": "window",
"capture": false,
"passive": false
}
]
},
Expand Down Expand Up @@ -9384,14 +9365,7 @@
}
],
"parts": [],
"listeners": [
{
"event": "pointerup",
"target": "window",
"capture": false,
"passive": true
}
]
"listeners": []
},
{
"dirPath": "./src/components/spinner",
Expand Down
10 changes: 3 additions & 7 deletions packages/core/src/components/dropdown/dropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ import {
EventEmitter,
h,
Host,
Listen,
Method,
Prop,
Watch,
} from '@stencil/core';
import { OnListener } from '../utils/listener';
import { AlignedPlacement } from './placement';

/**
Expand Down Expand Up @@ -285,9 +285,7 @@ export class Dropdown {
}
}

@Listen('click', {
target: 'window',
})
@OnListener<Dropdown>('click', (self) => self.show)
clickOutside(event: PointerEvent) {
const target = event.target as HTMLElement;

Expand Down Expand Up @@ -327,9 +325,7 @@ export class Dropdown {
}
}

@Listen('keydown', {
target: 'window',
})
@OnListener<Dropdown>('keydown', (self) => self.show)
keydown(event: KeyboardEvent) {
if (this.show === true && event.code === 'Escape') {
this.close();
Expand Down
5 changes: 2 additions & 3 deletions packages/core/src/components/select/select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
Watch,
} from '@stencil/core';
import { IxSelectItemLabelChangeEvent } from '../select-item/events';
import { OnListener } from '../utils/listener';

@Component({
tag: 'ix-select',
Expand Down Expand Up @@ -310,9 +311,7 @@ export class Select {
}
}

@Listen('keydown', {
target: 'window',
})
@OnListener<Select>('keydown', (self) => self.dropdownShow)
async onKeyDown(event: KeyboardEvent) {
if (event.code === 'ArrowDown' || event.code === 'ArrowUp') {
this.onArrowNavigation(event, event.code);
Expand Down
8 changes: 3 additions & 5 deletions packages/core/src/components/slider/slider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ import {
EventEmitter,
h,
Host,
Listen,
Prop,
State,
Watch,
} from '@stencil/core';
import { A11yAttributes, a11yHostAttributes } from '../utils/a11y';
import { OnListener } from '../utils/listener';

export type SliderMarker = Array<number>;

Expand All @@ -44,7 +44,7 @@ function between(min: number, value: number, max: number) {
styleUrl: 'slider.scss',
shadow: true,
})
export class IxSlider {
export class Slider {
@Element() hostElement!: HTMLIxSliderElement;

/**
Expand Down Expand Up @@ -177,9 +177,7 @@ export class IxSlider {
// Listen globally on window because sometimes the event listener
// of the DOM element input itself is not called if the release
// click is not inside the element anymore
@Listen('pointerup', {
target: 'window',
})
@OnListener<Slider>('pointerUp', (self) => self.showTooltip)
onPointerUp() {
this.showTooltip = false;
}
Expand Down
31 changes: 24 additions & 7 deletions packages/core/src/components/tabs/tabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
Prop,
State,
} from '@stencil/core';
import { requestAnimationFrameNoNgZone } from '../utils/requestAnimationFrame';

@Component({
tag: 'ix-tabs',
Expand Down Expand Up @@ -62,13 +63,23 @@ export class Tabs {
@State() totalItems = 0;
@State() currentScrollAmount = 0;
@State() scrollAmount = 100;
@State() styleNextArrow = {};
@State() stylePreviousArrow = {};

@State() scrollActionAmount = 0;

private windowStartSize = window.innerWidth;

private get arrowLeftElement() {
return this.hostElement.shadowRoot.querySelector(
'[data-arrow-left]'
) as HTMLElement;
}

private get arrowRightElement() {
return this.hostElement.shadowRoot.querySelector(
'[data-arrow-right]'
) as HTMLElement;
}

private clickAction: {
timeout: NodeJS.Timeout;
isClick: boolean;
Expand Down Expand Up @@ -244,11 +255,17 @@ export class Tabs {
}

componentWillRender() {
requestAnimationFrame(() => {
requestAnimationFrameNoNgZone(() => {
const showNextArrow = this.showNextArrow();
const previousArrow = this.showPreviousArrow();
this.styleNextArrow = this.getArrowStyle(showNextArrow);
this.stylePreviousArrow = this.getArrowStyle(previousArrow);
Object.assign(
this.arrowLeftElement.style,
this.getArrowStyle(previousArrow)
);
Object.assign(
this.arrowRightElement.style,
this.getArrowStyle(showNextArrow)
);
});
}

Expand Down Expand Up @@ -282,7 +299,7 @@ export class Tabs {
<Host>
<div
class="arrow"
style={this.stylePreviousArrow}
data-arrow-left
onClick={() => this.move(this.scrollAmount, true)}
>
<ix-icon name={'chevron-left-small'}></ix-icon>
Expand All @@ -302,7 +319,7 @@ export class Tabs {
</div>
<div
class="arrow right"
style={this.styleNextArrow}
data-arrow-right
onClick={() => this.move(-this.scrollAmount, true)}
>
<ix-icon name={'chevron-right-small'}></ix-icon>
Expand Down
89 changes: 89 additions & 0 deletions packages/core/src/components/utils/listener.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
* SPDX-FileCopyrightText: 2023 Siemens AG
*
* SPDX-License-Identifier: MIT
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

import { getElement } from '@stencil/core';

export type ListenerOptions = {
target?: 'window';
defaultEnabled?: boolean;
};

const defaultOptions: ListenerOptions = {
target: 'window',
defaultEnabled: true,
};

export function createListener(event: string, options: ListenerOptions = {}) {
const opts = {
...defaultOptions,
...options,
};

let callback: any;
const onEvent = (event: Event) => {
callback(event);
};

const resultObject = {
on: (onEventCallback: any) => {
callback = onEventCallback;
},

isEnabled: opts.defaultEnabled,

enable: (state: boolean) => {
resultObject.isEnabled = state;

if (state) {
addEventListener(event, onEvent);
} else {
removeEventListener(event, onEvent);
}
},

destroy: () => {
resultObject.enable(false);
},
};

resultObject.enable(opts.defaultEnabled);
return resultObject;
}

export function OnListener<T>(event: string, fnExp?: (self: T) => boolean) {
return (proto: any, methodName: string) => {
const { componentWillLoad, componentWillRender, disconnectedCallback } =
proto;

proto.componentWillRender = function () {
const host = getElement(this);
host[`__ix__${methodName}`].enable(fnExp(this));
return componentWillRender && componentWillRender.call(this);
};

proto.componentWillLoad = function () {
const listener = createListener(event);
const host = getElement(this);
const method = this[methodName];

host[`__ix__${methodName}`] = listener;

listener.on(method.bind(this));
return componentWillLoad && componentWillLoad.call(this);
};

proto.disconnectedCallback = function () {
const host = getElement(this);

host[`__ix__${methodName}`].destroy();
host[`__ix__${methodName}`] = null;
return disconnectedCallback && disconnectedCallback.call(this);
};
};
}
28 changes: 28 additions & 0 deletions packages/core/src/components/utils/requestAnimationFrame.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* SPDX-FileCopyrightText: 2023 Siemens AG
*
* SPDX-License-Identifier: MIT
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

declare const __zone_symbol__requestAnimationFrame: any;

/**
* Prevents angular from change detection when requesting an animation frame
*
* Credits goes to:
* https://github.com/ionic-team/ionic-framework/blob/main/core/src/utils/helpers.ts
*/
export const requestAnimationFrameNoNgZone = (
callback: (...args: any[]) => void
) => {
if (typeof __zone_symbol__requestAnimationFrame === 'function') {
return __zone_symbol__requestAnimationFrame(callback);
}
if (typeof requestAnimationFrame === 'function') {
return requestAnimationFrame(callback);
}
return setTimeout(callback);
};

0 comments on commit 98a615c

Please sign in to comment.