@yourself.create/ngx-form-designer 0.0.12 → 0.0.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -329,6 +329,8 @@ class RuleEvaluationService {
329
329
  return value !== null && value !== undefined && value !== '';
330
330
  case 'truthy':
331
331
  return !!value;
332
+ case 'notTruthy':
333
+ return !value;
332
334
  default: return false;
333
335
  }
334
336
  }
@@ -1379,11 +1381,10 @@ class DesignerStateService {
1379
1381
  const originalField = clipboardFields.get(oldRefId);
1380
1382
  if (originalField) {
1381
1383
  const newFieldId = v4();
1382
- const newField = {
1383
- ...originalField,
1384
+ const newField = this.cloneFieldForDuplication(originalField, {
1384
1385
  id: newFieldId,
1385
1386
  name: `${originalField.name}_${Date.now()}_${Math.floor(Math.random() * 1000)}`
1386
- };
1387
+ });
1387
1388
  targetSchema.fields.push(newField);
1388
1389
  newNode.refId = newFieldId;
1389
1390
  }
@@ -2308,12 +2309,11 @@ class DesignerStateService {
2308
2309
  const originalField = scopeFields.find(f => f.id === cloned.refId);
2309
2310
  if (originalField) {
2310
2311
  const newFieldId = v4();
2311
- const newField = {
2312
- ...this.cloneValue(originalField),
2312
+ const newField = this.cloneFieldForDuplication(originalField, {
2313
2313
  id: newFieldId,
2314
2314
  name: `${originalField.name}_copy_${Date.now()}`,
2315
2315
  label: `${originalField.label || 'Field'} (Copy)`
2316
- };
2316
+ });
2317
2317
  scopeFields.push(newField);
2318
2318
  cloned.refId = newFieldId;
2319
2319
  }
@@ -2383,11 +2383,10 @@ class DesignerStateService {
2383
2383
  if (!location)
2384
2384
  return;
2385
2385
  const newFieldId = v4();
2386
- const newField = {
2387
- ...this.cloneValue(location.field),
2386
+ const newField = this.cloneFieldForDuplication(location.field, {
2388
2387
  id: newFieldId,
2389
2388
  name: `${location.field.name}_copy_${Date.now()}`
2390
- };
2389
+ });
2391
2390
  location.schema.fields.push(newField);
2392
2391
  const widgetNode = this.findWidgetByRefId(location.schema.layout, fieldId);
2393
2392
  if (!widgetNode) {
@@ -2914,6 +2913,42 @@ class DesignerStateService {
2914
2913
  cloneValue(value) {
2915
2914
  return structuredClone(value);
2916
2915
  }
2916
+ cloneFieldForDuplication(field, overrides) {
2917
+ const clonedField = this.cloneValue(field);
2918
+ const bindingIdMap = new Map();
2919
+ const events = clonedField.events?.map(binding => {
2920
+ const nextBindingId = v4();
2921
+ bindingIdMap.set(binding.id, nextBindingId);
2922
+ return {
2923
+ ...binding,
2924
+ id: nextBindingId,
2925
+ actions: binding.actions.map(action => {
2926
+ if (action.type !== 'api') {
2927
+ return action;
2928
+ }
2929
+ return {
2930
+ ...action,
2931
+ datasourceId: action.datasourceId ? this.createEventDatasourceId() : action.datasourceId
2932
+ };
2933
+ })
2934
+ };
2935
+ });
2936
+ const dataConfig = clonedField.dataConfig?.eventId && bindingIdMap.has(clonedField.dataConfig.eventId)
2937
+ ? {
2938
+ ...clonedField.dataConfig,
2939
+ eventId: bindingIdMap.get(clonedField.dataConfig.eventId)
2940
+ }
2941
+ : clonedField.dataConfig;
2942
+ return {
2943
+ ...clonedField,
2944
+ ...overrides,
2945
+ events,
2946
+ dataConfig
2947
+ };
2948
+ }
2949
+ createEventDatasourceId() {
2950
+ return `evt_ds_${v4().replace(/-/g, '')}`;
2951
+ }
2917
2952
  clampIndex(index, length) {
2918
2953
  if (index < 0)
2919
2954
  return 0;
@@ -4471,6 +4506,9 @@ class FormEventRunner {
4471
4506
  if (!field || !field.events)
4472
4507
  return;
4473
4508
  const bindings = field.events.filter(b => b.on === evt.type && b.enabled !== false);
4509
+ if (evt.type === 'change' && this.isEmptyEventValue(evt.value)) {
4510
+ await this.clearInactiveApiBindingDatasources(field, bindings, evt);
4511
+ }
4474
4512
  for (const binding of bindings) {
4475
4513
  for (const action of binding.actions) {
4476
4514
  const stop = await this.executeAction(action, binding.id, evt, schema.fields);
@@ -4484,6 +4522,46 @@ class FormEventRunner {
4484
4522
  this.processingDepth--;
4485
4523
  }
4486
4524
  }
4525
+ async clearInactiveApiBindingDatasources(field, activeBindings, evt) {
4526
+ if (!this.dataSourceWriter || !field.events?.length) {
4527
+ return;
4528
+ }
4529
+ const activeBindingIds = new Set(activeBindings.map(binding => binding.id));
4530
+ const clearedDatasourceIds = new Set();
4531
+ for (const binding of field.events) {
4532
+ if (binding.enabled === false || activeBindingIds.has(binding.id)) {
4533
+ continue;
4534
+ }
4535
+ for (const action of binding.actions) {
4536
+ if (action.type !== 'api') {
4537
+ continue;
4538
+ }
4539
+ const datasourceId = this.resolveDatasourceId(action, evt);
4540
+ if (!datasourceId || clearedDatasourceIds.has(datasourceId)) {
4541
+ continue;
4542
+ }
4543
+ clearedDatasourceIds.add(datasourceId);
4544
+ try {
4545
+ await this.dataSourceWriter({
4546
+ datasourceId,
4547
+ rows: [],
4548
+ action,
4549
+ event: evt,
4550
+ response: []
4551
+ });
4552
+ this.engine.emitDataSourceUpdate(datasourceId);
4553
+ }
4554
+ catch (error) {
4555
+ this.logger.warn('Failed to clear API action datasource after the source field was emptied.', {
4556
+ event: evt,
4557
+ action,
4558
+ datasourceId,
4559
+ error
4560
+ });
4561
+ }
4562
+ }
4563
+ }
4564
+ }
4487
4565
  async executeAction(action, eventId, evt, fields) {
4488
4566
  if (action.type === 'log') {
4489
4567
  this.logger.log(action.message || 'Event fired', { event: evt });
@@ -4493,6 +4571,10 @@ class FormEventRunner {
4493
4571
  this.handleSetValue(action, evt, fields);
4494
4572
  return false;
4495
4573
  }
4574
+ if (action.type === 'clearFields') {
4575
+ this.handleClearFields(action, fields);
4576
+ return false;
4577
+ }
4496
4578
  if (action.type === 'navigate') {
4497
4579
  return this.handleNavigateAction(action, fields);
4498
4580
  }
@@ -4501,9 +4583,6 @@ class FormEventRunner {
4501
4583
  }
4502
4584
  handleSetValue(action, evt, fields) {
4503
4585
  const target = fields.find(f => f.id === action.targetFieldId);
4504
- if (!target)
4505
- return;
4506
- const previousValue = this.engine.getValue(target.name);
4507
4586
  let val;
4508
4587
  if (action.valueFrom === 'literal') {
4509
4588
  val = action.valueLiteral;
@@ -4517,15 +4596,30 @@ class FormEventRunner {
4517
4596
  val = this.engine.getValue(source.name);
4518
4597
  }
4519
4598
  }
4520
- this.engine.setValue(target.name, val);
4521
- if (!Object.is(previousValue, val)) {
4522
- this.engine.emitUiEvent({
4523
- fieldId: target.id,
4524
- fieldName: target.name,
4525
- type: 'change',
4526
- value: val
4527
- });
4599
+ this.applyFieldValue(target, val);
4600
+ }
4601
+ handleClearFields(action, fields) {
4602
+ const targetFieldIds = [...new Set(action.targetFieldIds ?? [])];
4603
+ for (const targetFieldId of targetFieldIds) {
4604
+ const target = fields.find(field => field.id === targetFieldId);
4605
+ this.applyFieldValue(target, null);
4606
+ }
4607
+ }
4608
+ applyFieldValue(target, value) {
4609
+ if (!target) {
4610
+ return;
4611
+ }
4612
+ const previousValue = this.engine.getValue(target.name);
4613
+ this.engine.setValue(target.name, value);
4614
+ if (Object.is(previousValue, value)) {
4615
+ return;
4528
4616
  }
4617
+ this.engine.emitUiEvent({
4618
+ fieldId: target.id,
4619
+ fieldName: target.name,
4620
+ type: 'change',
4621
+ value
4622
+ });
4529
4623
  }
4530
4624
  async handleApiAction(action, eventId, evt, fields) {
4531
4625
  if (!this.apiExecutor) {
@@ -4655,6 +4749,8 @@ class FormEventRunner {
4655
4749
  return !this.isEmptyValue(left);
4656
4750
  case 'truthy':
4657
4751
  return !!left;
4752
+ case 'notTruthy':
4753
+ return !left;
4658
4754
  default:
4659
4755
  return false;
4660
4756
  }
@@ -4737,6 +4833,18 @@ class FormEventRunner {
4737
4833
  }
4738
4834
  return [{ value }];
4739
4835
  }
4836
+ isEmptyEventValue(value) {
4837
+ if (value === undefined || value === null) {
4838
+ return true;
4839
+ }
4840
+ if (typeof value === 'string') {
4841
+ return value.trim().length === 0;
4842
+ }
4843
+ if (Array.isArray(value)) {
4844
+ return value.length === 0;
4845
+ }
4846
+ return false;
4847
+ }
4740
4848
  }
4741
4849
 
4742
4850
  const FILE_UPLOAD_CLIENT = new InjectionToken('FILE_UPLOAD_CLIENT');
@@ -5187,6 +5295,7 @@ class JsonFormRendererComponent {
5187
5295
  fieldDataAccessApi;
5188
5296
  formContentId;
5189
5297
  formContentVersion;
5298
+ refreshKey;
5190
5299
  valueChange = new EventEmitter();
5191
5300
  groupedValueChange = new EventEmitter();
5192
5301
  combinedValueChange = new EventEmitter();
@@ -5219,6 +5328,10 @@ class JsonFormRendererComponent {
5219
5328
  if (changes['mode'] && !changes['mode'].firstChange) {
5220
5329
  this.configureRunner();
5221
5330
  }
5331
+ if (changes['refreshKey'] && !changes['refreshKey'].firstChange) {
5332
+ this.createEngine();
5333
+ return;
5334
+ }
5222
5335
  if (changes['fieldDataAccessMap']
5223
5336
  || changes['fieldDataAccessApi']
5224
5337
  || changes['formContentId']
@@ -5960,7 +6073,7 @@ class JsonFormRendererComponent {
5960
6073
  || typeof record['type'] === 'string';
5961
6074
  }
5962
6075
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: JsonFormRendererComponent, deps: [{ token: DesignerStateService }], target: i0.ɵɵFactoryTarget.Component });
5963
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.17", type: JsonFormRendererComponent, isStandalone: true, selector: "app-json-form-renderer", inputs: { schema: "schema", initialValues: "initialValues", initialFieldLabels: "initialFieldLabels", mode: "mode", device: "device", showLayoutGuides: "showLayoutGuides", breakpoint: "breakpoint", eventLogger: "eventLogger", eventApis: "eventApis", eventApiExecutor: "eventApiExecutor", navigateToPage: "navigateToPage", uploadOnSubmit: "uploadOnSubmit", severityEvaluationMode: "severityEvaluationMode", fieldDataAccessMap: "fieldDataAccessMap", fieldDataAccessApi: "fieldDataAccessApi", formContentId: "formContentId", formContentVersion: "formContentVersion" }, outputs: { valueChange: "valueChange", groupedValueChange: "groupedValueChange", combinedValueChange: "combinedValueChange", validationChange: "validationChange", uploadedFilesChange: "uploadedFilesChange", formSubmit: "formSubmit" }, usesOnChanges: true, ngImport: i0, template: `
6076
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.17", type: JsonFormRendererComponent, isStandalone: true, selector: "app-json-form-renderer", inputs: { schema: "schema", initialValues: "initialValues", initialFieldLabels: "initialFieldLabels", mode: "mode", device: "device", showLayoutGuides: "showLayoutGuides", breakpoint: "breakpoint", eventLogger: "eventLogger", eventApis: "eventApis", eventApiExecutor: "eventApiExecutor", navigateToPage: "navigateToPage", uploadOnSubmit: "uploadOnSubmit", severityEvaluationMode: "severityEvaluationMode", fieldDataAccessMap: "fieldDataAccessMap", fieldDataAccessApi: "fieldDataAccessApi", formContentId: "formContentId", formContentVersion: "formContentVersion", refreshKey: "refreshKey" }, outputs: { valueChange: "valueChange", groupedValueChange: "groupedValueChange", combinedValueChange: "combinedValueChange", validationChange: "validationChange", uploadedFilesChange: "uploadedFilesChange", formSubmit: "formSubmit" }, usesOnChanges: true, ngImport: i0, template: `
5964
6077
  <div class="form-renderer-container"
5965
6078
  data-fd="renderer"
5966
6079
  [class.is-mobile]="device === 'mobile'"
@@ -6043,6 +6156,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImpo
6043
6156
  type: Input
6044
6157
  }], formContentVersion: [{
6045
6158
  type: Input
6159
+ }], refreshKey: [{
6160
+ type: Input
6046
6161
  }], valueChange: [{
6047
6162
  type: Output
6048
6163
  }], groupedValueChange: [{
@@ -6244,6 +6359,7 @@ class FormJourneyViewerComponent {
6244
6359
  fieldDataAccessApi;
6245
6360
  formContentId;
6246
6361
  formContentVersion;
6362
+ refreshKey;
6247
6363
  formDataChange = new EventEmitter();
6248
6364
  formDataByPageChange = new EventEmitter();
6249
6365
  formValidationChange = new EventEmitter();
@@ -6261,9 +6377,7 @@ class FormJourneyViewerComponent {
6261
6377
  return index >= 0 ? index : 0;
6262
6378
  });
6263
6379
  ngOnInit() {
6264
- this.activePageId.set(this.journey.startPageId || this.journey.pages[0]?.id || '');
6265
- this.values.set(this.normalizeInitialValues(this.initialValues));
6266
- this.fieldLabels.set(this.normalizeInitialFieldLabels(this.initialFieldLabels));
6380
+ this.resetViewerState();
6267
6381
  }
6268
6382
  ngOnChanges(changes) {
6269
6383
  if (changes['initialValues']) {
@@ -6272,6 +6386,10 @@ class FormJourneyViewerComponent {
6272
6386
  if (changes['initialFieldLabels']) {
6273
6387
  this.fieldLabels.set(this.normalizeInitialFieldLabels(this.initialFieldLabels));
6274
6388
  }
6389
+ if (changes['refreshKey'] && !changes['refreshKey'].firstChange) {
6390
+ this.resetViewerState();
6391
+ return;
6392
+ }
6275
6393
  const current = this.activePageId();
6276
6394
  if (current && this.journey.pages.some(page => page.id === current))
6277
6395
  return;
@@ -6320,6 +6438,11 @@ class FormJourneyViewerComponent {
6320
6438
  return {};
6321
6439
  return structuredClone(value);
6322
6440
  }
6441
+ resetViewerState() {
6442
+ this.activePageId.set(this.journey.startPageId || this.journey.pages[0]?.id || '');
6443
+ this.values.set(this.normalizeInitialValues(this.initialValues));
6444
+ this.fieldLabels.set(this.normalizeInitialFieldLabels(this.initialFieldLabels));
6445
+ }
6323
6446
  toValueScope(values) {
6324
6447
  const scope = {};
6325
6448
  for (const fieldValue of Object.values(values)) {
@@ -6378,7 +6501,7 @@ class FormJourneyViewerComponent {
6378
6501
  return flattened;
6379
6502
  }
6380
6503
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: FormJourneyViewerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
6381
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.17", type: FormJourneyViewerComponent, isStandalone: true, selector: "app-form-journey-viewer", inputs: { journey: "journey", viewOnly: "viewOnly", severityEvaluationMode: "severityEvaluationMode", eventApis: "eventApis", eventApiExecutor: "eventApiExecutor", initialValues: "initialValues", initialFieldLabels: "initialFieldLabels", fieldDataAccessMap: "fieldDataAccessMap", fieldDataAccessApi: "fieldDataAccessApi", formContentId: "formContentId", formContentVersion: "formContentVersion" }, outputs: { formDataChange: "formDataChange", formDataByPageChange: "formDataByPageChange", formValidationChange: "formValidationChange", uploadedFilesChange: "uploadedFilesChange", submit: "submit", activePageIdChange: "activePageIdChange" }, viewQueries: [{ propertyName: "renderer", first: true, predicate: JsonFormRendererComponent, descendants: true }], usesOnChanges: true, ngImport: i0, template: `
6504
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.17", type: FormJourneyViewerComponent, isStandalone: true, selector: "app-form-journey-viewer", inputs: { journey: "journey", viewOnly: "viewOnly", severityEvaluationMode: "severityEvaluationMode", eventApis: "eventApis", eventApiExecutor: "eventApiExecutor", initialValues: "initialValues", initialFieldLabels: "initialFieldLabels", fieldDataAccessMap: "fieldDataAccessMap", fieldDataAccessApi: "fieldDataAccessApi", formContentId: "formContentId", formContentVersion: "formContentVersion", refreshKey: "refreshKey" }, outputs: { formDataChange: "formDataChange", formDataByPageChange: "formDataByPageChange", formValidationChange: "formValidationChange", uploadedFilesChange: "uploadedFilesChange", submit: "submit", activePageIdChange: "activePageIdChange" }, viewQueries: [{ propertyName: "renderer", first: true, predicate: JsonFormRendererComponent, descendants: true }], usesOnChanges: true, ngImport: i0, template: `
6382
6505
  <div class="form-journey-viewer flex h-full w-full flex-col" data-fd="form-journey-viewer">
6383
6506
  <div class="border-b border-gray-200 bg-white px-4 py-2 text-xs text-gray-600" data-fd="journey-progress">
6384
6507
  {{ activePageIndex() + 1 }} / {{ journey.pages.length }}
@@ -6399,6 +6522,7 @@ class FormJourneyViewerComponent {
6399
6522
  [fieldDataAccessApi]="fieldDataAccessApi"
6400
6523
  [formContentId]="formContentId"
6401
6524
  [formContentVersion]="formContentVersion"
6525
+ [refreshKey]="refreshKey"
6402
6526
  [navigateToPage]="navigateToPage"
6403
6527
  (valueChange)="onValueChange($event)"
6404
6528
  (validationChange)="onValidationChange($event)"
@@ -6407,7 +6531,7 @@ class FormJourneyViewerComponent {
6407
6531
  </app-json-form-renderer>
6408
6532
  </div>
6409
6533
  </div>
6410
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: JsonFormRendererComponent, selector: "app-json-form-renderer", inputs: ["schema", "initialValues", "initialFieldLabels", "mode", "device", "showLayoutGuides", "breakpoint", "eventLogger", "eventApis", "eventApiExecutor", "navigateToPage", "uploadOnSubmit", "severityEvaluationMode", "fieldDataAccessMap", "fieldDataAccessApi", "formContentId", "formContentVersion"], outputs: ["valueChange", "groupedValueChange", "combinedValueChange", "validationChange", "uploadedFilesChange", "formSubmit"] }] });
6534
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: JsonFormRendererComponent, selector: "app-json-form-renderer", inputs: ["schema", "initialValues", "initialFieldLabels", "mode", "device", "showLayoutGuides", "breakpoint", "eventLogger", "eventApis", "eventApiExecutor", "navigateToPage", "uploadOnSubmit", "severityEvaluationMode", "fieldDataAccessMap", "fieldDataAccessApi", "formContentId", "formContentVersion", "refreshKey"], outputs: ["valueChange", "groupedValueChange", "combinedValueChange", "validationChange", "uploadedFilesChange", "formSubmit"] }] });
6411
6535
  }
6412
6536
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: FormJourneyViewerComponent, decorators: [{
6413
6537
  type: Component,
@@ -6436,6 +6560,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImpo
6436
6560
  [fieldDataAccessApi]="fieldDataAccessApi"
6437
6561
  [formContentId]="formContentId"
6438
6562
  [formContentVersion]="formContentVersion"
6563
+ [refreshKey]="refreshKey"
6439
6564
  [navigateToPage]="navigateToPage"
6440
6565
  (valueChange)="onValueChange($event)"
6441
6566
  (validationChange)="onValidationChange($event)"
@@ -6469,6 +6594,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImpo
6469
6594
  type: Input
6470
6595
  }], formContentVersion: [{
6471
6596
  type: Input
6597
+ }], refreshKey: [{
6598
+ type: Input
6472
6599
  }], formDataChange: [{
6473
6600
  type: Output
6474
6601
  }], formDataByPageChange: [{
@@ -6516,6 +6643,7 @@ class FormViewerComponent {
6516
6643
  fieldDataAccessApi;
6517
6644
  formContentId;
6518
6645
  formContentVersion;
6646
+ refreshKey;
6519
6647
  formDataChange = new EventEmitter();
6520
6648
  formDataByPageChange = new EventEmitter();
6521
6649
  formValidationChange = new EventEmitter();
@@ -6791,7 +6919,7 @@ class FormViewerComponent {
6791
6919
  return !!value && typeof value === 'object';
6792
6920
  }
6793
6921
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: FormViewerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
6794
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.17", type: FormViewerComponent, isStandalone: true, selector: "app-form-viewer", inputs: { schema: "schema", journey: "journey", data: "data", dataUsesFieldNameKeys: "dataUsesFieldNameKeys", options: "options", viewOnly: "viewOnly", severityEvaluationMode: "severityEvaluationMode", eventApis: "eventApis", eventApiExecutor: "eventApiExecutor", fieldDataAccessMap: "fieldDataAccessMap", fieldDataAccessApi: "fieldDataAccessApi", formContentId: "formContentId", formContentVersion: "formContentVersion" }, outputs: { formDataChange: "formDataChange", formDataByPageChange: "formDataByPageChange", formValidationChange: "formValidationChange", uploadedFilesChange: "uploadedFilesChange", submit: "submit", activePageIdChange: "activePageIdChange" }, viewQueries: [{ propertyName: "renderer", first: true, predicate: JsonFormRendererComponent, descendants: true }, { propertyName: "journeyViewer", first: true, predicate: FormJourneyViewerComponent, descendants: true }], ngImport: i0, template: `
6922
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.17", type: FormViewerComponent, isStandalone: true, selector: "app-form-viewer", inputs: { schema: "schema", journey: "journey", data: "data", dataUsesFieldNameKeys: "dataUsesFieldNameKeys", options: "options", viewOnly: "viewOnly", severityEvaluationMode: "severityEvaluationMode", eventApis: "eventApis", eventApiExecutor: "eventApiExecutor", fieldDataAccessMap: "fieldDataAccessMap", fieldDataAccessApi: "fieldDataAccessApi", formContentId: "formContentId", formContentVersion: "formContentVersion", refreshKey: "refreshKey" }, outputs: { formDataChange: "formDataChange", formDataByPageChange: "formDataByPageChange", formValidationChange: "formValidationChange", uploadedFilesChange: "uploadedFilesChange", submit: "submit", activePageIdChange: "activePageIdChange" }, viewQueries: [{ propertyName: "renderer", first: true, predicate: JsonFormRendererComponent, descendants: true }, { propertyName: "journeyViewer", first: true, predicate: FormJourneyViewerComponent, descendants: true }], ngImport: i0, template: `
6795
6923
  <div class="form-viewer-container flex flex-col h-full"
6796
6924
  data-fd="form-viewer"
6797
6925
  [attr.data-fd-mode]="viewOnly ? 'preview' : 'live'">
@@ -6810,6 +6938,7 @@ class FormViewerComponent {
6810
6938
  [fieldDataAccessApi]="fieldDataAccessApi"
6811
6939
  [formContentId]="formContentId"
6812
6940
  [formContentVersion]="formContentVersion"
6941
+ [refreshKey]="refreshKey"
6813
6942
  (formDataChange)="onValueChange($event)"
6814
6943
  (formDataByPageChange)="onJourneyValueByPageChange($event)"
6815
6944
  (formValidationChange)="onValidationChange($event)"
@@ -6834,6 +6963,7 @@ class FormViewerComponent {
6834
6963
  [fieldDataAccessApi]="fieldDataAccessApi"
6835
6964
  [formContentId]="formContentId"
6836
6965
  [formContentVersion]="formContentVersion"
6966
+ [refreshKey]="refreshKey"
6837
6967
  (valueChange)="onValueChange($event)"
6838
6968
  (validationChange)="onValidationChange($event)"
6839
6969
  (uploadedFilesChange)="onUploadedFilesChange($event)"
@@ -6854,7 +6984,7 @@ class FormViewerComponent {
6854
6984
  </button>
6855
6985
  </div>
6856
6986
  </div>
6857
- `, isInline: true, styles: [":host{display:block;height:100%;width:100%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: JsonFormRendererComponent, selector: "app-json-form-renderer", inputs: ["schema", "initialValues", "initialFieldLabels", "mode", "device", "showLayoutGuides", "breakpoint", "eventLogger", "eventApis", "eventApiExecutor", "navigateToPage", "uploadOnSubmit", "severityEvaluationMode", "fieldDataAccessMap", "fieldDataAccessApi", "formContentId", "formContentVersion"], outputs: ["valueChange", "groupedValueChange", "combinedValueChange", "validationChange", "uploadedFilesChange", "formSubmit"] }, { kind: "component", type: FormJourneyViewerComponent, selector: "app-form-journey-viewer", inputs: ["journey", "viewOnly", "severityEvaluationMode", "eventApis", "eventApiExecutor", "initialValues", "initialFieldLabels", "fieldDataAccessMap", "fieldDataAccessApi", "formContentId", "formContentVersion"], outputs: ["formDataChange", "formDataByPageChange", "formValidationChange", "uploadedFilesChange", "submit", "activePageIdChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
6987
+ `, isInline: true, styles: [":host{display:block;height:100%;width:100%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: JsonFormRendererComponent, selector: "app-json-form-renderer", inputs: ["schema", "initialValues", "initialFieldLabels", "mode", "device", "showLayoutGuides", "breakpoint", "eventLogger", "eventApis", "eventApiExecutor", "navigateToPage", "uploadOnSubmit", "severityEvaluationMode", "fieldDataAccessMap", "fieldDataAccessApi", "formContentId", "formContentVersion", "refreshKey"], outputs: ["valueChange", "groupedValueChange", "combinedValueChange", "validationChange", "uploadedFilesChange", "formSubmit"] }, { kind: "component", type: FormJourneyViewerComponent, selector: "app-form-journey-viewer", inputs: ["journey", "viewOnly", "severityEvaluationMode", "eventApis", "eventApiExecutor", "initialValues", "initialFieldLabels", "fieldDataAccessMap", "fieldDataAccessApi", "formContentId", "formContentVersion", "refreshKey"], outputs: ["formDataChange", "formDataByPageChange", "formValidationChange", "uploadedFilesChange", "submit", "activePageIdChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
6858
6988
  }
6859
6989
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: FormViewerComponent, decorators: [{
6860
6990
  type: Component,
@@ -6877,6 +7007,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImpo
6877
7007
  [fieldDataAccessApi]="fieldDataAccessApi"
6878
7008
  [formContentId]="formContentId"
6879
7009
  [formContentVersion]="formContentVersion"
7010
+ [refreshKey]="refreshKey"
6880
7011
  (formDataChange)="onValueChange($event)"
6881
7012
  (formDataByPageChange)="onJourneyValueByPageChange($event)"
6882
7013
  (formValidationChange)="onValidationChange($event)"
@@ -6901,6 +7032,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImpo
6901
7032
  [fieldDataAccessApi]="fieldDataAccessApi"
6902
7033
  [formContentId]="formContentId"
6903
7034
  [formContentVersion]="formContentVersion"
7035
+ [refreshKey]="refreshKey"
6904
7036
  (valueChange)="onValueChange($event)"
6905
7037
  (validationChange)="onValidationChange($event)"
6906
7038
  (uploadedFilesChange)="onUploadedFilesChange($event)"
@@ -6948,6 +7080,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImpo
6948
7080
  type: Input
6949
7081
  }], formContentVersion: [{
6950
7082
  type: Input
7083
+ }], refreshKey: [{
7084
+ type: Input
6951
7085
  }], formDataChange: [{
6952
7086
  type: Output
6953
7087
  }], formDataByPageChange: [{
@@ -9029,7 +9163,7 @@ class LayoutCanvasComponent {
9029
9163
 
9030
9164
  <!-- Hidden file input for import -->
9031
9165
  <input type="file" #fileInput accept=".json" (change)="onFileSelected($event)" style="display: none;">
9032
- `, isInline: true, styles: [".canvas-grid{background-image:radial-gradient(rgba(148,163,184,.35) 1px,transparent 1px);background-size:18px 18px}.custom-scrollbar::-webkit-scrollbar{width:6px;height:6px}.custom-scrollbar::-webkit-scrollbar-track{background:transparent}.custom-scrollbar::-webkit-scrollbar-thumb{background:transparent;border-radius:10px}.custom-scrollbar:hover::-webkit-scrollbar-thumb{background:#00000026}.custom-scrollbar{scrollbar-width:thin;scrollbar-color:transparent transparent}.custom-scrollbar:hover{scrollbar-color:rgba(0,0,0,.15) transparent}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: JsonFormRendererComponent, selector: "app-json-form-renderer", inputs: ["schema", "initialValues", "initialFieldLabels", "mode", "device", "showLayoutGuides", "breakpoint", "eventLogger", "eventApis", "eventApiExecutor", "navigateToPage", "uploadOnSubmit", "severityEvaluationMode", "fieldDataAccessMap", "fieldDataAccessApi", "formContentId", "formContentVersion"], outputs: ["valueChange", "groupedValueChange", "combinedValueChange", "validationChange", "uploadedFilesChange", "formSubmit"] }, { kind: "ngmodule", type: UiIconModule }, { kind: "component", type: i3$1.LucideAngularComponent, selector: "lucide-angular, lucide-icon, i-lucide, span-lucide", inputs: ["class", "name", "img", "color", "absoluteStrokeWidth", "size", "strokeWidth"] }, { kind: "component", type: MonacoEditorComponent, selector: "app-monaco-editor", inputs: ["value", "language", "theme", "readOnly", "minimap", "options"], outputs: ["valueChange"] }] });
9166
+ `, isInline: true, styles: [".canvas-grid{background-image:radial-gradient(rgba(148,163,184,.35) 1px,transparent 1px);background-size:18px 18px}.custom-scrollbar::-webkit-scrollbar{width:6px;height:6px}.custom-scrollbar::-webkit-scrollbar-track{background:transparent}.custom-scrollbar::-webkit-scrollbar-thumb{background:transparent;border-radius:10px}.custom-scrollbar:hover::-webkit-scrollbar-thumb{background:#00000026}.custom-scrollbar{scrollbar-width:thin;scrollbar-color:transparent transparent}.custom-scrollbar:hover{scrollbar-color:rgba(0,0,0,.15) transparent}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: JsonFormRendererComponent, selector: "app-json-form-renderer", inputs: ["schema", "initialValues", "initialFieldLabels", "mode", "device", "showLayoutGuides", "breakpoint", "eventLogger", "eventApis", "eventApiExecutor", "navigateToPage", "uploadOnSubmit", "severityEvaluationMode", "fieldDataAccessMap", "fieldDataAccessApi", "formContentId", "formContentVersion", "refreshKey"], outputs: ["valueChange", "groupedValueChange", "combinedValueChange", "validationChange", "uploadedFilesChange", "formSubmit"] }, { kind: "ngmodule", type: UiIconModule }, { kind: "component", type: i3$1.LucideAngularComponent, selector: "lucide-angular, lucide-icon, i-lucide, span-lucide", inputs: ["class", "name", "img", "color", "absoluteStrokeWidth", "size", "strokeWidth"] }, { kind: "component", type: MonacoEditorComponent, selector: "app-monaco-editor", inputs: ["value", "language", "theme", "readOnly", "minimap", "options"], outputs: ["valueChange"] }] });
9033
9167
  }
9034
9168
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: LayoutCanvasComponent, decorators: [{
9035
9169
  type: Component,
@@ -17177,7 +17311,8 @@ class QueryBuilderComponent {
17177
17311
  { label: 'Contains', value: 'contains' },
17178
17312
  { label: 'Is Empty', value: 'empty' },
17179
17313
  { label: 'Not Empty', value: 'notEmpty' },
17180
- { label: 'Is Truthy', value: 'truthy' }
17314
+ { label: 'Is Truthy', value: 'truthy' },
17315
+ { label: 'Is Not Truthy', value: 'notTruthy' }
17181
17316
  ];
17182
17317
  fieldOptions() {
17183
17318
  return [
@@ -17259,7 +17394,7 @@ class QueryBuilderComponent {
17259
17394
  this.emitChange();
17260
17395
  }
17261
17396
  requiresValue(operator) {
17262
- return !['empty', 'notEmpty', 'truthy'].includes(operator);
17397
+ return !['empty', 'notEmpty', 'truthy', 'notTruthy'].includes(operator);
17263
17398
  }
17264
17399
  conditionValueSource(condition) {
17265
17400
  return condition.valueSource === 'field' ? 'field' : 'literal';
@@ -19638,7 +19773,7 @@ class FormPreviewComponent {
19638
19773
  </div>
19639
19774
  </div>
19640
19775
  </div>
19641
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i1.JsonPipe, name: "json" }, { kind: "ngmodule", type: UiIconModule }, { kind: "component", type: i3$1.LucideAngularComponent, selector: "lucide-angular, lucide-icon, i-lucide, span-lucide", inputs: ["class", "name", "img", "color", "absoluteStrokeWidth", "size", "strokeWidth"] }, { kind: "component", type: JsonFormRendererComponent, selector: "app-json-form-renderer", inputs: ["schema", "initialValues", "initialFieldLabels", "mode", "device", "showLayoutGuides", "breakpoint", "eventLogger", "eventApis", "eventApiExecutor", "navigateToPage", "uploadOnSubmit", "severityEvaluationMode", "fieldDataAccessMap", "fieldDataAccessApi", "formContentId", "formContentVersion"], outputs: ["valueChange", "groupedValueChange", "combinedValueChange", "validationChange", "uploadedFilesChange", "formSubmit"] }] });
19776
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i1.JsonPipe, name: "json" }, { kind: "ngmodule", type: UiIconModule }, { kind: "component", type: i3$1.LucideAngularComponent, selector: "lucide-angular, lucide-icon, i-lucide, span-lucide", inputs: ["class", "name", "img", "color", "absoluteStrokeWidth", "size", "strokeWidth"] }, { kind: "component", type: JsonFormRendererComponent, selector: "app-json-form-renderer", inputs: ["schema", "initialValues", "initialFieldLabels", "mode", "device", "showLayoutGuides", "breakpoint", "eventLogger", "eventApis", "eventApiExecutor", "navigateToPage", "uploadOnSubmit", "severityEvaluationMode", "fieldDataAccessMap", "fieldDataAccessApi", "formContentId", "formContentVersion", "refreshKey"], outputs: ["valueChange", "groupedValueChange", "combinedValueChange", "validationChange", "uploadedFilesChange", "formSubmit"] }] });
19642
19777
  }
19643
19778
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: FormPreviewComponent, decorators: [{
19644
19779
  type: Component,
@@ -23646,7 +23781,8 @@ class EventsWorkspaceComponent {
23646
23781
  { label: 'Ends With', value: 'endsWith' },
23647
23782
  { label: 'Is Empty', value: 'empty' },
23648
23783
  { label: 'Is Not Empty', value: 'notEmpty' },
23649
- { label: 'Is Truthy', value: 'truthy' }
23784
+ { label: 'Is Truthy', value: 'truthy' },
23785
+ { label: 'Is Not Truthy', value: 'notTruthy' }
23650
23786
  ];
23651
23787
  currentSchema = computed(() => this.draftSchema() ?? this.schema());
23652
23788
  fields = computed(() => this.flattenFields(this.currentSchema()));
@@ -23755,6 +23891,7 @@ class EventsWorkspaceComponent {
23755
23891
  actionTypeOptions(binding, actionIndex) {
23756
23892
  return [
23757
23893
  { label: 'Set Value', value: 'setValue' },
23894
+ { label: 'Clear Fields', value: 'clearFields' },
23758
23895
  { label: 'Log', value: 'log' },
23759
23896
  { label: 'Navigate', value: 'navigate' },
23760
23897
  { label: 'API', value: 'api', disabled: !this.canSelectApiAction(binding, actionIndex) }
@@ -23828,6 +23965,15 @@ class EventsWorkspaceComponent {
23828
23965
  binding.actions[actionIndex] = nextAction;
23829
23966
  return;
23830
23967
  }
23968
+ if (currentAction.type === 'clearFields') {
23969
+ const nextAction = {
23970
+ ...currentAction,
23971
+ ...patch,
23972
+ targetFieldIds: [...patch.targetFieldIds ?? currentAction.targetFieldIds]
23973
+ };
23974
+ binding.actions[actionIndex] = nextAction;
23975
+ return;
23976
+ }
23831
23977
  if (currentAction.type === 'log') {
23832
23978
  const nextAction = {
23833
23979
  ...currentAction,
@@ -24043,6 +24189,9 @@ class EventsWorkspaceComponent {
24043
24189
  asSetValue(action) {
24044
24190
  return action;
24045
24191
  }
24192
+ asClearFields(action) {
24193
+ return action;
24194
+ }
24046
24195
  asLog(action) {
24047
24196
  return action;
24048
24197
  }
@@ -24124,7 +24273,7 @@ class EventsWorkspaceComponent {
24124
24273
  this.updateNavigateConditionGroup(bindingIndex, actionIndex, this.toNavigateLogicGroup(next));
24125
24274
  }
24126
24275
  navigateOperatorNeedsValue(operator) {
24127
- return !['empty', 'notEmpty', 'truthy'].includes(operator);
24276
+ return !['empty', 'notEmpty', 'truthy', 'notTruthy'].includes(operator);
24128
24277
  }
24129
24278
  navigateConditionFields() {
24130
24279
  return this.fields().map(field => ({
@@ -24149,6 +24298,22 @@ class EventsWorkspaceComponent {
24149
24298
  hasApiAction(binding) {
24150
24299
  return binding.actions.some(action => action.type === 'api');
24151
24300
  }
24301
+ isClearFieldSelected(action, fieldId) {
24302
+ return action.targetFieldIds.includes(fieldId);
24303
+ }
24304
+ toggleClearFieldTarget(action, fieldId, selected) {
24305
+ if (action.type !== 'clearFields') {
24306
+ return;
24307
+ }
24308
+ const targetFieldIds = selected
24309
+ ? [...new Set([...action.targetFieldIds, fieldId])]
24310
+ : action.targetFieldIds.filter(id => id !== fieldId);
24311
+ const path = this.findActionPath(action);
24312
+ if (!path) {
24313
+ return;
24314
+ }
24315
+ this.patchAction(path.bindingIndex, path.actionIndex, { targetFieldIds });
24316
+ }
24152
24317
  canSelectApiAction(binding, actionIndex) {
24153
24318
  const current = binding.actions[actionIndex];
24154
24319
  if (current?.type === 'api')
@@ -24205,6 +24370,12 @@ class EventsWorkspaceComponent {
24205
24370
  valueLiteral: ''
24206
24371
  };
24207
24372
  }
24373
+ if (type === 'clearFields') {
24374
+ return {
24375
+ type: 'clearFields',
24376
+ targetFieldIds: []
24377
+ };
24378
+ }
24208
24379
  if (type === 'api') {
24209
24380
  return {
24210
24381
  type: 'api',
@@ -24744,6 +24915,32 @@ class EventsWorkspaceComponent {
24744
24915
  </div>
24745
24916
  </ng-container>
24746
24917
 
24918
+ <ng-container *ngIf="action.type === 'clearFields'">
24919
+ <div>
24920
+ <label class="mb-1 block text-[11px] font-medium text-gray-600">Fields To Clear</label>
24921
+ <div class="max-h-40 overflow-y-auto rounded-md border border-gray-200 bg-white">
24922
+ <label
24923
+ *ngFor="let targetField of availableTargetFields(); trackBy: trackFieldById"
24924
+ class="flex items-center gap-2 border-b border-gray-100 px-2 py-1.5 text-xs last:border-b-0">
24925
+ <input
24926
+ type="checkbox"
24927
+ class="rounded border-gray-300 text-blue-600 focus:ring-blue-500"
24928
+ [disabled]="readOnly()"
24929
+ [ngModel]="isClearFieldSelected(asClearFields(action), targetField.id)"
24930
+ (ngModelChange)="toggleClearFieldTarget(action, targetField.id, $event)">
24931
+ <span>{{ targetField.label }}</span>
24932
+ <span class="text-[10px] text-gray-400">({{ targetField.name }})</span>
24933
+ </label>
24934
+ <div *ngIf="!availableTargetFields().length" class="px-2 py-3 text-xs text-gray-500">
24935
+ No fields available.
24936
+ </div>
24937
+ </div>
24938
+ <p class="mt-2 text-[10px] text-gray-500">
24939
+ Clears selected fields to an empty runtime value.
24940
+ </p>
24941
+ </div>
24942
+ </ng-container>
24943
+
24747
24944
  <ng-container *ngIf="action.type === 'log'">
24748
24945
  <label class="mb-1 block text-[11px] font-medium text-gray-600">Message</label>
24749
24946
  <input
@@ -25125,6 +25322,32 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImpo
25125
25322
  </div>
25126
25323
  </ng-container>
25127
25324
 
25325
+ <ng-container *ngIf="action.type === 'clearFields'">
25326
+ <div>
25327
+ <label class="mb-1 block text-[11px] font-medium text-gray-600">Fields To Clear</label>
25328
+ <div class="max-h-40 overflow-y-auto rounded-md border border-gray-200 bg-white">
25329
+ <label
25330
+ *ngFor="let targetField of availableTargetFields(); trackBy: trackFieldById"
25331
+ class="flex items-center gap-2 border-b border-gray-100 px-2 py-1.5 text-xs last:border-b-0">
25332
+ <input
25333
+ type="checkbox"
25334
+ class="rounded border-gray-300 text-blue-600 focus:ring-blue-500"
25335
+ [disabled]="readOnly()"
25336
+ [ngModel]="isClearFieldSelected(asClearFields(action), targetField.id)"
25337
+ (ngModelChange)="toggleClearFieldTarget(action, targetField.id, $event)">
25338
+ <span>{{ targetField.label }}</span>
25339
+ <span class="text-[10px] text-gray-400">({{ targetField.name }})</span>
25340
+ </label>
25341
+ <div *ngIf="!availableTargetFields().length" class="px-2 py-3 text-xs text-gray-500">
25342
+ No fields available.
25343
+ </div>
25344
+ </div>
25345
+ <p class="mt-2 text-[10px] text-gray-500">
25346
+ Clears selected fields to an empty runtime value.
25347
+ </p>
25348
+ </div>
25349
+ </ng-container>
25350
+
25128
25351
  <ng-container *ngIf="action.type === 'log'">
25129
25352
  <label class="mb-1 block text-[11px] font-medium text-gray-600">Message</label>
25130
25353
  <input
@@ -26293,7 +26516,7 @@ class FormDesignerShellComponent {
26293
26516
  </div>
26294
26517
  </section>
26295
26518
  </div>
26296
- `, isInline: true, styles: ["@keyframes slideInRight{0%{transform:translate(100%);opacity:0}to{transform:translate(0);opacity:1}}.animate-slide-in-right{animation:slideInRight .3s cubic-bezier(.16,1,.3,1)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i1.TitleCasePipe, name: "titlecase" }, { kind: "component", type: JsonFormDesignerComponent, selector: "app-json-form-designer", inputs: ["flavor", "mode", "eventApis", "eventApiExecutor", "pages", "activePageId", "canRemovePage"], outputs: ["pageAdd", "pageSelect", "pageRemove", "pageRename", "pageRouteChange"] }, { kind: "component", type: EventsWorkspaceComponent, selector: "app-events-workspace", inputs: ["schema", "readOnly", "eventApis", "eventApiBrowser", "pages"], outputs: ["schemaChange", "eventsSave"] }, { kind: "component", type: JsonFormRendererComponent, selector: "app-json-form-renderer", inputs: ["schema", "initialValues", "initialFieldLabels", "mode", "device", "showLayoutGuides", "breakpoint", "eventLogger", "eventApis", "eventApiExecutor", "navigateToPage", "uploadOnSubmit", "severityEvaluationMode", "fieldDataAccessMap", "fieldDataAccessApi", "formContentId", "formContentVersion"], outputs: ["valueChange", "groupedValueChange", "combinedValueChange", "validationChange", "uploadedFilesChange", "formSubmit"] }, { kind: "component", type: EmailRendererComponent, selector: "app-email-renderer", inputs: ["schema", "engine"] }, { kind: "component", type: GlobalDataManagerComponent, selector: "app-global-data-manager", outputs: ["close"] }, { kind: "component", type: AiChatDrawerComponent, selector: "app-ai-chat-drawer", outputs: ["close", "expand"] }, { kind: "component", type: AiWorkspaceComponent, selector: "app-ai-workspace", outputs: ["close"] }] });
26519
+ `, isInline: true, styles: ["@keyframes slideInRight{0%{transform:translate(100%);opacity:0}to{transform:translate(0);opacity:1}}.animate-slide-in-right{animation:slideInRight .3s cubic-bezier(.16,1,.3,1)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i1.TitleCasePipe, name: "titlecase" }, { kind: "component", type: JsonFormDesignerComponent, selector: "app-json-form-designer", inputs: ["flavor", "mode", "eventApis", "eventApiExecutor", "pages", "activePageId", "canRemovePage"], outputs: ["pageAdd", "pageSelect", "pageRemove", "pageRename", "pageRouteChange"] }, { kind: "component", type: EventsWorkspaceComponent, selector: "app-events-workspace", inputs: ["schema", "readOnly", "eventApis", "eventApiBrowser", "pages"], outputs: ["schemaChange", "eventsSave"] }, { kind: "component", type: JsonFormRendererComponent, selector: "app-json-form-renderer", inputs: ["schema", "initialValues", "initialFieldLabels", "mode", "device", "showLayoutGuides", "breakpoint", "eventLogger", "eventApis", "eventApiExecutor", "navigateToPage", "uploadOnSubmit", "severityEvaluationMode", "fieldDataAccessMap", "fieldDataAccessApi", "formContentId", "formContentVersion", "refreshKey"], outputs: ["valueChange", "groupedValueChange", "combinedValueChange", "validationChange", "uploadedFilesChange", "formSubmit"] }, { kind: "component", type: EmailRendererComponent, selector: "app-email-renderer", inputs: ["schema", "engine"] }, { kind: "component", type: GlobalDataManagerComponent, selector: "app-global-data-manager", outputs: ["close"] }, { kind: "component", type: AiChatDrawerComponent, selector: "app-ai-chat-drawer", outputs: ["close", "expand"] }, { kind: "component", type: AiWorkspaceComponent, selector: "app-ai-workspace", outputs: ["close"] }] });
26297
26520
  }
26298
26521
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: FormDesignerShellComponent, decorators: [{
26299
26522
  type: Component,
@@ -31547,7 +31770,7 @@ class WebsitePreviewShellComponent {
31547
31770
  }
31548
31771
  </main>
31549
31772
  </div>
31550
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "component", type: JsonFormRendererComponent, selector: "app-json-form-renderer", inputs: ["schema", "initialValues", "initialFieldLabels", "mode", "device", "showLayoutGuides", "breakpoint", "eventLogger", "eventApis", "eventApiExecutor", "navigateToPage", "uploadOnSubmit", "severityEvaluationMode", "fieldDataAccessMap", "fieldDataAccessApi", "formContentId", "formContentVersion"], outputs: ["valueChange", "groupedValueChange", "combinedValueChange", "validationChange", "uploadedFilesChange", "formSubmit"] }] });
31773
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "component", type: JsonFormRendererComponent, selector: "app-json-form-renderer", inputs: ["schema", "initialValues", "initialFieldLabels", "mode", "device", "showLayoutGuides", "breakpoint", "eventLogger", "eventApis", "eventApiExecutor", "navigateToPage", "uploadOnSubmit", "severityEvaluationMode", "fieldDataAccessMap", "fieldDataAccessApi", "formContentId", "formContentVersion", "refreshKey"], outputs: ["valueChange", "groupedValueChange", "combinedValueChange", "validationChange", "uploadedFilesChange", "formSubmit"] }] });
31551
31774
  }
31552
31775
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: WebsitePreviewShellComponent, decorators: [{
31553
31776
  type: Component,
@@ -32681,6 +32904,10 @@ class TextFieldWidgetComponent {
32681
32904
  this.clearResolvedValue();
32682
32905
  return;
32683
32906
  }
32907
+ if (clearWhenMissing && !(await this.hasResolvedRows())) {
32908
+ this.clearResolvedValue();
32909
+ return;
32910
+ }
32684
32911
  const val = await this.dataProvider.getValue(this.config, this.engine);
32685
32912
  this.displayPrefix = await this.dataProvider.getValueDisplayPrefix(this.config, this.engine);
32686
32913
  if (val === undefined || val === null) {
@@ -32690,23 +32917,37 @@ class TextFieldWidgetComponent {
32690
32917
  this.clearResolvedValue();
32691
32918
  return;
32692
32919
  }
32693
- this.control.setValue(val, { emitEvent: false });
32694
- this.syncFormattedNumberValue();
32695
- if (this.engine) {
32696
- this.engine.setValue(this.config.name, val);
32697
- }
32920
+ this.applyResolvedValue(val);
32698
32921
  }
32699
32922
  catch {
32700
32923
  // Ignore failed datasource refreshes; field remains editable.
32701
32924
  }
32702
32925
  }
32926
+ async hasResolvedRows() {
32927
+ const rows = await this.dataProvider.getList(this.config, this.engine);
32928
+ return rows.length > 0;
32929
+ }
32703
32930
  clearResolvedValue() {
32704
32931
  this.displayPrefix = '';
32705
- this.control.setValue(null, { emitEvent: false });
32932
+ this.applyResolvedValue(null);
32933
+ }
32934
+ applyResolvedValue(value) {
32935
+ const previousValue = this.engine?.getValue(this.config.name);
32936
+ this.control.setValue(value, { emitEvent: false });
32706
32937
  this.syncFormattedNumberValue();
32707
- if (this.engine) {
32708
- this.engine.setValue(this.config.name, null);
32938
+ if (!this.engine) {
32939
+ return;
32940
+ }
32941
+ this.engine.setValue(this.config.name, value);
32942
+ if (Object.is(previousValue, value)) {
32943
+ return;
32709
32944
  }
32945
+ this.engine.emitUiEvent({
32946
+ fieldId: this.config.id,
32947
+ fieldName: this.config.name,
32948
+ type: 'change',
32949
+ value
32950
+ });
32710
32951
  }
32711
32952
  async hasSelectedRowMatch() {
32712
32953
  const cfg = this.config.dataConfig;