import * as i2 from '@angular/common';
import { CommonModule } from '@angular/common';
import * as i0 from '@angular/core';
import { Component, ChangeDetectionStrategy, HostBinding, Input, ContentChild, ViewChild, NgModule } from '@angular/core';
import * as i3 from '@angular/material/icon';
import { MatIconModule } from '@angular/material/icon';
import { coerceNumberProperty } from '@angular/cdk/coercion';
import { Destroy } from '@deja-js/component/core';
import * as i1 from '@deja-js/component/core/item-list';
import { ViewPortService } from '@deja-js/component/core/item-list';
import { BehaviorSubject, Subject, fromEvent, debounceTime, takeUntil, switchMap, of, timer, map, distinctUntilChanged, interval, tap, mergeWith, filter } from 'rxjs';

/*
 *  @license
 *  Copyright Hôpitaux Universitaires de Genève. All Rights Reserved.
 *
 *  Use of this source code is governed by an Apache-2.0 license that can be
 *  found in the LICENSE file at https://github.com/DSI-HUG/dejajs-components/blob/master/LICENSE
 */
class DejaViewPortComponent extends Destroy {
    constructor(changeDetectorRef, viewPort) {
        super();
        this.changeDetectorRef = changeDetectorRef;
        this.viewPort = viewPort;
        this.hasUpButton = false;
        this.hasDownButton = false;
        this._isHorizontal = false;
        this._hasButtons = false;
        this.downButton$ = new BehaviorSubject(null);
        this.upButton$ = new Subject();
        this.scrollPosition = 0;
        console.warn('@deja-js/component/viewport is deprecated, and will be removed in a further version. Please use @deja-js/component/v2/viewport instead.');
        fromEvent(window, 'resize').pipe(debounceTime(5), takeUntil(this.destroyed$)).subscribe(() => {
            this.viewPort.deleteSizeCache();
            this.viewPort.refresh();
            this.changeDetectorRef.markForCheck();
        });
        const scroll = (vp) => {
            if (vp) {
                if (!this.hasButtons) {
                    if (this.element) {
                        if (this._isHorizontal) {
                            this.element.scrollLeft = vp.scrollPos;
                        }
                        else {
                            this.element.scrollTop = vp.scrollPos;
                        }
                        this.scrollPosition = vp.scrollPos;
                    }
                }
                else {
                    this.scrollPos = vp.scrollPos;
                    this.startOffset = this.scrollPos - vp.beforeSize;
                }
            }
            this.changeDetectorRef.markForCheck();
        };
        viewPort.viewPort$.pipe(switchMap(viewPortResult => {
            if (viewPort.mode !== 'disabled') {
                this.vpItems = viewPortResult.visibleItems;
                this.vpStartIndex = viewPortResult.startIndex;
                this.vpEndIndex = viewPortResult.endIndex;
            }
            else {
                this.vpStartIndex = 0;
                this.vpEndIndex = 0;
            }
            if (this.hasButtons) {
                this.startOffset = this.scrollPos - viewPortResult.beforeSize;
                this.beforeSize = null;
                this.afterSize = null;
                this.hasUpButton = this.scrollPos > 0;
                this.hasDownButton = this.scrollPos + viewPortResult.listSize < viewPortResult.beforeSize + viewPortResult.viewPortSize + viewPortResult.afterSize;
            }
            else {
                this.startOffset = 0;
                this.beforeSize = viewPortResult.beforeSize || null;
                this.afterSize = viewPortResult.afterSize || null;
                this.hasUpButton = false;
                this.hasDownButton = false;
            }
            if (viewPortResult.scrollPos !== undefined) {
                let length = 0;
                if (this.element) {
                    const listItems = this.element.getElementsByClassName('listitem');
                    length = listItems.length;
                }
                const rebind = length !== viewPortResult.visibleItems.length;
                if (!rebind) {
                    return of(viewPortResult);
                }
                else {
                    this.changeDetectorRef.markForCheck();
                    return timer(1).pipe(map(() => viewPortResult));
                }
            }
            else {
                return of(null);
            }
        }), takeUntil(this.destroyed$)).subscribe(scroll);
        const mouseWheel$ = () => {
            const mouseWheelEvent$ = fromEvent(this.element, 'mousewheel');
            return mouseWheelEvent$.pipe(map(event => {
                event.stopPropagation();
                event.preventDefault();
                return this.scrollPos + event.deltaY;
            }));
        };
        const downButton$ = this.downButton$.pipe(distinctUntilChanged());
        const upButton$ = this.upButton$.pipe(distinctUntilChanged());
        downButton$.pipe(switchMap(downButton => downButton ? mouseWheel$() : of(0)), takeUntil(this.destroyed$)).subscribe(scrollPos => this.scrollPos = scrollPos);
        const autoScroll$ = (event, sign) => timer(750).pipe(switchMap(() => interval(50)), tap(() => this.scrollPos += sign * (event.ctrlKey ? this.clientSize : this.buttonsStep * 2)));
        const initButton$ = (button) => {
            const sign = button.id === 'down' ? 1 : -1;
            const mouseUpEvent$ = fromEvent(button, 'mouseup');
            const mouseLeaveEvent$ = fromEvent(button, 'mouseleave');
            const mouseup$ = mouseUpEvent$.pipe(mergeWith(mouseLeaveEvent$), tap(upEvent => this.scrollPos += sign * (upEvent.ctrlKey ? this.clientSize : this.buttonsStep)));
            const mouseDownEvent$ = fromEvent(button, 'mousedown');
            return mouseDownEvent$.pipe(tap(event => this.scrollPos += sign * (event.ctrlKey ? this.clientSize : this.buttonsStep * 2)), switchMap(event => autoScroll$(event, sign).pipe(takeUntil(mouseup$))));
        };
        downButton$.pipe(filter(downButton => !!downButton), switchMap(initButton$), takeUntil(this.destroyed$)).subscribe();
        upButton$.pipe(filter(upButton => !!upButton), switchMap(initButton$), takeUntil(this.destroyed$)).subscribe();
        downButton$.pipe(mergeWith(upButton$), debounceTime(10), filter(needToRefresh => !!needToRefresh), takeUntil(this.destroyed$)).subscribe(() => this.viewPort.refresh());
    }
    get hasButtons() {
        return this._hasButtons;
    }
    get isHorizontal() {
        return this._isHorizontal;
    }
    set buttonsStep(value) {
        this._buttonsStep = value;
    }
    get buttonsStep() {
        return this._buttonsStep || 20;
    }
    set downButton(element) {
        this.downButton$.next(element?.nativeElement || null);
    }
    set upButton(element) {
        this.upButton$.next(element?.nativeElement || null);
    }
    /** Set the list of models to render inside the viewport control */
    set models(models) {
        this.items = models ? models.map(model => ({
            model: model
        })) : [];
    }
    /** Set the list of items to render inside the viewport control */
    set items(items) {
        this._items = items || [];
        if (this.viewPort.mode === 'disabled') {
            this.vpItems = this._items;
        }
        this.viewPort.items$.next(this._items);
    }
    /** Set the scrolling style
      * scrollbar: Classic scrollbars
      * buttons: A button before is placed at the top or at the left of the list, and a button after is placed at the right or the bottom of the list.
      */
    set scrollingStyle(value) {
        this._hasButtons = value === 'buttons';
    }
    /** Set the direction of the items rendering
     * vertical: The item are displayed vertically
     * horizontal: The item are displayed horizontally
     */
    set direction(value) {
        this.viewPort.direction$.next(value);
        this._isHorizontal = value === 'horizontal';
        this.changeDetectorRef.markForCheck();
    }
    /** Set the item size in fixed mode or the default item size before rendering in auto mode */
    set itemSize(value) {
        if (value) {
            const size = coerceNumberProperty(value);
            this.viewPort.itemsSize$.next(+value);
            if (!this._buttonsStep) {
                this._buttonsStep = size;
            }
        }
    }
    get itemSize() {
        return this.viewPort.itemsSize;
    }
    set wrapperElement(element) {
        this.element = element.nativeElement;
        this.viewPort.element$.next(this.element);
        fromEvent(this.element, 'scroll').pipe(map(event => event.target), map(target => Math.round(this._isHorizontal ? target.scrollLeft : target.scrollTop)), takeUntil(this.destroyed$)).subscribe(scrollPos => this.viewPort.scrollPosition$.next(scrollPos));
    }
    get itemTemplate() {
        return this.itemTemplateExternal || this.itemTemplateInternal;
    }
    get clientSize() {
        if (!this.element) {
            return 0;
        }
        return this._isHorizontal ? this.element.clientWidth : this.element.clientHeight;
    }
    set scrollPos(value) {
        const scrollPos = Math.max(coerceNumberProperty(value), 0);
        this.scrollPosition = scrollPos;
        this.viewPort.scrollPosition$.next(scrollPos);
    }
    get scrollPos() {
        return this.scrollPosition;
    }
    /**
     * Définit la méthode de calcul de la taille des éléments. Les valuers acceptées sont
     * disabled: Tous les éléments sont rendus. (< 100 éléments)
     * fixed: Seul les éléments visibles sont rendus. La taille des éléments est constante et définie par itemsSize. (performances ++)
     * variable: Seul les éléments visibles sont rendus. La taille des éléments est variable et définie par item.size. (performances +-)
     * auto: Seul les éléments visibles sont rendus. La taille des éléments est calculée automatiquement (performances --)
     */
    set viewportMode(mode) {
        this.viewPort.mode$.next(mode);
    }
    get viewportMode() {
        return this.viewPort.mode;
    }
    refresh() {
        this.changeDetectorRef.markForCheck();
    }
    /** Recalcule le viewport. */
    refreshViewPort(item, clearMeasuredHeight) {
        const refreshParams = {};
        if (item) {
            refreshParams.items = [item];
        }
        if (clearMeasuredHeight) {
            refreshParams.clearMeasuredSize = clearMeasuredHeight;
        }
        this.viewPort.refresh(refreshParams);
        this.changeDetectorRef.markForCheck();
    }
    ensureVisible(item) {
        this.viewPort.ensureItem$.next(item);
    }
    getCssSize(item) {
        const itemSize = this.getItemSize(item);
        return itemSize ? `${itemSize}px` : 'auto';
    }
    getItemSize(item) {
        if (this.viewPort.mode === 'disabled') {
            return null;
        }
        else if (this.viewPort.mode === 'fixed') {
            return this.itemSize;
        }
        else if (this.viewPort.mode === 'auto') {
            return item.size || null;
        }
        else {
            return (item.size && item.size > ViewPortService.itemDefaultSize) ? item.size : this.itemSize;
        }
    }
}
DejaViewPortComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.1.3", ngImport: i0, type: DejaViewPortComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: i1.ViewPortService }], target: i0.ɵɵFactoryTarget.Component });
DejaViewPortComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.1.3", type: DejaViewPortComponent, selector: "deja-viewport", inputs: { itemTemplateExternal: "itemTemplateExternal", buttonsStep: "buttonsStep", models: "models", items: "items", scrollingStyle: "scrollingStyle", direction: "direction", itemSize: "itemSize", viewportMode: "viewportMode" }, host: { properties: { "attr.hasUpBtn": "this.hasUpButton", "attr.hasDownBtn": "this.hasDownButton", "attr.horizontal": "this._isHorizontal", "attr.buttons": "this._hasButtons" } }, providers: [ViewPortService], queries: [{ propertyName: "itemTemplateInternal", first: true, predicate: ["itemTemplate"], descendants: true }], viewQueries: [{ propertyName: "downButton", first: true, predicate: ["down"], descendants: true }, { propertyName: "upButton", first: true, predicate: ["up"], descendants: true }, { propertyName: "wrapperElement", first: true, predicate: ["wrapper"], descendants: true, static: true }], usesInheritance: true, ngImport: i0, template: "<div #up id=\"up\" *ngIf=\"hasButtons\"><mat-icon>keyboard_arrow_up</mat-icon></div>\n<div #wrapper id=\"viewport-wrapper\">\n\t<ng-container *ngIf=\"!isHorizontal\">\n\t\t<div *ngIf=\"beforeSize\" [style.height.px]=\"beforeSize\"></div>\n\t\t<div class=\"listitem\" *ngFor=\"let item of vpItems; let index = index\" [attr.flat]=\"vpStartIndex + index\" [style.height]=\"getCssSize(item)\" [style.margin-top.px]=\"startOffset && index === 0 ? -startOffset : null\">\n\t\t\t<ng-template [ngTemplateOutlet]=\"itemTemplate\" [ngTemplateOutletContext]=\"{ $implicit: item, index: vpStartIndex + index, size: getItemSize(item), model: item.model }\"></ng-template>\n\t\t</div>\n\t\t<div *ngIf=\"afterSize\" [style.height.px]=\"afterSize\"></div>\n\t</ng-container>\n\t<ng-container *ngIf=\"isHorizontal\">\n\t\t<div *ngIf=\"beforeSize\" [style.flex-basis.px]=\"beforeSize\"></div>\n\t\t<div class=\"listitem\" *ngFor=\"let item of vpItems; let index = index\" [attr.flat]=\"vpStartIndex + index\" [style.flex-basis]=\"getCssSize(item)\" [style.margin-left.px]=\"startOffset && index === 0 ? -startOffset : null\">\n\t\t\t<ng-template [ngTemplateOutlet]=\"itemTemplate\" [ngTemplateOutletContext]=\"{ $implicit: item, index: vpStartIndex + index, size: getItemSize(item), model: item.model }\"></ng-template>\n\t\t</div>\n\t\t<div *ngIf=\"afterSize\" [style.flex-basis.px]=\"afterSize\"></div>\n\t</ng-container>\n</div>\n<div #down id=\"down\" *ngIf=\"hasButtons\"><mat-icon>keyboard_arrow_down</mat-icon></div>", styles: [":host{position:relative;display:flex;flex-direction:row;overflow:hidden}:host[hasUpBtn=false] #up{visibility:hidden}:host[hasDownBtn=false] #down{visibility:hidden}:host[buttons=false][horizontal=false] #viewport-wrapper{width:100%;overflow-y:auto}:host[buttons=false][horizontal=true] #viewport-wrapper{height:100%;overflow-x:auto}:host[buttons=true][horizontal=false]{flex-direction:column}:host #viewport-wrapper{position:relative;flex-grow:1;overflow:hidden}:host #viewport-wrapper>div{padding:0;margin:0}:host #up,:host #down{flex:0 0 auto;display:flex;justify-content:space-around;align-items:center;font-size:120%;cursor:pointer;z-index:1}:host[horizontal=false] #viewport-wrapper>div{width:100%}:host[horizontal=false] #up,:host[horizontal=false] #down{padding:.15rem 0}:host[horizontal=true] #viewport-wrapper{display:flex;justify-content:flex-start;align-items:stretch;flex-direction:row}:host[horizontal=true] #viewport-wrapper>div{height:100%;overflow:hidden;flex-grow:0;flex-shrink:0}:host[horizontal=true] #up,:host[horizontal=true] #down{padding:0 .15rem}:host[horizontal=true] #up .mat-icon,:host[horizontal=true] #down .mat-icon{transform:rotate(270Deg)}\n"], dependencies: [{ kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: i3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.1.3", ngImport: i0, type: DejaViewPortComponent, decorators: [{
            type: Component,
            args: [{ changeDetection: ChangeDetectionStrategy.OnPush, providers: [ViewPortService], selector: 'deja-viewport', template: "<div #up id=\"up\" *ngIf=\"hasButtons\"><mat-icon>keyboard_arrow_up</mat-icon></div>\n<div #wrapper id=\"viewport-wrapper\">\n\t<ng-container *ngIf=\"!isHorizontal\">\n\t\t<div *ngIf=\"beforeSize\" [style.height.px]=\"beforeSize\"></div>\n\t\t<div class=\"listitem\" *ngFor=\"let item of vpItems; let index = index\" [attr.flat]=\"vpStartIndex + index\" [style.height]=\"getCssSize(item)\" [style.margin-top.px]=\"startOffset && index === 0 ? -startOffset : null\">\n\t\t\t<ng-template [ngTemplateOutlet]=\"itemTemplate\" [ngTemplateOutletContext]=\"{ $implicit: item, index: vpStartIndex + index, size: getItemSize(item), model: item.model }\"></ng-template>\n\t\t</div>\n\t\t<div *ngIf=\"afterSize\" [style.height.px]=\"afterSize\"></div>\n\t</ng-container>\n\t<ng-container *ngIf=\"isHorizontal\">\n\t\t<div *ngIf=\"beforeSize\" [style.flex-basis.px]=\"beforeSize\"></div>\n\t\t<div class=\"listitem\" *ngFor=\"let item of vpItems; let index = index\" [attr.flat]=\"vpStartIndex + index\" [style.flex-basis]=\"getCssSize(item)\" [style.margin-left.px]=\"startOffset && index === 0 ? -startOffset : null\">\n\t\t\t<ng-template [ngTemplateOutlet]=\"itemTemplate\" [ngTemplateOutletContext]=\"{ $implicit: item, index: vpStartIndex + index, size: getItemSize(item), model: item.model }\"></ng-template>\n\t\t</div>\n\t\t<div *ngIf=\"afterSize\" [style.flex-basis.px]=\"afterSize\"></div>\n\t</ng-container>\n</div>\n<div #down id=\"down\" *ngIf=\"hasButtons\"><mat-icon>keyboard_arrow_down</mat-icon></div>", styles: [":host{position:relative;display:flex;flex-direction:row;overflow:hidden}:host[hasUpBtn=false] #up{visibility:hidden}:host[hasDownBtn=false] #down{visibility:hidden}:host[buttons=false][horizontal=false] #viewport-wrapper{width:100%;overflow-y:auto}:host[buttons=false][horizontal=true] #viewport-wrapper{height:100%;overflow-x:auto}:host[buttons=true][horizontal=false]{flex-direction:column}:host #viewport-wrapper{position:relative;flex-grow:1;overflow:hidden}:host #viewport-wrapper>div{padding:0;margin:0}:host #up,:host #down{flex:0 0 auto;display:flex;justify-content:space-around;align-items:center;font-size:120%;cursor:pointer;z-index:1}:host[horizontal=false] #viewport-wrapper>div{width:100%}:host[horizontal=false] #up,:host[horizontal=false] #down{padding:.15rem 0}:host[horizontal=true] #viewport-wrapper{display:flex;justify-content:flex-start;align-items:stretch;flex-direction:row}:host[horizontal=true] #viewport-wrapper>div{height:100%;overflow:hidden;flex-grow:0;flex-shrink:0}:host[horizontal=true] #up,:host[horizontal=true] #down{padding:0 .15rem}:host[horizontal=true] #up .mat-icon,:host[horizontal=true] #down .mat-icon{transform:rotate(270Deg)}\n"] }]
        }], ctorParameters: function () { return [{ type: i0.ChangeDetectorRef }, { type: i1.ViewPortService }]; }, propDecorators: { hasUpButton: [{
                type: HostBinding,
                args: ['attr.hasUpBtn']
            }], hasDownButton: [{
                type: HostBinding,
                args: ['attr.hasDownBtn']
            }], _isHorizontal: [{
                type: HostBinding,
                args: ['attr.horizontal']
            }], _hasButtons: [{
                type: HostBinding,
                args: ['attr.buttons']
            }], itemTemplateExternal: [{
                type: Input
            }], itemTemplateInternal: [{
                type: ContentChild,
                args: ['itemTemplate']
            }], buttonsStep: [{
                type: Input
            }], downButton: [{
                type: ViewChild,
                args: ['down']
            }], upButton: [{
                type: ViewChild,
                args: ['up']
            }], models: [{
                type: Input
            }], items: [{
                type: Input
            }], scrollingStyle: [{
                type: Input
            }], direction: [{
                type: Input
            }], itemSize: [{
                type: Input
            }], wrapperElement: [{
                type: ViewChild,
                args: ['wrapper', { static: true }]
            }], viewportMode: [{
                type: Input
            }] } });

