@yourself.create/ngx-form-designer 0.0.7 → 0.0.9

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.
@@ -23,6 +23,15 @@ import Quill from 'quill';
23
23
  import { DomSanitizer } from '@angular/platform-browser';
24
24
  import { HttpClient, provideHttpClient } from '@angular/common/http';
25
25
 
26
+ function usesFieldThousandSeparator(field) {
27
+ return field?.useThousandSeparator === true;
28
+ }
29
+ function isFieldReadonly(field) {
30
+ return field?.html5?.readonly === true;
31
+ }
32
+ function isFieldDisabled(field) {
33
+ return field?.html5?.disabled === true;
34
+ }
26
35
  const CURRENT_SCHEMA_VERSION = '1.0.0';
27
36
 
28
37
  /**
@@ -446,8 +455,8 @@ class FormEngine {
446
455
  setValue(fieldName, value) {
447
456
  this.values[fieldName] = value;
448
457
  this.valueSubject.next({ ...this.values }); // Emit new state
449
- // Re-run validation for this field.
450
- this.validateField(fieldName);
458
+ // Rules and dependencies can affect other fields, so revalidate the full form.
459
+ this.validate();
451
460
  }
452
461
  getValues() {
453
462
  return { ...this.values };
@@ -502,7 +511,7 @@ class FormEngine {
502
511
  if (!field)
503
512
  return false;
504
513
  // 1. Dependencies (Legacy)
505
- let enabled = this.evaluateDependencyRules(field, 'enable', 'disable', true);
514
+ let enabled = this.evaluateDependencyRules(field, 'enable', 'disable', !isFieldDisabled(field));
506
515
  // 2. Enterprise Rules
507
516
  enabled = this.evaluateEnterpriseRules(field, 'enable', 'disable', enabled);
508
517
  return enabled;
@@ -535,10 +544,16 @@ class FormEngine {
535
544
  return;
536
545
  const value = this.values[fieldName];
537
546
  const fieldErrors = [];
547
+ const ruleErrors = this.getRuleValidationErrors(field);
538
548
  // Visibility check
539
549
  const isVisible = this.isFieldVisible(field.id);
540
550
  if (!isVisible) {
541
- delete this.errors[fieldName];
551
+ if (ruleErrors.length > 0) {
552
+ this.errors[fieldName] = ruleErrors;
553
+ }
554
+ else {
555
+ delete this.errors[fieldName];
556
+ }
542
557
  return;
543
558
  }
544
559
  const isRequired = this.isFieldRequired(field.id);
@@ -568,30 +583,9 @@ class FormEngine {
568
583
  }
569
584
  }
570
585
  }
571
- // Custom Validation Rules
572
- if (field.validation) {
573
- field.validation.forEach(rule => {
574
- let isValid = true;
575
- if (rule.type === 'builtin') {
576
- if (rule.name === 'email') {
577
- isValid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(String(value));
578
- }
579
- }
580
- else if (rule.type === 'expression' && rule.expression) {
581
- try {
582
- const checkFn = new Function('value', 'form', `return ${rule.expression}`);
583
- isValid = checkFn(value, this.values);
584
- }
585
- catch (e) {
586
- console.error('Validation expression error', e);
587
- isValid = false;
588
- }
589
- }
590
- if (!isValid) {
591
- fieldErrors.push(rule.message || 'Validation failed.');
592
- }
593
- });
594
- }
586
+ }
587
+ if (ruleErrors.length > 0) {
588
+ fieldErrors.push(...ruleErrors);
595
589
  }
596
590
  if (fieldErrors.length > 0) {
597
591
  this.errors[fieldName] = fieldErrors;
@@ -600,6 +594,11 @@ class FormEngine {
600
594
  delete this.errors[fieldName];
601
595
  }
602
596
  }
597
+ getRuleValidationErrors(field) {
598
+ return this.getEnterpriseRuleOutcomes(field)
599
+ .filter(({ rule, conditionMet }) => rule.severity === 'error' && !conditionMet && rule.elseAction === undefined)
600
+ .map(({ rule }) => this.getRuleValidationMessage(rule));
601
+ }
603
602
  // Generic rule evaluator
604
603
  evaluateDependencyRules(field, positiveEffect, negativeEffect, startValue) {
605
604
  if (!field.dependencies)
@@ -638,13 +637,31 @@ class FormEngine {
638
637
  if (hasPositiveRules && startValue === true) {
639
638
  currentState = false; // Implicitly hide/disable
640
639
  }
641
- field.rules.forEach((rule) => {
642
- const conditionMet = this.ruleEvaluator.evaluateRule(rule, this.values, this.schema.fields);
643
- const nextAction = conditionMet ? rule.action : rule.elseAction;
640
+ this.getEnterpriseRuleOutcomes(field).forEach(({ nextAction }) => {
644
641
  currentState = this.applyRuleAction(currentState, nextAction, positiveAction, negativeAction);
645
642
  });
646
643
  return currentState;
647
644
  }
645
+ getEnterpriseRuleOutcomes(field) {
646
+ if (!field.rules || field.rules.length === 0) {
647
+ return [];
648
+ }
649
+ return field.rules.map((rule) => {
650
+ const conditionMet = this.ruleEvaluator.evaluateRule(rule, this.values, this.schema.fields);
651
+ return {
652
+ rule,
653
+ conditionMet,
654
+ nextAction: conditionMet ? rule.action : rule.elseAction
655
+ };
656
+ });
657
+ }
658
+ getRuleValidationMessage(rule) {
659
+ const name = rule.name?.trim();
660
+ if (name) {
661
+ return name;
662
+ }
663
+ return `Rule "${rule.action}" failed.`;
664
+ }
648
665
  applyRuleAction(currentState, action, positiveAction, negativeAction) {
649
666
  if (action === positiveAction) {
650
667
  return true;
@@ -4496,6 +4513,7 @@ class FormEventRunner {
4496
4513
  const target = fields.find(f => f.id === action.targetFieldId);
4497
4514
  if (!target)
4498
4515
  return;
4516
+ const previousValue = this.engine.getValue(target.name);
4499
4517
  let val;
4500
4518
  if (action.valueFrom === 'literal') {
4501
4519
  val = action.valueLiteral;
@@ -4510,6 +4528,14 @@ class FormEventRunner {
4510
4528
  }
4511
4529
  }
4512
4530
  this.engine.setValue(target.name, val);
4531
+ if (!Object.is(previousValue, val)) {
4532
+ this.engine.emitUiEvent({
4533
+ fieldId: target.id,
4534
+ fieldName: target.name,
4535
+ type: 'change',
4536
+ value: val
4537
+ });
4538
+ }
4513
4539
  }
4514
4540
  async handleApiAction(action, eventId, evt, fields) {
4515
4541
  if (!this.apiExecutor) {
@@ -5181,6 +5207,7 @@ class JsonFormRendererComponent {
5181
5207
  uploadClient = inject(FILE_UPLOAD_CLIENT, { optional: true }) ?? inject(DefaultFileUploadClient);
5182
5208
  runtimeFieldDataAccessRegistry = inject(RuntimeFieldDataAccessRegistryService);
5183
5209
  dataCatalog = inject(DataCatalog, { optional: true });
5210
+ ruleEvaluationService = inject(RuleEvaluationService);
5184
5211
  constructor(designerState) {
5185
5212
  this.designerState = designerState;
5186
5213
  }
@@ -5257,7 +5284,7 @@ class JsonFormRendererComponent {
5257
5284
  this.runtimeFieldDataAccessRegistry.unregister(this.engine);
5258
5285
  }
5259
5286
  async emitValuePayload(values) {
5260
- const fieldValueMap = await this.buildFieldValueMap(values);
5287
+ const fieldValueMap = await this.buildFieldValueMap(values, { normalizeFileFieldsForSubmit: true });
5261
5288
  const groupedValues = this.buildGroupedValues(fieldValueMap);
5262
5289
  const combinedValues = this.buildCombinedValues(fieldValueMap);
5263
5290
  this.valueChange.emit(fieldValueMap);
@@ -5419,16 +5446,10 @@ class JsonFormRendererComponent {
5419
5446
  this.validationChange.emit(validation);
5420
5447
  // Notify all widgets to show errors if any
5421
5448
  this.engine?.submit();
5422
- const preUploadFieldValueMap = this.uploadOnSubmit
5423
- ? await this.buildFieldValueMap(this.engine?.getValues() ?? {}, { normalizeFileFieldsForSubmit: true })
5424
- : undefined;
5425
5449
  const uploadedFiles = this.uploadOnSubmit ? await this.uploadPendingFiles() : {};
5426
5450
  this.uploadedFilesChange.emit(uploadedFiles);
5427
5451
  const values = this.engine?.getValues() ?? {};
5428
- const fieldValueMap = await this.buildFieldValueMap(values, { normalizeFileFieldsForSubmit: true });
5429
- const submitValues = preUploadFieldValueMap
5430
- ? this.mergeFileMetadata(fieldValueMap, preUploadFieldValueMap)
5431
- : fieldValueMap;
5452
+ const submitValues = await this.buildFieldValueMap(values, { normalizeFileFieldsForSubmit: true });
5432
5453
  this.formSubmit.emit({
5433
5454
  values: submitValues,
5434
5455
  groupedValues: this.buildGroupedValues(submitValues),
@@ -5441,10 +5462,12 @@ class JsonFormRendererComponent {
5441
5462
  const errors = revalidate
5442
5463
  ? (this.engine?.validate() ?? {})
5443
5464
  : (this.engine?.getErrors() ?? {});
5465
+ const fields = this.buildFieldValidationState(errors);
5444
5466
  return {
5445
5467
  errors: { ...errors },
5446
5468
  isValid: Object.keys(errors).length === 0,
5447
- fields: this.buildFieldValidationState(errors)
5469
+ isSeverityError: Object.values(fields).some(field => field.isSeverityError),
5470
+ fields
5448
5471
  };
5449
5472
  }
5450
5473
  buildFieldValidationState(errors) {
@@ -5465,71 +5488,99 @@ class JsonFormRendererComponent {
5465
5488
  visible,
5466
5489
  required,
5467
5490
  valid: fieldErrors.length === 0,
5491
+ isSeverityError: false,
5468
5492
  errors: fieldErrors,
5469
- validators: this.describeFieldValidators(field, visible, required)
5493
+ validators: this.describeFieldValidators(field, visible, required, fieldErrors)
5470
5494
  };
5495
+ states[field.name].isSeverityError = states[field.name].validators.some(validator => validator.severity === 'error' && validator.valid === false);
5471
5496
  }
5472
5497
  return states;
5473
5498
  }
5474
- describeFieldValidators(field, visible, required) {
5499
+ describeFieldValidators(field, visible, required, fieldErrors) {
5475
5500
  const validators = [];
5476
5501
  const value = this.engine?.getValue(field.name);
5477
5502
  const hasValue = !this.isValidationEmpty(value);
5503
+ const ruleOutcomes = this.getRuleOutcomes(field);
5504
+ const requiredSeverity = ruleOutcomes.some(outcome => outcome.rule.severity === 'error' && outcome.nextAction === 'required');
5478
5505
  if (this.hasRequiredValidation(field) || required) {
5506
+ const message = 'This field is required.';
5479
5507
  validators.push({
5480
5508
  name: 'required',
5481
5509
  source: 'required',
5482
5510
  active: visible && required,
5483
- message: 'This field is required.'
5511
+ valid: !fieldErrors.includes(message),
5512
+ message,
5513
+ ...(requiredSeverity ? { severity: 'error' } : {})
5484
5514
  });
5485
5515
  }
5486
5516
  if (field.html5?.minLength !== undefined) {
5517
+ const message = `Minimum length is ${field.html5.minLength}.`;
5487
5518
  validators.push({
5488
5519
  name: 'minLength',
5489
5520
  source: 'html5',
5490
5521
  active: visible && hasValue,
5491
- value: field.html5.minLength
5522
+ valid: !fieldErrors.includes(message),
5523
+ value: field.html5.minLength,
5524
+ message
5492
5525
  });
5493
5526
  }
5494
5527
  if (field.html5?.maxLength !== undefined) {
5528
+ const message = `Maximum length is ${field.html5.maxLength}.`;
5495
5529
  validators.push({
5496
5530
  name: 'maxLength',
5497
5531
  source: 'html5',
5498
5532
  active: visible && hasValue,
5499
- value: field.html5.maxLength
5533
+ valid: !fieldErrors.includes(message),
5534
+ value: field.html5.maxLength,
5535
+ message
5500
5536
  });
5501
5537
  }
5502
5538
  if (field.html5?.min !== undefined) {
5539
+ const message = `Minimum value is ${field.html5.min}.`;
5503
5540
  validators.push({
5504
5541
  name: 'min',
5505
5542
  source: 'html5',
5506
5543
  active: visible && hasValue,
5507
- value: field.html5.min
5544
+ valid: !fieldErrors.includes(message),
5545
+ value: field.html5.min,
5546
+ message
5508
5547
  });
5509
5548
  }
5510
5549
  if (field.html5?.max !== undefined) {
5550
+ const message = `Maximum value is ${field.html5.max}.`;
5511
5551
  validators.push({
5512
5552
  name: 'max',
5513
5553
  source: 'html5',
5514
5554
  active: visible && hasValue,
5515
- value: field.html5.max
5555
+ valid: !fieldErrors.includes(message),
5556
+ value: field.html5.max,
5557
+ message
5516
5558
  });
5517
5559
  }
5518
5560
  if (field.html5?.pattern) {
5561
+ const message = 'Invalid format.';
5519
5562
  validators.push({
5520
5563
  name: 'pattern',
5521
5564
  source: 'html5',
5522
5565
  active: visible && hasValue,
5523
- value: field.html5.pattern
5566
+ valid: !fieldErrors.includes(message),
5567
+ value: field.html5.pattern,
5568
+ message
5524
5569
  });
5525
5570
  }
5526
- for (const rule of field.validation ?? []) {
5571
+ for (const outcome of ruleOutcomes) {
5572
+ const message = this.getRuleValidationMessage(outcome.rule);
5527
5573
  validators.push({
5528
- name: rule.type === 'builtin' ? (rule.name ?? 'builtin') : 'expression',
5529
- source: 'custom',
5530
- active: visible && hasValue && this.isValidationRuleActive(rule),
5531
- value: rule.type === 'expression' ? rule.expression : rule.name,
5532
- message: rule.message
5574
+ name: outcome.rule.name?.trim() || this.describeRuleName(outcome.rule),
5575
+ source: 'rule',
5576
+ active: true,
5577
+ valid: outcome.conditionMet || outcome.nextAction !== undefined,
5578
+ value: {
5579
+ action: outcome.rule.action,
5580
+ elseAction: outcome.rule.elseAction
5581
+ },
5582
+ message,
5583
+ ...(outcome.rule.severity ? { severity: outcome.rule.severity } : {})
5533
5584
  });
5534
5585
  }
5535
5586
  return validators;
@@ -5548,16 +5599,30 @@ class JsonFormRendererComponent {
5548
5599
  }
5549
5600
  return false;
5550
5601
  }
5551
- isValidationRuleActive(rule) {
5552
- if (!rule.when)
5553
- return true;
5554
- try {
5555
- const checkFn = new Function('form', `return ${rule.when}`);
5556
- return checkFn(this.engine?.getValues() ?? {});
5602
+ getRuleOutcomes(field) {
5603
+ if (!field.rules?.length) {
5604
+ return [];
5557
5605
  }
5558
- catch {
5559
- return false;
5606
+ const schema = this.engine?.getSchema() ?? this.schema;
5607
+ const formValues = this.engine?.getValues() ?? {};
5608
+ return field.rules.map(rule => {
5609
+ const conditionMet = this.ruleEvaluationService.evaluateRule(rule, formValues, schema?.fields ?? []);
5610
+ return {
5611
+ rule,
5612
+ conditionMet,
5613
+ nextAction: conditionMet ? rule.action : rule.elseAction
5614
+ };
5615
+ });
5616
+ }
5617
+ describeRuleName(rule) {
5618
+ return rule.action.charAt(0).toUpperCase() + rule.action.slice(1);
5619
+ }
5620
+ getRuleValidationMessage(rule) {
5621
+ const name = rule.name?.trim();
5622
+ if (name) {
5623
+ return name;
5560
5624
  }
5625
+ return `Rule "${rule.action}" failed.`;
5561
5626
  }
5562
5627
  isValidationEmpty(value) {
5563
5628
  return value === null
@@ -5689,13 +5754,14 @@ class JsonFormRendererComponent {
5689
5754
  continue;
5690
5755
  }
5691
5756
  if (field.type === 'file') {
5757
+ const normalizeFileFieldsForSubmit = options.normalizeFileFieldsForSubmit === true;
5692
5758
  const fileValue = {
5693
5759
  fieldName: field.name,
5694
- fieldValue: options.normalizeFileFieldsForSubmit
5695
- ? this.normalizeFileFieldSubmitValue(rawValue)
5760
+ fieldValue: normalizeFileFieldsForSubmit
5761
+ ? await this.buildFileSubmitValueEntries(rawValue)
5696
5762
  : rawValue,
5697
- ...(options.normalizeFileFieldsForSubmit ? { fieldType: field.type } : {}),
5698
- ...(await this.buildFileFieldMetadata(rawValue))
5763
+ ...(normalizeFileFieldsForSubmit ? { fieldType: field.type } : {}),
5764
+ ...(!normalizeFileFieldsForSubmit ? await this.buildFileFieldMetadata(rawValue) : {})
5699
5765
  };
5700
5766
  mapped[field.id] = fileValue;
5701
5767
  continue;
@@ -5782,7 +5848,12 @@ class JsonFormRendererComponent {
5782
5848
  return undefined;
5783
5849
  return values.length === 1 ? values[0] : values;
5784
5850
  }
5785
- normalizeFileFieldSubmitValue(value) {
5851
+ async buildFileSubmitValueEntries(value) {
5852
+ const rawEntries = this.normalizeFileFieldEntries(value);
5853
+ const normalizedEntries = await Promise.all(rawEntries.map(entry => this.normalizeSingleFileFieldEntry(entry)));
5854
+ return normalizedEntries.filter((entry) => entry !== null);
5855
+ }
5856
+ normalizeFileFieldEntries(value) {
5786
5857
  if (this.isFileList(value)) {
5787
5858
  return Array.from(value);
5788
5859
  }
@@ -5794,6 +5865,23 @@ class JsonFormRendererComponent {
5794
5865
  }
5795
5866
  return [value];
5796
5867
  }
5868
+ async normalizeSingleFileFieldEntry(value) {
5869
+ if (this.isFile(value)) {
5870
+ return this.buildPendingSubmitFileValue(value);
5871
+ }
5872
+ if (this.isUploadedFileRef(value)) {
5873
+ return { ...value };
5874
+ }
5875
+ return null;
5876
+ }
5877
+ async buildPendingSubmitFileValue(file) {
5878
+ return {
5879
+ name: file.name || undefined,
5880
+ size: Number.isFinite(file.size) ? file.size : undefined,
5881
+ type: file.type || undefined,
5882
+ data: await this.readFileBase64(file)
5883
+ };
5884
+ }
5797
5885
  getUploadedFileRefs(value) {
5798
5886
  if (this.isUploadedFileRef(value))
5799
5887
  return [value];
@@ -5808,26 +5896,6 @@ class JsonFormRendererComponent {
5808
5896
  : undefined;
5809
5897
  return fieldLabel === undefined ? {} : { fieldLabel };
5810
5898
  }
5811
- mergeFileMetadata(target, source) {
5812
- const merged = { ...target };
5813
- for (const fieldId of Object.keys(source)) {
5814
- const sourceValue = source[fieldId];
5815
- const targetValue = merged[fieldId];
5816
- if (!sourceValue || !targetValue)
5817
- continue;
5818
- if (sourceValue.fileType === undefined && sourceValue.data === undefined)
5819
- continue;
5820
- const nextValue = { ...targetValue };
5821
- if (sourceValue.fileType !== undefined) {
5822
- nextValue.fileType = sourceValue.fileType;
5823
- }
5824
- if (sourceValue.data !== undefined) {
5825
- nextValue.data = sourceValue.data;
5826
- }
5827
- merged[fieldId] = nextValue;
5828
- }
5829
- return merged;
5830
- }
5831
5899
  buildGroupedValues(values) {
5832
5900
  const grouped = {};
5833
5901
  const schema = this.engine?.getSchema();
@@ -10274,13 +10342,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImpo
10274
10342
  class DynamicPropertiesComponent {
10275
10343
  onPropertyChange;
10276
10344
  designerCtx = inject(DesignerContext);
10277
- validatorTypeOptions = [
10278
- { label: 'Built-in', value: 'builtin' },
10279
- { label: 'Expression', value: 'expression' }
10280
- ];
10281
- builtinValidatorOptions = [
10282
- { label: 'Email', value: 'email' }
10283
- ];
10284
10345
  get properties() {
10285
10346
  if (!this.config)
10286
10347
  return [];
@@ -10612,68 +10673,6 @@ class DynamicPropertiesComponent {
10612
10673
  this.setValue(path, options);
10613
10674
  }
10614
10675
  }
10615
- // Validators editor methods
10616
- addValidator(path) {
10617
- if (this.readOnly)
10618
- return;
10619
- const validators = [...(this.getValue(path) || [])];
10620
- validators.push(this.createDefaultValidationRule());
10621
- this.setValue(path, validators);
10622
- this.handleFieldChange();
10623
- }
10624
- removeValidator(path, index) {
10625
- if (this.readOnly)
10626
- return;
10627
- const validators = [...(this.getValue(path) || [])];
10628
- validators.splice(index, 1);
10629
- this.setValue(path, validators);
10630
- this.handleFieldChange();
10631
- }
10632
- updateValidator(path, index, field, value) {
10633
- if (this.readOnly)
10634
- return;
10635
- const validators = [...(this.getValue(path) || [])];
10636
- const currentRule = validators[index];
10637
- if (!currentRule) {
10638
- return;
10639
- }
10640
- let nextRule = { ...currentRule };
10641
- if (field === 'type') {
10642
- nextRule = value === 'expression'
10643
- ? {
10644
- type: 'expression',
10645
- expression: 'return true;',
10646
- message: 'Validation failed.'
10647
- }
10648
- : this.createDefaultValidationRule();
10649
- }
10650
- else if (field === 'name') {
10651
- nextRule.name = String(value);
10652
- nextRule.message = this.defaultValidationMessage(nextRule);
10653
- }
10654
- else if (field === 'expression') {
10655
- nextRule.expression = String(value);
10656
- }
10657
- else if (field === 'message') {
10658
- nextRule.message = String(value);
10659
- }
10660
- validators[index] = nextRule;
10661
- this.setValue(path, validators);
10662
- this.handleFieldChange();
10663
- }
10664
- createDefaultValidationRule() {
10665
- return {
10666
- type: 'builtin',
10667
- name: 'email',
10668
- message: 'Enter a valid email address.'
10669
- };
10670
- }
10671
- defaultValidationMessage(rule) {
10672
- if (rule.type === 'builtin' && rule.name === 'email') {
10673
- return 'Enter a valid email address.';
10674
- }
10675
- return 'Validation failed.';
10676
- }
10677
10676
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: DynamicPropertiesComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
10678
10677
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.17", type: DynamicPropertiesComponent, isStandalone: true, selector: "app-dynamic-properties", inputs: { onPropertyChange: "onPropertyChange", config: "config", readOnly: "readOnly", includeSections: "includeSections", excludeSections: "excludeSections", allFields: "allFields" }, outputs: { configChange: "configChange" }, usesOnChanges: true, ngImport: i0, template: `
10679
10678
  <div class="dynamic-properties flex flex-col font-sans text-sm">
@@ -10816,51 +10815,6 @@ class DynamicPropertiesComponent {
10816
10815
  </div>
10817
10816
  </ui-field-wrapper>
10818
10817
 
10819
- <!-- Validators Editor -->
10820
- <ui-field-wrapper *ngIf="field.type === 'validators-editor'" [label]="field.label || ''" [helpText]="field.helpText || ''">
10821
- <div class="w-full border border-gray-200 rounded-lg p-3 bg-gray-50">
10822
- <div class="flex items-center justify-between mb-3">
10823
- <span class="text-xs font-semibold text-gray-600 uppercase">{{field.label}}</span>
10824
- <button type="button"
10825
- (click)="addValidator(field.key)"
10826
- class="text-xs px-2 py-1 bg-blue-600 text-white rounded hover:bg-blue-700 transition-colors">
10827
- +
10828
- </button>
10829
- </div>
10830
- <div class="space-y-2">
10831
- <div *ngFor="let val of getValue(field.key) || []; let i = index" class="flex items-center gap-2 p-2 bg-white rounded border border-gray-200">
10832
- <select [ngModel]="val.type || 'builtin'"
10833
- (ngModelChange)="updateValidator(field.key, i, 'type', $event)"
10834
- class="flex-1 h-8 px-2 text-sm rounded border border-gray-300 bg-white focus:border-blue-500 focus:ring-1 focus:ring-blue-500 outline-none">
10835
- <option *ngFor="let option of validatorTypeOptions" [value]="option.value">{{ option.label }}</option>
10836
- </select>
10837
- <select *ngIf="(val.type || 'builtin') === 'builtin'"
10838
- [ngModel]="val.name || 'email'"
10839
- (ngModelChange)="updateValidator(field.key, i, 'name', $event)"
10840
- class="flex-1 h-8 px-2 text-sm rounded border border-gray-300 bg-white focus:border-blue-500 focus:ring-1 focus:ring-blue-500 outline-none">
10841
- <option *ngFor="let option of builtinValidatorOptions" [value]="option.value">{{ option.label }}</option>
10842
- </select>
10843
- <input *ngIf="val.type === 'expression'"
10844
- type="text"
10845
- [ngModel]="val.expression || ''"
10846
- (ngModelChange)="updateValidator(field.key, i, 'expression', $event)"
10847
- placeholder="return true;"
10848
- class="flex-1 h-8 px-2 text-sm rounded border border-gray-300 bg-white focus:border-blue-500 focus:ring-1 focus:ring-blue-500 outline-none">
10849
- <input type="text"
10850
- [ngModel]="val.message || ''"
10851
- (ngModelChange)="updateValidator(field.key, i, 'message', $event)"
10852
- placeholder="Validation message"
10853
- class="flex-1 h-8 px-2 text-sm rounded border border-gray-300 bg-white focus:border-blue-500 focus:ring-1 focus:ring-blue-500 outline-none">
10854
- <button type="button"
10855
- (click)="removeValidator(field.key, i)"
10856
- class="w-8 h-8 flex items-center justify-center text-red-600 hover:bg-red-50 rounded transition-colors">
10857
- <lucide-icon name="x" class="w-4 h-4"></lucide-icon>
10858
- </button>
10859
- </div>
10860
- </div>
10861
- </div>
10862
- </ui-field-wrapper>
10863
-
10864
10818
  <!-- Field Reference -->
10865
10819
  <ui-field-wrapper *ngIf="field.type === 'field-reference'" [label]="field.label || ''" [helpText]="field.helpText || ''">
10866
10820
  <select [ngModel]="getValue(field.key) || ''"
@@ -11109,51 +11063,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImpo
11109
11063
  </div>
11110
11064
  </ui-field-wrapper>
11111
11065
 
11112
- <!-- Validators Editor -->
11113
- <ui-field-wrapper *ngIf="field.type === 'validators-editor'" [label]="field.label || ''" [helpText]="field.helpText || ''">
11114
- <div class="w-full border border-gray-200 rounded-lg p-3 bg-gray-50">
11115
- <div class="flex items-center justify-between mb-3">
11116
- <span class="text-xs font-semibold text-gray-600 uppercase">{{field.label}}</span>
11117
- <button type="button"
11118
- (click)="addValidator(field.key)"
11119
- class="text-xs px-2 py-1 bg-blue-600 text-white rounded hover:bg-blue-700 transition-colors">
11120
- +
11121
- </button>
11122
- </div>
11123
- <div class="space-y-2">
11124
- <div *ngFor="let val of getValue(field.key) || []; let i = index" class="flex items-center gap-2 p-2 bg-white rounded border border-gray-200">
11125
- <select [ngModel]="val.type || 'builtin'"
11126
- (ngModelChange)="updateValidator(field.key, i, 'type', $event)"
11127
- class="flex-1 h-8 px-2 text-sm rounded border border-gray-300 bg-white focus:border-blue-500 focus:ring-1 focus:ring-blue-500 outline-none">
11128
- <option *ngFor="let option of validatorTypeOptions" [value]="option.value">{{ option.label }}</option>
11129
- </select>
11130
- <select *ngIf="(val.type || 'builtin') === 'builtin'"
11131
- [ngModel]="val.name || 'email'"
11132
- (ngModelChange)="updateValidator(field.key, i, 'name', $event)"
11133
- class="flex-1 h-8 px-2 text-sm rounded border border-gray-300 bg-white focus:border-blue-500 focus:ring-1 focus:ring-blue-500 outline-none">
11134
- <option *ngFor="let option of builtinValidatorOptions" [value]="option.value">{{ option.label }}</option>
11135
- </select>
11136
- <input *ngIf="val.type === 'expression'"
11137
- type="text"
11138
- [ngModel]="val.expression || ''"
11139
- (ngModelChange)="updateValidator(field.key, i, 'expression', $event)"
11140
- placeholder="return true;"
11141
- class="flex-1 h-8 px-2 text-sm rounded border border-gray-300 bg-white focus:border-blue-500 focus:ring-1 focus:ring-blue-500 outline-none">
11142
- <input type="text"
11143
- [ngModel]="val.message || ''"
11144
- (ngModelChange)="updateValidator(field.key, i, 'message', $event)"
11145
- placeholder="Validation message"
11146
- class="flex-1 h-8 px-2 text-sm rounded border border-gray-300 bg-white focus:border-blue-500 focus:ring-1 focus:ring-blue-500 outline-none">
11147
- <button type="button"
11148
- (click)="removeValidator(field.key, i)"
11149
- class="w-8 h-8 flex items-center justify-center text-red-600 hover:bg-red-50 rounded transition-colors">
11150
- <lucide-icon name="x" class="w-4 h-4"></lucide-icon>
11151
- </button>
11152
- </div>
11153
- </div>
11154
- </div>
11155
- </ui-field-wrapper>
11156
-
11157
11066
  <!-- Field Reference -->
11158
11067
  <ui-field-wrapper *ngIf="field.type === 'field-reference'" [label]="field.label || ''" [helpText]="field.helpText || ''">
11159
11068
  <select [ngModel]="getValue(field.key) || ''"
@@ -15264,7 +15173,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImpo
15264
15173
  type: Output
15265
15174
  }] } });
15266
15175
 
15267
- function parsePathSegments$2(path) {
15176
+ function parsePathSegments$3(path) {
15268
15177
  const segments = [];
15269
15178
  const matcher = /([^[.\]]+)|\[(\d+)\]/g;
15270
15179
  let match;
@@ -15280,14 +15189,14 @@ function parsePathSegments$2(path) {
15280
15189
  }
15281
15190
  return segments;
15282
15191
  }
15283
- function resolvePathValue$1(value, path) {
15192
+ function resolvePathValue$2(value, path) {
15284
15193
  if (!path)
15285
15194
  return value;
15286
15195
  const normalized = path.trim();
15287
15196
  if (!normalized)
15288
15197
  return value;
15289
15198
  let current = value;
15290
- for (const segment of parsePathSegments$2(normalized)) {
15199
+ for (const segment of parsePathSegments$3(normalized)) {
15291
15200
  if (current === null || current === undefined)
15292
15201
  return undefined;
15293
15202
  if (Array.isArray(current)) {
@@ -15304,7 +15213,7 @@ function resolvePathValue$1(value, path) {
15304
15213
  return current;
15305
15214
  }
15306
15215
  function assignPathValue(target, path, value) {
15307
- const segments = parsePathSegments$2(path);
15216
+ const segments = parsePathSegments$3(path);
15308
15217
  if (!segments.length) {
15309
15218
  return;
15310
15219
  }
@@ -15440,7 +15349,6 @@ class DataPanelComponent {
15440
15349
  selectionFieldId;
15441
15350
  selectionMatchPath;
15442
15351
  childRowsPath;
15443
- formatNumericOptionLabels = false;
15444
15352
  optionLabelPrefixPath;
15445
15353
  rootPathOptions = [];
15446
15354
  rowPathOptions = [];
@@ -15682,7 +15590,6 @@ class DataPanelComponent {
15682
15590
  this.selectionFieldId = undefined;
15683
15591
  this.selectionMatchPath = undefined;
15684
15592
  this.childRowsPath = undefined;
15685
- this.formatNumericOptionLabels = false;
15686
15593
  this.optionLabelPrefixPath = undefined;
15687
15594
  this.rootPathOptions = [];
15688
15595
  this.rowPathOptions = [];
@@ -15724,7 +15631,6 @@ class DataPanelComponent {
15724
15631
  this.selectionFieldId = d.selectionFieldId;
15725
15632
  this.selectionMatchPath = d.selectionMatchPath;
15726
15633
  this.childRowsPath = d.childRowsPath;
15727
- this.formatNumericOptionLabels = d.formatNumericOptionLabels === true;
15728
15634
  this.optionLabelPrefixPath = d.optionLabelPrefixPath;
15729
15635
  // Search
15730
15636
  this.searchEnabled = !!d.searchEnabled;
@@ -15777,10 +15683,7 @@ class DataPanelComponent {
15777
15683
  labelKey: this.sourceType === 'source' ? this.labelKey : undefined,
15778
15684
  valueKey: this.sourceType === 'source' ? this.valueKey : undefined,
15779
15685
  rowsPath: this.sourceType === 'source' ? this.normalizedRowsPath() : undefined,
15780
- formatNumericOptionLabels: this.shouldPersistOptionLabelFormatting()
15781
- ? this.formatNumericOptionLabels
15782
- : undefined,
15783
- optionLabelPrefixPath: this.shouldPersistOptionLabelFormatting()
15686
+ optionLabelPrefixPath: this.shouldPersistOptionLabelPrefix()
15784
15687
  ? this.optionLabelPrefixPath
15785
15688
  : undefined,
15786
15689
  rowSelectionMode: this.sourceType === 'source' && this.bindingShape === 'scalar' && this.rowSelectionMode === 'selected'
@@ -15891,8 +15794,25 @@ class DataPanelComponent {
15891
15794
  showOptionMappingControls() {
15892
15795
  return this.sourceType === 'source' && this.widgetType !== 'table' && this.usesOptionMapping();
15893
15796
  }
15894
- showOptionLabelFormattingControls() {
15895
- return this.widgetType === 'select' && this.usesOptionMapping();
15797
+ showDisplayFormattingControls() {
15798
+ if (this.widgetType === 'select' && this.usesOptionMapping()) {
15799
+ return true;
15800
+ }
15801
+ return this.sourceType === 'source'
15802
+ && this.bindingShape === 'scalar'
15803
+ && (this.widgetType === 'text' || this.widgetType === 'number');
15804
+ }
15805
+ displayFormattingTitle() {
15806
+ return this.widgetType === 'select' ? 'Amount Display' : 'Field Display';
15807
+ }
15808
+ displayFormattingDescription() {
15809
+ if (this.widgetType === 'select') {
15810
+ return 'Format the visible dropdown label without changing the stored option value.';
15811
+ }
15812
+ if (this.widgetType === 'number') {
15813
+ return 'Show a fixed prefix in the visible field value without changing the stored number.';
15814
+ }
15815
+ return 'Show a fixed prefix beside the editable value without changing the stored field value.';
15896
15816
  }
15897
15817
  showStaticOptionsEditor() {
15898
15818
  return this.sourceType === 'static' && this.widgetType !== 'table' && this.usesOptionMapping();
@@ -15922,12 +15842,12 @@ class DataPanelComponent {
15922
15842
  }
15923
15843
  getPreviewLabel(row) {
15924
15844
  const key = this.labelKey || 'label';
15925
- const value = resolvePathValue$1(row, key);
15845
+ const value = resolvePathValue$2(row, key);
15926
15846
  return value === undefined || value === null || value === '' ? '(no label)' : String(value);
15927
15847
  }
15928
15848
  getPreviewValue(row) {
15929
15849
  const key = this.valueKey || 'value';
15930
- const value = resolvePathValue$1(row, key);
15850
+ const value = resolvePathValue$2(row, key);
15931
15851
  return value === undefined || value === null ? '' : String(value);
15932
15852
  }
15933
15853
  availableRootPaths() {
@@ -15944,8 +15864,13 @@ class DataPanelComponent {
15944
15864
  const sample = this.extractPreviewRows(this.previewRows, this.effectiveRowsPath())[0];
15945
15865
  return sample ? collectArrayPaths(sample) : [];
15946
15866
  }
15947
- shouldPersistOptionLabelFormatting() {
15948
- return this.widgetType === 'select' && this.usesOptionMapping();
15867
+ shouldPersistOptionLabelPrefix() {
15868
+ if (this.widgetType === 'select' && this.usesOptionMapping()) {
15869
+ return true;
15870
+ }
15871
+ return this.sourceType === 'source'
15872
+ && this.bindingShape === 'scalar'
15873
+ && (this.widgetType === 'text' || this.widgetType === 'number');
15949
15874
  }
15950
15875
  usesOptionMapping() {
15951
15876
  return this.bindingShape === 'list' || this.widgetType === 'search';
@@ -15955,7 +15880,7 @@ class DataPanelComponent {
15955
15880
  return normalized ? normalized : 'defaultValue';
15956
15881
  }
15957
15882
  readScalarTargetValue() {
15958
- const resolved = resolvePathValue$1(this.config, this.scalarTargetPath());
15883
+ const resolved = resolvePathValue$2(this.config, this.scalarTargetPath());
15959
15884
  if (resolved !== undefined) {
15960
15885
  return resolved;
15961
15886
  }
@@ -15978,7 +15903,7 @@ class DataPanelComponent {
15978
15903
  }
15979
15904
  const flattened = [];
15980
15905
  for (const row of rows) {
15981
- const resolved = resolvePathValue$1(row, normalizedPath);
15906
+ const resolved = resolvePathValue$2(row, normalizedPath);
15982
15907
  if (Array.isArray(resolved)) {
15983
15908
  flattened.push(...resolved);
15984
15909
  continue;
@@ -16266,18 +16191,9 @@ class DataPanelComponent {
16266
16191
  </div>
16267
16192
  </div>
16268
16193
 
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>
16194
+ <div class="rounded-lg border border-gray-200 bg-white p-3" *ngIf="showDisplayFormattingControls()">
16195
+ <div class="text-xs font-semibold uppercase tracking-wide text-gray-500">{{ displayFormattingTitle() }}</div>
16196
+ <div class="mt-1 text-[11px] text-gray-500">{{ displayFormattingDescription() }}</div>
16281
16197
 
16282
16198
  <div class="mt-3 flex flex-col gap-1">
16283
16199
  <label class="text-xs font-medium text-gray-500">Prefix Key</label>
@@ -16885,18 +16801,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImpo
16885
16801
  </div>
16886
16802
  </div>
16887
16803
 
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>
16804
+ <div class="rounded-lg border border-gray-200 bg-white p-3" *ngIf="showDisplayFormattingControls()">
16805
+ <div class="text-xs font-semibold uppercase tracking-wide text-gray-500">{{ displayFormattingTitle() }}</div>
16806
+ <div class="mt-1 text-[11px] text-gray-500">{{ displayFormattingDescription() }}</div>
16900
16807
 
16901
16808
  <div class="mt-3 flex flex-col gap-1">
16902
16809
  <label class="text-xs font-medium text-gray-500">Prefix Key</label>
@@ -17674,11 +17581,18 @@ class RulesPanelComponent {
17674
17581
  ruleActionOptions = [
17675
17582
  { label: 'Visible', value: 'visible' },
17676
17583
  { label: 'Hidden', value: 'hidden' },
17677
- { label: 'Enabled', value: 'enable' },
17678
17584
  { label: 'Disabled', value: 'disable' },
17679
17585
  { label: 'Required', value: 'required' },
17680
17586
  { label: 'Optional', value: 'optional' }
17681
17587
  ];
17588
+ actionLabels = {
17589
+ visible: 'Visible',
17590
+ hidden: 'Hidden',
17591
+ enable: 'Enabled',
17592
+ disable: 'Disabled',
17593
+ required: 'Required',
17594
+ optional: 'Optional'
17595
+ };
17682
17596
  severityOptions = [
17683
17597
  { label: 'Warn', value: 'warn' },
17684
17598
  { label: 'Error', value: 'error' }
@@ -17730,7 +17644,7 @@ class RulesPanelComponent {
17730
17644
  return `${primary} / else ${this.getActionLabel(rule.elseAction)}`;
17731
17645
  }
17732
17646
  getActionLabel(action) {
17733
- return this.ruleActionOptions.find((option) => option.value === action)?.label ?? action;
17647
+ return this.actionLabels[action] ?? action;
17734
17648
  }
17735
17649
  getActionColor(action) {
17736
17650
  switch (action) {
@@ -17749,7 +17663,7 @@ class RulesPanelComponent {
17749
17663
  <div class="flex items-center justify-between mb-2">
17750
17664
  <div class="flex flex-col">
17751
17665
  <span class="font-semibold text-gray-700">Rules</span>
17752
- <span class="text-xs text-gray-500">Rules can override visibility, enabled, and required set elsewhere.</span>
17666
+ <span class="text-xs text-gray-500">Rules can override visibility, disabled, and required state set elsewhere.</span>
17753
17667
  </div>
17754
17668
  <button (click)="addRule()"
17755
17669
  [disabled]="readOnly"
@@ -17832,7 +17746,7 @@ class RulesPanelComponent {
17832
17746
  <div class="flex flex-col gap-2">
17833
17747
  <div class="flex items-center gap-2 bg-amber-50 p-2 rounded-md border border-amber-100">
17834
17748
  <span class="text-xs font-semibold text-amber-700 uppercase">Severity</span>
17835
- <span class="text-xs text-amber-800 italic">Saved for backend validation only.</span>
17749
+ <span class="text-xs text-amber-800 italic">Error severity is emitted in runtime validation; warn stays informational.</span>
17836
17750
  </div>
17837
17751
 
17838
17752
  <div class="flex items-center gap-2">
@@ -17871,7 +17785,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImpo
17871
17785
  <div class="flex items-center justify-between mb-2">
17872
17786
  <div class="flex flex-col">
17873
17787
  <span class="font-semibold text-gray-700">Rules</span>
17874
- <span class="text-xs text-gray-500">Rules can override visibility, enabled, and required set elsewhere.</span>
17788
+ <span class="text-xs text-gray-500">Rules can override visibility, disabled, and required state set elsewhere.</span>
17875
17789
  </div>
17876
17790
  <button (click)="addRule()"
17877
17791
  [disabled]="readOnly"
@@ -17954,7 +17868,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImpo
17954
17868
  <div class="flex flex-col gap-2">
17955
17869
  <div class="flex items-center gap-2 bg-amber-50 p-2 rounded-md border border-amber-100">
17956
17870
  <span class="text-xs font-semibold text-amber-700 uppercase">Severity</span>
17957
- <span class="text-xs text-amber-800 italic">Saved for backend validation only.</span>
17871
+ <span class="text-xs text-amber-800 italic">Error severity is emitted in runtime validation; warn stays informational.</span>
17958
17872
  </div>
17959
17873
 
17960
17874
  <div class="flex items-center gap-2">
@@ -19546,34 +19460,35 @@ class FormPreviewComponent {
19546
19460
  copyData() {
19547
19461
  navigator.clipboard.writeText(JSON.stringify(this.previewData(), null, 2));
19548
19462
  }
19549
- sanitizePreviewData(value) {
19463
+ sanitizePreviewData(value, withinFileField = false) {
19550
19464
  if (Array.isArray(value)) {
19551
- return value.map(item => this.sanitizePreviewData(item));
19465
+ return value.map(item => this.sanitizePreviewData(item, withinFileField));
19552
19466
  }
19553
19467
  if (!this.isObjectRecord(value)) {
19554
19468
  return value;
19555
19469
  }
19470
+ const nextWithinFileField = withinFileField || this.isFileFieldRecord(value);
19556
19471
  const sanitized = {};
19557
19472
  for (const [key, entryValue] of Object.entries(value)) {
19558
- if (key === 'data' && this.shouldHideFileData(value, entryValue)) {
19473
+ if (key === 'data' && this.shouldHideFileData(nextWithinFileField, entryValue)) {
19559
19474
  continue;
19560
19475
  }
19561
- sanitized[key] = this.sanitizePreviewData(entryValue);
19476
+ sanitized[key] = this.sanitizePreviewData(entryValue, nextWithinFileField);
19562
19477
  }
19563
19478
  return sanitized;
19564
19479
  }
19565
19480
  isObjectRecord(value) {
19566
19481
  return !!value && typeof value === 'object';
19567
19482
  }
19568
- shouldHideFileData(record, value) {
19569
- if (!this.isFileFieldRecord(record))
19483
+ shouldHideFileData(withinFileField, value) {
19484
+ if (!withinFileField)
19570
19485
  return false;
19571
19486
  return this.isBytePayload(value) || this.isBase64Payload(value);
19572
19487
  }
19573
19488
  isFileFieldRecord(value) {
19574
19489
  return typeof value['fieldName'] === 'string'
19575
19490
  && Object.hasOwn(value, 'fieldValue')
19576
- && Object.hasOwn(value, 'fileType');
19491
+ && (value['fieldType'] === 'file' || Object.hasOwn(value, 'fileType'));
19577
19492
  }
19578
19493
  isBytePayload(value) {
19579
19494
  if (value instanceof Uint8Array)
@@ -20220,13 +20135,13 @@ async function updateFieldSettings(state, args, widgetDefs = []) {
20220
20135
  updates[key] = value;
20221
20136
  }
20222
20137
  }
20138
+ const html5Patch = {};
20223
20139
  if (typeof a['disabled'] === 'boolean') {
20224
- updates.disabled = a['disabled'];
20140
+ html5Patch['disabled'] = a['disabled'];
20225
20141
  }
20226
20142
  if (typeof a['readonly'] === 'boolean') {
20227
- updates.readonly = a['readonly'];
20143
+ html5Patch['readonly'] = a['readonly'];
20228
20144
  }
20229
- const html5Patch = {};
20230
20145
  const html5Keys = ['required', 'min', 'max', 'step', 'minLength', 'maxLength', 'pattern'];
20231
20146
  for (const key of html5Keys) {
20232
20147
  if (typeof a[key] !== 'undefined') {
@@ -22036,7 +21951,7 @@ const DATA_SOURCES_TEMPLATE = {
22036
21951
  "type": "text",
22037
21952
  "label": "Selected country code",
22038
21953
  "placeholder": "Auto-filled",
22039
- "readonly": true,
21954
+ "html5": { "readonly": true },
22040
21955
  "helpText": "Updated by an event binding on the Country field."
22041
21956
  },
22042
21957
  {
@@ -22442,8 +22357,8 @@ const TRANSACTION_KYC_REVIEW_TEMPLATE = {
22442
22357
  "name": "clientDisplay",
22443
22358
  "type": "text",
22444
22359
  "label": "Client",
22445
- "readonly": true,
22446
22360
  "html5": {
22361
+ "readonly": true,
22447
22362
  "required": false
22448
22363
  },
22449
22364
  "rules": [
@@ -22489,7 +22404,7 @@ const TRANSACTION_KYC_REVIEW_TEMPLATE = {
22489
22404
  "type": "text",
22490
22405
  "label": "Balance",
22491
22406
  "defaultValue": "USD 24,580.00",
22492
- "readonly": true,
22407
+ "html5": { "readonly": true },
22493
22408
  "rules": [
22494
22409
  {
22495
22410
  "id": "rule_balance_visible",
@@ -23257,7 +23172,7 @@ const JOURNEY_BRANCHING_TEMPLATE_STEP_TWO = {
23257
23172
  "name": "summaryCountryCode",
23258
23173
  "type": "text",
23259
23174
  "label": "Country Code",
23260
- "readonly": true,
23175
+ "html5": { "readonly": true },
23261
23176
  "dataConfig": {
23262
23177
  "type": "source",
23263
23178
  "datasourceId": "journey_branch_ds_country_profile",
@@ -23462,7 +23377,7 @@ const JOURNEY_BRANCHING_TEMPLATE_STEP_THREE = {
23462
23377
  "name": "summaryCurrency",
23463
23378
  "type": "text",
23464
23379
  "label": "Currency",
23465
- "readonly": true,
23380
+ "html5": { "readonly": true },
23466
23381
  "dataConfig": {
23467
23382
  "type": "source",
23468
23383
  "datasourceId": "journey_branch_ds_country_profile",
@@ -23475,7 +23390,7 @@ const JOURNEY_BRANCHING_TEMPLATE_STEP_THREE = {
23475
23390
  "name": "summaryTimezone",
23476
23391
  "type": "text",
23477
23392
  "label": "Timezone",
23478
- "readonly": true,
23393
+ "html5": { "readonly": true },
23479
23394
  "dataConfig": {
23480
23395
  "type": "source",
23481
23396
  "datasourceId": "journey_branch_ds_country_profile",
@@ -31707,7 +31622,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImpo
31707
31622
  }]
31708
31623
  }] });
31709
31624
 
31710
- function parsePathSegments$1(path) {
31625
+ function parsePathSegments$2(path) {
31711
31626
  const segments = [];
31712
31627
  const matcher = /([^[.\]]+)|\[(\d+)\]/g;
31713
31628
  let match;
@@ -31723,14 +31638,14 @@ function parsePathSegments$1(path) {
31723
31638
  }
31724
31639
  return segments;
31725
31640
  }
31726
- function resolvePathValue(value, path) {
31641
+ function resolvePathValue$1(value, path) {
31727
31642
  if (!path)
31728
31643
  return value;
31729
31644
  const normalizedPath = path.trim();
31730
31645
  if (!normalizedPath)
31731
31646
  return value;
31732
31647
  let current = value;
31733
- for (const segment of parsePathSegments$1(normalizedPath)) {
31648
+ for (const segment of parsePathSegments$2(normalizedPath)) {
31734
31649
  if (current === null || current === undefined) {
31735
31650
  return undefined;
31736
31651
  }
@@ -31754,7 +31669,7 @@ function hasPathSyntax(path) {
31754
31669
  return false;
31755
31670
  return path.includes('.') || path.includes('[');
31756
31671
  }
31757
- function valuesMatch(left, right) {
31672
+ function valuesMatch$1(left, right) {
31758
31673
  if (Object.is(left, right))
31759
31674
  return true;
31760
31675
  if (left === undefined || left === null || right === undefined || right === null)
@@ -31774,7 +31689,20 @@ function compareSortableValues(left, right) {
31774
31689
  }
31775
31690
  return String(left ?? '').localeCompare(String(right ?? ''));
31776
31691
  }
31692
+ function toDisplayText(value) {
31693
+ if (typeof value === 'string') {
31694
+ const trimmed = value.trim();
31695
+ return trimmed.length > 0 ? trimmed : undefined;
31696
+ }
31697
+ if (typeof value === 'number' || typeof value === 'boolean') {
31698
+ return String(value);
31699
+ }
31700
+ return undefined;
31701
+ }
31777
31702
  class DataProvider {
31703
+ async getValueDisplayPrefix(_field, _engine) {
31704
+ return '';
31705
+ }
31778
31706
  // Non-abstract with default implementation for backward compatibility
31779
31707
  async queryOptions(field, query, engine) {
31780
31708
  const options = await this.getOptions(field, engine);
@@ -31797,8 +31725,8 @@ class DataProvider {
31797
31725
  if (query.sort && query.sort.length > 0) {
31798
31726
  rows = [...rows].sort((a, b) => {
31799
31727
  for (const s of query.sort) {
31800
- const valA = resolvePathValue(a, s.column);
31801
- const valB = resolvePathValue(b, s.column);
31728
+ const valA = resolvePathValue$1(a, s.column);
31729
+ const valB = resolvePathValue$1(b, s.column);
31802
31730
  if (valA === valB)
31803
31731
  continue;
31804
31732
  const result = compareSortableValues(valA, valB);
@@ -31952,27 +31880,42 @@ class DefaultDataProvider extends DataProvider {
31952
31880
  return cfg.staticValue;
31953
31881
  return currentValue !== undefined ? currentValue : field.defaultValue;
31954
31882
  }
31955
- let rows = await this.getRawRows(cfg, field, engine);
31956
- rows = this.resolveCollectionRows(rows, cfg, engine);
31957
- rows = this.applyRowFilters(rows, cfg.filters, engine);
31958
- if (!rows || rows.length === 0) {
31959
- return currentValue !== undefined ? currentValue : field.defaultValue;
31960
- }
31961
- const selectedRow = this.selectScalarRow(rows, cfg, engine);
31962
- if (cfg.rowSelectionMode === 'selected' && !selectedRow) {
31883
+ const context = await this.getScalarValueContext(cfg, field, engine);
31884
+ if (!context) {
31963
31885
  return currentValue !== undefined ? currentValue : field.defaultValue;
31964
31886
  }
31965
- const row = selectedRow ?? rows[0];
31887
+ const row = context.row;
31966
31888
  const resolvedPath = cfg.valueKey;
31967
- const resolvedValue = resolvePathValue(row, resolvedPath);
31889
+ const resolvedValue = resolvePathValue$1(row, resolvedPath);
31968
31890
  if (resolvedPath && resolvedValue !== undefined) {
31969
31891
  return resolvedValue;
31970
31892
  }
31971
- if (resolvePathValue(row, 'value') !== undefined) {
31972
- return resolvePathValue(row, 'value');
31893
+ if (resolvePathValue$1(row, 'value') !== undefined) {
31894
+ return resolvePathValue$1(row, 'value');
31973
31895
  }
31974
31896
  return row;
31975
31897
  }
31898
+ async getValueDisplayPrefix(field, engine) {
31899
+ const cfg = getEffectiveDataConfig(field);
31900
+ const prefixPath = cfg.optionLabelPrefixPath?.trim();
31901
+ if (!prefixPath) {
31902
+ return '';
31903
+ }
31904
+ const context = await this.getScalarValueContext(cfg, field, engine);
31905
+ if (!context) {
31906
+ return '';
31907
+ }
31908
+ for (const candidate of [context.row, context.parentRow, context.sourceRow]) {
31909
+ if (!candidate)
31910
+ continue;
31911
+ const resolved = resolvePathValue$1(candidate, prefixPath);
31912
+ const value = toDisplayText(resolved);
31913
+ if (value) {
31914
+ return value;
31915
+ }
31916
+ }
31917
+ return '';
31918
+ }
31976
31919
  async resolveValue(field, value) {
31977
31920
  return String(value);
31978
31921
  }
@@ -32020,6 +31963,18 @@ class DefaultDataProvider extends DataProvider {
32020
31963
  const rows = await this.getRawRows(cfg, field, engine);
32021
31964
  return this.resolveCollectionRowContexts(rows, cfg, engine);
32022
31965
  }
31966
+ async getScalarValueContext(cfg, field, engine) {
31967
+ const contexts = await this.getOptionRowContexts(cfg, field, engine);
31968
+ const filteredContexts = this.applyOptionRowFilters(contexts, cfg.filters, engine);
31969
+ if (filteredContexts.length === 0) {
31970
+ return undefined;
31971
+ }
31972
+ const selectedContext = this.selectOptionContext(filteredContexts, cfg, engine);
31973
+ if (cfg.rowSelectionMode === 'selected' && !selectedContext) {
31974
+ return undefined;
31975
+ }
31976
+ return selectedContext ?? filteredContexts[0];
31977
+ }
32023
31978
  resolveCollectionRowContexts(rows, cfg, engine) {
32024
31979
  const parentContexts = this.extractRowContexts(rows, cfg.rowsPath);
32025
31980
  if (cfg.rowSelectionMode === 'selected' && cfg.childRowsPath) {
@@ -32056,7 +32011,7 @@ class DefaultDataProvider extends DataProvider {
32056
32011
  }
32057
32012
  const flattened = [];
32058
32013
  for (const row of rows) {
32059
- const resolved = resolvePathValue(row, normalizedPath);
32014
+ const resolved = resolvePathValue$1(row, normalizedPath);
32060
32015
  if (Array.isArray(resolved)) {
32061
32016
  for (const entry of resolved) {
32062
32017
  flattened.push({
@@ -32084,7 +32039,7 @@ class DefaultDataProvider extends DataProvider {
32084
32039
  }
32085
32040
  const flattened = [];
32086
32041
  for (const row of rows) {
32087
- const resolved = resolvePathValue(row, normalizedPath);
32042
+ const resolved = resolvePathValue$1(row, normalizedPath);
32088
32043
  if (Array.isArray(resolved)) {
32089
32044
  for (const entry of resolved) {
32090
32045
  flattened.push(this.toRowRecord(entry));
@@ -32112,7 +32067,7 @@ class DefaultDataProvider extends DataProvider {
32112
32067
  if (selectorValue === undefined || selectorValue === null || selectorValue === '') {
32113
32068
  return undefined;
32114
32069
  }
32115
- return rows.find(row => valuesMatch(resolvePathValue(row, cfg.selectionMatchPath), selectorValue));
32070
+ return rows.find(row => valuesMatch$1(resolvePathValue$1(row, cfg.selectionMatchPath), selectorValue));
32116
32071
  }
32117
32072
  selectOptionContext(contexts, cfg, engine) {
32118
32073
  if (contexts.length === 0)
@@ -32129,7 +32084,7 @@ class DefaultDataProvider extends DataProvider {
32129
32084
  if (selectorValue === undefined || selectorValue === null || selectorValue === '') {
32130
32085
  return undefined;
32131
32086
  }
32132
- return contexts.find(context => valuesMatch(resolvePathValue(context.row, cfg.selectionMatchPath), selectorValue));
32087
+ return contexts.find(context => valuesMatch$1(resolvePathValue$1(context.row, cfg.selectionMatchPath), selectorValue));
32133
32088
  }
32134
32089
  applyRowFilters(rows, filters, engine) {
32135
32090
  if (!filters || filters.length === 0)
@@ -32146,7 +32101,7 @@ class DefaultDataProvider extends DataProvider {
32146
32101
  const expected = this.resolveFilterValue(filter, engine);
32147
32102
  if (expected === undefined)
32148
32103
  return true;
32149
- const actual = resolvePathValue(row, filter.column);
32104
+ const actual = resolvePathValue$1(row, filter.column);
32150
32105
  const val = expected;
32151
32106
  switch (filter.op) {
32152
32107
  case 'eq': return actual === val;
@@ -32218,17 +32173,16 @@ class DefaultDataProvider extends DataProvider {
32218
32173
  }
32219
32174
  mapRowToOption(row, labelKey, valueKey) {
32220
32175
  return {
32221
- label: String(resolvePathValue(row, labelKey) ?? ''),
32222
- value: this.toOptionValue(resolvePathValue(row, valueKey))
32176
+ label: String(resolvePathValue$1(row, labelKey) ?? ''),
32177
+ value: this.toOptionValue(resolvePathValue$1(row, valueKey))
32223
32178
  };
32224
32179
  }
32225
32180
  mapContextToOption(context, labelKey, valueKey, cfg) {
32226
- const rawLabel = String(resolvePathValue(context.row, labelKey) ?? '');
32227
32181
  const prefix = this.resolveOptionLabelPrefix(context, cfg);
32228
- const label = this.formatOptionLabel(rawLabel, cfg);
32229
32182
  return {
32230
- label: prefix ? `${prefix} ${label}` : label,
32231
- value: this.toOptionValue(resolvePathValue(context.row, valueKey))
32183
+ label: String(resolvePathValue$1(context.row, labelKey) ?? ''),
32184
+ displayPrefix: prefix || undefined,
32185
+ value: this.toOptionValue(resolvePathValue$1(context.row, valueKey))
32232
32186
  };
32233
32187
  }
32234
32188
  resolveOptionLabelPrefix(context, cfg) {
@@ -32239,39 +32193,14 @@ class DefaultDataProvider extends DataProvider {
32239
32193
  for (const candidate of [context.row, context.parentRow, context.sourceRow]) {
32240
32194
  if (!candidate)
32241
32195
  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) {
32196
+ const resolved = resolvePathValue$1(candidate, prefixPath);
32197
+ const value = toDisplayText(resolved);
32198
+ if (value) {
32247
32199
  return value;
32248
32200
  }
32249
32201
  }
32250
32202
  return '';
32251
32203
  }
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
- }
32275
32204
  async getRuntimeOptions(field, engine) {
32276
32205
  if (!engine)
32277
32206
  return undefined;
@@ -32430,12 +32359,68 @@ function getHeadingClass(level) {
32430
32359
  }
32431
32360
  }
32432
32361
 
32362
+ function parsePathSegments$1(path) {
32363
+ const segments = [];
32364
+ const matcher = /([^[.\]]+)|\[(\d+)\]/g;
32365
+ let match;
32366
+ while ((match = matcher.exec(path)) !== null) {
32367
+ const [, property, index] = match;
32368
+ if (property) {
32369
+ segments.push(property);
32370
+ continue;
32371
+ }
32372
+ if (index !== undefined) {
32373
+ segments.push(index);
32374
+ }
32375
+ }
32376
+ return segments;
32377
+ }
32378
+ function resolvePathValue(value, path) {
32379
+ if (!path)
32380
+ return value;
32381
+ const normalizedPath = path.trim();
32382
+ if (!normalizedPath)
32383
+ return value;
32384
+ let current = value;
32385
+ for (const segment of parsePathSegments$1(normalizedPath)) {
32386
+ if (current === null || current === undefined) {
32387
+ return undefined;
32388
+ }
32389
+ if (Array.isArray(current)) {
32390
+ const index = Number(segment);
32391
+ if (!Number.isInteger(index)) {
32392
+ return undefined;
32393
+ }
32394
+ current = current[index];
32395
+ continue;
32396
+ }
32397
+ if (typeof current !== 'object') {
32398
+ return undefined;
32399
+ }
32400
+ current = current[segment];
32401
+ }
32402
+ return current;
32403
+ }
32404
+ function valuesMatch(left, right) {
32405
+ if (Object.is(left, right))
32406
+ return true;
32407
+ if (left === undefined || left === null || right === undefined || right === null)
32408
+ return false;
32409
+ const leftIsPrimitive = typeof left === 'string' || typeof left === 'number' || typeof left === 'boolean';
32410
+ const rightIsPrimitive = typeof right === 'string' || typeof right === 'number' || typeof right === 'boolean';
32411
+ if (leftIsPrimitive && rightIsPrimitive) {
32412
+ return String(left) === String(right);
32413
+ }
32414
+ return false;
32415
+ }
32433
32416
  class TextFieldWidgetComponent {
32434
32417
  _config;
32435
32418
  hasReceivedConfig = false;
32436
32419
  dependencyValueSnapshot = new Map();
32437
32420
  isControlFocused = false;
32438
32421
  isControlHovered = false;
32422
+ displayPrefix = '';
32423
+ formattedNumberValue = '';
32439
32424
  set config(value) {
32440
32425
  const previousSignature = this.getDataConfigSignature(this._config);
32441
32426
  this._config = value;
@@ -32450,7 +32435,10 @@ class TextFieldWidgetComponent {
32450
32435
  if (this.dataProvider && this._config?.dataConfig) {
32451
32436
  this.seedDependencySnapshotFromEngine();
32452
32437
  void this.refreshValueFromDataSource();
32438
+ return;
32453
32439
  }
32440
+ this.displayPrefix = '';
32441
+ this.syncFormattedNumberValue();
32454
32442
  }
32455
32443
  get config() {
32456
32444
  return this._config;
@@ -32473,6 +32461,12 @@ class TextFieldWidgetComponent {
32473
32461
  isColorField() {
32474
32462
  return this.config?.type === 'color';
32475
32463
  }
32464
+ usesFormattedNumberInput() {
32465
+ return this.config?.type === 'number' && usesFieldThousandSeparator(this.config);
32466
+ }
32467
+ hasDisplayPrefix() {
32468
+ return !this.isTextarea() && !this.isColorField() && this.displayPrefix.trim().length > 0;
32469
+ }
32476
32470
  getControlClass(multiline = false) {
32477
32471
  return getTextControlClass({
32478
32472
  invalid: !!this.error,
@@ -32510,6 +32504,9 @@ class TextFieldWidgetComponent {
32510
32504
  if (this.dataProvider && this.config.dataConfig) {
32511
32505
  void this.refreshValueFromDataSource();
32512
32506
  }
32507
+ else {
32508
+ this.syncFormattedNumberValue();
32509
+ }
32513
32510
  // Sync enabled state based on config + rules
32514
32511
  this.syncEnabledState();
32515
32512
  if (this.engine) {
@@ -32522,7 +32519,7 @@ class TextFieldWidgetComponent {
32522
32519
  const datasourceId = this.config?.dataConfig?.datasourceId;
32523
32520
  if (!datasourceId || update.datasourceId !== datasourceId)
32524
32521
  return;
32525
- void this.refreshValueFromDataSource();
32522
+ void this.refreshValueFromDataSource(true);
32526
32523
  });
32527
32524
  }
32528
32525
  this.engine.valueChanges$
@@ -32530,7 +32527,7 @@ class TextFieldWidgetComponent {
32530
32527
  .subscribe(values => {
32531
32528
  this.syncEnabledState();
32532
32529
  if (this.haveDependencyValuesChanged(values)) {
32533
- void this.refreshValueFromDataSource();
32530
+ void this.refreshValueFromDataSource(true);
32534
32531
  }
32535
32532
  });
32536
32533
  // Listen for submit attempts to show validation errors
@@ -32545,6 +32542,9 @@ class TextFieldWidgetComponent {
32545
32542
  this.control.valueChanges
32546
32543
  .pipe(takeUntilDestroyed(this.destroyRef))
32547
32544
  .subscribe(val => {
32545
+ if (this.usesFormattedNumberInput() && !this.isControlFocused) {
32546
+ this.syncFormattedNumberValue();
32547
+ }
32548
32548
  if (this.engine) {
32549
32549
  this.engine.setValue(this.config.name, val);
32550
32550
  this.engine.emitUiEvent({ fieldId: this.config.id, fieldName: this.config.name, type: 'change', value: val });
@@ -32556,6 +32556,7 @@ class TextFieldWidgetComponent {
32556
32556
  }
32557
32557
  onFocus() {
32558
32558
  this.isControlFocused = true;
32559
+ this.syncFormattedNumberValue();
32559
32560
  if (this.engine)
32560
32561
  this.engine.emitUiEvent({ fieldId: this.config.id, fieldName: this.config.name, type: 'focus' });
32561
32562
  }
@@ -32565,6 +32566,8 @@ class TextFieldWidgetComponent {
32565
32566
  }
32566
32567
  onBlur() {
32567
32568
  this.isControlFocused = false;
32569
+ this.control.markAsTouched();
32570
+ this.syncFormattedNumberValue();
32568
32571
  if (this.engine) {
32569
32572
  this.engine.emitUiEvent({ fieldId: this.config.id, fieldName: this.config.name, type: 'blur' });
32570
32573
  if (this.control.touched) {
@@ -32612,6 +32615,15 @@ class TextFieldWidgetComponent {
32612
32615
  invalid: !!this.error
32613
32616
  }), splitControlSurfaceStyles(this.config.style).controlStyles);
32614
32617
  }
32618
+ getSingleLineControlStyles() {
32619
+ const styles = this.getControlStyles();
32620
+ if (!this.hasDisplayPrefix()) {
32621
+ return styles;
32622
+ }
32623
+ return mergeAndNormalize(styles, {
32624
+ paddingLeft: `calc(20px + ${Math.max(this.displayPrefix.length, 1)}ch)`
32625
+ });
32626
+ }
32615
32627
  hasWrapperFrame() {
32616
32628
  return hasWrapperSurfaceStyles(this.config.style);
32617
32629
  }
@@ -32619,7 +32631,7 @@ class TextFieldWidgetComponent {
32619
32631
  return this.engine ? this.engine.isFieldVisible(this.config.id) : true;
32620
32632
  }
32621
32633
  get enabled() {
32622
- if (this.config?.disabled)
32634
+ if (isFieldDisabled(this.config))
32623
32635
  return false;
32624
32636
  return this.engine ? this.engine.isFieldEnabled(this.config.id) : true;
32625
32637
  }
@@ -32645,14 +32657,25 @@ class TextFieldWidgetComponent {
32645
32657
  this.control.disable({ emitEvent: false });
32646
32658
  }
32647
32659
  }
32648
- async refreshValueFromDataSource() {
32660
+ async refreshValueFromDataSource(clearWhenMissing = false) {
32649
32661
  if (!this.dataProvider || !this.config.dataConfig)
32650
32662
  return;
32651
32663
  try {
32664
+ if (clearWhenMissing && !(await this.hasSelectedRowMatch())) {
32665
+ this.clearResolvedValue();
32666
+ return;
32667
+ }
32652
32668
  const val = await this.dataProvider.getValue(this.config, this.engine);
32653
- if (val === undefined || val === null)
32669
+ this.displayPrefix = await this.dataProvider.getValueDisplayPrefix(this.config, this.engine);
32670
+ if (val === undefined || val === null) {
32671
+ if (!clearWhenMissing) {
32672
+ return;
32673
+ }
32674
+ this.clearResolvedValue();
32654
32675
  return;
32676
+ }
32655
32677
  this.control.setValue(val, { emitEvent: false });
32678
+ this.syncFormattedNumberValue();
32656
32679
  if (this.engine) {
32657
32680
  this.engine.setValue(this.config.name, val);
32658
32681
  }
@@ -32661,12 +32684,48 @@ class TextFieldWidgetComponent {
32661
32684
  // Ignore failed datasource refreshes; field remains editable.
32662
32685
  }
32663
32686
  }
32687
+ clearResolvedValue() {
32688
+ this.displayPrefix = '';
32689
+ this.control.setValue(null, { emitEvent: false });
32690
+ this.syncFormattedNumberValue();
32691
+ if (this.engine) {
32692
+ this.engine.setValue(this.config.name, null);
32693
+ }
32694
+ }
32695
+ async hasSelectedRowMatch() {
32696
+ const cfg = this.config.dataConfig;
32697
+ if (!cfg
32698
+ || cfg.rowSelectionMode !== 'selected'
32699
+ || !cfg.selectionFieldId
32700
+ || !cfg.selectionMatchPath
32701
+ || !this.engine) {
32702
+ return true;
32703
+ }
32704
+ const selectorField = this.engine.getSchema().fields.find(field => field.id === cfg.selectionFieldId);
32705
+ if (!selectorField) {
32706
+ return false;
32707
+ }
32708
+ const selectorValue = this.engine.getValue(selectorField.name);
32709
+ if (selectorValue === undefined || selectorValue === null || selectorValue === '') {
32710
+ return false;
32711
+ }
32712
+ const rows = await this.dataProvider.getList(this.config, this.engine);
32713
+ return rows.some(row => valuesMatch(resolvePathValue(row, cfg.selectionMatchPath), selectorValue));
32714
+ }
32664
32715
  getDataConfigSignature(config) {
32665
32716
  if (!config)
32666
32717
  return '';
32667
32718
  const dataConfig = config.dataConfig;
32668
32719
  if (!dataConfig) {
32669
- return `${config.id}::no-data-config`;
32720
+ return [
32721
+ config.id,
32722
+ config.name,
32723
+ config.type,
32724
+ String(config.html5?.readonly ?? ''),
32725
+ String(config.html5?.disabled ?? ''),
32726
+ String(config.useThousandSeparator ?? ''),
32727
+ 'no-data-config'
32728
+ ].join('::');
32670
32729
  }
32671
32730
  const sourceKey = dataConfig.type === 'source' || dataConfig.type === 'global' || dataConfig.type === 'api'
32672
32731
  ? String(dataConfig.datasourceId ?? '')
@@ -32674,13 +32733,18 @@ class TextFieldWidgetComponent {
32674
32733
  return [
32675
32734
  config.id,
32676
32735
  config.name,
32736
+ String(config.html5?.readonly ?? ''),
32737
+ String(config.html5?.disabled ?? ''),
32677
32738
  dataConfig.type ?? '',
32678
32739
  sourceKey,
32679
32740
  String(dataConfig.valueKey ?? ''),
32680
32741
  String(dataConfig.rowsPath ?? ''),
32742
+ String(dataConfig.optionLabelPrefixPath ?? ''),
32743
+ String(dataConfig.optionLabelPrefixFieldId ?? ''),
32681
32744
  String(dataConfig.rowSelectionMode ?? ''),
32682
32745
  String(dataConfig.selectionFieldId ?? ''),
32683
32746
  String(dataConfig.selectionMatchPath ?? ''),
32747
+ String(config.useThousandSeparator ?? ''),
32684
32748
  (dataConfig.filters ?? []).map(filter => `${filter.column}:${filter.valueFrom ?? ''}:${filter.fieldId ?? ''}`).join('|')
32685
32749
  ].join('::');
32686
32750
  }
@@ -32733,6 +32797,59 @@ class TextFieldWidgetComponent {
32733
32797
  .filter(field => dependencyIds.has(field.id))
32734
32798
  .map(field => field.name);
32735
32799
  }
32800
+ onFormattedNumberInput(event) {
32801
+ const input = event.target;
32802
+ this.formattedNumberValue = input.value;
32803
+ if (this.control.pristine) {
32804
+ this.control.markAsDirty();
32805
+ }
32806
+ const normalized = input.value.replace(/,/g, '').trim();
32807
+ if (!normalized) {
32808
+ this.control.setValue(null);
32809
+ return;
32810
+ }
32811
+ const parsed = Number(normalized);
32812
+ this.control.setValue(Number.isFinite(parsed) ? parsed : null);
32813
+ }
32814
+ syncFormattedNumberValue() {
32815
+ if (!this.usesFormattedNumberInput()) {
32816
+ return;
32817
+ }
32818
+ const rawValue = this.control.value;
32819
+ if (rawValue === undefined || rawValue === null || rawValue === '') {
32820
+ this.formattedNumberValue = '';
32821
+ return;
32822
+ }
32823
+ if (typeof rawValue !== 'string' && typeof rawValue !== 'number') {
32824
+ this.formattedNumberValue = '';
32825
+ return;
32826
+ }
32827
+ const normalized = String(rawValue).replace(/,/g, '').trim();
32828
+ if (!normalized) {
32829
+ this.formattedNumberValue = '';
32830
+ return;
32831
+ }
32832
+ if (this.isControlFocused) {
32833
+ this.formattedNumberValue = normalized;
32834
+ return;
32835
+ }
32836
+ this.formattedNumberValue = this.formatNumericValue(normalized);
32837
+ }
32838
+ formatNumericValue(value) {
32839
+ if (!/^-?\d+(\.\d+)?$/.test(value)) {
32840
+ return value;
32841
+ }
32842
+ const numericValue = Number(value);
32843
+ if (!Number.isFinite(numericValue)) {
32844
+ return value;
32845
+ }
32846
+ const fractionPart = value.split('.')[1];
32847
+ return new Intl.NumberFormat(undefined, {
32848
+ useGrouping: true,
32849
+ minimumFractionDigits: fractionPart?.length ?? 0,
32850
+ maximumFractionDigits: fractionPart?.length ?? 0
32851
+ }).format(numericValue);
32852
+ }
32736
32853
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: TextFieldWidgetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
32737
32854
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.17", type: TextFieldWidgetComponent, isStandalone: true, selector: "app-text-field-widget", inputs: { config: "config", engine: "engine", control: "control" }, ngImport: i0, template: `
32738
32855
  <div [class]="fieldContainerClass"
@@ -32762,7 +32879,7 @@ class TextFieldWidgetComponent {
32762
32879
  (blur)="onBlur()"
32763
32880
  (mouseenter)="onMouseEnter()"
32764
32881
  (mouseleave)="onMouseLeave()"
32765
- [readonly]="config.readonly"
32882
+ [readonly]="config.html5?.readonly"
32766
32883
  [attr.aria-required]="required"
32767
32884
  [attr.aria-invalid]="!!error"
32768
32885
  [attr.aria-describedby]="getAriaDescribedBy()"
@@ -32783,7 +32900,7 @@ class TextFieldWidgetComponent {
32783
32900
  (blur)="onBlur()"
32784
32901
  (mouseenter)="onMouseEnter()"
32785
32902
  (mouseleave)="onMouseLeave()"
32786
- [readonly]="config.readonly"
32903
+ [readonly]="config.html5?.readonly"
32787
32904
  [attr.aria-required]="required"
32788
32905
  [attr.aria-invalid]="!!error"
32789
32906
  [attr.aria-describedby]="getAriaDescribedBy()"
@@ -32795,9 +32912,48 @@ class TextFieldWidgetComponent {
32795
32912
  [cpAlphaChannel]="'disabled'"
32796
32913
  [cpOutputFormat]="'hex'"
32797
32914
  [cpFallbackColor]="'#000000'"
32798
- [cpDisabled]="config.readonly || !enabled"
32915
+ [cpDisabled]="config.html5?.readonly || !enabled"
32799
32916
  (colorPickerChange)="onColorPickerChange($event)">
32917
+ } @else if (usesFormattedNumberInput()) {
32918
+ @if (hasDisplayPrefix()) {
32919
+ <span
32920
+ class="pointer-events-none absolute left-3 top-1/2 z-[1] -translate-y-1/2 text-sm text-slate-500"
32921
+ data-fd-field-prefix>
32922
+ {{ displayPrefix }}
32923
+ </span>
32924
+ }
32925
+ <input
32926
+ [id]="fieldId"
32927
+ type="text"
32928
+ inputmode="decimal"
32929
+ [placeholder]="config.placeholder || ''"
32930
+ [value]="formattedNumberValue"
32931
+ (input)="onFormattedNumberInput($event)"
32932
+ (click)="onClick()"
32933
+ (focus)="onFocus()"
32934
+ (blur)="onBlur()"
32935
+ (mouseenter)="onMouseEnter()"
32936
+ (mouseleave)="onMouseLeave()"
32937
+ [readonly]="config.html5?.readonly"
32938
+ [disabled]="control.disabled"
32939
+ [attr.aria-required]="required"
32940
+ [attr.aria-invalid]="!!error"
32941
+ [attr.aria-describedby]="getAriaDescribedBy()"
32942
+ [attr.aria-label]="getAccessibleLabel()"
32943
+ [class]="getControlClass()"
32944
+ [ngStyle]="getSingleLineControlStyles()"
32945
+ data-fd="field-control"
32946
+ [attr.min]="config.html5?.min"
32947
+ [attr.max]="config.html5?.max"
32948
+ [attr.step]="config.html5?.step">
32800
32949
  } @else {
32950
+ @if (hasDisplayPrefix()) {
32951
+ <span
32952
+ class="pointer-events-none absolute left-3 top-1/2 z-[1] -translate-y-1/2 text-sm text-slate-500"
32953
+ data-fd-field-prefix>
32954
+ {{ displayPrefix }}
32955
+ </span>
32956
+ }
32801
32957
  <input
32802
32958
  [id]="fieldId"
32803
32959
  [type]="config.type"
@@ -32808,13 +32964,13 @@ class TextFieldWidgetComponent {
32808
32964
  (blur)="onBlur()"
32809
32965
  (mouseenter)="onMouseEnter()"
32810
32966
  (mouseleave)="onMouseLeave()"
32811
- [readonly]="config.readonly"
32967
+ [readonly]="config.html5?.readonly"
32812
32968
  [attr.aria-required]="required"
32813
32969
  [attr.aria-invalid]="!!error"
32814
32970
  [attr.aria-describedby]="getAriaDescribedBy()"
32815
32971
  [attr.aria-label]="getAccessibleLabel()"
32816
32972
  [class]="getControlClass()"
32817
- [ngStyle]="getControlStyles()"
32973
+ [ngStyle]="getSingleLineControlStyles()"
32818
32974
  data-fd="field-control"
32819
32975
  [attr.min]="config.html5?.min"
32820
32976
  [attr.max]="config.html5?.max"
@@ -32866,7 +33022,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImpo
32866
33022
  (blur)="onBlur()"
32867
33023
  (mouseenter)="onMouseEnter()"
32868
33024
  (mouseleave)="onMouseLeave()"
32869
- [readonly]="config.readonly"
33025
+ [readonly]="config.html5?.readonly"
32870
33026
  [attr.aria-required]="required"
32871
33027
  [attr.aria-invalid]="!!error"
32872
33028
  [attr.aria-describedby]="getAriaDescribedBy()"
@@ -32887,7 +33043,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImpo
32887
33043
  (blur)="onBlur()"
32888
33044
  (mouseenter)="onMouseEnter()"
32889
33045
  (mouseleave)="onMouseLeave()"
32890
- [readonly]="config.readonly"
33046
+ [readonly]="config.html5?.readonly"
32891
33047
  [attr.aria-required]="required"
32892
33048
  [attr.aria-invalid]="!!error"
32893
33049
  [attr.aria-describedby]="getAriaDescribedBy()"
@@ -32899,9 +33055,48 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImpo
32899
33055
  [cpAlphaChannel]="'disabled'"
32900
33056
  [cpOutputFormat]="'hex'"
32901
33057
  [cpFallbackColor]="'#000000'"
32902
- [cpDisabled]="config.readonly || !enabled"
33058
+ [cpDisabled]="config.html5?.readonly || !enabled"
32903
33059
  (colorPickerChange)="onColorPickerChange($event)">
33060
+ } @else if (usesFormattedNumberInput()) {
33061
+ @if (hasDisplayPrefix()) {
33062
+ <span
33063
+ class="pointer-events-none absolute left-3 top-1/2 z-[1] -translate-y-1/2 text-sm text-slate-500"
33064
+ data-fd-field-prefix>
33065
+ {{ displayPrefix }}
33066
+ </span>
33067
+ }
33068
+ <input
33069
+ [id]="fieldId"
33070
+ type="text"
33071
+ inputmode="decimal"
33072
+ [placeholder]="config.placeholder || ''"
33073
+ [value]="formattedNumberValue"
33074
+ (input)="onFormattedNumberInput($event)"
33075
+ (click)="onClick()"
33076
+ (focus)="onFocus()"
33077
+ (blur)="onBlur()"
33078
+ (mouseenter)="onMouseEnter()"
33079
+ (mouseleave)="onMouseLeave()"
33080
+ [readonly]="config.html5?.readonly"
33081
+ [disabled]="control.disabled"
33082
+ [attr.aria-required]="required"
33083
+ [attr.aria-invalid]="!!error"
33084
+ [attr.aria-describedby]="getAriaDescribedBy()"
33085
+ [attr.aria-label]="getAccessibleLabel()"
33086
+ [class]="getControlClass()"
33087
+ [ngStyle]="getSingleLineControlStyles()"
33088
+ data-fd="field-control"
33089
+ [attr.min]="config.html5?.min"
33090
+ [attr.max]="config.html5?.max"
33091
+ [attr.step]="config.html5?.step">
32904
33092
  } @else {
33093
+ @if (hasDisplayPrefix()) {
33094
+ <span
33095
+ class="pointer-events-none absolute left-3 top-1/2 z-[1] -translate-y-1/2 text-sm text-slate-500"
33096
+ data-fd-field-prefix>
33097
+ {{ displayPrefix }}
33098
+ </span>
33099
+ }
32905
33100
  <input
32906
33101
  [id]="fieldId"
32907
33102
  [type]="config.type"
@@ -32912,13 +33107,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImpo
32912
33107
  (blur)="onBlur()"
32913
33108
  (mouseenter)="onMouseEnter()"
32914
33109
  (mouseleave)="onMouseLeave()"
32915
- [readonly]="config.readonly"
33110
+ [readonly]="config.html5?.readonly"
32916
33111
  [attr.aria-required]="required"
32917
33112
  [attr.aria-invalid]="!!error"
32918
33113
  [attr.aria-describedby]="getAriaDescribedBy()"
32919
33114
  [attr.aria-label]="getAccessibleLabel()"
32920
33115
  [class]="getControlClass()"
32921
- [ngStyle]="getControlStyles()"
33116
+ [ngStyle]="getSingleLineControlStyles()"
32922
33117
  data-fd="field-control"
32923
33118
  [attr.min]="config.html5?.min"
32924
33119
  [attr.max]="config.html5?.max"
@@ -33025,12 +33220,12 @@ class FileUploadWidgetComponent {
33025
33220
  return this.engine ? this.engine.isFieldVisible(this.config.id) : true;
33026
33221
  }
33027
33222
  get enabled() {
33028
- if (this.config?.disabled)
33223
+ if (isFieldDisabled(this.config))
33029
33224
  return false;
33030
33225
  return this.engine ? this.engine.isFieldEnabled(this.config.id) : true;
33031
33226
  }
33032
33227
  get isDisabled() {
33033
- return !this.enabled || !!this.config?.readonly;
33228
+ return !this.enabled || isFieldReadonly(this.config);
33034
33229
  }
33035
33230
  get required() {
33036
33231
  return this.engine ? this.engine.isFieldRequired(this.config.id) : !!this.config?.html5?.required;
@@ -33521,7 +33716,7 @@ class SelectWidgetComponent {
33521
33716
  this.runtimeOptionsLoaded = false;
33522
33717
  this.currentSearchTerm = '';
33523
33718
  this.setOptionsFromRaw([]);
33524
- void this.loadOptions(this.currentSearchTerm);
33719
+ void this.loadOptions(this.currentSearchTerm, false, true);
33525
33720
  });
33526
33721
  }
33527
33722
  }
@@ -33664,7 +33859,7 @@ class SelectWidgetComponent {
33664
33859
  return this.engine ? this.engine.isFieldVisible(this.config.id) : true;
33665
33860
  }
33666
33861
  get enabled() {
33667
- if (this.config?.disabled)
33862
+ if (isFieldDisabled(this.config))
33668
33863
  return false;
33669
33864
  return this.engine ? this.engine.isFieldEnabled(this.config.id) : true;
33670
33865
  }
@@ -33721,7 +33916,7 @@ class SelectWidgetComponent {
33721
33916
  event.preventDefault();
33722
33917
  }
33723
33918
  onPillClick() {
33724
- if (this.config.readonly) {
33919
+ if (isFieldReadonly(this.config)) {
33725
33920
  return;
33726
33921
  }
33727
33922
  this.onClick();
@@ -33788,7 +33983,7 @@ class SelectWidgetComponent {
33788
33983
  assign('--fd-select-icon-color', controlStyles['iconColor'] ?? controlStyles['color']);
33789
33984
  return vars;
33790
33985
  }
33791
- async loadOptions(term = '', isSearch = false) {
33986
+ async loadOptions(term = '', isSearch = false, clearStaleSelection = false) {
33792
33987
  if (!this.config)
33793
33988
  return;
33794
33989
  const reqId = ++this.requestId;
@@ -33806,8 +34001,17 @@ class SelectWidgetComponent {
33806
34001
  opts = await this.dataProvider.getOptions(this.config, this.engine);
33807
34002
  }
33808
34003
  if (this.requestId === reqId) {
33809
- this.setOptionsFromRaw(this.withSelectedValueFallbackOptions(opts));
33810
- this.syncStoredFieldLabel(this.control.value);
34004
+ const selectedValue = this.resolveSelectedValueForFallback();
34005
+ const shouldClearStaleSelection = clearStaleSelection
34006
+ && this.hasMeaningfulValue(selectedValue)
34007
+ && !this.hasOptionValue(opts, this.toComparableOptionValue(selectedValue));
34008
+ this.setOptionsFromRaw(shouldClearStaleSelection ? opts : this.withSelectedValueFallbackOptions(opts));
34009
+ if (shouldClearStaleSelection) {
34010
+ this.control.setValue(null);
34011
+ }
34012
+ else {
34013
+ this.syncStoredFieldLabel(this.control.value);
34014
+ }
33811
34015
  this.loading = false;
33812
34016
  this.loadError = null;
33813
34017
  this.cdr.markForCheck();
@@ -33914,6 +34118,9 @@ class SelectWidgetComponent {
33914
34118
  config.id,
33915
34119
  config.name,
33916
34120
  config.type,
34121
+ String(config.html5?.readonly ?? ''),
34122
+ String(config.html5?.disabled ?? ''),
34123
+ String(config.useThousandSeparator ?? ''),
33917
34124
  'no-data-config',
33918
34125
  fieldStaticOptions
33919
34126
  ].join('::');
@@ -33926,11 +34133,13 @@ class SelectWidgetComponent {
33926
34133
  config.id,
33927
34134
  config.name,
33928
34135
  config.type,
34136
+ String(config.html5?.readonly ?? ''),
34137
+ String(config.html5?.disabled ?? ''),
33929
34138
  dataConfig.type ?? '',
33930
34139
  String(dataConfig.datasourceId ?? ''),
33931
34140
  String(dataConfig.labelKey ?? ''),
33932
34141
  String(dataConfig.valueKey ?? ''),
33933
- String(dataConfig.formatNumericOptionLabels ?? ''),
34142
+ String(config.useThousandSeparator ?? ''),
33934
34143
  String(dataConfig.optionLabelPrefixFieldId ?? ''),
33935
34144
  String(dataConfig.searchEnabled ?? ''),
33936
34145
  String(dataConfig.optionsLimit ?? ''),
@@ -33983,15 +34192,18 @@ class SelectWidgetComponent {
33983
34192
  applyDisplayFormatting() {
33984
34193
  this.options = this.rawOptions.map(option => ({
33985
34194
  ...option,
33986
- label: this.formatOptionLabel(option.label)
34195
+ label: this.formatOptionLabel(option)
33987
34196
  }));
33988
34197
  }
33989
- formatOptionLabel(label) {
33990
- const formattedLabel = this.config.dataConfig?.formatNumericOptionLabels
34198
+ formatOptionLabel(option) {
34199
+ const label = option.label;
34200
+ const formattedLabel = usesFieldThousandSeparator(this.config)
33991
34201
  ? this.formatNumericLabel(label)
33992
34202
  : label;
33993
- const prefix = this.resolveOptionLabelPrefix();
33994
- return prefix ? `${prefix} ${formattedLabel}` : formattedLabel;
34203
+ const prefixParts = [option.displayPrefix, this.resolveOptionLabelPrefix()]
34204
+ .map(value => value?.trim())
34205
+ .filter((value) => !!value);
34206
+ return prefixParts.length > 0 ? `${prefixParts.join(' ')} ${formattedLabel}` : formattedLabel;
33995
34207
  }
33996
34208
  resolveOptionLabelPrefix() {
33997
34209
  const prefixFieldId = this.config.dataConfig?.optionLabelPrefixFieldId;
@@ -34074,6 +34286,12 @@ class SelectWidgetComponent {
34074
34286
  hasOptionValue(options, value) {
34075
34287
  return options.some(option => Object.is(option.value, value) || String(option.value) === String(value));
34076
34288
  }
34289
+ toComparableOptionValue(value) {
34290
+ if (typeof value === 'number' && Number.isFinite(value)) {
34291
+ return value;
34292
+ }
34293
+ return String(value ?? '');
34294
+ }
34077
34295
  resolveSelectedValueForFallback() {
34078
34296
  const controlValue = this.control.value;
34079
34297
  if (this.hasMeaningfulValue(controlValue)) {
@@ -34118,7 +34336,7 @@ class SelectWidgetComponent {
34118
34336
  if (this.config.dataConfig?.optionLabelPrefixFieldId) {
34119
34337
  return this.resolveOptionLabelPrefix().length === 0;
34120
34338
  }
34121
- return this.config.dataConfig?.formatNumericOptionLabels !== true;
34339
+ return !usesFieldThousandSeparator(this.config);
34122
34340
  }
34123
34341
  setStoredFieldLabel(label) {
34124
34342
  if (!this.engine || typeof this.engine.setFieldLabel !== 'function') {
@@ -34156,8 +34374,8 @@ class SelectWidgetComponent {
34156
34374
  [loadingText]="'Loading options...'"
34157
34375
  [notFoundText]="loadError || 'No options found'"
34158
34376
  [searchable]="isSearchable()"
34159
- [clearable]="!required && !config.readonly && !hasPillLabel()"
34160
- [readonly]="config.readonly"
34377
+ [clearable]="!required && !config.html5?.readonly && !hasPillLabel()"
34378
+ [readonly]="config.html5?.readonly"
34161
34379
  [labelForId]="fieldId"
34162
34380
  [inputAttrs]="getInputAttributes()"
34163
34381
  [typeahead]="searchTerms$"
@@ -34183,7 +34401,7 @@ class SelectWidgetComponent {
34183
34401
  class="fd-select-pill"
34184
34402
  [ngStyle]="getPillStyles()"
34185
34403
  data-fd-select-pill
34186
- [disabled]="config.readonly"
34404
+ [disabled]="config.html5?.readonly"
34187
34405
  (mousedown)="onPillMouseDown($event)"
34188
34406
  (click)="onPillClick()">
34189
34407
  <span class="fd-select-pill__label">{{ pillLabel() }}</span>
@@ -34236,8 +34454,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImpo
34236
34454
  [loadingText]="'Loading options...'"
34237
34455
  [notFoundText]="loadError || 'No options found'"
34238
34456
  [searchable]="isSearchable()"
34239
- [clearable]="!required && !config.readonly && !hasPillLabel()"
34240
- [readonly]="config.readonly"
34457
+ [clearable]="!required && !config.html5?.readonly && !hasPillLabel()"
34458
+ [readonly]="config.html5?.readonly"
34241
34459
  [labelForId]="fieldId"
34242
34460
  [inputAttrs]="getInputAttributes()"
34243
34461
  [typeahead]="searchTerms$"
@@ -34263,7 +34481,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImpo
34263
34481
  class="fd-select-pill"
34264
34482
  [ngStyle]="getPillStyles()"
34265
34483
  data-fd-select-pill
34266
- [disabled]="config.readonly"
34484
+ [disabled]="config.html5?.readonly"
34267
34485
  (mousedown)="onPillMouseDown($event)"
34268
34486
  (click)="onPillClick()">
34269
34487
  <span class="fd-select-pill__label">{{ pillLabel() }}</span>
@@ -34358,7 +34576,7 @@ class SearchWidgetComponent {
34358
34576
  return this.engine ? this.engine.isFieldVisible(this.config.id) : true;
34359
34577
  }
34360
34578
  get enabled() {
34361
- if (this.config?.disabled)
34579
+ if (isFieldDisabled(this.config))
34362
34580
  return false;
34363
34581
  return this.engine ? this.engine.isFieldEnabled(this.config.id) : true;
34364
34582
  }
@@ -34421,7 +34639,7 @@ class SearchWidgetComponent {
34421
34639
  const datasourceId = this.config?.dataConfig?.datasourceId;
34422
34640
  if (!datasourceId || update.datasourceId !== datasourceId)
34423
34641
  return;
34424
- void this.refreshForCurrentQuery();
34642
+ void this.refreshForCurrentQuery(true);
34425
34643
  });
34426
34644
  }
34427
34645
  this.engine.valueChanges$
@@ -34565,7 +34783,7 @@ class SearchWidgetComponent {
34565
34783
  this.queryControl.disable({ emitEvent: false });
34566
34784
  }
34567
34785
  }
34568
- async refreshForCurrentQuery() {
34786
+ async refreshForCurrentQuery(clearStaleSelection = false) {
34569
34787
  const query = this.queryControl.value ?? '';
34570
34788
  if (!this.shouldQuery(query)) {
34571
34789
  this.options = [];
@@ -34575,7 +34793,7 @@ class SearchWidgetComponent {
34575
34793
  this.cdr.markForCheck();
34576
34794
  return;
34577
34795
  }
34578
- await this.loadOptions(query);
34796
+ await this.loadOptions(query, clearStaleSelection);
34579
34797
  }
34580
34798
  async handleQueryChange(query) {
34581
34799
  if (this.selectingOption) {
@@ -34595,7 +34813,7 @@ class SearchWidgetComponent {
34595
34813
  }
34596
34814
  await this.loadOptions(normalizedQuery);
34597
34815
  }
34598
- async loadOptions(term) {
34816
+ async loadOptions(term, clearStaleSelection = false) {
34599
34817
  const requestId = ++this.requestId;
34600
34818
  this.loading = true;
34601
34819
  this.loadError = null;
@@ -34609,6 +34827,15 @@ class SearchWidgetComponent {
34609
34827
  return;
34610
34828
  }
34611
34829
  this.options = this.normalizeOptions(options);
34830
+ if (clearStaleSelection && this.isSelectedOptionState() && !this.hasOptionValue(this.options, this.control.value)) {
34831
+ this.queryControl.setValue('', { emitEvent: false });
34832
+ this.control.setValue(null);
34833
+ this.loading = false;
34834
+ this.loadError = null;
34835
+ this.activeOptionIndex = -1;
34836
+ this.cdr.markForCheck();
34837
+ return;
34838
+ }
34612
34839
  this.activeOptionIndex = this.options.length > 0 ? 0 : -1;
34613
34840
  this.loading = false;
34614
34841
  this.loadError = null;
@@ -34640,6 +34867,31 @@ class SearchWidgetComponent {
34640
34867
  const minChars = this.getMinChars();
34641
34868
  return (query ?? '').trim().length >= minChars;
34642
34869
  }
34870
+ isSelectedOptionState() {
34871
+ if (!this.hasMeaningfulValue(this.control.value)) {
34872
+ return false;
34873
+ }
34874
+ const storedLabel = this.getStoredFieldLabel();
34875
+ if (typeof storedLabel === 'string' && storedLabel.length > 0) {
34876
+ return true;
34877
+ }
34878
+ return String(this.control.value ?? '') !== String(this.queryControl.value ?? '');
34879
+ }
34880
+ hasOptionValue(options, value) {
34881
+ if (!this.hasMeaningfulValue(value)) {
34882
+ return false;
34883
+ }
34884
+ return options.some(option => Object.is(option.value, value) || String(option.value) === String(value));
34885
+ }
34886
+ hasMeaningfulValue(value) {
34887
+ if (value === undefined || value === null)
34888
+ return false;
34889
+ if (typeof value === 'string')
34890
+ return value.length > 0;
34891
+ if (Array.isArray(value))
34892
+ return value.length > 0;
34893
+ return true;
34894
+ }
34643
34895
  getMinChars() {
34644
34896
  return this.toSafeNonNegativeInt(this.config?.['searchMinChars'], 1);
34645
34897
  }
@@ -34707,7 +34959,7 @@ class SearchWidgetComponent {
34707
34959
  type="search"
34708
34960
  [placeholder]="config.placeholder || 'Search...'"
34709
34961
  [formControl]="queryControl"
34710
- [readonly]="config.readonly"
34962
+ [readonly]="config.html5?.readonly"
34711
34963
  [attr.aria-required]="required"
34712
34964
  [attr.aria-invalid]="!!error"
34713
34965
  [attr.aria-describedby]="getAriaDescribedBy()"
@@ -34786,7 +35038,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImpo
34786
35038
  type="search"
34787
35039
  [placeholder]="config.placeholder || 'Search...'"
34788
35040
  [formControl]="queryControl"
34789
- [readonly]="config.readonly"
35041
+ [readonly]="config.html5?.readonly"
34790
35042
  [attr.aria-required]="required"
34791
35043
  [attr.aria-invalid]="!!error"
34792
35044
  [attr.aria-describedby]="getAriaDescribedBy()"
@@ -35073,7 +35325,7 @@ class TreeSelectWidgetComponent {
35073
35325
  expandRecursive(filtered);
35074
35326
  }
35075
35327
  toggleDropdown(e) {
35076
- if (this.config.disabled || this.config.readonly)
35328
+ if (isFieldDisabled(this.config) || isFieldReadonly(this.config))
35077
35329
  return;
35078
35330
  this.engine?.emitUiEvent({ fieldId: this.config.id, fieldName: this.config.name, type: 'click' });
35079
35331
  if (this.isOpen) {
@@ -35185,8 +35437,8 @@ class TreeSelectWidgetComponent {
35185
35437
  <!-- Dropdown Trigger -->
35186
35438
  <div class="relative group" (click)="toggleDropdown($event)">
35187
35439
  <div class="flex items-center justify-between w-full rounded-md border text-sm px-3 py-2 cursor-pointer transition-colors"
35188
- [class.bg-gray-50]="config.disabled"
35189
- [class.cursor-not-allowed]="config.disabled"
35440
+ [class.bg-gray-50]="config.html5?.disabled"
35441
+ [class.cursor-not-allowed]="config.html5?.disabled"
35190
35442
  [class.border-gray-300]="!isInvalid && !isOpen"
35191
35443
  [class.border-blue-500]="isOpen"
35192
35444
  [class.ring-2]="isOpen"
@@ -35289,8 +35541,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImpo
35289
35541
  <!-- Dropdown Trigger -->
35290
35542
  <div class="relative group" (click)="toggleDropdown($event)">
35291
35543
  <div class="flex items-center justify-between w-full rounded-md border text-sm px-3 py-2 cursor-pointer transition-colors"
35292
- [class.bg-gray-50]="config.disabled"
35293
- [class.cursor-not-allowed]="config.disabled"
35544
+ [class.bg-gray-50]="config.html5?.disabled"
35545
+ [class.cursor-not-allowed]="config.html5?.disabled"
35294
35546
  [class.border-gray-300]="!isInvalid && !isOpen"
35295
35547
  [class.border-blue-500]="isOpen"
35296
35548
  [class.ring-2]="isOpen"
@@ -35540,7 +35792,7 @@ class RadioWidgetComponent {
35540
35792
  return this.engine ? this.engine.isFieldVisible(this.config.id) : true;
35541
35793
  }
35542
35794
  get enabled() {
35543
- if (this.config?.disabled)
35795
+ if (isFieldDisabled(this.config))
35544
35796
  return false;
35545
35797
  return this.engine ? this.engine.isFieldEnabled(this.config.id) : true;
35546
35798
  }
@@ -35856,7 +36108,7 @@ class CheckboxGroupWidgetComponent {
35856
36108
  return this.engine ? this.engine.isFieldVisible(this.config.id) : true;
35857
36109
  }
35858
36110
  get enabled() {
35859
- if (this.config?.disabled)
36111
+ if (isFieldDisabled(this.config))
35860
36112
  return false;
35861
36113
  return this.engine ? this.engine.isFieldEnabled(this.config.id) : true;
35862
36114
  }
@@ -36092,7 +36344,7 @@ class CheckboxWidgetComponent {
36092
36344
  return this.engine ? this.engine.isFieldVisible(this.config.id) : true;
36093
36345
  }
36094
36346
  get enabled() {
36095
- if (this.config?.disabled)
36347
+ if (isFieldDisabled(this.config))
36096
36348
  return false;
36097
36349
  return this.engine ? this.engine.isFieldEnabled(this.config.id) : true;
36098
36350
  }
@@ -36326,7 +36578,7 @@ class RepeatableGroupWidgetComponent {
36326
36578
  return this.engine ? this.engine.isFieldVisible(this.config.id) : true;
36327
36579
  }
36328
36580
  get enabled() {
36329
- if (this.config?.disabled)
36581
+ if (isFieldDisabled(this.config))
36330
36582
  return false;
36331
36583
  return this.engine ? this.engine.isFieldEnabled(this.config.id) : true;
36332
36584
  }
@@ -37660,6 +37912,10 @@ function generateId$2() {
37660
37912
  const COMMON_GROUP_FIELDS = [
37661
37913
  { key: 'groupKey', type: 'text', label: 'Group Key', placeholder: 'documents', helpText: 'Group fields into one object on submit/preview (uses the field key inside the group).' }
37662
37914
  ];
37915
+ const COMMON_INTERACTIVITY_FIELDS = [
37916
+ { key: 'html5.readonly', type: 'checkbox', label: 'Read Only' },
37917
+ { key: 'html5.disabled', type: 'checkbox', label: 'Disabled' }
37918
+ ];
37663
37919
  const COMMON_BASIC_FIELDS = [
37664
37920
  { key: 'name', type: 'text', label: 'Field Key (Name)' },
37665
37921
  { key: 'label', type: 'text', label: 'Label' },
@@ -37667,6 +37923,7 @@ const COMMON_BASIC_FIELDS = [
37667
37923
  { key: 'placeholder', type: 'text', label: 'Placeholder' },
37668
37924
  { key: 'helpText', type: 'text', label: 'Help Text' }, // Hint
37669
37925
  { key: 'tooltip', type: 'text', label: 'Tooltip' },
37926
+ ...COMMON_INTERACTIVITY_FIELDS,
37670
37927
  ...COMMON_GROUP_FIELDS
37671
37928
  ];
37672
37929
  const FILE_BASIC_FIELDS = [
@@ -37675,6 +37932,7 @@ const FILE_BASIC_FIELDS = [
37675
37932
  { key: 'showLabel', type: 'checkbox', label: 'Show Native Label' },
37676
37933
  { key: 'helpText', type: 'text', label: 'Help Text' },
37677
37934
  { key: 'tooltip', type: 'text', label: 'Tooltip' },
37935
+ ...COMMON_INTERACTIVITY_FIELDS,
37678
37936
  ...COMMON_GROUP_FIELDS
37679
37937
  ];
37680
37938
  const COMMON_APPEARANCE_FIELDS = [
@@ -37699,9 +37957,7 @@ const COMMON_APPEARANCE_FIELDS = [
37699
37957
  { label: 'Third Width (33%)', value: '33.33%' },
37700
37958
  { label: 'Quarter Width (25%)', value: '25%' }
37701
37959
  ]
37702
- },
37703
- { key: 'readonly', type: 'checkbox', label: 'Read Only' },
37704
- { key: 'disabled', type: 'checkbox', label: 'Disabled' }
37960
+ }
37705
37961
  ];
37706
37962
  const COMMON_STYLE_SECTIONS = [
37707
37963
  STYLE_LAYOUT_SECTION,
@@ -37718,16 +37974,10 @@ const BASE_REQUIRED_FIELD = {
37718
37974
  label: 'Required',
37719
37975
  helpText: 'Base required state; Rules can override this dynamically.'
37720
37976
  };
37721
- const CUSTOM_VALIDATION_RULES_FIELD = {
37722
- key: 'validation',
37723
- type: 'validators-editor',
37724
- label: 'Custom Validation Rules',
37725
- helpText: 'Use these for field-level validation checks. Use the Rules tab for conditional behaviour.'
37726
- };
37727
37977
  function createValidationSection(...fields) {
37728
37978
  return {
37729
37979
  label: 'Validation',
37730
- fields: [...fields, CUSTOM_VALIDATION_RULES_FIELD]
37980
+ fields
37731
37981
  };
37732
37982
  }
37733
37983
  const TEXT_VALIDATION_SECTION = createValidationSection(BASE_REQUIRED_FIELD, { key: 'html5.minLength', type: 'number', label: 'Min Length' }, { key: 'html5.maxLength', type: 'number', label: 'Max Length' }, { key: 'html5.pattern', type: 'text', label: 'Pattern (regex)' });
@@ -37737,6 +37987,28 @@ const TIME_VALIDATION_SECTION = createValidationSection(BASE_REQUIRED_FIELD, { k
37737
37987
  const DATETIME_VALIDATION_SECTION = createValidationSection(BASE_REQUIRED_FIELD, { key: 'html5.min', type: 'text', label: 'Earliest (YYYY-MM-DDTHH:MM)' }, { key: 'html5.max', type: 'text', label: 'Latest (YYYY-MM-DDTHH:MM)' });
37738
37988
  const SELECT_VALIDATION_SECTION = createValidationSection(BASE_REQUIRED_FIELD);
37739
37989
  const FILE_VALIDATION_SECTION = createValidationSection(BASE_REQUIRED_FIELD);
37990
+ const NUMBER_DISPLAY_SECTION = {
37991
+ label: 'Display',
37992
+ fields: [
37993
+ {
37994
+ key: 'useThousandSeparator',
37995
+ type: 'checkbox',
37996
+ label: 'Show thousand separators',
37997
+ helpText: 'Formats grouped digits in the visible field value without changing the stored number.'
37998
+ }
37999
+ ]
38000
+ };
38001
+ const SELECT_DISPLAY_SECTION = {
38002
+ label: 'Display',
38003
+ fields: [
38004
+ {
38005
+ key: 'useThousandSeparator',
38006
+ type: 'checkbox',
38007
+ label: 'Show thousand separators for numeric labels',
38008
+ helpText: 'Formats numeric option labels without changing the stored option value.'
38009
+ }
38010
+ ]
38011
+ };
37740
38012
  const BUTTON_VARIANT_OPTIONS = [
37741
38013
  { label: 'Primary (Blue)', value: 'primary' },
37742
38014
  { label: 'Secondary (Outline)', value: 'secondary' }
@@ -37752,6 +38024,7 @@ const BUTTON_PROPERTIES = [
37752
38024
  label: 'Style',
37753
38025
  fields: [
37754
38026
  { key: 'variant', type: 'select', label: 'Variant', options: BUTTON_VARIANT_OPTIONS },
38027
+ ...COMMON_INTERACTIVITY_FIELDS,
37755
38028
  ...COMMON_APPEARANCE_FIELDS
37756
38029
  ]
37757
38030
  },
@@ -37770,6 +38043,7 @@ const IMAGE_BUTTON_PROPERTIES = [
37770
38043
  label: 'Style',
37771
38044
  fields: [
37772
38045
  { key: 'variant', type: 'select', label: 'Variant', options: BUTTON_VARIANT_OPTIONS },
38046
+ ...COMMON_INTERACTIVITY_FIELDS,
37773
38047
  ...COMMON_APPEARANCE_FIELDS
37774
38048
  ]
37775
38049
  },
@@ -37819,6 +38093,7 @@ const REPEATABLE_WIDGET_PROPERTIES = [
37819
38093
  { key: 'showLabel', type: 'checkbox', label: 'Show Native Label' },
37820
38094
  { key: 'helpText', type: 'text', label: 'Help Text' },
37821
38095
  { key: 'tooltip', type: 'text', label: 'Tooltip' },
38096
+ ...COMMON_INTERACTIVITY_FIELDS,
37822
38097
  ...COMMON_GROUP_FIELDS
37823
38098
  ]
37824
38099
  },
@@ -37942,6 +38217,7 @@ const FIELD_WIDGETS = [
37942
38217
  ]
37943
38218
  },
37944
38219
  NUMBER_VALIDATION_SECTION,
38220
+ NUMBER_DISPLAY_SECTION,
37945
38221
  { label: 'Appearance', fields: COMMON_APPEARANCE_FIELDS },
37946
38222
  ...COMMON_STYLE_SECTIONS
37947
38223
  ]
@@ -38403,6 +38679,7 @@ const FIELD_WIDGETS = [
38403
38679
  },
38404
38680
  // Options are now handled in Data Tab
38405
38681
  SELECT_VALIDATION_SECTION,
38682
+ SELECT_DISPLAY_SECTION,
38406
38683
  { label: 'Appearance', fields: COMMON_APPEARANCE_FIELDS },
38407
38684
  ...COMMON_STYLE_SECTIONS
38408
38685
  ]
@@ -41218,5 +41495,5 @@ function provideHttpDataSourceClient(config) {
41218
41495
  * Generated bundle index. Do not edit.
41219
41496
  */
