integration-libs/epd-visualization/components/visual-viewer/toolbar/visual-viewer-animation-slider/visual-viewer-animation-slider.service.ts
Properties |
|
Methods |
|
Accessors |
Public
constructor(elementRef: ElementRef, windowRef: WindowRef, renderer: Renderer2, changeDetectorRef: ChangeDetectorRef)
|
|||||||||||||||
|
Parameters :
|
| Private applyValue | ||||||
applyValue(value: number)
|
||||||
|
Parameters :
Returns :
void
|
| Private clampToRange | ||||||
clampToRange(value: number)
|
||||||
|
Parameters :
Returns :
number
|
| Private findTouch | |||||||||
findTouch(touchList: TouchList, touchIdentifier?: number)
|
|||||||||
|
Parameters :
Returns :
Touch | undefined
|
| Private getClientRect | ||||||
getClientRect(elementRef: ElementRef)
|
||||||
|
Parameters :
Returns :
DOMRect
|
| Private getClientWidth | ||||||
getClientWidth(elementRef: ElementRef)
|
||||||
|
Parameters :
Returns :
number | undefined
|
| Private getKeyHandler |
getKeyHandler(keyCode: string, rightToLeft: boolean)
|
|
Returns :
| undefined
|
| Public initialize |
initialize()
|
|
Returns :
void
|
| Private onHandleBlur |
onHandleBlur()
|
|
Returns :
void
|
| Private onHandleFocus |
onHandleFocus()
|
|
Returns :
void
|
| Private onKeyboardEvent | ||||||
onKeyboardEvent(event: KeyboardEvent)
|
||||||
|
Parameters :
Returns :
void
|
| Private onMouseDown | ||||||
onMouseDown(event: MouseEvent)
|
||||||
|
Parameters :
Returns :
void
|
| Private onMouseDownOnBar | ||||||
onMouseDownOnBar(event: MouseEvent)
|
||||||
|
Parameters :
Returns :
void
|
| Private onMouseMove | ||||||
onMouseMove(event: MouseEvent)
|
||||||
|
Parameters :
Returns :
void
|
| Private onMouseUp | ||||||
onMouseUp(_event: MouseEvent)
|
||||||
|
Parameters :
Returns :
void
|
| Private onResize |
onResize()
|
|
Returns :
void
|
| Private onTouchEnd | ||||||
onTouchEnd(event: TouchEvent)
|
||||||
|
Parameters :
Returns :
void
|
| Private onTouchMove | ||||||
onTouchMove(event: TouchEvent)
|
||||||
|
Parameters :
Returns :
void
|
| Private onTouchStart | ||||||
onTouchStart(event: TouchEvent)
|
||||||
|
Parameters :
Returns :
void
|
| Private onTouchStartOnBar | ||||||
onTouchStartOnBar(event: TouchEvent)
|
||||||
|
Parameters :
Returns :
void
|
| Private positionToValue | ||||||
positionToValue(position: number)
|
||||||
|
Parameters :
Returns :
number
|
| Private resizeObserverSupported |
resizeObserverSupported()
|
|
Returns :
boolean
|
| Private setInitialized |
setInitialized()
|
|
Returns :
void
|
| Private setupResizeObserver |
setupResizeObserver()
|
|
Returns :
void
|
| Private updateEventBindings |
updateEventBindings()
|
|
Returns :
void
|
| Private valueToPosition | ||||||
valueToPosition(value: number)
|
||||||
|
Parameters :
Returns :
number
|
| _barElement |
Type : ElementRef
|
| Private _disabled |
Type : boolean
|
Default value : false
|
| _handleElement |
Type : ElementRef
|
| Private _hidden |
Type : boolean
|
| Private _initialized |
Default value : false
|
| Private Optional _resizeObserver |
Type : ResizeObserver
|
Default value : undefined
|
| Private Optional _touchIdentifier |
Type : number
|
Default value : undefined
|
| Private _value |
Type : number
|
Default value : 0
|
| Private eventListenerUtils |
Default value : new EventListenerUtils()
|
| Public initializedChange |
Default value : new EventEmitter<boolean>()
|
| Protected Readonly pageDelta |
Default value : 1 / 10
|
| sizeChange |
Default value : new EventEmitter()
|
| Protected Readonly stepDelta |
Default value : 1 / 50
|
| valueChange |
Type : EventEmitter<number>
|
Default value : new EventEmitter()
|
| initialized |
getinitialized()
|
| value | ||||||
getvalue()
|
||||||
setvalue(value: number)
|
||||||
|
Slider value. Value is in the range [0-1].
Parameters :
Returns :
void
|
| disabled | ||||||
getdisabled()
|
||||||
setdisabled(disabled: boolean)
|
||||||
|
Parameters :
Returns :
void
|
| hidden | ||||||
gethidden()
|
||||||
sethidden(hidden: boolean)
|
||||||
|
Parameters :
Returns :
void
|
| position |
getposition()
|
| rightToLeft |
getrightToLeft()
|
| barElement | ||||||
getbarElement()
|
||||||
setbarElement(barElement: ElementRef)
|
||||||
|
Parameters :
Returns :
void
|
| handleElement | ||||||
gethandleElement()
|
||||||
sethandleElement(handleElement: ElementRef)
|
||||||
|
Parameters :
Returns :
void
|
| resizeObserver | ||||||
getresizeObserver()
|
||||||
setresizeObserver(resizeObserver: ResizeObserver | undefined)
|
||||||
|
Parameters :
Returns :
void
|
| touchIdentifier | ||||||
gettouchIdentifier()
|
||||||
settouchIdentifier(touchIdentifier: number | undefined)
|
||||||
|
Parameters :
Returns :
void
|
| handleWidth |
gethandleWidth()
|
| barWidth |
getbarWidth()
|
| handleMaxPosition |
gethandleMaxPosition()
|
| sliderClientPosition |
getsliderClientPosition()
|
import {
ChangeDetectorRef,
ElementRef,
EventEmitter,
Injectable,
Renderer2,
} from '@angular/core';
import { WindowRef } from '@spartacus/core';
import { EventListenerUtils } from '@spartacus/epd-visualization/root';
@Injectable({
providedIn: 'any',
})
export class VisualViewerAnimationSliderService {
public constructor(
private elementRef: ElementRef,
private windowRef: WindowRef,
private renderer: Renderer2,
private changeDetectorRef: ChangeDetectorRef
) {
this.eventListenerUtils.initialize(this.renderer);
}
public initialize(): void {
this.updateEventBindings();
this.setupResizeObserver();
this.setInitialized();
}
private setInitialized() {
this._initialized = true;
this.initializedChange.emit(true);
this.initializedChange.complete();
}
public get initialized(): boolean {
return this._initialized;
}
private _initialized = false;
public initializedChange = new EventEmitter<boolean>();
/**
* Slider value. Value is in the range [0-1].
*/
set value(value: number) {
value = this.clampToRange(value);
if (this._value === value) {
return;
}
this._value = value;
this.valueChange.emit(this.value);
}
get value() {
return this._value;
}
private _value: number = 0;
valueChange: EventEmitter<number> = new EventEmitter();
set disabled(disabled: boolean) {
if (this._disabled === disabled) {
return;
}
this._disabled = disabled;
this.updateEventBindings();
}
get disabled() {
return this._disabled;
}
private _disabled: boolean = false;
set hidden(hidden: boolean) {
if (this._hidden === hidden) {
return;
}
this._hidden = hidden;
// Ensure handle position is recalculated when the animation slider visibility changes
// Fixes a bug in which the initial position of the slider handle is incorrect
// because the bar width is calculated while the animation slider is hidden (noticeable in RTL mode)
this.changeDetectorRef.detectChanges();
}
get hidden(): boolean {
return this._hidden;
}
private _hidden: boolean;
get position(): number {
return this.valueToPosition(this.value);
}
get rightToLeft(): boolean {
return this.windowRef.document.documentElement.dir === 'rtl';
}
set barElement(barElement: ElementRef) {
this._barElement = barElement;
}
get barElement(): ElementRef {
return this._barElement;
}
_barElement: ElementRef;
set handleElement(handleElement: ElementRef) {
this._handleElement = handleElement;
}
get handleElement(): ElementRef {
return this._handleElement;
}
_handleElement: ElementRef;
private set resizeObserver(resizeObserver: ResizeObserver | undefined) {
this._resizeObserver = resizeObserver;
}
private get resizeObserver() {
return this._resizeObserver;
}
private _resizeObserver?: ResizeObserver = undefined;
private eventListenerUtils = new EventListenerUtils();
private set touchIdentifier(touchIdentifier: number | undefined) {
this._touchIdentifier = touchIdentifier;
}
private get touchIdentifier(): number | undefined {
return this._touchIdentifier;
}
private _touchIdentifier?: number = undefined;
private getClientWidth(elementRef: ElementRef): number | undefined {
if (!elementRef || !elementRef.nativeElement) {
return undefined;
}
const clientRect = this.getClientRect(elementRef);
return clientRect.right - clientRect.left;
}
private getClientRect(elementRef: ElementRef): DOMRect {
return elementRef.nativeElement.getBoundingClientRect();
}
private resizeObserverSupported(): boolean {
return window.ResizeObserver !== undefined;
}
private setupResizeObserver() {
if (this.resizeObserverSupported()) {
this.resizeObserver = new ResizeObserver(this.onResize.bind(this));
this.resizeObserver.observe(this.elementRef.nativeElement);
}
}
private onResize() {
// Ensure handle position is recalculated on resize
this.changeDetectorRef.detectChanges();
}
sizeChange = new EventEmitter();
private updateEventBindings(): void {
if (this.disabled) {
this.eventListenerUtils.detachAllEventListeners(document);
this.eventListenerUtils.detachAllEventListeners(
this.barElement.nativeElement
);
this.eventListenerUtils.detachAllEventListeners(
this.handleElement.nativeElement
);
} else {
this.eventListenerUtils.attachEventListener(
this.handleElement.nativeElement,
'mousedown',
this.onMouseDown.bind(this)
);
this.eventListenerUtils.attachEventListener(
this.barElement.nativeElement,
'mousedown',
this.onMouseDownOnBar.bind(this)
);
this.eventListenerUtils.attachEventListener(
this.handleElement.nativeElement,
'touchstart',
this.onTouchStart.bind(this)
);
this.eventListenerUtils.attachEventListener(
this.barElement.nativeElement,
'touchstart',
this.onTouchStartOnBar.bind(this)
);
this.eventListenerUtils.attachEventListener(
this.handleElement.nativeElement,
'focus',
this.onHandleFocus.bind(this)
);
}
}
get handleWidth(): number {
return this.getClientWidth(this.handleElement) ?? 0;
}
get barWidth(): number {
return this.getClientWidth(this.barElement) ?? 0;
}
get handleMaxPosition(): number {
return this.barWidth - this.handleWidth;
}
private valueToPosition(value: number): number {
let position: number = this.clampToRange(value);
if (this.rightToLeft) {
position = 1 - position;
}
return position * this.handleMaxPosition;
}
private positionToValue(position: number): number {
let value: number = position / this.handleMaxPosition;
if (this.rightToLeft) {
value = 1 - value;
}
return value;
}
private findTouch(
touchList: TouchList,
touchIdentifier?: number
): Touch | undefined {
for (let i = 0; i < touchList.length; i++) {
const touch = touchList.item(i) as Touch;
if (touch.identifier === touchIdentifier) {
return touch;
}
}
return undefined;
}
private get sliderClientPosition(): number {
return this.getClientRect(this.elementRef).left;
}
private onTouchStart(event: TouchEvent): void {
event.stopPropagation();
event.preventDefault();
if (this.touchIdentifier !== undefined) {
return;
}
this.eventListenerUtils.detachEventListeners(document, 'touchmove');
this.eventListenerUtils.attachEventListener(
document,
'touchmove',
this.onTouchMove.bind(this)
);
this.eventListenerUtils.detachEventListeners(document, 'touchend');
this.eventListenerUtils.attachEventListener(
document,
'touchend',
this.onTouchEnd.bind(this)
);
this.touchIdentifier = (event.changedTouches as TouchList)[0].identifier;
}
private onTouchStartOnBar(event: TouchEvent): void {
this.onTouchStart(event);
this.onTouchMove(event);
}
private onMouseDown(event: MouseEvent): void {
event.stopPropagation();
event.preventDefault();
this.eventListenerUtils.detachEventListeners(document, 'mousemove');
this.eventListenerUtils.attachEventListener(
document,
'mousemove',
this.onMouseMove.bind(this)
);
this.eventListenerUtils.detachEventListeners(document, 'mouseup');
this.eventListenerUtils.attachEventListener(
document,
'mouseup',
this.onMouseUp.bind(this)
);
}
private onMouseDownOnBar(event: MouseEvent): void {
this.onMouseDown(event);
this.onMouseMove(event);
}
private onMouseMove(event: MouseEvent): void {
const position =
event.clientX - this.sliderClientPosition - this.handleWidth / 2;
this.applyValue(this.positionToValue(position));
}
private onMouseUp(_event: MouseEvent): void {
this.eventListenerUtils.detachEventListeners(document, 'mousemove');
this.eventListenerUtils.detachEventListeners(document, 'mouseup');
}
private onTouchMove(event: TouchEvent): void {
let touchInitiatedOnSlider = this.findTouch(
event.changedTouches,
this.touchIdentifier
);
if (touchInitiatedOnSlider === undefined) {
return;
}
let touch = this.findTouch(event.touches, this.touchIdentifier) as Touch;
const position: number =
touch.clientX - this.sliderClientPosition - this.handleWidth / 2;
this.applyValue(this.positionToValue(position));
}
private onTouchEnd(event: TouchEvent): void {
let touchInitiatedOnSlider = this.findTouch(
event.changedTouches,
this.touchIdentifier
);
if (touchInitiatedOnSlider === undefined) {
return;
}
this.touchIdentifier = undefined;
this.eventListenerUtils.detachEventListeners(document, 'touchmove');
this.eventListenerUtils.detachEventListeners(document, 'touchend');
}
private onHandleFocus(): void {
const nativeElement = this.handleElement.nativeElement;
this.eventListenerUtils.attachEventListener(
nativeElement,
'blur',
this.onHandleBlur.bind(this)
);
this.eventListenerUtils.attachEventListener(
nativeElement,
'keydown',
this.onKeyboardEvent.bind(this)
);
}
private onHandleBlur(): void {
const nativeElement = this.handleElement.nativeElement;
this.eventListenerUtils.detachEventListeners(nativeElement, 'blur');
this.eventListenerUtils.detachEventListeners(nativeElement, 'keydown');
this.eventListenerUtils.detachEventListeners(nativeElement, 'keyup');
}
private onKeyboardEvent(event: KeyboardEvent): void {
const keyHandler = this.getKeyHandler(event.code, this.rightToLeft);
if (keyHandler === undefined) {
return;
}
event.preventDefault();
this.applyValue(keyHandler(this.value));
}
protected readonly stepDelta = 1 / 50;
protected readonly pageDelta = 1 / 10;
private getKeyHandler(
keyCode: string,
rightToLeft: boolean
): ((value: number) => number) | undefined {
let increaseStep = (currentValue: number) => currentValue + this.stepDelta;
let decreaseStep = (currentValue: number) => currentValue - this.stepDelta;
let increasePage = (currentValue: number) => currentValue + this.pageDelta;
let decreasePage = (currentValue: number) => currentValue - this.pageDelta;
let stepLeft = rightToLeft ? increaseStep : decreaseStep;
let stepRight = rightToLeft ? decreaseStep : increaseStep;
let home = () => 0;
let end = () => 1;
switch (keyCode) {
case 'ArrowUp':
return increaseStep;
case 'ArrowDown':
return decreaseStep;
case 'ArrowLeft':
return stepLeft;
case 'ArrowRight':
return stepRight;
case 'PageUp':
return increasePage;
case 'PageDown':
return decreasePage;
case 'Home':
return home;
case 'End':
return end;
default:
return undefined;
}
}
private applyValue(value: number): void {
value = this.clampToRange(value);
if (this.value !== value) {
this.value = value;
this.valueChange.emit(this.value);
}
}
private clampToRange(value: number): number {
return Math.min(Math.max(value, 0), 1);
}
}