/*
 *  @license
 *  Copyright Hôpitaux Universitaires de Genève. All Rights Reserved.
 *
 *  Use of this source code is governed by an Apache-2.0 license that can be
 *  found in the LICENSE file at https://github.com/DSI-HUG/dejajs-components/blob/master/LICENSE
 */
class DejaViewPortModule {
}
DejaViewPortModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.1.3", ngImport: i0, type: DejaViewPortModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
DejaViewPortModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "14.1.3", ngImport: i0, type: DejaViewPortModule, declarations: [DejaViewPortComponent], imports: [CommonModule,
        MatIconModule], exports: [DejaViewPortComponent] });
DejaViewPortModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "14.1.3", ngImport: i0, type: DejaViewPortModule, imports: [CommonModule,
        MatIconModule] });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.1.3", ngImport: i0, type: DejaViewPortModule, decorators: [{
            type: NgModule,
            args: [{
                    declarations: [
                        DejaViewPortComponent
                    ],
                    exports: [
                        DejaViewPortComponent
                    ],
                    imports: [
                        CommonModule,
                        MatIconModule
                    ]
                }]
        }] });

/**
 * Generated bundle index. Do not edit.
 */

export { DejaViewPortComponent, DejaViewPortModule };
//# sourceMappingURL=deja-js-component-viewport.mjs.map
