@yourself.create/ngx-form-designer 0.0.5 → 0.0.6
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 +393 -94
- package/fesm2022/uch-web-ngx-form-designer.mjs.map +1 -1
- package/lib/data/data-provider.d.ts +9 -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/designer-state.service.d.ts +1 -1
- package/lib/form-designer/form-preview.component.d.ts +1 -1
- package/lib/form-designer/layout-canvas.component.d.ts +1 -1
- package/lib/form-renderer/json-form-renderer.component.d.ts +3 -1
- package/lib/website/website-preview-shell.component.d.ts +1 -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);
|
|
@@ -31877,8 +31957,13 @@ class DefaultDataProvider extends DataProvider {
|
|
|
31877
31957
|
if (!rows || rows.length === 0) {
|
|
31878
31958
|
return field.defaultValue;
|
|
31879
31959
|
}
|
|
31880
|
-
const
|
|
31881
|
-
|
|
31960
|
+
const selectedRow = this.selectScalarRow(rows, cfg, engine);
|
|
31961
|
+
if (cfg.rowSelectionMode === 'selected' && !selectedRow) {
|
|
31962
|
+
const currentValue = field.name && engine ? engine.getValue(field.name) : undefined;
|
|
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;
|
|
@@ -31928,6 +32013,21 @@ class DefaultDataProvider extends DataProvider {
|
|
|
31928
32013
|
const result = await this.client.query(cfg.datasourceId);
|
|
31929
32014
|
return result.rows;
|
|
31930
32015
|
}
|
|
32016
|
+
async getOptionRowContexts(cfg, field, engine) {
|
|
32017
|
+
const rows = await this.getRawRows(cfg, field, engine);
|
|
32018
|
+
return this.resolveCollectionRowContexts(rows, cfg, engine);
|
|
32019
|
+
}
|
|
32020
|
+
resolveCollectionRowContexts(rows, cfg, engine) {
|
|
32021
|
+
const parentContexts = this.extractRowContexts(rows, cfg.rowsPath);
|
|
32022
|
+
if (cfg.rowSelectionMode === 'selected' && cfg.childRowsPath) {
|
|
32023
|
+
const selectedParentContext = this.selectOptionContext(parentContexts, cfg, engine);
|
|
32024
|
+
if (!selectedParentContext) {
|
|
32025
|
+
return [];
|
|
32026
|
+
}
|
|
32027
|
+
return this.extractRowContexts([selectedParentContext.row], cfg.childRowsPath, selectedParentContext);
|
|
32028
|
+
}
|
|
32029
|
+
return parentContexts;
|
|
32030
|
+
}
|
|
31931
32031
|
resolveCollectionRows(rows, cfg, engine) {
|
|
31932
32032
|
const parentRows = this.extractRows(rows, cfg.rowsPath);
|
|
31933
32033
|
if (cfg.rowSelectionMode === 'selected' && cfg.childRowsPath) {
|
|
@@ -31942,6 +32042,38 @@ class DefaultDataProvider extends DataProvider {
|
|
|
31942
32042
|
}
|
|
31943
32043
|
return parentRows;
|
|
31944
32044
|
}
|
|
32045
|
+
extractRowContexts(rows, path, sourceContext) {
|
|
32046
|
+
const normalizedPath = path?.trim();
|
|
32047
|
+
if (!normalizedPath) {
|
|
32048
|
+
return rows.map(row => ({
|
|
32049
|
+
row,
|
|
32050
|
+
parentRow: sourceContext?.row,
|
|
32051
|
+
sourceRow: sourceContext?.sourceRow ?? row
|
|
32052
|
+
}));
|
|
32053
|
+
}
|
|
32054
|
+
const flattened = [];
|
|
32055
|
+
for (const row of rows) {
|
|
32056
|
+
const resolved = resolvePathValue(row, normalizedPath);
|
|
32057
|
+
if (Array.isArray(resolved)) {
|
|
32058
|
+
for (const entry of resolved) {
|
|
32059
|
+
flattened.push({
|
|
32060
|
+
row: this.toRowRecord(entry),
|
|
32061
|
+
parentRow: sourceContext?.row,
|
|
32062
|
+
sourceRow: sourceContext?.sourceRow ?? row
|
|
32063
|
+
});
|
|
32064
|
+
}
|
|
32065
|
+
continue;
|
|
32066
|
+
}
|
|
32067
|
+
if (resolved !== undefined && resolved !== null) {
|
|
32068
|
+
flattened.push({
|
|
32069
|
+
row: this.toRowRecord(resolved),
|
|
32070
|
+
parentRow: sourceContext?.row,
|
|
32071
|
+
sourceRow: sourceContext?.sourceRow ?? row
|
|
32072
|
+
});
|
|
32073
|
+
}
|
|
32074
|
+
}
|
|
32075
|
+
return flattened;
|
|
32076
|
+
}
|
|
31945
32077
|
extractRows(rows, path) {
|
|
31946
32078
|
const normalizedPath = path?.trim();
|
|
31947
32079
|
if (!normalizedPath) {
|
|
@@ -31979,36 +32111,57 @@ class DefaultDataProvider extends DataProvider {
|
|
|
31979
32111
|
}
|
|
31980
32112
|
return rows.find(row => valuesMatch(resolvePathValue(row, cfg.selectionMatchPath), selectorValue));
|
|
31981
32113
|
}
|
|
32114
|
+
selectOptionContext(contexts, cfg, engine) {
|
|
32115
|
+
if (contexts.length === 0)
|
|
32116
|
+
return undefined;
|
|
32117
|
+
if (cfg.rowSelectionMode !== 'selected' || !cfg.selectionFieldId || !cfg.selectionMatchPath || !engine) {
|
|
32118
|
+
return contexts[0];
|
|
32119
|
+
}
|
|
32120
|
+
const schema = engine.getSchema();
|
|
32121
|
+
const selectorField = schema.fields.find(candidate => candidate.id === cfg.selectionFieldId);
|
|
32122
|
+
if (!selectorField) {
|
|
32123
|
+
return undefined;
|
|
32124
|
+
}
|
|
32125
|
+
const selectorValue = engine.getValue(selectorField.name);
|
|
32126
|
+
if (selectorValue === undefined || selectorValue === null || selectorValue === '') {
|
|
32127
|
+
return undefined;
|
|
32128
|
+
}
|
|
32129
|
+
return contexts.find(context => valuesMatch(resolvePathValue(context.row, cfg.selectionMatchPath), selectorValue));
|
|
32130
|
+
}
|
|
31982
32131
|
applyRowFilters(rows, filters, engine) {
|
|
31983
32132
|
if (!filters || filters.length === 0)
|
|
31984
32133
|
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
|
-
|
|
32134
|
+
return rows.filter(row => this.matchesRowFilters(row, filters, engine));
|
|
32135
|
+
}
|
|
32136
|
+
applyOptionRowFilters(contexts, filters, engine) {
|
|
32137
|
+
if (!filters || filters.length === 0)
|
|
32138
|
+
return contexts;
|
|
32139
|
+
return contexts.filter(context => this.matchesRowFilters(context.row, filters, engine));
|
|
32140
|
+
}
|
|
32141
|
+
matchesRowFilters(row, filters, engine) {
|
|
32142
|
+
return filters.every(filter => {
|
|
32143
|
+
const expected = this.resolveFilterValue(filter, engine);
|
|
32144
|
+
if (expected === undefined)
|
|
32145
|
+
return true;
|
|
32146
|
+
const actual = resolvePathValue(row, filter.column);
|
|
32147
|
+
const val = expected;
|
|
32148
|
+
switch (filter.op) {
|
|
32149
|
+
case 'eq': return actual === val;
|
|
32150
|
+
case 'neq': return actual !== val;
|
|
32151
|
+
case 'in': return Array.isArray(val) ? val.includes(actual) : actual === val;
|
|
32152
|
+
case 'contains':
|
|
32153
|
+
case 'like':
|
|
32154
|
+
return String(actual ?? '').toLowerCase().includes(String(val ?? '').toLowerCase());
|
|
32155
|
+
case 'startsWith':
|
|
32156
|
+
return String(actual ?? '').toLowerCase().startsWith(String(val ?? '').toLowerCase());
|
|
32157
|
+
case 'endsWith':
|
|
32158
|
+
return String(actual ?? '').toLowerCase().endsWith(String(val ?? '').toLowerCase());
|
|
32159
|
+
case 'gt': return (actual ?? 0) > (val ?? 0);
|
|
32160
|
+
case 'gte': return (actual ?? 0) >= (val ?? 0);
|
|
32161
|
+
case 'lt': return (actual ?? 0) < (val ?? 0);
|
|
32162
|
+
case 'lte': return (actual ?? 0) <= (val ?? 0);
|
|
32163
|
+
default: return true;
|
|
32164
|
+
}
|
|
32012
32165
|
});
|
|
32013
32166
|
}
|
|
32014
32167
|
resolveFilters(cfg, engine) {
|
|
@@ -32066,6 +32219,56 @@ class DefaultDataProvider extends DataProvider {
|
|
|
32066
32219
|
value: this.toOptionValue(resolvePathValue(row, valueKey))
|
|
32067
32220
|
};
|
|
32068
32221
|
}
|
|
32222
|
+
mapContextToOption(context, labelKey, valueKey, cfg) {
|
|
32223
|
+
const rawLabel = String(resolvePathValue(context.row, labelKey) ?? '');
|
|
32224
|
+
const prefix = this.resolveOptionLabelPrefix(context, cfg);
|
|
32225
|
+
const label = this.formatOptionLabel(rawLabel, cfg);
|
|
32226
|
+
return {
|
|
32227
|
+
label: prefix ? `${prefix} ${label}` : label,
|
|
32228
|
+
value: this.toOptionValue(resolvePathValue(context.row, valueKey))
|
|
32229
|
+
};
|
|
32230
|
+
}
|
|
32231
|
+
resolveOptionLabelPrefix(context, cfg) {
|
|
32232
|
+
const prefixPath = cfg.optionLabelPrefixPath?.trim();
|
|
32233
|
+
if (!prefixPath) {
|
|
32234
|
+
return '';
|
|
32235
|
+
}
|
|
32236
|
+
for (const candidate of [context.row, context.parentRow, context.sourceRow]) {
|
|
32237
|
+
if (!candidate)
|
|
32238
|
+
continue;
|
|
32239
|
+
const resolved = resolvePathValue(candidate, prefixPath);
|
|
32240
|
+
if (resolved === undefined || resolved === null)
|
|
32241
|
+
continue;
|
|
32242
|
+
const value = String(resolved).trim();
|
|
32243
|
+
if (value.length > 0) {
|
|
32244
|
+
return value;
|
|
32245
|
+
}
|
|
32246
|
+
}
|
|
32247
|
+
return '';
|
|
32248
|
+
}
|
|
32249
|
+
formatOptionLabel(label, cfg) {
|
|
32250
|
+
if (!cfg.formatNumericOptionLabels) {
|
|
32251
|
+
return label;
|
|
32252
|
+
}
|
|
32253
|
+
const trimmed = label.trim();
|
|
32254
|
+
if (!trimmed) {
|
|
32255
|
+
return label;
|
|
32256
|
+
}
|
|
32257
|
+
const normalized = trimmed.replace(/,/g, '');
|
|
32258
|
+
if (!/^-?\d+(\.\d+)?$/.test(normalized)) {
|
|
32259
|
+
return label;
|
|
32260
|
+
}
|
|
32261
|
+
const numericValue = Number(normalized);
|
|
32262
|
+
if (!Number.isFinite(numericValue)) {
|
|
32263
|
+
return label;
|
|
32264
|
+
}
|
|
32265
|
+
const fractionPart = normalized.split('.')[1];
|
|
32266
|
+
return new Intl.NumberFormat(undefined, {
|
|
32267
|
+
useGrouping: true,
|
|
32268
|
+
minimumFractionDigits: fractionPart?.length ?? 0,
|
|
32269
|
+
maximumFractionDigits: fractionPart?.length ?? 0
|
|
32270
|
+
}).format(numericValue);
|
|
32271
|
+
}
|
|
32069
32272
|
async getRuntimeOptions(field, engine) {
|
|
32070
32273
|
if (!engine)
|
|
32071
32274
|
return undefined;
|
|
@@ -32080,7 +32283,7 @@ class DefaultDataProvider extends DataProvider {
|
|
|
32080
32283
|
return cfg.type === 'source' || cfg.type === 'global' || cfg.type === 'api';
|
|
32081
32284
|
}
|
|
32082
32285
|
shouldUseLocalResolution(cfg) {
|
|
32083
|
-
if (cfg.rowsPath || cfg.
|
|
32286
|
+
if (cfg.rowsPath || cfg.optionLabelPrefixPath || cfg.rowSelectionMode || cfg.selectionFieldId || cfg.selectionMatchPath || cfg.childRowsPath) {
|
|
32084
32287
|
return true;
|
|
32085
32288
|
}
|
|
32086
32289
|
if (hasPathSyntax(cfg.labelKey) || hasPathSyntax(cfg.valueKey)) {
|
|
@@ -32471,7 +32674,6 @@ class TextFieldWidgetComponent {
|
|
|
32471
32674
|
dataConfig.type ?? '',
|
|
32472
32675
|
sourceKey,
|
|
32473
32676
|
String(dataConfig.valueKey ?? ''),
|
|
32474
|
-
String(dataConfig.valuePath ?? ''),
|
|
32475
32677
|
String(dataConfig.rowsPath ?? ''),
|
|
32476
32678
|
String(dataConfig.rowSelectionMode ?? ''),
|
|
32477
32679
|
String(dataConfig.selectionFieldId ?? ''),
|
|
@@ -33266,12 +33468,14 @@ class SelectWidgetComponent {
|
|
|
33266
33468
|
loading = false;
|
|
33267
33469
|
loadError = null;
|
|
33268
33470
|
options = [];
|
|
33471
|
+
rawOptions = [];
|
|
33269
33472
|
searchTerms$ = new Subject();
|
|
33270
33473
|
requestId = 0;
|
|
33271
33474
|
currentSearchTerm = '';
|
|
33272
33475
|
runtimeManagedField = false;
|
|
33273
33476
|
runtimeOptionsLoaded = false;
|
|
33274
33477
|
dependencyValueSnapshot = new Map();
|
|
33478
|
+
displayDependencyValueSnapshot = new Map();
|
|
33275
33479
|
cachedStyleSource;
|
|
33276
33480
|
cachedWrapperStyles = { width: '100%' };
|
|
33277
33481
|
cachedControlCssVars = this.toCssVarMap({});
|
|
@@ -33313,7 +33517,7 @@ class SelectWidgetComponent {
|
|
|
33313
33517
|
return;
|
|
33314
33518
|
this.runtimeOptionsLoaded = false;
|
|
33315
33519
|
this.currentSearchTerm = '';
|
|
33316
|
-
this.
|
|
33520
|
+
this.setOptionsFromRaw([]);
|
|
33317
33521
|
void this.loadOptions(this.currentSearchTerm);
|
|
33318
33522
|
});
|
|
33319
33523
|
}
|
|
@@ -33335,7 +33539,7 @@ class SelectWidgetComponent {
|
|
|
33335
33539
|
this.runtimeManagedField = this.runtimeFieldDataAccessRegistry.hasFieldAccess(this.config, this.engine);
|
|
33336
33540
|
if (!this.runtimeManagedField) {
|
|
33337
33541
|
if (this.areApiCallsSuppressed() && this.hasSelectedValue()) {
|
|
33338
|
-
this.
|
|
33542
|
+
this.setOptionsFromRaw(this.withSelectedValueFallbackOptions([]));
|
|
33339
33543
|
}
|
|
33340
33544
|
else {
|
|
33341
33545
|
void this.loadOptions(this.currentSearchTerm);
|
|
@@ -33344,7 +33548,7 @@ class SelectWidgetComponent {
|
|
|
33344
33548
|
}
|
|
33345
33549
|
else if (this.hasSelectedValue()) {
|
|
33346
33550
|
if (this.areApiCallsSuppressed()) {
|
|
33347
|
-
this.
|
|
33551
|
+
this.setOptionsFromRaw(this.withSelectedValueFallbackOptions([]));
|
|
33348
33552
|
this.runtimeOptionsLoaded = true;
|
|
33349
33553
|
}
|
|
33350
33554
|
else {
|
|
@@ -33353,32 +33557,38 @@ class SelectWidgetComponent {
|
|
|
33353
33557
|
});
|
|
33354
33558
|
}
|
|
33355
33559
|
}
|
|
33356
|
-
const dependencyIds = this.getDependencyFieldIds();
|
|
33357
33560
|
if (this.engine) {
|
|
33358
33561
|
const initialValues = this.engine.getValues();
|
|
33359
|
-
this.
|
|
33562
|
+
this.seedValueSnapshotFromValues(this.dependencyValueSnapshot, this.getDependencyFieldIds(), initialValues);
|
|
33563
|
+
this.seedValueSnapshotFromValues(this.displayDependencyValueSnapshot, this.getDisplayDependencyFieldIds(), initialValues);
|
|
33360
33564
|
this.engine.valueChanges$
|
|
33361
33565
|
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
33362
33566
|
.subscribe(values => {
|
|
33363
33567
|
this.syncEnabledState();
|
|
33364
33568
|
if (this.areApiCallsSuppressed()) {
|
|
33365
|
-
this.
|
|
33569
|
+
this.setOptionsFromRaw(this.withSelectedValueFallbackOptions(this.rawOptions));
|
|
33366
33570
|
this.cdr.markForCheck();
|
|
33367
33571
|
}
|
|
33368
|
-
if (dependencyIds.length === 0)
|
|
33369
|
-
return;
|
|
33370
33572
|
const schema = this.engine.getSchema();
|
|
33371
|
-
const
|
|
33372
|
-
|
|
33373
|
-
|
|
33374
|
-
|
|
33573
|
+
const dependencyIds = this.getDependencyFieldIds();
|
|
33574
|
+
const displayDependencyIds = this.getDisplayDependencyFieldIds();
|
|
33575
|
+
const dependencyNames = this.resolveFieldNames(schema.fields, dependencyIds);
|
|
33576
|
+
const displayDependencyNames = this.resolveFieldNames(schema.fields, displayDependencyIds);
|
|
33577
|
+
if (displayDependencyNames.length > 0 && this.haveFieldValuesChanged(this.displayDependencyValueSnapshot, displayDependencyNames, values)) {
|
|
33578
|
+
this.applyDisplayFormatting();
|
|
33579
|
+
this.syncStoredFieldLabel(this.control.value);
|
|
33580
|
+
this.cdr.markForCheck();
|
|
33581
|
+
}
|
|
33582
|
+
if (dependencyNames.length === 0)
|
|
33583
|
+
return;
|
|
33584
|
+
if (!this.haveFieldValuesChanged(this.dependencyValueSnapshot, dependencyNames, values))
|
|
33375
33585
|
return;
|
|
33376
33586
|
this.runtimeFieldDataAccessRegistry.invalidateFieldAndDescendants(this.engine, this.config.id);
|
|
33377
33587
|
if (this.hasSelectedValue()) {
|
|
33378
33588
|
this.control.setValue(null);
|
|
33379
33589
|
}
|
|
33380
33590
|
this.currentSearchTerm = '';
|
|
33381
|
-
this.
|
|
33591
|
+
this.setOptionsFromRaw([]);
|
|
33382
33592
|
this.runtimeOptionsLoaded = false;
|
|
33383
33593
|
if (!this.runtimeManagedField) {
|
|
33384
33594
|
void this.loadOptions(this.currentSearchTerm);
|
|
@@ -33521,6 +33731,10 @@ class SelectWidgetComponent {
|
|
|
33521
33731
|
return [];
|
|
33522
33732
|
return cfg.dependsOn.map(d => d.fieldId).filter((id) => !!id);
|
|
33523
33733
|
}
|
|
33734
|
+
getDisplayDependencyFieldIds() {
|
|
33735
|
+
const prefixFieldId = this.config.dataConfig?.optionLabelPrefixFieldId;
|
|
33736
|
+
return prefixFieldId ? [prefixFieldId] : [];
|
|
33737
|
+
}
|
|
33524
33738
|
toCssVarMap(controlStyles) {
|
|
33525
33739
|
const vars = {
|
|
33526
33740
|
'--fd-select-border-color': OUTLINED_FIELD_IDLE_BORDER_COLOR,
|
|
@@ -33589,7 +33803,7 @@ class SelectWidgetComponent {
|
|
|
33589
33803
|
opts = await this.dataProvider.getOptions(this.config, this.engine);
|
|
33590
33804
|
}
|
|
33591
33805
|
if (this.requestId === reqId) {
|
|
33592
|
-
this.
|
|
33806
|
+
this.setOptionsFromRaw(this.withSelectedValueFallbackOptions(opts));
|
|
33593
33807
|
this.syncStoredFieldLabel(this.control.value);
|
|
33594
33808
|
this.loading = false;
|
|
33595
33809
|
this.loadError = null;
|
|
@@ -33601,7 +33815,7 @@ class SelectWidgetComponent {
|
|
|
33601
33815
|
this.loading = false;
|
|
33602
33816
|
this.loadError = 'Failed to load options.';
|
|
33603
33817
|
if (!isSearch) {
|
|
33604
|
-
this.
|
|
33818
|
+
this.setOptionsFromRaw(this.withSelectedValueFallbackOptions(this.config.staticOptions || []));
|
|
33605
33819
|
}
|
|
33606
33820
|
this.cdr.markForCheck();
|
|
33607
33821
|
}
|
|
@@ -33622,7 +33836,7 @@ class SelectWidgetComponent {
|
|
|
33622
33836
|
if (this.runtimeOptionsLoaded && this.options.length > 0)
|
|
33623
33837
|
return;
|
|
33624
33838
|
if (this.areApiCallsSuppressed()) {
|
|
33625
|
-
this.
|
|
33839
|
+
this.setOptionsFromRaw(this.withSelectedValueFallbackOptions(this.rawOptions));
|
|
33626
33840
|
this.runtimeOptionsLoaded = true;
|
|
33627
33841
|
return;
|
|
33628
33842
|
}
|
|
@@ -33631,25 +33845,29 @@ class SelectWidgetComponent {
|
|
|
33631
33845
|
this.runtimeOptionsLoaded = true;
|
|
33632
33846
|
}
|
|
33633
33847
|
}
|
|
33634
|
-
|
|
33635
|
-
if (!this.engine ||
|
|
33848
|
+
seedValueSnapshotFromValues(snapshot, fieldIds, values) {
|
|
33849
|
+
if (!this.engine || fieldIds.length === 0)
|
|
33636
33850
|
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]);
|
|
33851
|
+
for (const fieldName of this.resolveFieldNames(this.engine.getSchema().fields, fieldIds)) {
|
|
33852
|
+
snapshot.set(fieldName, values[fieldName]);
|
|
33643
33853
|
}
|
|
33644
33854
|
}
|
|
33645
|
-
|
|
33855
|
+
resolveFieldNames(fields, fieldIds) {
|
|
33856
|
+
if (fieldIds.length === 0) {
|
|
33857
|
+
return [];
|
|
33858
|
+
}
|
|
33859
|
+
return fields
|
|
33860
|
+
.filter(field => fieldIds.includes(field.id))
|
|
33861
|
+
.map(field => field.name);
|
|
33862
|
+
}
|
|
33863
|
+
haveFieldValuesChanged(snapshot, fieldNames, values) {
|
|
33646
33864
|
let changed = false;
|
|
33647
|
-
for (const
|
|
33648
|
-
const previousValue =
|
|
33649
|
-
const nextValue = values[
|
|
33865
|
+
for (const fieldName of fieldNames) {
|
|
33866
|
+
const previousValue = snapshot.get(fieldName);
|
|
33867
|
+
const nextValue = values[fieldName];
|
|
33650
33868
|
if (!Object.is(previousValue, nextValue)) {
|
|
33651
33869
|
changed = true;
|
|
33652
|
-
|
|
33870
|
+
snapshot.set(fieldName, nextValue);
|
|
33653
33871
|
}
|
|
33654
33872
|
}
|
|
33655
33873
|
return changed;
|
|
@@ -33661,12 +33879,19 @@ class SelectWidgetComponent {
|
|
|
33661
33879
|
this.syncEnabledState();
|
|
33662
33880
|
this.runtimeManagedField = this.runtimeFieldDataAccessRegistry.hasFieldAccess(this.config, this.engine);
|
|
33663
33881
|
this.runtimeOptionsLoaded = false;
|
|
33664
|
-
this.
|
|
33882
|
+
this.dependencyValueSnapshot.clear();
|
|
33883
|
+
this.displayDependencyValueSnapshot.clear();
|
|
33884
|
+
this.setOptionsFromRaw([]);
|
|
33665
33885
|
this.loadError = null;
|
|
33666
33886
|
this.currentSearchTerm = '';
|
|
33887
|
+
if (this.engine) {
|
|
33888
|
+
const currentValues = this.engine.getValues();
|
|
33889
|
+
this.seedValueSnapshotFromValues(this.dependencyValueSnapshot, this.getDependencyFieldIds(), currentValues);
|
|
33890
|
+
this.seedValueSnapshotFromValues(this.displayDependencyValueSnapshot, this.getDisplayDependencyFieldIds(), currentValues);
|
|
33891
|
+
}
|
|
33667
33892
|
if (!this.runtimeManagedField) {
|
|
33668
33893
|
if (this.areApiCallsSuppressed() && this.hasSelectedValue()) {
|
|
33669
|
-
this.
|
|
33894
|
+
this.setOptionsFromRaw(this.withSelectedValueFallbackOptions([]));
|
|
33670
33895
|
return;
|
|
33671
33896
|
}
|
|
33672
33897
|
void this.loadOptions(this.currentSearchTerm);
|
|
@@ -33702,6 +33927,8 @@ class SelectWidgetComponent {
|
|
|
33702
33927
|
String(dataConfig.datasourceId ?? ''),
|
|
33703
33928
|
String(dataConfig.labelKey ?? ''),
|
|
33704
33929
|
String(dataConfig.valueKey ?? ''),
|
|
33930
|
+
String(dataConfig.formatNumericOptionLabels ?? ''),
|
|
33931
|
+
String(dataConfig.optionLabelPrefixFieldId ?? ''),
|
|
33705
33932
|
String(dataConfig.searchEnabled ?? ''),
|
|
33706
33933
|
String(dataConfig.optionsLimit ?? ''),
|
|
33707
33934
|
dependencySignature,
|
|
@@ -33746,6 +33973,62 @@ class SelectWidgetComponent {
|
|
|
33746
33973
|
this.cachedInputAttributes = { 'aria-label': accessibleLabel };
|
|
33747
33974
|
}
|
|
33748
33975
|
}
|
|
33976
|
+
setOptionsFromRaw(options) {
|
|
33977
|
+
this.rawOptions = [...options];
|
|
33978
|
+
this.applyDisplayFormatting();
|
|
33979
|
+
}
|
|
33980
|
+
applyDisplayFormatting() {
|
|
33981
|
+
this.options = this.rawOptions.map(option => ({
|
|
33982
|
+
...option,
|
|
33983
|
+
label: this.formatOptionLabel(option.label)
|
|
33984
|
+
}));
|
|
33985
|
+
}
|
|
33986
|
+
formatOptionLabel(label) {
|
|
33987
|
+
const formattedLabel = this.config.dataConfig?.formatNumericOptionLabels
|
|
33988
|
+
? this.formatNumericLabel(label)
|
|
33989
|
+
: label;
|
|
33990
|
+
const prefix = this.resolveOptionLabelPrefix();
|
|
33991
|
+
return prefix ? `${prefix} ${formattedLabel}` : formattedLabel;
|
|
33992
|
+
}
|
|
33993
|
+
resolveOptionLabelPrefix() {
|
|
33994
|
+
const prefixFieldId = this.config.dataConfig?.optionLabelPrefixFieldId;
|
|
33995
|
+
if (!prefixFieldId
|
|
33996
|
+
|| !this.engine
|
|
33997
|
+
|| typeof this.engine.getValue !== 'function') {
|
|
33998
|
+
return '';
|
|
33999
|
+
}
|
|
34000
|
+
const prefixField = this.engine.getSchema().fields.find(field => field.id === prefixFieldId);
|
|
34001
|
+
if (!prefixField) {
|
|
34002
|
+
return '';
|
|
34003
|
+
}
|
|
34004
|
+
const value = this.engine.getValue(prefixField.name);
|
|
34005
|
+
if (value === undefined || value === null) {
|
|
34006
|
+
return '';
|
|
34007
|
+
}
|
|
34008
|
+
const trimmed = String(value).trim();
|
|
34009
|
+
return trimmed;
|
|
34010
|
+
}
|
|
34011
|
+
formatNumericLabel(label) {
|
|
34012
|
+
const trimmed = label.trim();
|
|
34013
|
+
if (!trimmed) {
|
|
34014
|
+
return label;
|
|
34015
|
+
}
|
|
34016
|
+
const normalized = trimmed.replace(/,/g, '');
|
|
34017
|
+
if (!/^-?\d+(\.\d+)?$/.test(normalized)) {
|
|
34018
|
+
return label;
|
|
34019
|
+
}
|
|
34020
|
+
const numericValue = Number(normalized);
|
|
34021
|
+
if (!Number.isFinite(numericValue)) {
|
|
34022
|
+
return label;
|
|
34023
|
+
}
|
|
34024
|
+
const fractionPart = normalized.split('.')[1];
|
|
34025
|
+
const formatter = new Intl.NumberFormat(undefined, {
|
|
34026
|
+
useGrouping: true,
|
|
34027
|
+
minimumFractionDigits: fractionPart?.length ?? 0,
|
|
34028
|
+
maximumFractionDigits: fractionPart?.length ?? 0
|
|
34029
|
+
});
|
|
34030
|
+
return formatter.format(numericValue);
|
|
34031
|
+
}
|
|
33749
34032
|
toSafeNonNegativeInt(value, fallback) {
|
|
33750
34033
|
if (typeof value === 'number' && Number.isFinite(value) && value >= 0) {
|
|
33751
34034
|
return Math.floor(value);
|
|
@@ -33779,7 +34062,11 @@ class SelectWidgetComponent {
|
|
|
33779
34062
|
if (!this.hasMeaningfulValue(rawValue)) {
|
|
33780
34063
|
return [];
|
|
33781
34064
|
}
|
|
33782
|
-
|
|
34065
|
+
const storedLabel = this.getStoredFieldLabel();
|
|
34066
|
+
if (this.shouldUseStoredFallbackLabel(storedLabel)) {
|
|
34067
|
+
return buildFallbackOptions(rawValue, storedLabel);
|
|
34068
|
+
}
|
|
34069
|
+
return buildFallbackOptions(rawValue);
|
|
33783
34070
|
}
|
|
33784
34071
|
hasOptionValue(options, value) {
|
|
33785
34072
|
return options.some(option => Object.is(option.value, value) || String(option.value) === String(value));
|
|
@@ -33818,6 +34105,18 @@ class SelectWidgetComponent {
|
|
|
33818
34105
|
}
|
|
33819
34106
|
return this.engine.getFieldLabel(this.config.name);
|
|
33820
34107
|
}
|
|
34108
|
+
shouldUseStoredFallbackLabel(label) {
|
|
34109
|
+
if (label === undefined) {
|
|
34110
|
+
return false;
|
|
34111
|
+
}
|
|
34112
|
+
if (this.config.dataConfig?.optionLabelPrefixPath) {
|
|
34113
|
+
return true;
|
|
34114
|
+
}
|
|
34115
|
+
if (this.config.dataConfig?.optionLabelPrefixFieldId) {
|
|
34116
|
+
return this.resolveOptionLabelPrefix().length === 0;
|
|
34117
|
+
}
|
|
34118
|
+
return this.config.dataConfig?.formatNumericOptionLabels !== true;
|
|
34119
|
+
}
|
|
33821
34120
|
setStoredFieldLabel(label) {
|
|
33822
34121
|
if (!this.engine || typeof this.engine.setFieldLabel !== 'function') {
|
|
33823
34122
|
return;
|