UNPKG

@obliczeniowo/elementary

Version:
292 lines (284 loc) 20.7 kB
import * as i0 from '@angular/core'; import { model, input, effect, HostListener, HostBinding, Component, Pipe, TemplateRef, EventEmitter, Output, Input, NgModule } from '@angular/core'; import * as i2 from '@angular/common'; import { CommonModule } from '@angular/common'; import * as i4 from '@obliczeniowo/elementary/buttons'; import { ButtonsModule } from '@obliczeniowo/elementary/buttons'; import * as i5 from '@obliczeniowo/elementary/scroller-nav'; import { PageControllerRecordAdapter, ScrollerNavModule } from '@obliczeniowo/elementary/scroller-nav'; import * as i3 from '@obliczeniowo/elementary/icons'; import { IconsModule } from '@obliczeniowo/elementary/icons'; import * as i1 from '@obliczeniowo/elementary/input'; import { InputModule } from '@obliczeniowo/elementary/input'; import { ElementaryMath } from '@obliczeniowo/elementary/math'; import * as i1$1 from '@obliczeniowo/elementary/files'; import { Csv } from '@obliczeniowo/elementary/files'; import { FormsModule } from '@angular/forms'; class EditableCellComponent { elementRef; value = model.required(); editable = input(true); type = 'none'; inputValue; focus = false; get isNumber() { return this.type === 'number'; } get isText() { return this.type === 'string'; } clicked() { if (this.editable()) { this.focus = true; } } blur(event) { console.log(event.target.value); this.value.set(event.target.value); this.focus = false; } constructor(elementRef) { this.elementRef = elementRef; effect(() => { this.value(); this.changed(); }, { allowSignalWrites: true }); } ngAfterViewChecked() { if (this.focus) { const input = this.elementRef.nativeElement.querySelector('input'); if (input) { input.focus(); } } } changed() { const value = this.value(); const isString = typeof value === 'string'; if (isString || typeof value === 'number') { if (isString && ElementaryMath.isNumeric(value, true)) { this.value.set(parseFloat(value)); } this.type = typeof value; this.inputValue = value; } else if (value === undefined) { this.type = 'none'; this.inputValue = ''; } else { this.type = 'json'; this.inputValue = 'object'; } } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: EditableCellComponent, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.4", type: EditableCellComponent, isStandalone: false, selector: "td[obl-editable-cell]", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: true, transformFunction: null }, editable: { classPropertyName: "editable", publicName: "editable", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange" }, host: { listeners: { "click": "clicked()" }, properties: { "class.focus": "this.focus", "class.number": "this.isNumber", "class.string": "this.isText" } }, ngImport: i0, template: "@if (editable() && focus && (type === 'string' || type === 'number')) {\n <obl-input-wrapper\n display=\"none\"\n >\n <input oblInput type=\"text\" [value]=\"inputValue\" (blur)=\"blur($event)\" />\n </obl-input-wrapper>\n} @else {\n {{ inputValue }}\n}\n", styles: ["obl-input-wrapper{width:100%}input{min-width:100px}:host:not(.focus){padding:13px 10px}:host:not(.focus).number{text-align:right}:host:not(.focus).string{word-break:break-all;min-width:100px}\n"], dependencies: [{ kind: "directive", type: i1.InputDirective, selector: "input[oblInput]", inputs: ["outlined"], exportAs: ["oblInput"] }, { kind: "component", type: i1.InputWrapperComponent, selector: "obl-input-wrapper", inputs: ["label", "display", "cancellable", "step", "errors", "translations", "labelAnimation", "small"], outputs: ["loaded"] }] }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: EditableCellComponent, decorators: [{ type: Component, args: [{ selector: 'td[obl-editable-cell]', standalone: false, template: "@if (editable() && focus && (type === 'string' || type === 'number')) {\n <obl-input-wrapper\n display=\"none\"\n >\n <input oblInput type=\"text\" [value]=\"inputValue\" (blur)=\"blur($event)\" />\n </obl-input-wrapper>\n} @else {\n {{ inputValue }}\n}\n", styles: ["obl-input-wrapper{width:100%}input{min-width:100px}:host:not(.focus){padding:13px 10px}:host:not(.focus).number{text-align:right}:host:not(.focus).string{word-break:break-all;min-width:100px}\n"] }] }], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { focus: [{ type: HostBinding, args: ['class.focus'] }], isNumber: [{ type: HostBinding, args: ['class.number'] }], isText: [{ type: HostBinding, args: ['class.string'] }], clicked: [{ type: HostListener, args: ['click'] }] } }); class IsPipe { transform(value, ...args) { // eslint-disable-next-line valid-typeof return typeof value === args[0]; } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: IsPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.0.4", ngImport: i0, type: IsPipe, isStandalone: false, name: "is" }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: IsPipe, decorators: [{ type: Pipe, args: [{ name: 'is', standalone: false }] }] }); class IsTemplatePipe { transform(value, ...args) { return value instanceof TemplateRef && value || undefined; } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: IsTemplatePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.0.4", ngImport: i0, type: IsTemplatePipe, isStandalone: false, name: "isTemplate" }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: IsTemplatePipe, decorators: [{ type: Pipe, args: [{ name: 'isTemplate', standalone: false }] }] }); /** * Component prepared to display raw data like text/numbers as a quick formatted table */ class ArrayToTableComponent { elementRef; change; file; /** * Array of elements to display number/string/table of number/string */ array = []; /** */ headers = input([]); /** * Caption text of table to display */ caption = input(); /** * Maximum number of columns in one line, if number of columns is greater then this value * then it will split data in new record line */ maxLength = input(); /** */ editable = input(false); /** */ display = input({ index: true, download: 'csv', }); arrayChanged = new EventEmitter(); /** * Prepared to display data */ records = []; /** * body of table reference for scroller */ tbody = null; /** * y table wrapper reference for scroller */ yWrapper = null; pageAdapter = new PageControllerRecordAdapter(this.records.length); constructor(elementRef, change, file) { this.elementRef = elementRef; this.change = change; this.file = file; } ngOnChanges(changes) { if (changes.array || changes.maxLength) { this.array = this.array.map((v) => { if (v instanceof Array) { return v; } return [v]; }); this.records = this.convert().reduce((p, c) => { const copy = [...c]; while (this.maxLength() && copy.length > (this.maxLength() || 0)) { p.push(copy.splice(0, this.maxLength())); } if (copy.length) { p.push(copy); } return p; }, []) || []; this.pageAdapter.setMax(this.records.length); } } ngAfterViewInit() { setTimeout(() => { this.tbody = this.elementRef.nativeElement.querySelector('tbody'); this.yWrapper = this.elementRef.nativeElement.querySelector('.y-wrapper'); }); } downloadCsv(name = 'Table') { if (this.array.length) { const converted = this.convert(); const csv = Csv.fromArray(converted.map((v) => v.map((v) => v.value)), this.headers()); this.file.saveTextFile(csv.prepare(), name, 'text/csv'); } } downloadJson(name = 'Table') { if (this.array.length) { const converted = this.convert(); const csv = Csv.fromArray(converted.map((v) => v.map((v) => v.value)), this.headers()); this.file.saveTextFile(JSON.stringify(csv.toJson(), undefined, ' '), name, 'text/json'); } } changed(value, record, index) { if (ElementaryMath.isNumeric(value, true)) { value = parseFloat(value); this.records[record][index].value = value; this.array[this.records[record][index].y][this.records[record][index].x] = value; } else { this.records[record][index].value = value; this.array[this.records[record][index].y][this.records[record][index].x] = value; } this.arrayChanged.emit(this.array); } convert() { return this.array.map((v, y) => v.map((value, x) => ({ x, y, value }))); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: ArrayToTableComponent, deps: [{ token: i0.ElementRef }, { token: i0.ChangeDetectorRef }, { token: i1$1.OblFileService }], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.4", type: ArrayToTableComponent, isStandalone: false, selector: "obl-array-to-table", inputs: { array: { classPropertyName: "array", publicName: "array", isSignal: false, isRequired: false, transformFunction: null }, headers: { classPropertyName: "headers", publicName: "headers", isSignal: true, isRequired: false, transformFunction: null }, caption: { classPropertyName: "caption", publicName: "caption", isSignal: true, isRequired: false, transformFunction: null }, maxLength: { classPropertyName: "maxLength", publicName: "maxLength", isSignal: true, isRequired: false, transformFunction: null }, editable: { classPropertyName: "editable", publicName: "editable", isSignal: true, isRequired: false, transformFunction: null }, display: { classPropertyName: "display", publicName: "display", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { arrayChanged: "arrayChanged" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"table-wrapper\">\n <div class=\"y-wrapper\">\n <table>\n @if (caption()) {\n <caption>\n @if (caption() | is: 'string') {\n {{ caption() }}\n } @else {\n <ng-container *ngTemplateOutlet=\"(caption() | isTemplate) || captionRef\"></ng-container>\n }\n </caption>\n }\n\n <tbody>\n @if (!maxLength() && headers()?.length) {\n @if (!maxLength() && headers()?.length) {\n <tr>\n @if (display().index) {\n <th class=\"index\"></th>\n }\n @for ( header of headers(); track header) {\n <th>{{ header }}</th>\n }\n </tr>\n }\n }\n @for(record of records; track $index; let vIndex = $index) { \n @if (\n vIndex >= pageAdapter.position &&\n vIndex < pageAdapter.page.jump + pageAdapter.position\n ) {\n <tr>\n @if (display().index) {\n <td class=\"index\">{{ vIndex + 1 }}</td>\n }\n @for (value of record; track value) {\n <td\n obl-editable-cell\n [value]=\"value.value\"\n [editable]=\"editable()\"\n (valueChanged)=\"changed($event, vIndex, $index)\"\n ></td>\n }\n </tr>\n }\n }\n </tbody>\n </table>\n </div>\n <div class=\"buttons-vertical\">\n @if (pageAdapter.getMax() > pageAdapter.page.jump) {\n <obl-scroller-nav\n [horizontal]=\"false\"\n [pageController]=\"pageAdapter\"\n [element]=\"tbody\"\n ></obl-scroller-nav>\n }\n\n @if (display().download === 'csv') {\n <button oblButton (click)=\"downloadCsv()\">\n <obl-icon [name]=\"'csv-file'\"></obl-icon>\n </button>\n }\n\n @if (display().download === 'json') {\n <button oblButton (click)=\"downloadJson()\">\n <obl-icon [name]=\"'json-file'\"></obl-icon>\n </button>\n }\n\n <ng-content></ng-content>\n </div>\n</div>\n\n<obl-scroller-nav [element]=\"yWrapper\"></obl-scroller-nav>\n\n<ng-template #captionRef></ng-template>", styles: [":host{display:flex;width:100%;flex-direction:column}table{border-collapse:collapse}caption{padding:5px}td,th{border:1px solid;padding:5px;min-width:40px;max-width:300px}.buttons-vertical{display:flex;flex-direction:column;justify-content:flex-end}.buttons-horizontal{display:flex;flex-direction:row}.table-wrapper{display:flex;width:100%}.y-wrapper{max-width:calc(100% - 45px);overflow:hidden}.index{text-align:right;font-weight:800}table th.index{background-image:linear-gradient(to bottom left,transparent calc(50% - 1px),var(--default-border-color),transparent calc(50% + 1px))}\n"], dependencies: [{ kind: "directive", type: i2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: i3.IconComponent, selector: "obl-icon", inputs: ["name", "width"] }, { kind: "directive", type: i4.ButtonDirective, selector: "oblButton, [oblButton]", inputs: ["oblButton", "icon", "blockWhenLoading", "loading", "toggle", "selected", "link", "target"], exportAs: ["oblButton"] }, { kind: "component", type: i5.ScrollerNavComponent, selector: "obl-scroller-nav", inputs: ["element", "show", "horizontal", "pageController"] }, { kind: "component", type: EditableCellComponent, selector: "td[obl-editable-cell]", inputs: ["value", "editable"], outputs: ["valueChange"] }, { kind: "pipe", type: IsPipe, name: "is" }, { kind: "pipe", type: IsTemplatePipe, name: "isTemplate" }] }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: ArrayToTableComponent, decorators: [{ type: Component, args: [{ selector: 'obl-array-to-table', standalone: false, template: "<div class=\"table-wrapper\">\n <div class=\"y-wrapper\">\n <table>\n @if (caption()) {\n <caption>\n @if (caption() | is: 'string') {\n {{ caption() }}\n } @else {\n <ng-container *ngTemplateOutlet=\"(caption() | isTemplate) || captionRef\"></ng-container>\n }\n </caption>\n }\n\n <tbody>\n @if (!maxLength() && headers()?.length) {\n @if (!maxLength() && headers()?.length) {\n <tr>\n @if (display().index) {\n <th class=\"index\"></th>\n }\n @for ( header of headers(); track header) {\n <th>{{ header }}</th>\n }\n </tr>\n }\n }\n @for(record of records; track $index; let vIndex = $index) { \n @if (\n vIndex >= pageAdapter.position &&\n vIndex < pageAdapter.page.jump + pageAdapter.position\n ) {\n <tr>\n @if (display().index) {\n <td class=\"index\">{{ vIndex + 1 }}</td>\n }\n @for (value of record; track value) {\n <td\n obl-editable-cell\n [value]=\"value.value\"\n [editable]=\"editable()\"\n (valueChanged)=\"changed($event, vIndex, $index)\"\n ></td>\n }\n </tr>\n }\n }\n </tbody>\n </table>\n </div>\n <div class=\"buttons-vertical\">\n @if (pageAdapter.getMax() > pageAdapter.page.jump) {\n <obl-scroller-nav\n [horizontal]=\"false\"\n [pageController]=\"pageAdapter\"\n [element]=\"tbody\"\n ></obl-scroller-nav>\n }\n\n @if (display().download === 'csv') {\n <button oblButton (click)=\"downloadCsv()\">\n <obl-icon [name]=\"'csv-file'\"></obl-icon>\n </button>\n }\n\n @if (display().download === 'json') {\n <button oblButton (click)=\"downloadJson()\">\n <obl-icon [name]=\"'json-file'\"></obl-icon>\n </button>\n }\n\n <ng-content></ng-content>\n </div>\n</div>\n\n<obl-scroller-nav [element]=\"yWrapper\"></obl-scroller-nav>\n\n<ng-template #captionRef></ng-template>", styles: [":host{display:flex;width:100%;flex-direction:column}table{border-collapse:collapse}caption{padding:5px}td,th{border:1px solid;padding:5px;min-width:40px;max-width:300px}.buttons-vertical{display:flex;flex-direction:column;justify-content:flex-end}.buttons-horizontal{display:flex;flex-direction:row}.table-wrapper{display:flex;width:100%}.y-wrapper{max-width:calc(100% - 45px);overflow:hidden}.index{text-align:right;font-weight:800}table th.index{background-image:linear-gradient(to bottom left,transparent calc(50% - 1px),var(--default-border-color),transparent calc(50% + 1px))}\n"] }] }], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.ChangeDetectorRef }, { type: i1$1.OblFileService }], propDecorators: { array: [{ type: Input }], arrayChanged: [{ type: Output }] } }); class ArrayToTableModule { static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: ArrayToTableModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.0.4", ngImport: i0, type: ArrayToTableModule, declarations: [ArrayToTableComponent, EditableCellComponent, IsPipe, IsTemplatePipe], imports: [CommonModule, FormsModule, IconsModule, ButtonsModule, ScrollerNavModule, InputModule], exports: [ArrayToTableComponent] }); static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: ArrayToTableModule, imports: [CommonModule, FormsModule, IconsModule, ButtonsModule, ScrollerNavModule, InputModule] }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: ArrayToTableModule, decorators: [{ type: NgModule, args: [{ declarations: [ ArrayToTableComponent, EditableCellComponent, IsPipe, IsTemplatePipe, ], imports: [ CommonModule, FormsModule, IconsModule, ButtonsModule, ScrollerNavModule, InputModule, ], exports: [ ArrayToTableComponent ] }] }] }); /** * Generated bundle index. Do not edit. */ export { ArrayToTableComponent, ArrayToTableModule }; //# sourceMappingURL=obliczeniowo-elementary-array-to-table.mjs.map