@maro-tech/form 0.0.13 → 0.0.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
2
|
import { signal, Injectable, inject, InjectionToken, input, output, ViewChild, Component, ChangeDetectorRef, DestroyRef, EventEmitter, ElementRef, forwardRef, booleanAttribute, HostListener, Output, Input, ChangeDetectionStrategy, ViewContainerRef, computed } from '@angular/core';
|
|
3
3
|
import { HttpClient } from '@angular/common/http';
|
|
4
|
+
import { map, Subject, debounceTime, distinctUntilChanged, forkJoin, Observable, from, switchMap, of } from 'rxjs';
|
|
4
5
|
import * as i1$2 from '@angular/common';
|
|
5
6
|
import { CommonModule } from '@angular/common';
|
|
6
7
|
import * as i2 from '@angular/forms';
|
|
7
8
|
import { FormsModule, NG_VALUE_ACCESSOR } from '@angular/forms';
|
|
8
|
-
import { Subject, debounceTime, distinctUntilChanged, forkJoin, Observable, from, switchMap, of } from 'rxjs';
|
|
9
9
|
import { BrowserMultiFormatOneDReader } from '@zxing/browser';
|
|
10
10
|
import { DecodeHintType, BarcodeFormat } from '@zxing/library';
|
|
11
11
|
import { UiButtonComponent } from '@maro-tech/core';
|
|
@@ -48,6 +48,11 @@ class MaroFormsApiService {
|
|
|
48
48
|
const suffix = query ? `?q=${encodeURIComponent(query)}` : '';
|
|
49
49
|
return this.http.get(`/api/suggest/${encodeURIComponent(name)}${suffix}`);
|
|
50
50
|
}
|
|
51
|
+
uploadFile(file) {
|
|
52
|
+
const formData = new FormData();
|
|
53
|
+
formData.append('file', file);
|
|
54
|
+
return this.http.post('/api/files/', formData).pipe(map((response) => ({ imageId: response.id, type: response.type })));
|
|
55
|
+
}
|
|
51
56
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: MaroFormsApiService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
52
57
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: MaroFormsApiService, providedIn: 'root' });
|
|
53
58
|
}
|
|
@@ -2463,12 +2468,12 @@ class UiDynamicFormComponent {
|
|
|
2463
2468
|
const next = { ...this.value };
|
|
2464
2469
|
for (const field of this.getLeafFields()) {
|
|
2465
2470
|
const defaultValue = field.type === 'checkbox' ? false : '';
|
|
2466
|
-
const
|
|
2471
|
+
const rawValue = this.value[field.id] !== undefined
|
|
2467
2472
|
? this.value[field.id]
|
|
2468
2473
|
: field.default !== undefined
|
|
2469
2474
|
? field.default
|
|
2470
2475
|
: defaultValue;
|
|
2471
|
-
next[field.id] =
|
|
2476
|
+
next[field.id] = this.normalizeFieldValue(field, rawValue);
|
|
2472
2477
|
}
|
|
2473
2478
|
this.modelSignal.set(next);
|
|
2474
2479
|
this.formContext.setValue(next);
|
|
@@ -2491,6 +2496,13 @@ class UiDynamicFormComponent {
|
|
|
2491
2496
|
return updated;
|
|
2492
2497
|
});
|
|
2493
2498
|
}
|
|
2499
|
+
normalizeFieldValue(field, value) {
|
|
2500
|
+
if (field.type === 'photo' && field.multiple) {
|
|
2501
|
+
const values = Array.isArray(value) ? value : String(value || '').split(',');
|
|
2502
|
+
return values.map((item) => String(item || '').trim()).filter(Boolean);
|
|
2503
|
+
}
|
|
2504
|
+
return value;
|
|
2505
|
+
}
|
|
2494
2506
|
onBarcodeValueChange(field, value) {
|
|
2495
2507
|
this.updateField(field.id, value);
|
|
2496
2508
|
this.lookupErrorsSignal.update((current) => ({ ...current, [field.id]: '' }));
|
|
@@ -2910,6 +2922,8 @@ class UiActionFormModalComponent {
|
|
|
2910
2922
|
}), ...(ngDevMode ? [{ debugName: "panelClass" }] : /* istanbul ignore next */ []));
|
|
2911
2923
|
http = inject(HttpClient);
|
|
2912
2924
|
router = inject(Router);
|
|
2925
|
+
formsApi = inject(MaroFormsApiService);
|
|
2926
|
+
defaultUploadPhoto = (file) => this.formsApi.uploadFile(file);
|
|
2913
2927
|
ngOnChanges(changes) {
|
|
2914
2928
|
if (this.isOpen() && ('isOpen' in changes || 'formId' in changes || 'recordId' in changes)) {
|
|
2915
2929
|
this.loadForm();
|
|
@@ -2936,7 +2950,7 @@ class UiActionFormModalComponent {
|
|
|
2936
2950
|
this.isUploading.set(uploading);
|
|
2937
2951
|
}
|
|
2938
2952
|
onFieldActionTriggered(event) {
|
|
2939
|
-
const payload = { ...this.formValue(), ...(event.action.payload || {}) };
|
|
2953
|
+
const payload = this.normalizePayload({ ...this.formValue(), ...(event.action.payload || {}) });
|
|
2940
2954
|
const endpoint = this.resolveTemplate(this.resolveEndpoint(event.action.api), payload);
|
|
2941
2955
|
if (!endpoint)
|
|
2942
2956
|
return;
|
|
@@ -2956,7 +2970,7 @@ class UiActionFormModalComponent {
|
|
|
2956
2970
|
const endpoint = this.resolveEndpoint(endpointTemplate);
|
|
2957
2971
|
if (!endpoint)
|
|
2958
2972
|
return;
|
|
2959
|
-
const payload = { ...(this.initialValue() || {}), ...(action?.payload || {}), ...this.formValue() };
|
|
2973
|
+
const payload = this.normalizePayload({ ...(this.initialValue() || {}), ...(action?.payload || {}), ...this.formValue() });
|
|
2960
2974
|
const recordId = String(this.recordId() || '').trim();
|
|
2961
2975
|
if (recordId && !Object.prototype.hasOwnProperty.call(payload, 'id'))
|
|
2962
2976
|
payload['id'] = recordId;
|
|
@@ -3010,6 +3024,17 @@ class UiActionFormModalComponent {
|
|
|
3010
3024
|
return value;
|
|
3011
3025
|
return `/api${value.startsWith('/') ? value : `/${value}`}`;
|
|
3012
3026
|
}
|
|
3027
|
+
normalizePayload(payload) {
|
|
3028
|
+
for (const field of this.fields()) {
|
|
3029
|
+
if (field.type !== 'photo' || !field.multiple)
|
|
3030
|
+
continue;
|
|
3031
|
+
const value = payload[field.id];
|
|
3032
|
+
if (typeof value === 'string') {
|
|
3033
|
+
payload[field.id] = value.split(',').map((item) => item.trim()).filter(Boolean);
|
|
3034
|
+
}
|
|
3035
|
+
}
|
|
3036
|
+
return payload;
|
|
3037
|
+
}
|
|
3013
3038
|
normalizeActionType(type) {
|
|
3014
3039
|
const normalized = String(type || '').trim().toLowerCase();
|
|
3015
3040
|
if (normalized === 'api' || normalized === 'action_api')
|
|
@@ -3048,11 +3073,11 @@ class UiActionFormModalComponent {
|
|
|
3048
3073
|
});
|
|
3049
3074
|
}
|
|
3050
3075
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: UiActionFormModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
3051
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.20", type: UiActionFormModalComponent, isStandalone: true, selector: "forms-action-form-modal", inputs: { formId: { classPropertyName: "formId", publicName: "formId", isSignal: true, isRequired: false, transformFunction: null }, recordId: { classPropertyName: "recordId", publicName: "recordId", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, isOpen: { classPropertyName: "isOpen", publicName: "isOpen", isSignal: true, isRequired: false, transformFunction: null }, cancelLabel: { classPropertyName: "cancelLabel", publicName: "cancelLabel", isSignal: true, isRequired: false, transformFunction: null }, initialValue: { classPropertyName: "initialValue", publicName: "initialValue", isSignal: true, isRequired: false, transformFunction: null }, uploadPhoto: { classPropertyName: "uploadPhoto", publicName: "uploadPhoto", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { closed: "closed", completed: "completed" }, usesOnChanges: true, ngImport: i0, template: "@if (isOpen()) {\n <div class=\"fixed inset-0 z-50 flex min-h-full items-center justify-center overflow-y-auto bg-slate-950/40 p-4 sm:p-6\" role=\"dialog\" aria-modal=\"true\">\n <div class=\"my-0 flex min-h-0 max-h-[calc(100vh-2rem)] w-full flex-col overflow-hidden rounded-2xl bg-slate-50 shadow-2xl sm:max-h-[calc(100vh-3rem)]\" [ngClass]=\"panelClass()\">\n <div class=\"flex shrink-0 items-center justify-between border-b border-slate-200 bg-white px-5 py-4 sm:px-6\">\n <h2 class=\"text-lg font-semibold\">{{ formConfig()?.title || 'Form' }}</h2>\n <button type=\"button\" class=\"text-xl text-slate-400 hover:text-slate-900\" aria-label=\"Close\" (click)=\"close()\">\u00D7</button>\n </div>\n <div class=\"min-h-0 flex-1 overflow-y-auto p-4 sm:p-6\">\n @if (isLoading()) {\n <p class=\"text-sm text-slate-500\">Loading form\u2026</p>\n } @else {\n <forms-dynamic-form [fields]=\"fields()\" [value]=\"formValue()\" [showSubmit]=\"false\" [uploadPhoto]=\"uploadPhoto()\" (valueChange)=\"onValueChange($event)\" (lookupCompleted)=\"onLookupCompleted($event)\" (uploadingChange)=\"onUploadingChange($event)\" (fieldActionTriggered)=\"onFieldActionTriggered($event)\"></forms-dynamic-form>\n }\n </div>\n <div class=\"relative z-10 flex shrink-0 justify-end gap-3 border-t border-slate-200 bg-white px-5 py-4 sm:px-6\">\n <ui-button variant=\"ghost\" (click)=\"close()\">{{ cancelLabel() }}</ui-button>\n @if (submitAction()) { <ui-button variant=\"primary\" [disabled]=\"isLoading() || isSubmitting() || isUploading()\" (click)=\"confirm()\">{{ submitLabel() }}</ui-button> }\n </div>\n </div>\n </div>\n}\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: UiButtonComponent, selector: "ui-button", inputs: ["variant", "size", "disabled", "fullWidth", "type", "withShadow", "icon", "iconOnly", "ariaLabel"] }, { kind: "component", type: UiDynamicFormComponent, selector: "forms-dynamic-form", inputs: ["fields", "value", "errors", "submitLabel", "showSubmit", "disabled", "uploadPhoto"], outputs: ["valueChange", "lookupCompleted", "submitted", "filesChange", "uploadingChange", "fieldActionTriggered"] }] });
|
|
3076
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.20", type: UiActionFormModalComponent, isStandalone: true, selector: "forms-action-form-modal", inputs: { formId: { classPropertyName: "formId", publicName: "formId", isSignal: true, isRequired: false, transformFunction: null }, recordId: { classPropertyName: "recordId", publicName: "recordId", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, isOpen: { classPropertyName: "isOpen", publicName: "isOpen", isSignal: true, isRequired: false, transformFunction: null }, cancelLabel: { classPropertyName: "cancelLabel", publicName: "cancelLabel", isSignal: true, isRequired: false, transformFunction: null }, initialValue: { classPropertyName: "initialValue", publicName: "initialValue", isSignal: true, isRequired: false, transformFunction: null }, uploadPhoto: { classPropertyName: "uploadPhoto", publicName: "uploadPhoto", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { closed: "closed", completed: "completed" }, usesOnChanges: true, ngImport: i0, template: "@if (isOpen()) {\n <div class=\"fixed inset-0 z-50 flex min-h-full items-center justify-center overflow-y-auto bg-slate-950/40 p-4 sm:p-6\" role=\"dialog\" aria-modal=\"true\">\n <div class=\"my-0 flex min-h-0 max-h-[calc(100vh-2rem)] w-full flex-col overflow-hidden rounded-2xl bg-slate-50 shadow-2xl sm:max-h-[calc(100vh-3rem)]\" [ngClass]=\"panelClass()\">\n <div class=\"flex shrink-0 items-center justify-between border-b border-slate-200 bg-white px-5 py-4 sm:px-6\">\n <h2 class=\"text-lg font-semibold\">{{ formConfig()?.title || 'Form' }}</h2>\n <button type=\"button\" class=\"text-xl text-slate-400 hover:text-slate-900\" aria-label=\"Close\" (click)=\"close()\">\u00D7</button>\n </div>\n <div class=\"min-h-0 flex-1 overflow-y-auto p-4 sm:p-6\">\n @if (isLoading()) {\n <p class=\"text-sm text-slate-500\">Loading form\u2026</p>\n } @else {\n <forms-dynamic-form [fields]=\"fields()\" [value]=\"formValue()\" [showSubmit]=\"false\" [uploadPhoto]=\"uploadPhoto() || defaultUploadPhoto\" (valueChange)=\"onValueChange($event)\" (lookupCompleted)=\"onLookupCompleted($event)\" (uploadingChange)=\"onUploadingChange($event)\" (fieldActionTriggered)=\"onFieldActionTriggered($event)\"></forms-dynamic-form>\n }\n </div>\n <div class=\"relative z-10 flex shrink-0 justify-end gap-3 border-t border-slate-200 bg-white px-5 py-4 sm:px-6\">\n <ui-button variant=\"ghost\" (click)=\"close()\">{{ cancelLabel() }}</ui-button>\n @if (submitAction()) { <ui-button variant=\"primary\" [disabled]=\"isLoading() || isSubmitting() || isUploading()\" (click)=\"confirm()\">{{ submitLabel() }}</ui-button> }\n </div>\n </div>\n </div>\n}\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: UiButtonComponent, selector: "ui-button", inputs: ["variant", "size", "disabled", "fullWidth", "type", "withShadow", "icon", "iconOnly", "ariaLabel"] }, { kind: "component", type: UiDynamicFormComponent, selector: "forms-dynamic-form", inputs: ["fields", "value", "errors", "submitLabel", "showSubmit", "disabled", "uploadPhoto"], outputs: ["valueChange", "lookupCompleted", "submitted", "filesChange", "uploadingChange", "fieldActionTriggered"] }] });
|
|
3052
3077
|
}
|
|
3053
3078
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: UiActionFormModalComponent, decorators: [{
|
|
3054
3079
|
type: Component,
|
|
3055
|
-
args: [{ selector: 'forms-action-form-modal', standalone: true, imports: [CommonModule, UiButtonComponent, UiDynamicFormComponent], template: "@if (isOpen()) {\n <div class=\"fixed inset-0 z-50 flex min-h-full items-center justify-center overflow-y-auto bg-slate-950/40 p-4 sm:p-6\" role=\"dialog\" aria-modal=\"true\">\n <div class=\"my-0 flex min-h-0 max-h-[calc(100vh-2rem)] w-full flex-col overflow-hidden rounded-2xl bg-slate-50 shadow-2xl sm:max-h-[calc(100vh-3rem)]\" [ngClass]=\"panelClass()\">\n <div class=\"flex shrink-0 items-center justify-between border-b border-slate-200 bg-white px-5 py-4 sm:px-6\">\n <h2 class=\"text-lg font-semibold\">{{ formConfig()?.title || 'Form' }}</h2>\n <button type=\"button\" class=\"text-xl text-slate-400 hover:text-slate-900\" aria-label=\"Close\" (click)=\"close()\">\u00D7</button>\n </div>\n <div class=\"min-h-0 flex-1 overflow-y-auto p-4 sm:p-6\">\n @if (isLoading()) {\n <p class=\"text-sm text-slate-500\">Loading form\u2026</p>\n } @else {\n <forms-dynamic-form [fields]=\"fields()\" [value]=\"formValue()\" [showSubmit]=\"false\" [uploadPhoto]=\"uploadPhoto()\" (valueChange)=\"onValueChange($event)\" (lookupCompleted)=\"onLookupCompleted($event)\" (uploadingChange)=\"onUploadingChange($event)\" (fieldActionTriggered)=\"onFieldActionTriggered($event)\"></forms-dynamic-form>\n }\n </div>\n <div class=\"relative z-10 flex shrink-0 justify-end gap-3 border-t border-slate-200 bg-white px-5 py-4 sm:px-6\">\n <ui-button variant=\"ghost\" (click)=\"close()\">{{ cancelLabel() }}</ui-button>\n @if (submitAction()) { <ui-button variant=\"primary\" [disabled]=\"isLoading() || isSubmitting() || isUploading()\" (click)=\"confirm()\">{{ submitLabel() }}</ui-button> }\n </div>\n </div>\n </div>\n}\n" }]
|
|
3080
|
+
args: [{ selector: 'forms-action-form-modal', standalone: true, imports: [CommonModule, UiButtonComponent, UiDynamicFormComponent], template: "@if (isOpen()) {\n <div class=\"fixed inset-0 z-50 flex min-h-full items-center justify-center overflow-y-auto bg-slate-950/40 p-4 sm:p-6\" role=\"dialog\" aria-modal=\"true\">\n <div class=\"my-0 flex min-h-0 max-h-[calc(100vh-2rem)] w-full flex-col overflow-hidden rounded-2xl bg-slate-50 shadow-2xl sm:max-h-[calc(100vh-3rem)]\" [ngClass]=\"panelClass()\">\n <div class=\"flex shrink-0 items-center justify-between border-b border-slate-200 bg-white px-5 py-4 sm:px-6\">\n <h2 class=\"text-lg font-semibold\">{{ formConfig()?.title || 'Form' }}</h2>\n <button type=\"button\" class=\"text-xl text-slate-400 hover:text-slate-900\" aria-label=\"Close\" (click)=\"close()\">\u00D7</button>\n </div>\n <div class=\"min-h-0 flex-1 overflow-y-auto p-4 sm:p-6\">\n @if (isLoading()) {\n <p class=\"text-sm text-slate-500\">Loading form\u2026</p>\n } @else {\n <forms-dynamic-form [fields]=\"fields()\" [value]=\"formValue()\" [showSubmit]=\"false\" [uploadPhoto]=\"uploadPhoto() || defaultUploadPhoto\" (valueChange)=\"onValueChange($event)\" (lookupCompleted)=\"onLookupCompleted($event)\" (uploadingChange)=\"onUploadingChange($event)\" (fieldActionTriggered)=\"onFieldActionTriggered($event)\"></forms-dynamic-form>\n }\n </div>\n <div class=\"relative z-10 flex shrink-0 justify-end gap-3 border-t border-slate-200 bg-white px-5 py-4 sm:px-6\">\n <ui-button variant=\"ghost\" (click)=\"close()\">{{ cancelLabel() }}</ui-button>\n @if (submitAction()) { <ui-button variant=\"primary\" [disabled]=\"isLoading() || isSubmitting() || isUploading()\" (click)=\"confirm()\">{{ submitLabel() }}</ui-button> }\n </div>\n </div>\n </div>\n}\n" }]
|
|
3056
3081
|
}], propDecorators: { formId: [{ type: i0.Input, args: [{ isSignal: true, alias: "formId", required: false }] }], recordId: [{ type: i0.Input, args: [{ isSignal: true, alias: "recordId", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], isOpen: [{ type: i0.Input, args: [{ isSignal: true, alias: "isOpen", required: false }] }], cancelLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "cancelLabel", required: false }] }], initialValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "initialValue", required: false }] }], uploadPhoto: [{ type: i0.Input, args: [{ isSignal: true, alias: "uploadPhoto", required: false }] }], closed: [{ type: i0.Output, args: ["closed"] }], completed: [{ type: i0.Output, args: ["completed"] }] } });
|
|
3057
3082
|
|
|
3058
3083
|
class UiInputComponent {
|