import * as i3 from '@angular/common';
import { CommonModule } from '@angular/common';
import * as i0 from '@angular/core';
import { Injectable, EventEmitter, Component, ChangeDetectionStrategy, HostBinding, Input, Output, ContentChild, ViewChild, ViewChildren, NgModule } from '@angular/core';
import * as i2 from '@angular/material/icon';
import { MatIconModule } from '@angular/material/icon';
import { coerceNumberProperty, coerceBooleanProperty } from '@angular/cdk/coercion';
import { Destroy } from '@deja-js/component/core';
import { BehaviorSubject, Subject, ReplaySubject, distinctUntilChanged, combineLatestWith, take, tap, switchMap, map, of, mergeWith, delay, withLatestFrom, startWith, shareReplay, fromEvent, debounceTime, filter, takeUntil, from, mergeMap, timer, interval } 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
 */
/** Service de gestion du viewport d'une liste.
 * Ce service permet la gestion du viewport verticalement ou horizontalement.
 */
class ViewPortService {
    constructor() {
        this.emptyViewPort = {
            beforeSize: 0,
            afterSize: 0,
            viewPortSize: 0,
            listSize: 0,
            visibleItems: new Array(),
            startIndex: 0,
            endIndex: 0,
            targetScrollPos: 0,
            items: new Array()
        };
        this.mode$ = new BehaviorSubject('fixed');
        this.items$ = new BehaviorSubject([]);
        this.maxSize$ = new BehaviorSubject(0);
        this.ensureItem$ = new Subject();
        this.scrollPosition$ = new BehaviorSubject(0);
        this.element$ = new ReplaySubject(1);
        this.itemsSize$ = new BehaviorSubject(40);
        this.direction$ = new BehaviorSubject('vertical');
        this.debug = false;
        this.refresh$ = new BehaviorSubject(null);
        this.deleteSizeCache$ = new BehaviorSubject(undefined);
        this.clear$ = new Subject();
        this.measureViewPort = {
            beforeSize: 0,
            afterSize: 200000,
            viewPortSize: 0,
            listSize: 0,
            visibleItems: new Array(),
            startIndex: 0,
            endIndex: 0,
            targetScrollPos: undefined,
            items: new Array()
        };
        const element$ = this.element$.pipe(distinctUntilChanged());
        const maxSize$ = this.maxSize$.pipe(distinctUntilChanged());
        const dir$ = this.direction$.pipe(distinctUntilChanged());
        const mode$ = this.mode$.pipe(distinctUntilChanged());
        const deleteSizeCache$ = () => this.items$.pipe(combineLatestWith(mode$), take(1), tap(([items, mode]) => {
            if ((items === null || items === void 0 ? void 0 : items.length) && mode === 'auto') {
                items.forEach(item => item.size = undefined);
            }
        }));
        const direction$ = dir$.pipe(combineLatestWith(this.deleteSizeCache$), switchMap(([direction]) => 
        // Reset items size when direction change in auto mode
        deleteSizeCache$().pipe(map(() => direction))));
        const itemsSize$ = this.itemsSize$.pipe(distinctUntilChanged());
        const innerSize = (direction) => direction === 'horizontal' ? window.innerWidth : window.innerHeight;
        const clientSize = (element, direction) => Math.ceil(direction === 'horizontal' ? element.clientWidth : element.clientHeight);
        const calcFixedSizeViewPort$ = (params, items, scrollPosition, containerSize) => {
            var _a;
            this.log('calcFixedSizeViewPort');
            const maxCount = Math.ceil(+containerSize / params.itemsSize) + 1;
            const startRow = Math.floor(scrollPosition / params.itemsSize);
            const rowsCount = Math.min(items.length - startRow, maxCount);
            let startIndex;
            let endIndex;
            let newScrollPos;
            if (!params.ensureParams || params.ensureParams.index === undefined || !params.ensureParams.atEnd) {
                if (rowsCount < 0) {
                    endIndex = items.length - 1;
                    startIndex = endIndex + 1 - Math.min(items.length, maxCount);
                }
                else if (((_a = params.ensureParams) === null || _a === void 0 ? void 0 : _a.index) !== undefined) {
                    startIndex = params.ensureParams.index;
                    endIndex = startIndex + rowsCount - 1;
                    newScrollPos = startIndex * params.itemsSize;
                }
                else {
                    startIndex = startRow;
                    endIndex = startIndex + rowsCount - 1;
                }
            }
            else {
                // Ensure visible from the end
                startIndex = Math.max(0, params.ensureParams.index + 1 - Math.min(items.length, maxCount));
                endIndex = Math.max(params.ensureParams.index, rowsCount - 1);
                newScrollPos = (endIndex + 1) * params.itemsSize - +containerSize;
            }
            const visibleItems = items.slice(startIndex, endIndex + 1);
            return of({
                beforeSize: startIndex * params.itemsSize,
                afterSize: (items.length - endIndex - 1) * params.itemsSize,
                listSize: containerSize,
                viewPortSize: visibleItems.length * params.itemsSize,
                visibleItems: visibleItems,
                startIndex: startIndex,
                endIndex: endIndex,
                targetScrollPos: newScrollPos,
                items
            });
        };
        const calcVariableSizeViewPort$ = (params, items, scrollPosition, containerSize) => {
            var _a;
            this.log('calcVariableSizeViewPort');
            const visibleItems = new Array();
            let startIndex;
            let endIndex;
            let beforeSize = 0;
            let viewPortSize = 0;
            let afterSize = 0;
            let newScrollPos;
            if (!params.ensureParams || params.ensureParams.index === undefined || !params.ensureParams.atEnd) {
                items.forEach((item, index) => {
                    const itemSize = item.size || params.itemsSize;
                    if (params.ensureParams && params.ensureParams.index === index) {
                        startIndex = index;
                        newScrollPos = beforeSize;
                    }
                    if (startIndex === undefined) {
                        if (beforeSize + itemSize >= scrollPosition) {
                            startIndex = index;
                        }
                        else {
                            beforeSize += itemSize;
                        }
                    }
                    if (startIndex !== undefined && endIndex === undefined) {
                        viewPortSize += itemSize;
                        visibleItems.push(item);
                    }
                    if (endIndex === undefined) {
                        if ((beforeSize + viewPortSize > (newScrollPos || scrollPosition) + +containerSize) || index === items.length - 1) {
                            endIndex = index;
                        }
                    }
                    else {
                        afterSize += itemSize;
                    }
                });
            }
            else {
                // Ensure visible from the end
                let index = items.length;
                // eslint-disable-next-line no-loops/no-loops
                while (--index >= 0) {
                    const item = items[index];
                    const itemSize = item.size || params.itemsSize;
                    if (params.ensureParams.index === index) {
                        endIndex = index;
                    }
                    if (endIndex !== undefined) {
                        if (startIndex === undefined) {
                            viewPortSize += itemSize;
                            visibleItems.unshift(item);
                            if (viewPortSize > +containerSize || index === 0) {
                                startIndex = index;
                                newScrollPos = viewPortSize - +containerSize;
                            }
                        }
                        else {
                            newScrollPos += itemSize;
                            beforeSize += itemSize;
                        }
                    }
                    else {
                        afterSize += itemSize;
                    }
                }
            }
            if (((_a = params.ensureParams) === null || _a === void 0 ? void 0 : _a.index) !== undefined && viewPortSize < containerSize && visibleItems.length < items.length) {
                params.ensureParams = {
                    index: params.ensureParams.atEnd ? 0 : items.length - 1,
                    atEnd: !params.ensureParams.atEnd
                };
                return calcVariableSizeViewPort$(params, items, scrollPosition, containerSize);
            }
            return of({
                beforeSize: beforeSize,
                afterSize: afterSize,
                listSize: containerSize,
                viewPortSize: viewPortSize,
                visibleItems: visibleItems,
                startIndex: startIndex || 0,
                endIndex: endIndex || items.length,
                targetScrollPos: newScrollPos,
                items
            });
        };
        const calcAutoSizeViewPort$ = (params, items, scrollPosition, containerSize, isCalculation) => {
            this.log('calcAutoSizeViewPort');
            return calcVariableSizeViewPort$(params, items, scrollPosition, containerSize).pipe(switchMap(viewPort => {
                const calculationRequired = !isCalculation && viewPort.visibleItems.find(item => !item.size);
                const measure$ = of(viewPort);
                if (!calculationRequired) {
                    this.log('calcAutoSizeViewPort: no calculationRequired', viewPort);
                    return measure$;
                }
                else {
                    // Measure items size
                    this.log('calcAutoSizeViewPort: measure');
                    return measure$.pipe(mergeWith(measure$.pipe(delay(1), tap(() => {
                        const elements = Array.from(params.element.getElementsByClassName('listitem'));
                        elements.forEach(itemElement => {
                            const index = +itemElement.getAttribute('flat');
                            const item = viewPort.visibleItems[index - viewPort.startIndex];
                            if (item) {
                                item.size = clientSize(itemElement, params.direction);
                                this.log('calcAutoSizeViewPort: item found', item, 'measured size', item.size, 'HTML Element', itemElement);
                            }
                            else {
                                this.log('calcAutoSizeViewPort: item not found', item);
                            }
                        });
                        // Recalc Viewport size
                        viewPort.viewPortSize = viewPort.visibleItems.reduce((size, item) => size += item.size || params.itemsSize, 0);
                    }), switchMap(() => calcVariableSizeViewPort$(params, items, viewPort.targetScrollPos || scrollPosition, containerSize)))));
                }
            }));
        };
        const calcDisabledViewPort$ = (params, items, scrollPosition, containerSize, bindIfAny) => {
            let viewPortSize = 0;
            let startIndex;
            let endIndex;
            let newScrollPos;
            const elements = params.element.getElementsByClassName('listitem');
            const viewPort = {
                beforeSize: 0,
                afterSize: 0,
                listSize: containerSize,
                viewPortSize: 0,
                visibleItems: new Array(),
                startIndex: 0,
                endIndex: 0,
                targetScrollPos: 0,
                items
            };
            if (elements.length !== items.length && (bindIfAny !== null && bindIfAny !== void 0 ? bindIfAny : true)) {
                const measure$ = of(viewPort);
                return measure$.pipe(mergeWith(measure$.pipe(delay(1), switchMap(() => calcDisabledViewPort$(params, items, scrollPosition, containerSize, false)))));
            }
            if (!params.ensureParams || params.ensureParams.index === undefined || !params.ensureParams.atEnd) {
                // eslint-disable-next-line no-loops/no-loops
                const itemElements = Array.from(elements);
                itemElements.forEach((itemElement, i) => {
                    const itemSize = items[i].size = clientSize(itemElement, params.direction);
                    if (params.ensureParams && params.ensureParams.index === i) {
                        startIndex = i;
                        newScrollPos = viewPortSize;
                    }
                    viewPortSize += itemSize;
                    if (startIndex === undefined && viewPortSize > scrollPosition) {
                        startIndex = i;
                    }
                    if (endIndex === undefined && viewPortSize > (newScrollPos || scrollPosition) + +containerSize) {
                        endIndex = i;
                    }
                });
            }
            else {
                // Ensure visible from the end
                newScrollPos = 0;
                let listSize = 0;
                let i = elements.length;
                // eslint-disable-next-line no-loops/no-loops
                while (--i >= 0) {
                    const itemElement = elements[i];
                    const itemSize = items[i].size = clientSize(itemElement, params.direction);
                    if (params.ensureParams.index === i) {
                        endIndex = i;
                    }
                    if (endIndex !== undefined) {
                        if (startIndex === undefined) {
                            listSize += itemSize;
                            if (listSize > +containerSize) {
                                startIndex = i;
                                newScrollPos = listSize - +containerSize;
                            }
                        }
                        else {
                            newScrollPos += itemSize;
                        }
                    }
                    viewPortSize += itemSize;
                }
            }
            startIndex = startIndex || 0;
            endIndex = endIndex || items.length - 1;
            this.log(`viewPortSize ${viewPortSize}`);
            return of({
                beforeSize: 0,
                afterSize: 0,
                listSize: containerSize,
                viewPortSize: viewPortSize,
                visibleItems: items.slice(startIndex, 1 + endIndex),
                startIndex: startIndex,
                endIndex: endIndex,
                targetScrollPos: newScrollPos,
                items
            });
        };
        const calcViewPort$ = (params, items, scrollPosition, fromMeasure) => {
            this.log('calcViewPort');
            if (!(items === null || items === void 0 ? void 0 : items.length)) {
                return of(this.emptyViewPort);
            }
            let listSize = params.maxSize || clientSize(params.element, params.direction);
            if (listSize <= 40) {
                listSize = innerSize(params.direction);
            }
            let viewPort$;
            switch (params.mode) {
                case 'disabled':
                    viewPort$ = calcDisabledViewPort$(params, items, scrollPosition, listSize, true);
                    break;
                case 'variable':
                    viewPort$ = calcVariableSizeViewPort$(params, items, scrollPosition, listSize);
                    break;
                case 'auto':
                    viewPort$ = calcAutoSizeViewPort$(params, items, scrollPosition, listSize);
                    break;
                default: // 'fixed'
                    viewPort$ = calcFixedSizeViewPort$(params, items, scrollPosition, listSize);
            }
            return viewPort$.pipe(switchMap(viewPort => {
                if (!fromMeasure && viewPort.viewPortSize > 0) {
                    const endScrollPos = viewPort.beforeSize + viewPort.viewPortSize + viewPort.afterSize - viewPort.listSize;
                    if (params.mode !== 'disabled' && listSize < 80) {
                        // Measure again container and recalc viewport
                        const measure$ = of(this.measureViewPort);
                        return measure$.pipe(mergeWith(measure$.pipe(delay(1), switchMap(() => calcViewPort$(params, items, scrollPosition, true)))));
                    }
                    else if (endScrollPos < 0 || (items.length && endScrollPos > 0 && (viewPort.targetScrollPos || scrollPosition) > endScrollPos)) {
                        // Scroll position is over the last item
                        // Ensure last item visible and recalc viewport
                        params.ensureParams = {
                            index: items.length - 1,
                            atEnd: true
                        };
                        return calcViewPort$(params, items, 0, true);
                    }
                    else if (params.mode === 'auto' && viewPort.viewPortSize < listSize) {
                        // Rendered viewport is to small, render again to complete
                        params.ensureParams = {
                            index: items.length - 1,
                            atEnd: true
                        };
                        return calcViewPort$(params, items, 0, true);
                    }
                }
                // Return calculated viewport
                return of(viewPort);
            }), tap(() => {
                if (params.ensureParams) {
                    params.ensureParams.index = undefined;
                }
            }));
        };
        // Ensure item visible by index or instance
        const ensureParams$ = this.ensureItem$.pipe(withLatestFrom(this.items$), switchMap(([ensureItem, items]) => this.viewPort$.pipe(take(1), map(viewPort => {
            const ensureParams = {};
            if (ensureItem !== undefined && ensureItem !== null && (items === null || items === void 0 ? void 0 : items.length)) {
                let ensureIndex = ensureItem;
                if (isNaN(ensureIndex)) {
                    ensureIndex = items.findIndex(itm => ensureItem === itm);
                }
                if (ensureIndex >= 0) {
                    if (viewPort && ensureIndex <= viewPort.startIndex) {
                        ensureParams.index = ensureIndex;
                        ensureParams.atEnd = false;
                    }
                    else if (!viewPort || ensureIndex >= viewPort.endIndex) {
                        ensureParams.index = ensureIndex;
                        ensureParams.atEnd = true;
                    }
                }
            }
            return ensureParams;
        }))), startWith(null), shareReplay({ bufferSize: 1, refCount: false }));
        const resize$ = fromEvent(window, 'resize').pipe(debounceTime(5), switchMap(() => deleteSizeCache$().pipe(map(() => false))));
        const refresh$ = this.refresh$.pipe(mergeWith(resize$), withLatestFrom(this.items$), tap(([clearMeasuredSize, items]) => {
            if (clearMeasuredSize && (items === null || items === void 0 ? void 0 : items.length)) {
                this.lastCalculatedSize = undefined;
                items.forEach(item => item.size = undefined);
            }
        }), debounceTime(100));
        const scrollPosition$ = this.scrollPosition$.pipe(distinctUntilChanged());
        // Calc view port observable
        const viewPort$ = element$.pipe(combineLatestWith(maxSize$, mode$, direction$, itemsSize$, ensureParams$), map(([element, maxSize, mode, direction, itemsSize, ensureParams]) => ({ element, maxSize, mode, direction, itemsSize, ensureParams })), debounceTime(1), combineLatestWith(this.items$, scrollPosition$, refresh$), filter(([params]) => !!params.element), switchMap(([params, items, scrollPosition]) => {
            this.log('scrollPosition', scrollPosition);
            const listSize = this.lastCalculatedSize || params.maxSize || clientSize(params.element, params.direction);
            let maxSizeValue = params.maxSize === 'auto' ? 0 : +params.maxSize;
            let vp$;
            if ((items === null || items === void 0 ? void 0 : items.length) && (listSize === 'auto' || listSize < 80)) {
                // Set the viewlist to the maximum height to measure the real max-height defined in the css
                // Use a blank div to do that
                const measure$ = of(this.measureViewPort);
                vp$ = measure$.pipe(mergeWith(measure$.pipe(delay(1), // Wait next life cycle for the result
                switchMap(() => {
                    // Get max size from container
                    maxSizeValue = this.lastCalculatedSize = clientSize(params.element, params.direction);
                    // Ensure that max size is not more than the items size
                    if (params.mode === 'fixed') {
                        if (items.length * params.itemsSize < maxSizeValue) {
                            maxSizeValue = items.length * params.itemsSize;
                        }
                    }
                    else if (params.mode === 'variable') {
                        let maxItemsSize = 0;
                        items.find(item => {
                            maxItemsSize += item.size || params.itemsSize;
                            return maxItemsSize > maxSizeValue;
                        });
                        if (maxItemsSize < maxSizeValue) {
                            maxSizeValue = maxItemsSize;
                        }
                    }
                    return calcViewPort$(params, items, scrollPosition);
                }))));
            }
            else {
                vp$ = calcViewPort$(params, items, scrollPosition);
            }
            return vp$.pipe(map(vp => (Object.assign(Object.assign(Object.assign({}, vp), params), { scrollPosition }))));
        }));
        this.viewPort$ = viewPort$.pipe(mergeWith(this.clear$), shareReplay({ bufferSize: 1, refCount: true }));
    }
    deleteSizeCache() {
        this.deleteSizeCache$.next();
    }
    clear() {
        this.clear$.next(this.emptyViewPort);
    }
    refresh(clearMeasuredSize) {
        this.refresh$.next(clearMeasuredSize);
    }
    log(message, ...optionalParams) {
        if (this.debug) {
            console.log(message, optionalParams.length ? optionalParams : undefined);
        }
    }
}
ViewPortService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.1", ngImport: i0, type: ViewPortService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
ViewPortService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.3.1", ngImport: i0, type: ViewPortService });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.1", ngImport: i0, type: ViewPortService, decorators: [{
            type: Injectable
        }], ctorParameters: function () { return []; } });

