UNPKG

@obliczeniowo/elementary

Version:
434 lines (425 loc) 24.8 kB
import { NG_VALUE_ACCESSOR, FormsModule, ReactiveFormsModule } from '@angular/forms'; import * as i0 from '@angular/core'; import { EventEmitter, Output, Input, Component, ContentChild, NgModule } from '@angular/core'; import * as i1 from '@angular/common'; import { CommonModule } from '@angular/common'; import * as i3$1 from '@angular/cdk/overlay'; import { OverlayModule } from '@angular/cdk/overlay'; import * as i4 from '@obliczeniowo/elementary/loading'; import { LoadingModule } from '@obliczeniowo/elementary/loading'; import * as i3 from '@obliczeniowo/elementary/buttons'; import { ButtonsModule } from '@obliczeniowo/elementary/buttons'; import * as i2 from '@obliczeniowo/elementary/icons'; import { IconsModule } from '@obliczeniowo/elementary/icons'; import { of, BehaviorSubject, finalize, map } from 'rxjs'; class AbstractValueSetModel { set = []; max = 12; columns = 3; loading = false; nextDisabled() { return of(undefined); } previousDisabled() { return of(undefined); } decreaseDisabled(current) { return current === this.set[0]; } increaseDisabled(current) { return current === (this.set && this.set[this.set.length - 1]); } selected(item, current) { return current === item; } } class ValueSetStaticModel extends AbstractValueSetModel { offset = 0; init(current) { this.offset = current && Math.floor((this.set.indexOf(current) || 0) / this.max) || 0; return of(); } displayNextPrevious() { return of(this.set.length > this.max); } getNextSet() { const offset = this.offset + 1; if (offset * this.max < this.set.length - 1) { this.offset = offset; return of(true); } return of(false); } getPreviousSet() { if (this.offset) { this.offset = this.offset - 1; return of(true); } return of(false); } getCurrentSet() { return of(this.set.slice(this.offset * this.max, (this.offset + 1) * this.max)); } next(current) { if (!this.set) { return of(); } if (current) { const index = this.set.findIndex(item => item === current); return of(this.set[Math.min(index + 1, this.set.length - 1)]); } else if (!current) { return of(this.set && this.set[this.set.length - 1]); } return of(); } previous(current) { if (!this.set) { return of(); } if (current) { const index = this.set.findIndex(item => item === current); return of(this.set[Math.max(index - 1, 0)]); } else if (!current) { return of(this.set?.[0]); } return of(); } nextDisabled() { return of(this.max * this.offset + this.max >= (this.set.length || 0)); } previousDisabled() { return of(!this.offset || undefined); } } class ValuesSetComponent { /** * Input item that can have external optional data, must be the same reference object as one exist on items table */ current; model = new ValueSetStaticModel(); itemRef; selected = new EventEmitter(); next = new EventEmitter(); previous = new EventEmitter(); ngOnInit() { this.model.init(this.current); } setOffset(type) { (type === 'next' && this.model.getNextSet() || this.model.getPreviousSet()).subscribe(resp => { if (resp) { this[type].emit(); } }); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: ValuesSetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.4", type: ValuesSetComponent, isStandalone: false, selector: "obl-values-set", inputs: { current: "current", model: "model", itemRef: "itemRef" }, outputs: { selected: "selected", next: "next", previous: "previous" }, ngImport: i0, template: "@if (!model.loading) {\n @if (model.displayNextPrevious() | async) {\n <div class=\"navigation\">\n <button\n oblButton\n (click)=\"setOffset('previous')\"\n [disabled]=\"model.previousDisabled() | async\"\n >\n <obl-icon [name]=\"'left'\"></obl-icon>\n </button>\n <button\n oblButton\n (click)=\"setOffset('next')\"\n [disabled]=\"model.nextDisabled() | async\"\n >\n <obl-icon [name]=\"'right'\"></obl-icon>\n </button>\n </div>\n }\n <div class=\"items\">\n <ng-container>\n @for (item of model.getCurrentSet() | async; track item) {\n <button\n oblButton\n [selected]=\"model.selected(item, current)\"\n [style.width]=\"'calc(100% / ' + model.columns + ')'\"\n (click)=\"selected.emit(item)\"\n >\n <ng-container\n *ngTemplateOutlet=\"\n itemRef || defaultRef;\n context: { $implicit: item }\n \"\n ></ng-container>\n </button>\n }\n </ng-container>\n </div>\n} @else {\n <div class=\"loading\">\n <obl-spinner></obl-spinner>\n </div>\n}\n\n<ng-template let-item #defaultRef>\n {{ item?.value.toString() }}\n</ng-template>\n\n", styles: [":host{display:flex;flex-wrap:wrap;max-width:100%;width:300px;border-radius:5px;overflow:hidden;background-color:var(--obl-button-background-color, #d4d4d4);box-shadow:0 0 4px gray}:host .navigation{display:flex;flex-direction:row;width:100%;justify-content:right}:host .navigation button{--obl-default-button-padding: 5px}.items{display:flex;flex-wrap:wrap;max-width:100%;justify-items:stretch;width:300px}.items button{font-size:var(--obl-font-size, 16px);--obl-default-button-padding: 10px;border-radius:5px}.items button div{padding:var(--obl-default-button-padding)}.loading{display:flex;justify-content:center;width:100%}.loading obl-spinner{--obl-spinner-width: 80px;--obl-spinner-color: gray;display:block;margin:20px auto}\n"], dependencies: [{ kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: i2.IconComponent, selector: "obl-icon", inputs: ["name", "width"] }, { kind: "directive", type: i3.ButtonDirective, selector: "oblButton, [oblButton]", inputs: ["oblButton", "icon", "blockWhenLoading", "loading", "toggle", "selected", "link", "target"], exportAs: ["oblButton"] }, { kind: "component", type: i4.SpinnerComponent, selector: "obl-spinner" }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }] }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: ValuesSetComponent, decorators: [{ type: Component, args: [{ selector: 'obl-values-set', standalone: false, template: "@if (!model.loading) {\n @if (model.displayNextPrevious() | async) {\n <div class=\"navigation\">\n <button\n oblButton\n (click)=\"setOffset('previous')\"\n [disabled]=\"model.previousDisabled() | async\"\n >\n <obl-icon [name]=\"'left'\"></obl-icon>\n </button>\n <button\n oblButton\n (click)=\"setOffset('next')\"\n [disabled]=\"model.nextDisabled() | async\"\n >\n <obl-icon [name]=\"'right'\"></obl-icon>\n </button>\n </div>\n }\n <div class=\"items\">\n <ng-container>\n @for (item of model.getCurrentSet() | async; track item) {\n <button\n oblButton\n [selected]=\"model.selected(item, current)\"\n [style.width]=\"'calc(100% / ' + model.columns + ')'\"\n (click)=\"selected.emit(item)\"\n >\n <ng-container\n *ngTemplateOutlet=\"\n itemRef || defaultRef;\n context: { $implicit: item }\n \"\n ></ng-container>\n </button>\n }\n </ng-container>\n </div>\n} @else {\n <div class=\"loading\">\n <obl-spinner></obl-spinner>\n </div>\n}\n\n<ng-template let-item #defaultRef>\n {{ item?.value.toString() }}\n</ng-template>\n\n", styles: [":host{display:flex;flex-wrap:wrap;max-width:100%;width:300px;border-radius:5px;overflow:hidden;background-color:var(--obl-button-background-color, #d4d4d4);box-shadow:0 0 4px gray}:host .navigation{display:flex;flex-direction:row;width:100%;justify-content:right}:host .navigation button{--obl-default-button-padding: 5px}.items{display:flex;flex-wrap:wrap;max-width:100%;justify-items:stretch;width:300px}.items button{font-size:var(--obl-font-size, 16px);--obl-default-button-padding: 10px;border-radius:5px}.items button div{padding:var(--obl-default-button-padding)}.loading{display:flex;justify-content:center;width:100%}.loading obl-spinner{--obl-spinner-width: 80px;--obl-spinner-color: gray;display:block;margin:20px auto}\n"] }] }], propDecorators: { current: [{ type: Input }], model: [{ type: Input }], itemRef: [{ type: Input }], selected: [{ type: Output }], next: [{ type: Output }], previous: [{ type: Output }] } }); class ValuePickerComponent { _current; /** * Input item that can have external optional data, must be the same reference object as one exist on items table */ set current(value) { this._current = value; } get current() { return this._current; } /** * items to pickup */ set items(value) { if (!this.model) { this.model = new ValueSetStaticModel(); } this.model.set = value; } get items() { return this._model.set; } /** * disable the whole things */ disabled = false; /** */ placeholder; _model = new ValueSetStaticModel(); /** * Set model class that need to extends AbstractValueSetModel */ set model(value) { value.set = value.set.length && value.set || this._model?.set; this._model = value; } get model() { return this._model; } /** * Options to control displaying different parts, right now only next/previous buttons */ display = { nextPrevious: true }; /** * Picker settings */ picker = {}; /** * Emit when picker value changed */ changed = new EventEmitter(); /** define if list is opened */ isOpen = false; touched = false; valueRef; itemRef; onChange = (value) => { }; onTouched = () => { }; registerOnValidatorChange(fn) { this.onChange = fn; } writeValue(set) { this.current = set; } registerOnChange(onChange) { this.onChange = onChange; } registerOnTouched(onTouched) { this.onTouched = onTouched; } setDisabledState(disabled) { this.disabled = disabled; } markAsTouched() { if (this.disabled) { return; } if (!this.touched) { this.onTouched(); this.touched = true; } } previous() { this.setValue(this.model.previous(this.current)); } next() { this.setValue(this.model.next(this.current)); } open() { if (!this.isOpen) { this.isOpen = true; } } toggle() { this.isOpen = !this.isOpen; } selected(item) { this.isOpen = false; this.setValue(of(item)); } setValue(value) { value.subscribe(value => { this.writeValue(value); this.onChange(value); this.changed.emit(value); }); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: ValuePickerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.4", type: ValuePickerComponent, isStandalone: false, selector: "obl-value-picker", inputs: { current: "current", items: "items", disabled: "disabled", placeholder: "placeholder", model: "model", display: "display", picker: "picker" }, outputs: { changed: "changed" }, providers: [ { provide: NG_VALUE_ACCESSOR, multi: true, useExisting: ValuePickerComponent }, ], queries: [{ propertyName: "valueRef", first: true, predicate: ["valueRef"], descendants: true }, { propertyName: "itemRef", first: true, predicate: ["itemRef"], descendants: true }], ngImport: i0, template: "@if (display.nextPrevious) {\n <button\n oblButton\n (click)=\"previous()\"\n [icon]=\"'left'\"\n [disabled]=\"disabled || model.loading || model.decreaseDisabled(current)\"\n ></button>\n}\n<button\n [disabled]=\"disabled\"\n oblButton\n class=\"current\"\n (click)=\"open()\"\n cdkOverlayOrigin\n #dropDown=\"cdkOverlayOrigin\"\n>\n @if (model.loading) {\n <obl-spinner></obl-spinner>\n }\n <ng-container\n *ngTemplateOutlet=\"\n (current && (valueRef || defaultRef)) || placeholderRef;\n context: { $implicit: current }\n \"\n ></ng-container>\n</button>\n\n@if (display.nextPrevious) {\n <button\n oblButton\n (click)=\"next()\"\n [icon]=\"'right'\"\n [disabled]=\"disabled || model.loading || model.increaseDisabled(current)\"\n >\n </button>\n}\n\n<ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"dropDown\"\n [cdkConnectedOverlayOpen]=\"isOpen\"\n [cdkConnectedOverlayOffsetX]=\"2\"\n (overlayOutsideClick)=\"toggle()\"\n>\n <obl-values-set\n [class]=\"picker.className\"\n [current]=\"current\"\n [model]=\"model\"\n [itemRef]=\"itemRef\"\n (selected)=\"selected($event)\"\n >\n </obl-values-set>\n</ng-template>\n\n<ng-template #defaultRef>\n {{ current?.value.toString() }}\n</ng-template>\n\n<ng-content></ng-content>\n\n<ng-template #placeholderRef>\n {{ placeholder }}\n</ng-template>\n", styles: [":host{display:flex;flex-wrap:nowrap;border-radius:5px;overflow:hidden;min-height:30px}.current{min-width:100px;width:100%}button{--obl-default-button-padding: 5px;font-size:var(--obl-font-size, 16px)}obl-spinner{--obl-spinner-width: 14px;--obl-spinner-color: gray;display:block;margin:0 5px}\n"], dependencies: [{ kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i3.ButtonDirective, selector: "oblButton, [oblButton]", inputs: ["oblButton", "icon", "blockWhenLoading", "loading", "toggle", "selected", "link", "target"], exportAs: ["oblButton"] }, { kind: "directive", type: i3$1.CdkConnectedOverlay, selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", inputs: ["cdkConnectedOverlayOrigin", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPositionStrategy", "cdkConnectedOverlayOffsetX", "cdkConnectedOverlayOffsetY", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHeight", "cdkConnectedOverlayMinWidth", "cdkConnectedOverlayMinHeight", "cdkConnectedOverlayBackdropClass", "cdkConnectedOverlayPanelClass", "cdkConnectedOverlayViewportMargin", "cdkConnectedOverlayScrollStrategy", "cdkConnectedOverlayOpen", "cdkConnectedOverlayDisableClose", "cdkConnectedOverlayTransformOriginOn", "cdkConnectedOverlayHasBackdrop", "cdkConnectedOverlayLockPosition", "cdkConnectedOverlayFlexibleDimensions", "cdkConnectedOverlayGrowAfterOpen", "cdkConnectedOverlayPush", "cdkConnectedOverlayDisposeOnNavigation"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }, { kind: "directive", type: i3$1.CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }, { kind: "component", type: i4.SpinnerComponent, selector: "obl-spinner" }, { kind: "component", type: ValuesSetComponent, selector: "obl-values-set", inputs: ["current", "model", "itemRef"], outputs: ["selected", "next", "previous"] }] }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: ValuePickerComponent, decorators: [{ type: Component, args: [{ selector: 'obl-value-picker', providers: [ { provide: NG_VALUE_ACCESSOR, multi: true, useExisting: ValuePickerComponent }, ], standalone: false, template: "@if (display.nextPrevious) {\n <button\n oblButton\n (click)=\"previous()\"\n [icon]=\"'left'\"\n [disabled]=\"disabled || model.loading || model.decreaseDisabled(current)\"\n ></button>\n}\n<button\n [disabled]=\"disabled\"\n oblButton\n class=\"current\"\n (click)=\"open()\"\n cdkOverlayOrigin\n #dropDown=\"cdkOverlayOrigin\"\n>\n @if (model.loading) {\n <obl-spinner></obl-spinner>\n }\n <ng-container\n *ngTemplateOutlet=\"\n (current && (valueRef || defaultRef)) || placeholderRef;\n context: { $implicit: current }\n \"\n ></ng-container>\n</button>\n\n@if (display.nextPrevious) {\n <button\n oblButton\n (click)=\"next()\"\n [icon]=\"'right'\"\n [disabled]=\"disabled || model.loading || model.increaseDisabled(current)\"\n >\n </button>\n}\n\n<ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"dropDown\"\n [cdkConnectedOverlayOpen]=\"isOpen\"\n [cdkConnectedOverlayOffsetX]=\"2\"\n (overlayOutsideClick)=\"toggle()\"\n>\n <obl-values-set\n [class]=\"picker.className\"\n [current]=\"current\"\n [model]=\"model\"\n [itemRef]=\"itemRef\"\n (selected)=\"selected($event)\"\n >\n </obl-values-set>\n</ng-template>\n\n<ng-template #defaultRef>\n {{ current?.value.toString() }}\n</ng-template>\n\n<ng-content></ng-content>\n\n<ng-template #placeholderRef>\n {{ placeholder }}\n</ng-template>\n", styles: [":host{display:flex;flex-wrap:nowrap;border-radius:5px;overflow:hidden;min-height:30px}.current{min-width:100px;width:100%}button{--obl-default-button-padding: 5px;font-size:var(--obl-font-size, 16px)}obl-spinner{--obl-spinner-width: 14px;--obl-spinner-color: gray;display:block;margin:0 5px}\n"] }] }], propDecorators: { current: [{ type: Input }], items: [{ type: Input }], disabled: [{ type: Input }], placeholder: [{ type: Input }], model: [{ type: Input }], display: [{ type: Input }], picker: [{ type: Input }], changed: [{ type: Output }], valueRef: [{ type: ContentChild, args: ['valueRef'] }], itemRef: [{ type: ContentChild, args: ['itemRef'] }] } }); class ValuePickerModule { static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: ValuePickerModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.0.4", ngImport: i0, type: ValuePickerModule, declarations: [ValuePickerComponent, ValuesSetComponent], imports: [CommonModule, IconsModule, ButtonsModule, OverlayModule, FormsModule, ReactiveFormsModule, LoadingModule], exports: [ValuePickerComponent, ValuesSetComponent] }); static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: ValuePickerModule, imports: [CommonModule, IconsModule, ButtonsModule, OverlayModule, FormsModule, ReactiveFormsModule, LoadingModule] }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: ValuePickerModule, decorators: [{ type: NgModule, args: [{ declarations: [ ValuePickerComponent, ValuesSetComponent ], imports: [ CommonModule, IconsModule, ButtonsModule, OverlayModule, FormsModule, ReactiveFormsModule, LoadingModule, ], exports: [ ValuePickerComponent, ValuesSetComponent ] }] }] }); class ValueSetDynamicModel extends AbstractValueSetModel { values = new BehaviorSubject(undefined); observer = this.values.asObservable(); getSet; get(type, current) { if (this.getSet) { setTimeout(() => this.loading = true); return this.getSet(this, type, current).pipe(finalize(() => this.loading = false)); } return of(); } init(current) { const values = this.values.value; if (values && current) { if (values.items.findIndex(item => item.value.id === current?.value.id) !== -1) { return of(); } } this.get(current && 'current' || 'default', current).subscribe(resp => this.values.next(resp)); return of(); } getNextSet() { return this.getPreviousNextSet('next'); } getPreviousSet() { return this.getPreviousNextSet('previous'); } getPreviousNextSet(type, current) { const res = this.values.value; if (this.getSet) { if (res) { if (res.next) { this.loading = true; this.get(type).subscribe(resp => this.values.next(resp)); } return of(res.next); } this.loading = true; this.get(current && 'current' || 'default', current).subscribe(resp => this.values.next(resp)); } return of(false); } getCurrentSet() { return this.observer.pipe(map(resp => resp?.items || [])); } displayNextPrevious() { const value = this.values.value; return of(value && (value.next && value.previous) || false); } next(current) { return this.getPreviousNext('next', current); } previous(current) { return this.getPreviousNext('previous', current); } getPreviousNext(type, current) { if (this.loading) { return of(current); } const value = this.values.value; const get = () => { if (this.getSet) { setTimeout(() => this.loading = true); return this.getSet(this, type).pipe(map(resp => (this.values.next(resp), resp.items[type === 'next' ? 0 : resp.items.length - 1])), finalize(() => this.loading = false)); } return of(); }; if (value) { const { items, next, previous } = value; const index = items.findIndex(item => item.value.id === current?.value.id); if (type === 'next' && next) { return items[index + 1] && of(items[index + 1]) || get(); } else if (type === 'previous' && previous) { return items[index - 1] && of(items[index - 1]) || get(); } return of(); } return get(); } nextDisabled() { return of(this.values.value?.next !== true || false); } previousDisabled() { return of(this.values.value?.previous !== true || false); } decreaseDisabled(current) { const values = this.values.value; return current?.value.id === values?.items[0].value.id && values?.previous !== true || false; } increaseDisabled(current) { const values = this.values.value; return current?.value.id === values?.items[(values?.items.length || 0) - 1]?.value.id && values?.next !== true || false; } selected(item, current) { return current?.value.id === item?.value.id; } } /** * Generated bundle index. Do not edit. */ export { AbstractValueSetModel, ValuePickerComponent, ValuePickerModule, ValueSetDynamicModel, ValueSetStaticModel, ValuesSetComponent }; //# sourceMappingURL=obliczeniowo-elementary-value-picker.mjs.map