41220
41497
 
41221
- export { APPEARANCE_FIELDS, AiToolRegistryService, CONTENT_TEXT_CLASS, CONTENT_TEXT_MUTED_CLASS, CORE_DESIGNER_PLUGINS, CURRENT_SCHEMA_VERSION, DATA_PROVIDER, DATA_SOURCE_CLIENT, DEFAULT_TEMPLATE_LIBRARY, DEFAULT_WEBSITE_SECTIONS, DEFAULT_WIDGET_PACKS, DESIGNER_PLUGINS, DESIGNER_SECTIONS, DataCatalog, DataPanelComponent, DataProvider, DefaultDataProvider, DefaultDataSourceClient, DefaultFileUploadClient, DesignerContext, DesignerStateService, DynamicPropertiesComponent, EFFECTS_FIELDS, EMAIL_SAFE_STYLE_SECTIONS, EMAIL_WIDGETS, EmailRendererComponent, EventsPanelComponent, EventsWorkspaceComponent, FIELD_CHOICE_LABEL_CLASS, FIELD_CHOICE_SURFACE_CLASS, FIELD_CONTAINER_CLASS, FIELD_ERROR_CLASS, FIELD_HELP_CLASS, FIELD_LABEL_CLASS, FIELD_OPTION_CLASS, FIELD_REQUIRED_CLASS, FIELD_RESULTS_PANEL_CLASS, FIELD_WIDGETS, FILE_UPLOAD_CARD_CLASS, FILE_UPLOAD_CLIENT, FORM_DESIGNER_AI_TOOL_PLUGIN, FULL_WEB_STYLE_SECTIONS, FieldPaletteComponent, FormDesignerAiFeatureStateService, FormDesignerShellComponent, FormEngine, FormJourneyStateService, FormJourneyViewerComponent, FormViewerComponent, GlobalDataManagerComponent, HTTP_DATA_SOURCE_CLIENT_CONFIG, HttpDataSourceClient, InMemoryDataCatalogService, InspectorAdvancedSectionComponent, InspectorBackgroundsSectionComponent, InspectorBordersSectionComponent, InspectorEffectsSectionComponent, InspectorLayoutSectionComponent, InspectorPositionSectionComponent, InspectorSizeSectionComponent, InspectorSpacingSectionComponent, InspectorSpinInputComponent, InspectorTypographySectionComponent, JsonFormDesignerComponent, JsonFormRendererComponent, LAYOUT_FIELDS, LayoutCanvasComponent, LayoutNodeComponent, OUTLINED_FIELD_BORDER_WIDTH, OUTLINED_FIELD_IDLE_BORDER_COLOR, OUTLINED_FIELD_INTERACTIVE_BORDER_COLOR, OUTLINED_FIELD_INVALID_BORDER_COLOR, PAGE_WIDGETS, PropertiesPanelComponent, RulesPanelComponent, RuntimeFieldDataAccessRegistryService, SELECT_THEME_HOOKS, SELECT_THEME_TOKENS, SPACING_BOX_MODEL_FIELD, SPACING_MARGIN_FIELDS, SPACING_PADDING_FIELDS, STANDARD_FORM_STYLE_SECTIONS, STYLE_APPEARANCE_SECTION, STYLE_EFFECTS_SECTION, STYLE_LAYOUT_SECTION, STYLE_SECTIONS, STYLE_SPACING_SECTION, STYLE_TRANSFORM_SECTION, STYLE_TYPOGRAPHY_SECTION, TABLE_CELL_CLASS, TABLE_EMPTY_CLASS, TABLE_GRID_CLASS, TABLE_HEAD_CELL_CLASS, TABLE_HEAD_CLASS, TABLE_ICON_BUTTON_CLASS, TABLE_ROOT_CLASS, TABLE_ROW_CLASS, TABLE_TOOLBAR_CLASS, TRANSFORM_FIELDS, TYPOGRAPHY_FIELDS, ThemeService, UiAccordionComponent, UiBoxModelComponent, UiColorSwatchComponent, UiDimensionComponent, UiEdgeBoxComponent, UiFieldWrapperComponent, UiInputComponent, UiRangeNumberComponent, UiSelectIconComponent, UiTabComponent, UiTabsComponent, WIDGET_DEFINITIONS, WIDGET_ID_SEPARATOR, WebsiteBrickStudioComponent, WebsiteDesignerShellComponent, WebsitePreviewShellComponent, WebsiteProjectService, WidgetDefinitionResolverService, WidgetInspectorComponent, appendSectionToSchema, buildWidgetId, checkSchemaForApiOnlySources, createDefaultWebsiteBrick, createDefaultWebsiteBricks, createDefaultWebsiteTheme, createEmptySchema, createFormEngine, createFormJourneyPage, createFormJourneyProject, createPluginContext, createWebsiteBrick, createWebsitePage, createWebsiteProject, defineWidget, flattenPluginWidgets, getButtonClass, getChoiceControlClass, getEffectiveDataConfig, getFileInputClass, getFileUploadShellClass, getHeadingClass, getOutlinedFieldInlineStyle, getTextControlClass, getWidgetsForFlavor, hasWrapperSurfaceStyles, inferSchema, isFormJourneyProject, isWidgetVisibleInPalette, mergeAndNormalize, normalizeRuntimeOptions, normalizeStyle$1 as normalizeStyle, normalizeToJourney, parseCsv, parseJsonArray, parseSchema, provideDesignerPlugins, provideFormDesignerAngaiFeature, provideHttpDataSourceClient, serializeSchema, slugifyId, splitControlSurfaceStyles, stripEmbeddedSourceData, unwrapSinglePageJourney };
41498
+ export { APPEARANCE_FIELDS, AiToolRegistryService, CONTENT_TEXT_CLASS, CONTENT_TEXT_MUTED_CLASS, CORE_DESIGNER_PLUGINS, CURRENT_SCHEMA_VERSION, DATA_PROVIDER, DATA_SOURCE_CLIENT, DEFAULT_TEMPLATE_LIBRARY, DEFAULT_WEBSITE_SECTIONS, DEFAULT_WIDGET_PACKS, DESIGNER_PLUGINS, DESIGNER_SECTIONS, DataCatalog, DataPanelComponent, DataProvider, DefaultDataProvider, DefaultDataSourceClient, DefaultFileUploadClient, DesignerContext, DesignerStateService, DynamicPropertiesComponent, EFFECTS_FIELDS, EMAIL_SAFE_STYLE_SECTIONS, EMAIL_WIDGETS, EmailRendererComponent, EventsPanelComponent, EventsWorkspaceComponent, FIELD_CHOICE_LABEL_CLASS, FIELD_CHOICE_SURFACE_CLASS, FIELD_CONTAINER_CLASS, FIELD_ERROR_CLASS, FIELD_HELP_CLASS, FIELD_LABEL_CLASS, FIELD_OPTION_CLASS, FIELD_REQUIRED_CLASS, FIELD_RESULTS_PANEL_CLASS, FIELD_WIDGETS, FILE_UPLOAD_CARD_CLASS, FILE_UPLOAD_CLIENT, FORM_DESIGNER_AI_TOOL_PLUGIN, FULL_WEB_STYLE_SECTIONS, FieldPaletteComponent, FormDesignerAiFeatureStateService, FormDesignerShellComponent, FormEngine, FormJourneyStateService, FormJourneyViewerComponent, FormViewerComponent, GlobalDataManagerComponent, HTTP_DATA_SOURCE_CLIENT_CONFIG, HttpDataSourceClient, InMemoryDataCatalogService, InspectorAdvancedSectionComponent, InspectorBackgroundsSectionComponent, InspectorBordersSectionComponent, InspectorEffectsSectionComponent, InspectorLayoutSectionComponent, InspectorPositionSectionComponent, InspectorSizeSectionComponent, InspectorSpacingSectionComponent, InspectorSpinInputComponent, InspectorTypographySectionComponent, JsonFormDesignerComponent, JsonFormRendererComponent, LAYOUT_FIELDS, LayoutCanvasComponent, LayoutNodeComponent, OUTLINED_FIELD_BORDER_WIDTH, OUTLINED_FIELD_IDLE_BORDER_COLOR, OUTLINED_FIELD_INTERACTIVE_BORDER_COLOR, OUTLINED_FIELD_INVALID_BORDER_COLOR, PAGE_WIDGETS, PropertiesPanelComponent, RulesPanelComponent, RuntimeFieldDataAccessRegistryService, SELECT_THEME_HOOKS, SELECT_THEME_TOKENS, SPACING_BOX_MODEL_FIELD, SPACING_MARGIN_FIELDS, SPACING_PADDING_FIELDS, STANDARD_FORM_STYLE_SECTIONS, STYLE_APPEARANCE_SECTION, STYLE_EFFECTS_SECTION, STYLE_LAYOUT_SECTION, STYLE_SECTIONS, STYLE_SPACING_SECTION, STYLE_TRANSFORM_SECTION, STYLE_TYPOGRAPHY_SECTION, TABLE_CELL_CLASS, TABLE_EMPTY_CLASS, TABLE_GRID_CLASS, TABLE_HEAD_CELL_CLASS, TABLE_HEAD_CLASS, TABLE_ICON_BUTTON_CLASS, TABLE_ROOT_CLASS, TABLE_ROW_CLASS, TABLE_TOOLBAR_CLASS, TRANSFORM_FIELDS, TYPOGRAPHY_FIELDS, ThemeService, UiAccordionComponent, UiBoxModelComponent, UiColorSwatchComponent, UiDimensionComponent, UiEdgeBoxComponent, UiFieldWrapperComponent, UiInputComponent, UiRangeNumberComponent, UiSelectIconComponent, UiTabComponent, UiTabsComponent, WIDGET_DEFINITIONS, WIDGET_ID_SEPARATOR, WebsiteBrickStudioComponent, WebsiteDesignerShellComponent, WebsitePreviewShellComponent, WebsiteProjectService, WidgetDefinitionResolverService, WidgetInspectorComponent, appendSectionToSchema, buildWidgetId, checkSchemaForApiOnlySources, createDefaultWebsiteBrick, createDefaultWebsiteBricks, createDefaultWebsiteTheme, createEmptySchema, createFormEngine, createFormJourneyPage, createFormJourneyProject, createPluginContext, createWebsiteBrick, createWebsitePage, createWebsiteProject, defineWidget, flattenPluginWidgets, getButtonClass, getChoiceControlClass, getEffectiveDataConfig, getFileInputClass, getFileUploadShellClass, getHeadingClass, getOutlinedFieldInlineStyle, getTextControlClass, getWidgetsForFlavor, hasWrapperSurfaceStyles, inferSchema, isFieldDisabled, isFieldReadonly, isFormJourneyProject, isWidgetVisibleInPalette, mergeAndNormalize, normalizeRuntimeOptions, normalizeStyle$1 as normalizeStyle, normalizeToJourney, parseCsv, parseJsonArray, parseSchema, provideDesignerPlugins, provideFormDesignerAngaiFeature, provideHttpDataSourceClient, serializeSchema, slugifyId, splitControlSurfaceStyles, stripEmbeddedSourceData, unwrapSinglePageJourney, usesFieldThousandSeparator };
41222
41499
  //# sourceMappingURL=uch-web-ngx-form-designer.mjs.map