/*
 *  @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 ViewPortItemClassEvent {
}
class ViewPortComponent extends Destroy {
    constructor(viewPortService) {
        super();
        this.viewPortService = viewPortService;
        this.hasButtons = null;
        this.isHorizontal = null;
        this.itemClass = new EventEmitter();
        this.hasUpButton = null;
        this.hasDownButton = null;
        this.buttons$ = new Subject();
        this.viewPortElementSize$ = new Subject();
        this.reloadViewPort$ = new BehaviorSubject(null);
        this.viewPort$ = viewPortService.viewPort$.pipe(combineLatestWith(this.reloadViewPort$), map(([viewPort]) => (Object.assign({}, viewPort))));
        viewPortService.element$.pipe(distinctUntilChanged(), switchMap(element => fromEvent(element, 'scroll').pipe(distinctUntilChanged(), withLatestFrom(viewPortService.direction$), map(([_, direction]) => Math.round(direction === 'horizontal' ? element.scrollLeft : element.scrollTop)))), takeUntil(this.destroyed$)).subscribe(scrollPosition => {
            this.viewPortService.scrollPosition$.next(scrollPosition);
        });
        viewPortService.element$.pipe(distinctUntilChanged(), switchMap(element => fromEvent(element, 'mousewheel').pipe(withLatestFrom(viewPortService.direction$), filter(([_, direction]) => direction === 'horizontal' || this.hasButtons), tap(([event, direction]) => {
            event.stopPropagation();
            event.preventDefault();
            if (direction === 'horizontal') {
                element.scroll({ left: element.scrollLeft + event.deltaY, top: 0 });
            }
            else {
                element.scroll({ left: 0, top: element.scrollTop + event.deltaY });
            }
        }))), takeUntil(this.destroyed$)).subscribe();
        viewPortService.viewPort$.pipe(takeUntil(this.destroyed$)).subscribe(viewPort => {
            var _a, _b;
            this.hasUpButton = this.hasButtons && viewPort.mode === 'disabled' ? viewPort.scrollPosition > 0 : viewPort.beforeSize > 0;
            this.hasDownButton = this.hasButtons && viewPort.mode === 'disabled' ? viewPort.scrollPosition < viewPort.viewPortSize - viewPort.listSize : viewPort.afterSize > 0;
            if (viewPort === null || viewPort === void 0 ? void 0 : viewPort.element) {
                if (viewPort.direction === 'horizontal') {
                    viewPort.element.scrollLeft = (_a = viewPort.targetScrollPos) !== null && _a !== void 0 ? _a : viewPort.scrollPosition;
                }
                else {
                    viewPort.element.scrollTop = (_b = viewPort.targetScrollPos) !== null && _b !== void 0 ? _b : viewPort.scrollPosition;
                }
                this.viewPortElementSize$.next({ size: viewPort.direction === 'horizontal' ? viewPort.element.clientWidth : viewPort.element.clientHeight });
            }
        });
        this.viewPortElementSize$.pipe(debounceTime(1), distinctUntilChanged(), withLatestFrom(viewPortService.direction$, viewPortService.element$), takeUntil(this.destroyed$)).subscribe(([viewPortElementSize, direction, element]) => {
            const newElementSize = direction === 'horizontal' ? element.clientWidth : element.clientHeight;
            if (viewPortElementSize.size > 0 && newElementSize > viewPortElementSize.size) {
                console.log('ViewPort element size to small, refresh view port');
                this.viewPortService.refresh();
            }
            viewPortElementSize.size = newElementSize;
        });
        const buttons$ = this.buttons$.pipe(debounceTime(100), map(buttons => buttons.map(button => button.nativeElement)), distinctUntilChanged((b1, b2) => (b1 === null || b1 === void 0 ? void 0 : b1.length) === (b2 === null || b2 === void 0 ? void 0 : b2.length)));
        const destroyButtons$ = buttons$.pipe(filter(buttons => buttons.length === 0));
        buttons$.pipe(filter(buttons => buttons.length === 2), withLatestFrom(viewPortService.element$), switchMap(([buttons, viewPortElement]) => {
            const clientSize = this.isHorizontal ? viewPortElement.clientWidth : viewPortElement.clientHeight;
            const scroll = (event, sign) => {
                const delta = sign * (event.ctrlKey ? clientSize : +this.buttonsStep * 2);
                if (this.isHorizontal) {
                    viewPortElement.scroll({ left: viewPortElement.scrollLeft + delta, top: 0 });
                }
                else {
                    viewPortElement.scroll({ left: 0, top: viewPortElement.scrollTop + delta });
                }
            };
            return from(buttons).pipe(mergeMap(button => {
                const sign = button.id === 'down' ? 1 : -1;
                const autoScroll$ = (event) => {
                    const mouseup$ = fromEvent(buttons[0], 'mouseup').pipe(mergeWith(fromEvent(buttons[0], 'mouseleave'), fromEvent(buttons[1], 'mouseup'), fromEvent(buttons[1], 'mouseleave')));
                    return timer(750).pipe(mergeMap(() => interval(50)), tap(() => scroll(event, sign)), takeUntil(mouseup$));
                };
                return fromEvent(button, 'mousedown').pipe(switchMap(event => {
                    scroll(event, sign);
                    return autoScroll$(event);
                }), takeUntil(destroyButtons$));
            }));
        }), takeUntil(this.destroyed$)).subscribe();
    }
    set buttonsStep(value) {
        this._buttonsStep = coerceNumberProperty(value);
    }
    get buttonsStep() {
        return this._buttonsStep || 20;
    }
    /** 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.viewPortService.items$.next(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';
        this.viewPortService.refresh();
    }
    /** Set the direction of the items rendering
     * vertical: The item are displayed vertically
     * horizontal: The item are displayed horizontally
     */
    set direction(value) {
        this.isHorizontal = value === 'horizontal';
        this.viewPortService.direction$.next(value);
    }
    /** Set the item size in fixed mode or the default item size before rendering in auto mode */
    set itemSize(value) {
        if (value) {
            const itemSize = coerceNumberProperty(value);
            this.viewPortService.itemsSize$.next(itemSize);
        }
    }
    set wrapperElement(element) {
        this.viewPortService.element$.next(element.nativeElement);
    }
    set buttons(buttons) {
        this.buttons$.next(buttons);
    }
    get viewPortItemTemplate() {
        return this.viewPortItemTemplateExternal || this.viewPortItemTemplateInternal;
    }
    set scrollPos(value) {
        const scrollPosition = Math.max(coerceNumberProperty(value), 0);
        this.viewPortService.scrollPosition$.next(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.viewPortService.mode$.next(mode);
    }
    set debugMode(value) {
        this.viewPortService.debug = coerceBooleanProperty(value);
    }
    /** Recalcule le viewport. */
    refreshViewPort(clearMeasuredSize) {
        this.viewPortService.refresh(clearMeasuredSize);
    }
    /** Rebind le viewport */
    reloadViewPort() {
        this.reloadViewPort$.next();
    }
    /** Efface le viewport */
    clearViewPort() {
        this.viewPortService.clear();
    }
    ensureVisible(item) {
        this.viewPortService.ensureItem$.next(item);
    }
    getCssSize(item, defaultItemSize, mode) {
        const itemSize = this.getItemSize(item, defaultItemSize, mode);
        return itemSize ? `${itemSize}px` : 'auto';
    }
    getItemSize(item, defaultItemSize, mode) {
        if (mode === 'disabled') {
            return null;
        }
        else if (mode === 'fixed') {
            return defaultItemSize;
        }
        else if (mode === 'auto') {
            return item.size || null;
        }
        else {
            return (item.size && item.size > 40) ? item.size : defaultItemSize;
        }
    }
    getItemClassName(item) {
        const classes = ['listitem'];
        if (this.itemClass.observed) {
            this.itemClass.next({
                item,
                classes
            });
        }
        return classes.join(' ');
    }
}
ViewPortComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.1", ngImport: i0, type: ViewPortComponent, deps: [{ token: ViewPortService }], target: i0.ɵɵFactoryTarget.Component });
ViewPortComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.3.1", type: ViewPortComponent, selector: "viewport", inputs: { viewPortItemTemplateExternal: "viewPortItemTemplateExternal", buttonsStep: "buttonsStep", models: "models", items: "items", scrollingStyle: "scrollingStyle", direction: "direction", itemSize: "itemSize", viewPortMode: "viewPortMode", debugMode: "debugMode" }, outputs: { itemClass: "itemClass" }, host: { properties: { "attr.buttons": "this.hasButtons", "attr.horizontal": "this.isHorizontal" } }, providers: [ViewPortService], queries: [{ propertyName: "viewPortItemTemplateInternal", first: true, predicate: ["viewPortItemTemplate"], descendants: true }], viewQueries: [{ propertyName: "wrapperElement", first: true, predicate: ["wrapper"], descendants: true }, { propertyName: "buttons", predicate: ["button"], descendants: true }], usesInheritance: true, ngImport: i0, template: "<div #button id=\"up\" *ngIf=\"hasButtons\" [attr.hidden]=\"!hasUpButton || null\">\n    <mat-icon>keyboard_arrow_up</mat-icon>\n</div>\n<div #wrapper class=\"viewport-wrapper\">\n    <ng-container *ngIf=\"viewPort$ | async as viewPort\">\n        <ng-container *ngIf=\"viewPort.direction === 'vertical';else horizontal\">\n            <div class=\"fake\" *ngIf=\"viewPort.beforeSize > 0\" [style.height.px]=\"viewPort.beforeSize\">&nbsp;</div>\n            <div *ngFor=\"let item of (viewPort.mode === 'disabled' ? viewPort.items : viewPort.visibleItems); let index = index\" [attr.class]=\"getItemClassName(item)\" [attr.flat]=\"viewPort.startIndex + index\" [attr.mode]=\"viewPort.mode\" [style.width]=\"'100%'\" [style.height]=\"getCssSize(item, viewPort.itemsSize, viewPort.mode)\">\n                <ng-template [ngTemplateOutlet]=\"viewPortItemTemplate\" [ngTemplateOutletContext]=\"{ $implicit: item, index: viewPort.startIndex + index, size: getItemSize(item, viewPort.itemsSize, viewPort.mode), model: item.model }\"></ng-template>\n            </div>\n            <div class=\"fake\" *ngIf=\"viewPort.afterSize > 0\" [style.height.px]=\"viewPort.afterSize\">&nbsp;</div>\n        </ng-container>\n        <ng-template #horizontal>\n            <div class=\"fake\" *ngIf=\"viewPort.beforeSize > 0\" [style.flex-basis.px]=\"viewPort.beforeSize\">&nbsp;</div>\n            <div *ngFor=\"let item of (viewPort.mode === 'disabled' ? viewPort.items : viewPort.visibleItems); let index = index\" [attr.class]=\"getItemClassName(item)\" [attr.flat]=\"viewPort.startIndex + index\" [attr.mode]=\"viewPort.mode\" [style.height]=\"'100%'\" [style.flex-basis]=\"getCssSize(item, viewPort.itemsSize, viewPort.mode)\">\n                <ng-template [ngTemplateOutlet]=\"viewPortItemTemplate\" [ngTemplateOutletContext]=\"{ $implicit: item, index: viewPort.startIndex + index, size: getItemSize(item, viewPort.itemsSize, viewPort.mode), model: item.model }\"></ng-template>\n            </div>\n            <div class=\"fake\" *ngIf=\"viewPort.afterSize > 0\" [style.flex-basis.px]=\"viewPort.afterSize\">&nbsp;</div>\n        </ng-template>\n    </ng-container>\n</div>\n<div #button id=\"down\" *ngIf=\"hasButtons\" [attr.hidden]=\"!hasDownButton || null\">\n    <mat-icon>keyboard_arrow_down</mat-icon>\n</div>\n", styles: [":host{display:flex;flex-direction:column;overflow:hidden;position:relative}: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;padding:.15rem 0}:host #up[hidden=true],:host #down[hidden=true]{visibility:hidden}:host .viewport-wrapper{flex:1 1 auto;width:100%;overflow:visible;overflow-y:auto;position:relative}:host .viewport-wrapper>div{padding:0;margin:0;position:relative;box-sizing:border-box}:host .viewport-wrapper>div[mode=fixed],:host .viewport-wrapper>div[mode=variable]{overflow:hidden}:host .viewport-wrapper>div.fake{opacity:0}:host[horizontal=true] .viewport-wrapper{height:100%;overflow:visible;overflow-x:auto;display:flex;flex-direction:row;align-items:stretch;justify-content:flex-start}:host[horizontal=true] .viewport-wrapper>div{flex-grow:0;flex-shrink:0}:host[horizontal=true][buttons=true]{flex-direction:row}:host[horizontal=true][buttons=true] #up,:host[horizontal=true][buttons=true] #down{padding:0 .15rem}:host[horizontal=true][buttons=true] #up .mat-icon,:host[horizontal=true][buttons=true] #down .mat-icon{transform:rotate(270Deg)}:host[buttons=true] .viewport-wrapper{overflow:hidden}\n"], components: [{ type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }], directives: [{ type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i3.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { type: i3.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet"] }], pipes: { "async": i3.AsyncPipe }, changeDetection: i0.ChangeDetectionStrategy.OnPush });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.1", ngImport: i0, type: ViewPortComponent, decorators: [{
            type: Component,
            args: [{ changeDetection: ChangeDetectionStrategy.OnPush, providers: [ViewPortService], selector: 'viewport', template: "<div #button id=\"up\" *ngIf=\"hasButtons\" [attr.hidden]=\"!hasUpButton || null\">\n    <mat-icon>keyboard_arrow_up</mat-icon>\n</div>\n<div #wrapper class=\"viewport-wrapper\">\n    <ng-container *ngIf=\"viewPort$ | async as viewPort\">\n        <ng-container *ngIf=\"viewPort.direction === 'vertical';else horizontal\">\n            <div class=\"fake\" *ngIf=\"viewPort.beforeSize > 0\" [style.height.px]=\"viewPort.beforeSize\">&nbsp;</div>\n            <div *ngFor=\"let item of (viewPort.mode === 'disabled' ? viewPort.items : viewPort.visibleItems); let index = index\" [attr.class]=\"getItemClassName(item)\" [attr.flat]=\"viewPort.startIndex + index\" [attr.mode]=\"viewPort.mode\" [style.width]=\"'100%'\" [style.height]=\"getCssSize(item, viewPort.itemsSize, viewPort.mode)\">\n                <ng-template [ngTemplateOutlet]=\"viewPortItemTemplate\" [ngTemplateOutletContext]=\"{ $implicit: item, index: viewPort.startIndex + index, size: getItemSize(item, viewPort.itemsSize, viewPort.mode), model: item.model }\"></ng-template>\n            </div>\n            <div class=\"fake\" *ngIf=\"viewPort.afterSize > 0\" [style.height.px]=\"viewPort.afterSize\">&nbsp;</div>\n        </ng-container>\n        <ng-template #horizontal>\n            <div class=\"fake\" *ngIf=\"viewPort.beforeSize > 0\" [style.flex-basis.px]=\"viewPort.beforeSize\">&nbsp;</div>\n            <div *ngFor=\"let item of (viewPort.mode === 'disabled' ? viewPort.items : viewPort.visibleItems); let index = index\" [attr.class]=\"getItemClassName(item)\" [attr.flat]=\"viewPort.startIndex + index\" [attr.mode]=\"viewPort.mode\" [style.height]=\"'100%'\" [style.flex-basis]=\"getCssSize(item, viewPort.itemsSize, viewPort.mode)\">\n                <ng-template [ngTemplateOutlet]=\"viewPortItemTemplate\" [ngTemplateOutletContext]=\"{ $implicit: item, index: viewPort.startIndex + index, size: getItemSize(item, viewPort.itemsSize, viewPort.mode), model: item.model }\"></ng-template>\n            </div>\n            <div class=\"fake\" *ngIf=\"viewPort.afterSize > 0\" [style.flex-basis.px]=\"viewPort.afterSize\">&nbsp;</div>\n        </ng-template>\n    </ng-container>\n</div>\n<div #button id=\"down\" *ngIf=\"hasButtons\" [attr.hidden]=\"!hasDownButton || null\">\n    <mat-icon>keyboard_arrow_down</mat-icon>\n</div>\n", styles: [":host{display:flex;flex-direction:column;overflow:hidden;position:relative}: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;padding:.15rem 0}:host #up[hidden=true],:host #down[hidden=true]{visibility:hidden}:host .viewport-wrapper{flex:1 1 auto;width:100%;overflow:visible;overflow-y:auto;position:relative}:host .viewport-wrapper>div{padding:0;margin:0;position:relative;box-sizing:border-box}:host .viewport-wrapper>div[mode=fixed],:host .viewport-wrapper>div[mode=variable]{overflow:hidden}:host .viewport-wrapper>div.fake{opacity:0}:host[horizontal=true] .viewport-wrapper{height:100%;overflow:visible;overflow-x:auto;display:flex;flex-direction:row;align-items:stretch;justify-content:flex-start}:host[horizontal=true] .viewport-wrapper>div{flex-grow:0;flex-shrink:0}:host[horizontal=true][buttons=true]{flex-direction:row}:host[horizontal=true][buttons=true] #up,:host[horizontal=true][buttons=true] #down{padding:0 .15rem}:host[horizontal=true][buttons=true] #up .mat-icon,:host[horizontal=true][buttons=true] #down .mat-icon{transform:rotate(270Deg)}:host[buttons=true] .viewport-wrapper{overflow:hidden}\n"] }]
        }], ctorParameters: function () { return [{ type: ViewPortService }]; }, propDecorators: { hasButtons: [{
                type: HostBinding,
                args: ['attr.buttons']
            }], isHorizontal: [{
                type: HostBinding,
                args: ['attr.horizontal']
            }], viewPortItemTemplateExternal: [{
                type: Input
            }], itemClass: [{
                type: Output
            }], viewPortItemTemplateInternal: [{
                type: ContentChild,
                args: ['viewPortItemTemplate']
            }], buttonsStep: [{
                type: Input
            }], models: [{
                type: Input
            }], items: [{
                type: Input
            }], scrollingStyle: [{
                type: Input
            }], direction: [{
                type: Input
            }], itemSize: [{
                type: Input
            }], wrapperElement: [{
                type: ViewChild,
                args: ['wrapper']
            }], buttons: [{
                type: ViewChildren,
                args: ['button']
            }], viewPortMode: [{
                type: Input
            }], debugMode: [{
                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 ViewPortModule {
}
ViewPortModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.1", ngImport: i0, type: ViewPortModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
ViewPortModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "13.3.1", ngImport: i0, type: ViewPortModule, declarations: [ViewPortComponent], imports: [CommonModule,
        MatIconModule], exports: [ViewPortComponent] });
ViewPortModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "13.3.1", ngImport: i0, type: ViewPortModule, imports: [[
            CommonModule,
            MatIconModule
        ]] });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.1", ngImport: i0, type: ViewPortModule, decorators: [{
            type: NgModule,
            args: [{
                    declarations: [
                        ViewPortComponent
                    ],
                    exports: [
                        ViewPortComponent
                    ],
                    imports: [
                        CommonModule,
                        MatIconModule
                    ]
                }]
        }] });

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

export { ViewPortComponent, ViewPortItemClassEvent, ViewPortModule, ViewPortService };
//# sourceMappingURL=deja-js-component-v2-viewport.mjs.map
