@yourself.create/ngx-form-designer 0.0.5 → 0.0.7
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.
- package/fesm2022/uch-web-ngx-form-designer.mjs +398 -96
- package/fesm2022/uch-web-ngx-form-designer.mjs.map +1 -1
- package/lib/data/data-provider.d.ts +10 -0
- package/lib/form-core/models.d.ts +3 -2
- package/lib/form-designer/data-panel/data-panel.component.d.ts +4 -2
- package/lib/form-designer/widget-inspector.component.d.ts +1 -1
- package/lib/form-renderer/json-form-renderer.component.d.ts +3 -1
- package/lib/widgets/field-widgets/select/select-widget.component.d.ts +12 -2
- package/package.json +1 -1
|
@@ -5420,12 +5420,12 @@ class JsonFormRendererComponent {
|
|
|
5420
5420
|
// Notify all widgets to show errors if any
|
|
5421
5421
|
this.engine?.submit();
|
|
5422
5422
|
const preUploadFieldValueMap = this.uploadOnSubmit
|
|
5423
|
-
? await this.buildFieldValueMap(this.engine?.getValues() ?? {})
|
|
5423
|
+
? await this.buildFieldValueMap(this.engine?.getValues() ?? {}, { normalizeFileFieldsForSubmit: true })
|
|
5424
5424
|
: undefined;
|
|
5425
5425
|
const uploadedFiles = this.uploadOnSubmit ? await this.uploadPendingFiles() : {};
|
|
5426
5426
|
this.uploadedFilesChange.emit(uploadedFiles);
|
|
5427
5427
|
const values = this.engine?.getValues() ?? {};
|
|
5428
|
-
const fieldValueMap = await this.buildFieldValueMap(values);
|
|
5428
|
+
const fieldValueMap = await this.buildFieldValueMap(values, { normalizeFileFieldsForSubmit: true });
|
|
5429
5429
|
const submitValues = preUploadFieldValueMap
|
|
5430
5430
|
? this.mergeFileMetadata(fieldValueMap, preUploadFieldValueMap)
|
|
5431
5431
|
: fieldValueMap;
|
|
@@ -5658,13 +5658,13 @@ class JsonFormRendererComponent {
|
|
|
5658
5658
|
isObjectRecord(value) {
|
|
5659
5659
|
return !!value && typeof value === 'object' && !Array.isArray(value);
|
|
5660
5660
|
}
|
|
5661
|
-
async buildFieldValueMap(values) {
|
|
5661
|
+
async buildFieldValueMap(values, options = {}) {
|
|
5662
5662
|
const schema = this.engine?.getSchema();
|
|
5663
5663
|
if (!schema)
|
|
5664
5664
|
return {};
|
|
5665
|
-
return this.buildFieldValueMapForSchema(schema, values);
|
|
5665
|
+
return this.buildFieldValueMapForSchema(schema, values, options);
|
|
5666
5666
|
}
|
|
5667
|
-
async buildFieldValueMapForSchema(schema, valuesScope) {
|
|
5667
|
+
async buildFieldValueMapForSchema(schema, valuesScope, options = {}) {
|
|
5668
5668
|
const mapped = {};
|
|
5669
5669
|
for (const field of schema.fields) {
|
|
5670
5670
|
if (!field?.id || !field?.name)
|
|
@@ -5679,7 +5679,7 @@ class JsonFormRendererComponent {
|
|
|
5679
5679
|
rows.push({});
|
|
5680
5680
|
continue;
|
|
5681
5681
|
}
|
|
5682
|
-
rows.push(await this.buildFieldValueMapForSchema(itemSchema, row));
|
|
5682
|
+
rows.push(await this.buildFieldValueMapForSchema(itemSchema, row, options));
|
|
5683
5683
|
}
|
|
5684
5684
|
}
|
|
5685
5685
|
mapped[field.id] = {
|
|
@@ -5691,7 +5691,10 @@ class JsonFormRendererComponent {
|
|
|
5691
5691
|
if (field.type === 'file') {
|
|
5692
5692
|
const fileValue = {
|
|
5693
5693
|
fieldName: field.name,
|
|
5694
|
-
fieldValue:
|
|
5694
|
+
fieldValue: options.normalizeFileFieldsForSubmit
|
|
5695
|
+
? this.normalizeFileFieldSubmitValue(rawValue)
|
|
5696
|
+
: rawValue,
|
|
5697
|
+
...(options.normalizeFileFieldsForSubmit ? { fieldType: field.type } : {}),
|
|
5695
5698
|
...(await this.buildFileFieldMetadata(rawValue))
|
|
5696
5699
|
};
|
|
5697
5700
|
mapped[field.id] = fileValue;
|
|
@@ -5779,6 +5782,18 @@ class JsonFormRendererComponent {
|
|
|
5779
5782
|
return undefined;
|
|
5780
5783
|
return values.length === 1 ? values[0] : values;
|
|
5781
5784
|
}
|
|
5785
|
+
normalizeFileFieldSubmitValue(value) {
|
|
5786
|
+
if (this.isFileList(value)) {
|
|
5787
|
+
return Array.from(value);
|
|
5788
|
+
}
|
|
5789
|
+
if (Array.isArray(value)) {
|
|
5790
|
+
return [...value];
|
|
5791
|
+
}
|
|
5792
|
+
if (value === null || value === undefined) {
|
|
5793
|
+
return [];
|
|
5794
|
+
}
|
|
5795
|
+
return [value];
|
|
5796
|
+
}
|
|
5782
5797
|
getUploadedFileRefs(value) {
|
|
5783
5798
|
if (this.isUploadedFileRef(value))
|
|
5784
5799
|
return [value];
|
|
@@ -15425,6 +15440,8 @@ class DataPanelComponent {
|
|
|
15425
15440
|
selectionFieldId;
|
|
15426
15441
|
selectionMatchPath;
|
|
15427
15442
|
childRowsPath;
|
|
15443
|
+
formatNumericOptionLabels = false;
|
|
15444
|
+
optionLabelPrefixPath;
|
|
15428
15445
|
rootPathOptions = [];
|
|
15429
15446
|
rowPathOptions = [];
|
|
15430
15447
|
// Value/Image Config
|
|
@@ -15665,6 +15682,8 @@ class DataPanelComponent {
|
|
|
15665
15682
|
this.selectionFieldId = undefined;
|
|
15666
15683
|
this.selectionMatchPath = undefined;
|
|
15667
15684
|
this.childRowsPath = undefined;
|
|
15685
|
+
this.formatNumericOptionLabels = false;
|
|
15686
|
+
this.optionLabelPrefixPath = undefined;
|
|
15668
15687
|
this.rootPathOptions = [];
|
|
15669
15688
|
this.rowPathOptions = [];
|
|
15670
15689
|
}
|
|
@@ -15698,13 +15717,15 @@ class DataPanelComponent {
|
|
|
15698
15717
|
this.staticOptions = (d.staticOptions || []).map(option => ({ ...option }));
|
|
15699
15718
|
this.staticValue = d.staticValue !== undefined ? d.staticValue : this.readScalarTargetValue();
|
|
15700
15719
|
this.selectedSourceId = d.datasourceId;
|
|
15701
|
-
this.labelKey = d.
|
|
15702
|
-
this.valueKey = d.
|
|
15720
|
+
this.labelKey = d.labelKey;
|
|
15721
|
+
this.valueKey = d.valueKey;
|
|
15703
15722
|
this.rowsPath = d.rowsPath;
|
|
15704
15723
|
this.rowSelectionMode = d.rowSelectionMode ?? 'first';
|
|
15705
15724
|
this.selectionFieldId = d.selectionFieldId;
|
|
15706
15725
|
this.selectionMatchPath = d.selectionMatchPath;
|
|
15707
15726
|
this.childRowsPath = d.childRowsPath;
|
|
15727
|
+
this.formatNumericOptionLabels = d.formatNumericOptionLabels === true;
|
|
15728
|
+
this.optionLabelPrefixPath = d.optionLabelPrefixPath;
|
|
15708
15729
|
// Search
|
|
15709
15730
|
this.searchEnabled = !!d.searchEnabled;
|
|
15710
15731
|
this.optionsLimit = d.optionsLimit;
|
|
@@ -15756,8 +15777,12 @@ class DataPanelComponent {
|
|
|
15756
15777
|
labelKey: this.sourceType === 'source' ? this.labelKey : undefined,
|
|
15757
15778
|
valueKey: this.sourceType === 'source' ? this.valueKey : undefined,
|
|
15758
15779
|
rowsPath: this.sourceType === 'source' ? this.normalizedRowsPath() : undefined,
|
|
15759
|
-
|
|
15760
|
-
|
|
15780
|
+
formatNumericOptionLabels: this.shouldPersistOptionLabelFormatting()
|
|
15781
|
+
? this.formatNumericOptionLabels
|
|
15782
|
+
: undefined,
|
|
15783
|
+
optionLabelPrefixPath: this.shouldPersistOptionLabelFormatting()
|
|
15784
|
+
? this.optionLabelPrefixPath
|
|
15785
|
+
: undefined,
|
|
15761
15786
|
rowSelectionMode: this.sourceType === 'source' && this.bindingShape === 'scalar' && this.rowSelectionMode === 'selected'
|
|
15762
15787
|
? 'selected'
|
|
15763
15788
|
: this.sourceType === 'source' && this.bindingShape === 'list' && this.rowSelectionMode === 'selected'
|
|
@@ -15866,6 +15891,9 @@ class DataPanelComponent {
|
|
|
15866
15891
|
showOptionMappingControls() {
|
|
15867
15892
|
return this.sourceType === 'source' && this.widgetType !== 'table' && this.usesOptionMapping();
|
|
15868
15893
|
}
|
|
15894
|
+
showOptionLabelFormattingControls() {
|
|
15895
|
+
return this.widgetType === 'select' && this.usesOptionMapping();
|
|
15896
|
+
}
|
|
15869
15897
|
showStaticOptionsEditor() {
|
|
15870
15898
|
return this.sourceType === 'static' && this.widgetType !== 'table' && this.usesOptionMapping();
|
|
15871
15899
|
}
|
|
@@ -15916,17 +15944,8 @@ class DataPanelComponent {
|
|
|
15916
15944
|
const sample = this.extractPreviewRows(this.previewRows, this.effectiveRowsPath())[0];
|
|
15917
15945
|
return sample ? collectArrayPaths(sample) : [];
|
|
15918
15946
|
}
|
|
15919
|
-
|
|
15920
|
-
|
|
15921
|
-
return false;
|
|
15922
|
-
return !!this.normalizedRowsPath() || !!this.childRowsPath || hasPathSyntax$1(this.labelKey);
|
|
15923
|
-
}
|
|
15924
|
-
shouldPersistStructuredValuePath() {
|
|
15925
|
-
if (!this.valueKey)
|
|
15926
|
-
return false;
|
|
15927
|
-
if (!this.usesOptionMapping())
|
|
15928
|
-
return true;
|
|
15929
|
-
return !!this.normalizedRowsPath() || !!this.childRowsPath || hasPathSyntax$1(this.valueKey);
|
|
15947
|
+
shouldPersistOptionLabelFormatting() {
|
|
15948
|
+
return this.widgetType === 'select' && this.usesOptionMapping();
|
|
15930
15949
|
}
|
|
15931
15950
|
usesOptionMapping() {
|
|
15932
15951
|
return this.bindingShape === 'list' || this.widgetType === 'search';
|
|
@@ -16247,6 +16266,38 @@ class DataPanelComponent {
|
|
|
16247
16266
|
</div>
|
|
16248
16267
|
</div>
|
|
16249
16268
|
|
|
16269
|
+
<div class="rounded-lg border border-gray-200 bg-white p-3" *ngIf="showOptionLabelFormattingControls()">
|
|
16270
|
+
<div class="text-xs font-semibold uppercase tracking-wide text-gray-500">Amount Display</div>
|
|
16271
|
+
<div class="mt-1 text-[11px] text-gray-500">Format the visible dropdown label without changing the stored option value.</div>
|
|
16272
|
+
|
|
16273
|
+
<label class="mt-3 flex items-center gap-2 text-sm text-gray-700">
|
|
16274
|
+
<input
|
|
16275
|
+
type="checkbox"
|
|
16276
|
+
[checked]="formatNumericOptionLabels"
|
|
16277
|
+
(change)="formatNumericOptionLabels = $any($event.target).checked; emitChange()"
|
|
16278
|
+
class="rounded border-gray-300 text-blue-600 focus:ring-blue-500">
|
|
16279
|
+
<span>Show thousand separators for numeric labels</span>
|
|
16280
|
+
</label>
|
|
16281
|
+
|
|
16282
|
+
<div class="mt-3 flex flex-col gap-1">
|
|
16283
|
+
<label class="text-xs font-medium text-gray-500">Prefix Key</label>
|
|
16284
|
+
<input
|
|
16285
|
+
[attr.list]="'prefix-cols-' + config.id"
|
|
16286
|
+
[(ngModel)]="optionLabelPrefixPath"
|
|
16287
|
+
(ngModelChange)="emitChange()"
|
|
16288
|
+
[placeholder]="effectiveRowsPath() ? 'e.g. currency or meta.currency' : 'e.g. currency'"
|
|
16289
|
+
class="h-8 w-full rounded-md border border-gray-300 px-2 text-sm focus:border-blue-500 focus:ring-1 focus:ring-blue-500 outline-none">
|
|
16290
|
+
<datalist [id]="'prefix-cols-' + config.id">
|
|
16291
|
+
<option *ngFor="let col of sourceColumns" [value]="col.name"></option>
|
|
16292
|
+
<option *ngFor="let path of availableRowPaths()" [value]="path"></option>
|
|
16293
|
+
<option *ngFor="let path of availableRootPaths()" [value]="path"></option>
|
|
16294
|
+
</datalist>
|
|
16295
|
+
<p class="text-[10px] text-gray-400">
|
|
16296
|
+
Reads a display-only prefix from the datasource or event payload. The select still stores only the mapped value.
|
|
16297
|
+
</p>
|
|
16298
|
+
</div>
|
|
16299
|
+
</div>
|
|
16300
|
+
|
|
16250
16301
|
<div class="rounded-lg border border-gray-200 bg-white p-3" *ngIf="showScalarMappingControls()">
|
|
16251
16302
|
<div class="text-xs font-semibold uppercase tracking-wide text-gray-500">Value Mapping</div>
|
|
16252
16303
|
<div class="mt-1 text-[11px] text-gray-500">Choose the value path and how this field picks a row from the datasource.</div>
|
|
@@ -16834,6 +16885,38 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImpo
|
|
|
16834
16885
|
</div>
|
|
16835
16886
|
</div>
|
|
16836
16887
|
|
|
16888
|
+
<div class="rounded-lg border border-gray-200 bg-white p-3" *ngIf="showOptionLabelFormattingControls()">
|
|
16889
|
+
<div class="text-xs font-semibold uppercase tracking-wide text-gray-500">Amount Display</div>
|
|
16890
|
+
<div class="mt-1 text-[11px] text-gray-500">Format the visible dropdown label without changing the stored option value.</div>
|
|
16891
|
+
|
|
16892
|
+
<label class="mt-3 flex items-center gap-2 text-sm text-gray-700">
|
|
16893
|
+
<input
|
|
16894
|
+
type="checkbox"
|
|
16895
|
+
[checked]="formatNumericOptionLabels"
|
|
16896
|
+
(change)="formatNumericOptionLabels = $any($event.target).checked; emitChange()"
|
|
16897
|
+
class="rounded border-gray-300 text-blue-600 focus:ring-blue-500">
|
|
16898
|
+
<span>Show thousand separators for numeric labels</span>
|
|
16899
|
+
</label>
|
|
16900
|
+
|
|
16901
|
+
<div class="mt-3 flex flex-col gap-1">
|
|
16902
|
+
<label class="text-xs font-medium text-gray-500">Prefix Key</label>
|
|
16903
|
+
<input
|
|
16904
|
+
[attr.list]="'prefix-cols-' + config.id"
|
|
16905
|
+
[(ngModel)]="optionLabelPrefixPath"
|
|
16906
|
+
(ngModelChange)="emitChange()"
|
|
16907
|
+
[placeholder]="effectiveRowsPath() ? 'e.g. currency or meta.currency' : 'e.g. currency'"
|
|
16908
|
+
class="h-8 w-full rounded-md border border-gray-300 px-2 text-sm focus:border-blue-500 focus:ring-1 focus:ring-blue-500 outline-none">
|
|
16909
|
+
<datalist [id]="'prefix-cols-' + config.id">
|
|
16910
|
+
<option *ngFor="let col of sourceColumns" [value]="col.name"></option>
|
|
16911
|
+
<option *ngFor="let path of availableRowPaths()" [value]="path"></option>
|
|
16912
|
+
<option *ngFor="let path of availableRootPaths()" [value]="path"></option>
|
|
16913
|
+
</datalist>
|
|
16914
|
+
<p class="text-[10px] text-gray-400">
|
|
16915
|
+
Reads a display-only prefix from the datasource or event payload. The select still stores only the mapped value.
|
|
16916
|
+
</p>
|
|
16917
|
+
</div>
|
|
16918
|
+
</div>
|
|
16919
|
+
|
|
16837
16920
|
<div class="rounded-lg border border-gray-200 bg-white p-3" *ngIf="showScalarMappingControls()">
|
|
16838
16921
|
<div class="text-xs font-semibold uppercase tracking-wide text-gray-500">Value Mapping</div>
|
|
16839
16922
|
<div class="mt-1 text-[11px] text-gray-500">Choose the value path and how this field picks a row from the datasource.</div>
|
|
@@ -31748,18 +31831,15 @@ class DefaultDataProvider extends DataProvider {
|
|
|
31748
31831
|
return runtimeOptions;
|
|
31749
31832
|
}
|
|
31750
31833
|
const cfg = getEffectiveDataConfig(field);
|
|
31751
|
-
|
|
31752
|
-
|
|
31753
|
-
rows = this.resolveCollectionRows(rows, cfg, engine);
|
|
31754
|
-
// 2. Apply Filters
|
|
31755
|
-
rows = this.applyRowFilters(rows, cfg.filters, engine);
|
|
31834
|
+
const rowContexts = await this.getOptionRowContexts(cfg, field, engine);
|
|
31835
|
+
const filteredContexts = this.applyOptionRowFilters(rowContexts, cfg.filters, engine);
|
|
31756
31836
|
// 3. Map to Options
|
|
31757
31837
|
if (cfg.type === 'static') {
|
|
31758
|
-
return
|
|
31838
|
+
return filteredContexts.map(context => this.mapContextToOption(context, 'label', 'value', cfg));
|
|
31759
31839
|
}
|
|
31760
|
-
const labelKey = cfg.
|
|
31761
|
-
const valueKey = cfg.
|
|
31762
|
-
return
|
|
31840
|
+
const labelKey = cfg.labelKey || 'label';
|
|
31841
|
+
const valueKey = cfg.valueKey || 'value';
|
|
31842
|
+
return filteredContexts.map(context => this.mapContextToOption(context, labelKey, valueKey, cfg));
|
|
31763
31843
|
}
|
|
31764
31844
|
async queryOptions(field, query, engine) {
|
|
31765
31845
|
const runtimeOptions = await this.getRuntimeOptions(field, engine);
|
|
@@ -31866,19 +31946,24 @@ class DefaultDataProvider extends DataProvider {
|
|
|
31866
31946
|
}
|
|
31867
31947
|
async getValue(field, engine) {
|
|
31868
31948
|
const cfg = getEffectiveDataConfig(field);
|
|
31949
|
+
const currentValue = this.getCurrentFieldValue(field, engine);
|
|
31869
31950
|
if (cfg.type === 'static') {
|
|
31870
31951
|
if (cfg.staticValue !== undefined)
|
|
31871
31952
|
return cfg.staticValue;
|
|
31872
|
-
return field.defaultValue;
|
|
31953
|
+
return currentValue !== undefined ? currentValue : field.defaultValue;
|
|
31873
31954
|
}
|
|
31874
31955
|
let rows = await this.getRawRows(cfg, field, engine);
|
|
31875
31956
|
rows = this.resolveCollectionRows(rows, cfg, engine);
|
|
31876
31957
|
rows = this.applyRowFilters(rows, cfg.filters, engine);
|
|
31877
31958
|
if (!rows || rows.length === 0) {
|
|
31878
|
-
return field.defaultValue;
|
|
31959
|
+
return currentValue !== undefined ? currentValue : field.defaultValue;
|
|
31879
31960
|
}
|
|
31880
|
-
const
|
|
31881
|
-
|
|
31961
|
+
const selectedRow = this.selectScalarRow(rows, cfg, engine);
|
|
31962
|
+
if (cfg.rowSelectionMode === 'selected' && !selectedRow) {
|
|
31963
|
+
return currentValue !== undefined ? currentValue : field.defaultValue;
|
|
31964
|
+
}
|
|
31965
|
+
const row = selectedRow ?? rows[0];
|
|
31966
|
+
const resolvedPath = cfg.valueKey;
|
|
31882
31967
|
const resolvedValue = resolvePathValue(row, resolvedPath);
|
|
31883
31968
|
if (resolvedPath && resolvedValue !== undefined) {
|
|
31884
31969
|
return resolvedValue;
|
|
@@ -31922,12 +32007,30 @@ class DefaultDataProvider extends DataProvider {
|
|
|
31922
32007
|
return [];
|
|
31923
32008
|
}
|
|
31924
32009
|
}
|
|
32010
|
+
getCurrentFieldValue(field, engine) {
|
|
32011
|
+
return field.name && engine ? engine.getValue(field.name) : undefined;
|
|
32012
|
+
}
|
|
31925
32013
|
async getGlobalRows(cfg) {
|
|
31926
32014
|
if (!cfg.datasourceId)
|
|
31927
32015
|
return [];
|
|
31928
32016
|
const result = await this.client.query(cfg.datasourceId);
|
|
31929
32017
|
return result.rows;
|
|
31930
32018
|
}
|
|
32019
|
+
async getOptionRowContexts(cfg, field, engine) {
|
|
32020
|
+
const rows = await this.getRawRows(cfg, field, engine);
|
|
32021
|
+
return this.resolveCollectionRowContexts(rows, cfg, engine);
|
|
32022
|
+
}
|
|
32023
|
+
resolveCollectionRowContexts(rows, cfg, engine) {
|
|
32024
|
+
const parentContexts = this.extractRowContexts(rows, cfg.rowsPath);
|
|
32025
|
+
if (cfg.rowSelectionMode === 'selected' && cfg.childRowsPath) {
|
|
32026
|
+
const selectedParentContext = this.selectOptionContext(parentContexts, cfg, engine);
|
|
32027
|
+
if (!selectedParentContext) {
|
|
32028
|
+
return [];
|
|
32029
|
+
}
|
|
32030
|
+
return this.extractRowContexts([selectedParentContext.row], cfg.childRowsPath, selectedParentContext);
|
|
32031
|
+
}
|
|
32032
|
+
return parentContexts;
|
|
32033
|
+
}
|
|
31931
32034
|
resolveCollectionRows(rows, cfg, engine) {
|
|
31932
32035
|
const parentRows = this.extractRows(rows, cfg.rowsPath);
|
|
31933
32036
|
if (cfg.rowSelectionMode === 'selected' && cfg.childRowsPath) {
|
|
@@ -31942,6 +32045,38 @@ class DefaultDataProvider extends DataProvider {
|
|
|
31942
32045
|
}
|
|
31943
32046
|
return parentRows;
|
|
31944
32047
|
}
|
|
32048
|
+
extractRowContexts(rows, path, sourceContext) {
|
|
32049
|
+
const normalizedPath = path?.trim();
|
|
32050
|
+
if (!normalizedPath) {
|
|
32051
|
+
return rows.map(row => ({
|
|
32052
|
+
row,
|
|
32053
|
+
parentRow: sourceContext?.row,
|
|
32054
|
+
sourceRow: sourceContext?.sourceRow ?? row
|
|
32055
|
+
}));
|
|
32056
|
+
}
|
|
32057
|
+
const flattened = [];
|
|
32058
|
+
for (const row of rows) {
|
|
32059
|
+
const resolved = resolvePathValue(row, normalizedPath);
|
|
32060
|
+
if (Array.isArray(resolved)) {
|
|
32061
|
+
for (const entry of resolved) {
|
|
32062
|
+
flattened.push({
|
|
32063
|
+
row: this.toRowRecord(entry),
|
|
32064
|
+
parentRow: sourceContext?.row,
|
|
32065
|
+
sourceRow: sourceContext?.sourceRow ?? row
|
|
32066
|
+
});
|
|
32067
|
+
}
|
|
32068
|
+
continue;
|
|
32069
|
+
}
|
|
32070
|
+
if (resolved !== undefined && resolved !== null) {
|
|
32071
|
+
flattened.push({
|
|
32072
|
+
row: this.toRowRecord(resolved),
|
|
32073
|
+
parentRow: sourceContext?.row,
|
|
32074
|
+
sourceRow: sourceContext?.sourceRow ?? row
|
|
32075
|
+
});
|
|
32076
|
+
}
|
|
32077
|
+
}
|
|
32078
|
+
return flattened;
|
|
32079
|
+
}
|
|
31945
32080
|
extractRows(rows, path) {
|
|
31946
32081
|
const normalizedPath = path?.trim();
|
|
31947
32082
|
if (!normalizedPath) {
|
|
@@ -31979,36 +32114,57 @@ class DefaultDataProvider extends DataProvider {
|
|
|
31979
32114
|
}
|
|
31980
32115
|
return rows.find(row => valuesMatch(resolvePathValue(row, cfg.selectionMatchPath), selectorValue));
|
|
31981
32116
|
}
|
|
32117
|
+
selectOptionContext(contexts, cfg, engine) {
|
|
32118
|
+
if (contexts.length === 0)
|
|
32119
|
+
return undefined;
|
|
32120
|
+
if (cfg.rowSelectionMode !== 'selected' || !cfg.selectionFieldId || !cfg.selectionMatchPath || !engine) {
|
|
32121
|
+
return contexts[0];
|
|
32122
|
+
}
|
|
32123
|
+
const schema = engine.getSchema();
|
|
32124
|
+
const selectorField = schema.fields.find(candidate => candidate.id === cfg.selectionFieldId);
|
|
32125
|
+
if (!selectorField) {
|
|
32126
|
+
return undefined;
|
|
32127
|
+
}
|
|
32128
|
+
const selectorValue = engine.getValue(selectorField.name);
|
|
32129
|
+
if (selectorValue === undefined || selectorValue === null || selectorValue === '') {
|
|
32130
|
+
return undefined;
|
|
32131
|
+
}
|
|
32132
|
+
return contexts.find(context => valuesMatch(resolvePathValue(context.row, cfg.selectionMatchPath), selectorValue));
|
|
32133
|
+
}
|
|
31982
32134
|
applyRowFilters(rows, filters, engine) {
|
|
31983
32135
|
if (!filters || filters.length === 0)
|
|
31984
32136
|
return rows;
|
|
31985
|
-
return rows.filter(row =>
|
|
31986
|
-
|
|
31987
|
-
|
|
31988
|
-
|
|
31989
|
-
|
|
31990
|
-
|
|
31991
|
-
|
|
31992
|
-
|
|
31993
|
-
|
|
31994
|
-
|
|
31995
|
-
|
|
31996
|
-
|
|
31997
|
-
|
|
31998
|
-
|
|
31999
|
-
|
|
32000
|
-
|
|
32001
|
-
|
|
32002
|
-
|
|
32003
|
-
|
|
32004
|
-
|
|
32005
|
-
|
|
32006
|
-
|
|
32007
|
-
|
|
32008
|
-
|
|
32009
|
-
|
|
32010
|
-
|
|
32011
|
-
|
|
32137
|
+
return rows.filter(row => this.matchesRowFilters(row, filters, engine));
|
|
32138
|
+
}
|
|
32139
|
+
applyOptionRowFilters(contexts, filters, engine) {
|
|
32140
|
+
if (!filters || filters.length === 0)
|
|
32141
|
+
return contexts;
|
|
32142
|
+
return contexts.filter(context => this.matchesRowFilters(context.row, filters, engine));
|
|
32143
|
+
}
|
|
32144
|
+
matchesRowFilters(row, filters, engine) {
|
|
32145
|
+
return filters.every(filter => {
|
|
32146
|
+
const expected = this.resolveFilterValue(filter, engine);
|
|
32147
|
+
if (expected === undefined)
|
|
32148
|
+
return true;
|
|
32149
|
+
const actual = resolvePathValue(row, filter.column);
|
|
32150
|
+
const val = expected;
|
|
32151
|
+
switch (filter.op) {
|
|
32152
|
+
case 'eq': return actual === val;
|
|
32153
|
+
case 'neq': return actual !== val;
|
|
32154
|
+
case 'in': return Array.isArray(val) ? val.includes(actual) : actual === val;
|
|
32155
|
+
case 'contains':
|
|
32156
|
+
case 'like':
|
|
32157
|
+
return String(actual ?? '').toLowerCase().includes(String(val ?? '').toLowerCase());
|
|
32158
|
+
case 'startsWith':
|
|
32159
|
+
return String(actual ?? '').toLowerCase().startsWith(String(val ?? '').toLowerCase());
|
|
32160
|
+
case 'endsWith':
|
|
32161
|
+
return String(actual ?? '').toLowerCase().endsWith(String(val ?? '').toLowerCase());
|
|
32162
|
+
case 'gt': return (actual ?? 0) > (val ?? 0);
|
|
32163
|
+
case 'gte': return (actual ?? 0) >= (val ?? 0);
|
|
32164
|
+
case 'lt': return (actual ?? 0) < (val ?? 0);
|
|
32165
|
+
case 'lte': return (actual ?? 0) <= (val ?? 0);
|
|
32166
|
+
default: return true;
|
|
32167
|
+
}
|
|
32012
32168
|
});
|
|
32013
32169
|
}
|
|
32014
32170
|
resolveFilters(cfg, engine) {
|
|
@@ -32066,6 +32222,56 @@ class DefaultDataProvider extends DataProvider {
|
|
|
32066
32222
|
value: this.toOptionValue(resolvePathValue(row, valueKey))
|
|
32067
32223
|
};
|
|
32068
32224
|
}
|
|
32225
|
+
mapContextToOption(context, labelKey, valueKey, cfg) {
|
|
32226
|
+
const rawLabel = String(resolvePathValue(context.row, labelKey) ?? '');
|
|
32227
|
+
const prefix = this.resolveOptionLabelPrefix(context, cfg);
|
|
32228
|
+
const label = this.formatOptionLabel(rawLabel, cfg);
|
|
32229
|
+
return {
|
|
32230
|
+
label: prefix ? `${prefix} ${label}` : label,
|
|
32231
|
+
value: this.toOptionValue(resolvePathValue(context.row, valueKey))
|
|
32232
|
+
};
|
|
32233
|
+
}
|
|
32234
|
+
resolveOptionLabelPrefix(context, cfg) {
|
|
32235
|
+
const prefixPath = cfg.optionLabelPrefixPath?.trim();
|
|
32236
|
+
if (!prefixPath) {
|
|
32237
|
+
return '';
|
|
32238
|
+
}
|
|
32239
|
+
for (const candidate of [context.row, context.parentRow, context.sourceRow]) {
|
|
32240
|
+
if (!candidate)
|
|
32241
|
+
continue;
|
|
32242
|
+
const resolved = resolvePathValue(candidate, prefixPath);
|
|
32243
|
+
if (resolved === undefined || resolved === null)
|
|
32244
|
+
continue;
|
|
32245
|
+
const value = String(resolved).trim();
|
|
32246
|
+
if (value.length > 0) {
|
|
32247
|
+
return value;
|
|
32248
|
+
}
|
|
32249
|
+
}
|
|
32250
|
+
return '';
|
|
32251
|
+
}
|
|
32252
|
+
formatOptionLabel(label, cfg) {
|
|
32253
|
+
if (!cfg.formatNumericOptionLabels) {
|
|
32254
|
+
return label;
|
|
32255
|
+
}
|
|
32256
|
+
const trimmed = label.trim();
|
|
32257
|
+
if (!trimmed) {
|
|
32258
|
+
return label;
|
|
32259
|
+
}
|
|
32260
|
+
const normalized = trimmed.replace(/,/g, '');
|
|
32261
|
+
if (!/^-?\d+(\.\d+)?$/.test(normalized)) {
|
|
32262
|
+
return label;
|
|
32263
|
+
}
|
|
32264
|
+
const numericValue = Number(normalized);
|
|
32265
|
+
if (!Number.isFinite(numericValue)) {
|
|
32266
|
+
return label;
|
|
32267
|
+
}
|
|
32268
|
+
const fractionPart = normalized.split('.')[1];
|
|
32269
|
+
return new Intl.NumberFormat(undefined, {
|
|
32270
|
+
useGrouping: true,
|
|
32271
|
+
minimumFractionDigits: fractionPart?.length ?? 0,
|
|
32272
|
+
maximumFractionDigits: fractionPart?.length ?? 0
|
|
32273
|
+
}).format(numericValue);
|
|
32274
|
+
}
|
|
32069
32275
|
async getRuntimeOptions(field, engine) {
|
|
32070
32276
|
if (!engine)
|
|
32071
32277
|
return undefined;
|
|
@@ -32080,7 +32286,7 @@ class DefaultDataProvider extends DataProvider {
|
|
|
32080
32286
|
return cfg.type === 'source' || cfg.type === 'global' || cfg.type === 'api';
|
|
32081
32287
|
}
|
|
32082
32288
|
shouldUseLocalResolution(cfg) {
|
|
32083
|
-
if (cfg.rowsPath || cfg.
|
|
32289
|
+
if (cfg.rowsPath || cfg.optionLabelPrefixPath || cfg.rowSelectionMode || cfg.selectionFieldId || cfg.selectionMatchPath || cfg.childRowsPath) {
|
|
32084
32290
|
return true;
|
|
32085
32291
|
}
|
|
32086
32292
|
if (hasPathSyntax(cfg.labelKey) || hasPathSyntax(cfg.valueKey)) {
|
|
@@ -32471,7 +32677,6 @@ class TextFieldWidgetComponent {
|
|
|
32471
32677
|
dataConfig.type ?? '',
|
|
32472
32678
|
sourceKey,
|
|
32473
32679
|
String(dataConfig.valueKey ?? ''),
|
|
32474
|
-
String(dataConfig.valuePath ?? ''),
|
|
32475
32680
|
String(dataConfig.rowsPath ?? ''),
|
|
32476
32681
|
String(dataConfig.rowSelectionMode ?? ''),
|
|
32477
32682
|
String(dataConfig.selectionFieldId ?? ''),
|
|
@@ -33266,12 +33471,14 @@ class SelectWidgetComponent {
|
|
|
33266
33471
|
loading = false;
|
|
33267
33472
|
loadError = null;
|
|
33268
33473
|
options = [];
|
|
33474
|
+
rawOptions = [];
|
|
33269
33475
|
searchTerms$ = new Subject();
|
|
33270
33476
|
requestId = 0;
|
|
33271
33477
|
currentSearchTerm = '';
|
|
33272
33478
|
runtimeManagedField = false;
|
|
33273
33479
|
runtimeOptionsLoaded = false;
|
|
33274
33480
|
dependencyValueSnapshot = new Map();
|
|
33481
|
+
displayDependencyValueSnapshot = new Map();
|
|
33275
33482
|
cachedStyleSource;
|
|
33276
33483
|
cachedWrapperStyles = { width: '100%' };
|
|
33277
33484
|
cachedControlCssVars = this.toCssVarMap({});
|
|
@@ -33313,7 +33520,7 @@ class SelectWidgetComponent {
|
|
|
33313
33520
|
return;
|
|
33314
33521
|
this.runtimeOptionsLoaded = false;
|
|
33315
33522
|
this.currentSearchTerm = '';
|
|
33316
|
-
this.
|
|
33523
|
+
this.setOptionsFromRaw([]);
|
|
33317
33524
|
void this.loadOptions(this.currentSearchTerm);
|
|
33318
33525
|
});
|
|
33319
33526
|
}
|
|
@@ -33335,7 +33542,7 @@ class SelectWidgetComponent {
|
|
|
33335
33542
|
this.runtimeManagedField = this.runtimeFieldDataAccessRegistry.hasFieldAccess(this.config, this.engine);
|
|
33336
33543
|
if (!this.runtimeManagedField) {
|
|
33337
33544
|
if (this.areApiCallsSuppressed() && this.hasSelectedValue()) {
|
|
33338
|
-
this.
|
|
33545
|
+
this.setOptionsFromRaw(this.withSelectedValueFallbackOptions([]));
|
|
33339
33546
|
}
|
|
33340
33547
|
else {
|
|
33341
33548
|
void this.loadOptions(this.currentSearchTerm);
|
|
@@ -33344,7 +33551,7 @@ class SelectWidgetComponent {
|
|
|
33344
33551
|
}
|
|
33345
33552
|
else if (this.hasSelectedValue()) {
|
|
33346
33553
|
if (this.areApiCallsSuppressed()) {
|
|
33347
|
-
this.
|
|
33554
|
+
this.setOptionsFromRaw(this.withSelectedValueFallbackOptions([]));
|
|
33348
33555
|
this.runtimeOptionsLoaded = true;
|
|
33349
33556
|
}
|
|
33350
33557
|
else {
|
|
@@ -33353,32 +33560,38 @@ class SelectWidgetComponent {
|
|
|
33353
33560
|
});
|
|
33354
33561
|
}
|
|
33355
33562
|
}
|
|
33356
|
-
const dependencyIds = this.getDependencyFieldIds();
|
|
33357
33563
|
if (this.engine) {
|
|
33358
33564
|
const initialValues = this.engine.getValues();
|
|
33359
|
-
this.
|
|
33565
|
+
this.seedValueSnapshotFromValues(this.dependencyValueSnapshot, this.getDependencyFieldIds(), initialValues);
|
|
33566
|
+
this.seedValueSnapshotFromValues(this.displayDependencyValueSnapshot, this.getDisplayDependencyFieldIds(), initialValues);
|
|
33360
33567
|
this.engine.valueChanges$
|
|
33361
33568
|
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
33362
33569
|
.subscribe(values => {
|
|
33363
33570
|
this.syncEnabledState();
|
|
33364
33571
|
if (this.areApiCallsSuppressed()) {
|
|
33365
|
-
this.
|
|
33572
|
+
this.setOptionsFromRaw(this.withSelectedValueFallbackOptions(this.rawOptions));
|
|
33366
33573
|
this.cdr.markForCheck();
|
|
33367
33574
|
}
|
|
33368
|
-
if (dependencyIds.length === 0)
|
|
33369
|
-
return;
|
|
33370
33575
|
const schema = this.engine.getSchema();
|
|
33371
|
-
const
|
|
33372
|
-
|
|
33373
|
-
|
|
33374
|
-
|
|
33576
|
+
const dependencyIds = this.getDependencyFieldIds();
|
|
33577
|
+
const displayDependencyIds = this.getDisplayDependencyFieldIds();
|
|
33578
|
+
const dependencyNames = this.resolveFieldNames(schema.fields, dependencyIds);
|
|
33579
|
+
const displayDependencyNames = this.resolveFieldNames(schema.fields, displayDependencyIds);
|
|
33580
|
+
if (displayDependencyNames.length > 0 && this.haveFieldValuesChanged(this.displayDependencyValueSnapshot, displayDependencyNames, values)) {
|
|
33581
|
+
this.applyDisplayFormatting();
|
|
33582
|
+
this.syncStoredFieldLabel(this.control.value);
|
|
33583
|
+
this.cdr.markForCheck();
|
|
33584
|
+
}
|
|
33585
|
+
if (dependencyNames.length === 0)
|
|
33586
|
+
return;
|
|
33587
|
+
if (!this.haveFieldValuesChanged(this.dependencyValueSnapshot, dependencyNames, values))
|
|
33375
33588
|
return;
|
|
33376
33589
|
this.runtimeFieldDataAccessRegistry.invalidateFieldAndDescendants(this.engine, this.config.id);
|
|
33377
33590
|
if (this.hasSelectedValue()) {
|
|
33378
33591
|
this.control.setValue(null);
|
|
33379
33592
|
}
|
|
33380
33593
|
this.currentSearchTerm = '';
|
|
33381
|
-
this.
|
|
33594
|
+
this.setOptionsFromRaw([]);
|
|
33382
33595
|
this.runtimeOptionsLoaded = false;
|
|
33383
33596
|
if (!this.runtimeManagedField) {
|
|
33384
33597
|
void this.loadOptions(this.currentSearchTerm);
|
|
@@ -33521,6 +33734,10 @@ class SelectWidgetComponent {
|
|
|
33521
33734
|
return [];
|
|
33522
33735
|
return cfg.dependsOn.map(d => d.fieldId).filter((id) => !!id);
|
|
33523
33736
|
}
|
|
33737
|
+
getDisplayDependencyFieldIds() {
|
|
33738
|
+
const prefixFieldId = this.config.dataConfig?.optionLabelPrefixFieldId;
|
|
33739
|
+
return prefixFieldId ? [prefixFieldId] : [];
|
|
33740
|
+
}
|
|
33524
33741
|
toCssVarMap(controlStyles) {
|
|
33525
33742
|
const vars = {
|
|
33526
33743
|
'--fd-select-border-color': OUTLINED_FIELD_IDLE_BORDER_COLOR,
|
|
@@ -33589,7 +33806,7 @@ class SelectWidgetComponent {
|
|
|
33589
33806
|
opts = await this.dataProvider.getOptions(this.config, this.engine);
|
|
33590
33807
|
}
|
|
33591
33808
|
if (this.requestId === reqId) {
|
|
33592
|
-
this.
|
|
33809
|
+
this.setOptionsFromRaw(this.withSelectedValueFallbackOptions(opts));
|
|
33593
33810
|
this.syncStoredFieldLabel(this.control.value);
|
|
33594
33811
|
this.loading = false;
|
|
33595
33812
|
this.loadError = null;
|
|
@@ -33601,7 +33818,7 @@ class SelectWidgetComponent {
|
|
|
33601
33818
|
this.loading = false;
|
|
33602
33819
|
this.loadError = 'Failed to load options.';
|
|
33603
33820
|
if (!isSearch) {
|
|
33604
|
-
this.
|
|
33821
|
+
this.setOptionsFromRaw(this.withSelectedValueFallbackOptions(this.config.staticOptions || []));
|
|
33605
33822
|
}
|
|
33606
33823
|
this.cdr.markForCheck();
|
|
33607
33824
|
}
|
|
@@ -33622,7 +33839,7 @@ class SelectWidgetComponent {
|
|
|
33622
33839
|
if (this.runtimeOptionsLoaded && this.options.length > 0)
|
|
33623
33840
|
return;
|
|
33624
33841
|
if (this.areApiCallsSuppressed()) {
|
|
33625
|
-
this.
|
|
33842
|
+
this.setOptionsFromRaw(this.withSelectedValueFallbackOptions(this.rawOptions));
|
|
33626
33843
|
this.runtimeOptionsLoaded = true;
|
|
33627
33844
|
return;
|
|
33628
33845
|
}
|
|
@@ -33631,25 +33848,29 @@ class SelectWidgetComponent {
|
|
|
33631
33848
|
this.runtimeOptionsLoaded = true;
|
|
33632
33849
|
}
|
|
33633
33850
|
}
|
|
33634
|
-
|
|
33635
|
-
if (!this.engine ||
|
|
33851
|
+
seedValueSnapshotFromValues(snapshot, fieldIds, values) {
|
|
33852
|
+
if (!this.engine || fieldIds.length === 0)
|
|
33636
33853
|
return;
|
|
33637
|
-
const
|
|
33638
|
-
|
|
33639
|
-
.filter(field => dependencyIds.includes(field.id))
|
|
33640
|
-
.map(field => field.name);
|
|
33641
|
-
for (const depName of depNames) {
|
|
33642
|
-
this.dependencyValueSnapshot.set(depName, values[depName]);
|
|
33854
|
+
for (const fieldName of this.resolveFieldNames(this.engine.getSchema().fields, fieldIds)) {
|
|
33855
|
+
snapshot.set(fieldName, values[fieldName]);
|
|
33643
33856
|
}
|
|
33644
33857
|
}
|
|
33645
|
-
|
|
33858
|
+
resolveFieldNames(fields, fieldIds) {
|
|
33859
|
+
if (fieldIds.length === 0) {
|
|
33860
|
+
return [];
|
|
33861
|
+
}
|
|
33862
|
+
return fields
|
|
33863
|
+
.filter(field => fieldIds.includes(field.id))
|
|
33864
|
+
.map(field => field.name);
|
|
33865
|
+
}
|
|
33866
|
+
haveFieldValuesChanged(snapshot, fieldNames, values) {
|
|
33646
33867
|
let changed = false;
|
|
33647
|
-
for (const
|
|
33648
|
-
const previousValue =
|
|
33649
|
-
const nextValue = values[
|
|
33868
|
+
for (const fieldName of fieldNames) {
|
|
33869
|
+
const previousValue = snapshot.get(fieldName);
|
|
33870
|
+
const nextValue = values[fieldName];
|
|
33650
33871
|
if (!Object.is(previousValue, nextValue)) {
|
|
33651
33872
|
changed = true;
|
|
33652
|
-
|
|
33873
|
+
snapshot.set(fieldName, nextValue);
|
|
33653
33874
|
}
|
|
33654
33875
|
}
|
|
33655
33876
|
return changed;
|
|
@@ -33661,12 +33882,19 @@ class SelectWidgetComponent {
|
|
|
33661
33882
|
this.syncEnabledState();
|
|
33662
33883
|
this.runtimeManagedField = this.runtimeFieldDataAccessRegistry.hasFieldAccess(this.config, this.engine);
|
|
33663
33884
|
this.runtimeOptionsLoaded = false;
|
|
33664
|
-
this.
|
|
33885
|
+
this.dependencyValueSnapshot.clear();
|
|
33886
|
+
this.displayDependencyValueSnapshot.clear();
|
|
33887
|
+
this.setOptionsFromRaw([]);
|
|
33665
33888
|
this.loadError = null;
|
|
33666
33889
|
this.currentSearchTerm = '';
|
|
33890
|
+
if (this.engine) {
|
|
33891
|
+
const currentValues = this.engine.getValues();
|
|
33892
|
+
this.seedValueSnapshotFromValues(this.dependencyValueSnapshot, this.getDependencyFieldIds(), currentValues);
|
|
33893
|
+
this.seedValueSnapshotFromValues(this.displayDependencyValueSnapshot, this.getDisplayDependencyFieldIds(), currentValues);
|
|
33894
|
+
}
|
|
33667
33895
|
if (!this.runtimeManagedField) {
|
|
33668
33896
|
if (this.areApiCallsSuppressed() && this.hasSelectedValue()) {
|
|
33669
|
-
this.
|
|
33897
|
+
this.setOptionsFromRaw(this.withSelectedValueFallbackOptions([]));
|
|
33670
33898
|
return;
|
|
33671
33899
|
}
|
|
33672
33900
|
void this.loadOptions(this.currentSearchTerm);
|
|
@@ -33702,6 +33930,8 @@ class SelectWidgetComponent {
|
|
|
33702
33930
|
String(dataConfig.datasourceId ?? ''),
|
|
33703
33931
|
String(dataConfig.labelKey ?? ''),
|
|
33704
33932
|
String(dataConfig.valueKey ?? ''),
|
|
33933
|
+
String(dataConfig.formatNumericOptionLabels ?? ''),
|
|
33934
|
+
String(dataConfig.optionLabelPrefixFieldId ?? ''),
|
|
33705
33935
|
String(dataConfig.searchEnabled ?? ''),
|
|
33706
33936
|
String(dataConfig.optionsLimit ?? ''),
|
|
33707
33937
|
dependencySignature,
|
|
@@ -33746,6 +33976,62 @@ class SelectWidgetComponent {
|
|
|
33746
33976
|
this.cachedInputAttributes = { 'aria-label': accessibleLabel };
|
|
33747
33977
|
}
|
|
33748
33978
|
}
|
|
33979
|
+
setOptionsFromRaw(options) {
|
|
33980
|
+
this.rawOptions = [...options];
|
|
33981
|
+
this.applyDisplayFormatting();
|
|
33982
|
+
}
|
|
33983
|
+
applyDisplayFormatting() {
|
|
33984
|
+
this.options = this.rawOptions.map(option => ({
|
|
33985
|
+
...option,
|
|
33986
|
+
label: this.formatOptionLabel(option.label)
|
|
33987
|
+
}));
|
|
33988
|
+
}
|
|
33989
|
+
formatOptionLabel(label) {
|
|
33990
|
+
const formattedLabel = this.config.dataConfig?.formatNumericOptionLabels
|
|
33991
|
+
? this.formatNumericLabel(label)
|
|
33992
|
+
: label;
|
|
33993
|
+
const prefix = this.resolveOptionLabelPrefix();
|
|
33994
|
+
return prefix ? `${prefix} ${formattedLabel}` : formattedLabel;
|
|
33995
|
+
}
|
|
33996
|
+
resolveOptionLabelPrefix() {
|
|
33997
|
+
const prefixFieldId = this.config.dataConfig?.optionLabelPrefixFieldId;
|
|
33998
|
+
if (!prefixFieldId
|
|
33999
|
+
|| !this.engine
|
|
34000
|
+
|| typeof this.engine.getValue !== 'function') {
|
|
34001
|
+
return '';
|
|
34002
|
+
}
|
|
34003
|
+
const prefixField = this.engine.getSchema().fields.find(field => field.id === prefixFieldId);
|
|
34004
|
+
if (!prefixField) {
|
|
34005
|
+
return '';
|
|
34006
|
+
}
|
|
34007
|
+
const value = this.engine.getValue(prefixField.name);
|
|
34008
|
+
if (value === undefined || value === null) {
|
|
34009
|
+
return '';
|
|
34010
|
+
}
|
|
34011
|
+
const trimmed = String(value).trim();
|
|
34012
|
+
return trimmed;
|
|
34013
|
+
}
|
|
34014
|
+
formatNumericLabel(label) {
|
|
34015
|
+
const trimmed = label.trim();
|
|
34016
|
+
if (!trimmed) {
|
|
34017
|
+
return label;
|
|
34018
|
+
}
|
|
34019
|
+
const normalized = trimmed.replace(/,/g, '');
|
|
34020
|
+
if (!/^-?\d+(\.\d+)?$/.test(normalized)) {
|
|
34021
|
+
return label;
|
|
34022
|
+
}
|
|
34023
|
+
const numericValue = Number(normalized);
|
|
34024
|
+
if (!Number.isFinite(numericValue)) {
|
|
34025
|
+
return label;
|
|
34026
|
+
}
|
|
34027
|
+
const fractionPart = normalized.split('.')[1];
|
|
34028
|
+
const formatter = new Intl.NumberFormat(undefined, {
|
|
34029
|
+
useGrouping: true,
|
|
34030
|
+
minimumFractionDigits: fractionPart?.length ?? 0,
|
|
34031
|
+
maximumFractionDigits: fractionPart?.length ?? 0
|
|
34032
|
+
});
|
|
34033
|
+
return formatter.format(numericValue);
|
|
34034
|
+
}
|
|
33749
34035
|
toSafeNonNegativeInt(value, fallback) {
|
|
33750
34036
|
if (typeof value === 'number' && Number.isFinite(value) && value >= 0) {
|
|
33751
34037
|
return Math.floor(value);
|
|
@@ -33779,7 +34065,11 @@ class SelectWidgetComponent {
|
|
|
33779
34065
|
if (!this.hasMeaningfulValue(rawValue)) {
|
|
33780
34066
|
return [];
|
|
33781
34067
|
}
|
|
33782
|
-
|
|
34068
|
+
const storedLabel = this.getStoredFieldLabel();
|
|
34069
|
+
if (this.shouldUseStoredFallbackLabel(storedLabel)) {
|
|
34070
|
+
return buildFallbackOptions(rawValue, storedLabel);
|
|
34071
|
+
}
|
|
34072
|
+
return buildFallbackOptions(rawValue);
|
|
33783
34073
|
}
|
|
33784
34074
|
hasOptionValue(options, value) {
|
|
33785
34075
|
return options.some(option => Object.is(option.value, value) || String(option.value) === String(value));
|
|
@@ -33818,6 +34108,18 @@ class SelectWidgetComponent {
|
|
|
33818
34108
|
}
|
|
33819
34109
|
return this.engine.getFieldLabel(this.config.name);
|
|
33820
34110
|
}
|
|
34111
|
+
shouldUseStoredFallbackLabel(label) {
|
|
34112
|
+
if (label === undefined) {
|
|
34113
|
+
return false;
|
|
34114
|
+
}
|
|
34115
|
+
if (this.config.dataConfig?.optionLabelPrefixPath) {
|
|
34116
|
+
return true;
|
|
34117
|
+
}
|
|
34118
|
+
if (this.config.dataConfig?.optionLabelPrefixFieldId) {
|
|
34119
|
+
return this.resolveOptionLabelPrefix().length === 0;
|
|
34120
|
+
}
|
|
34121
|
+
return this.config.dataConfig?.formatNumericOptionLabels !== true;
|
|
34122
|
+
}
|
|
33821
34123
|
setStoredFieldLabel(label) {
|
|
33822
34124
|
if (!this.engine || typeof this.engine.setFieldLabel !== 'function') {
|
|
33823
34125
|
return;
|