@openg2p/registry-widgets 1.1.2-dev.5 → 1.1.2-dev.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/dist/components/SectionBuilder/schemas.d.ts +112 -0
  2. package/dist/components/SectionBuilder/schemas.d.ts.map +1 -1
  3. package/dist/components/SectionRenderer.d.ts.map +1 -1
  4. package/dist/components/WidgetFieldLabel.d.ts +11 -0
  5. package/dist/components/WidgetFieldLabel.d.ts.map +1 -0
  6. package/dist/hooks/useBaseWidget.d.ts +2 -0
  7. package/dist/hooks/useBaseWidget.d.ts.map +1 -1
  8. package/dist/index.d.ts +45 -13
  9. package/dist/index.esm.js +878 -258
  10. package/dist/index.esm.js.map +1 -1
  11. package/dist/index.js +885 -257
  12. package/dist/index.js.map +1 -1
  13. package/dist/registry/defaultWidgets.d.ts.map +1 -1
  14. package/dist/types/index.d.ts +11 -1
  15. package/dist/types/index.d.ts.map +1 -1
  16. package/dist/utils/conditions.d.ts +17 -11
  17. package/dist/utils/conditions.d.ts.map +1 -1
  18. package/dist/utils/geoHierarchy.d.ts +9 -0
  19. package/dist/utils/geoHierarchy.d.ts.map +1 -1
  20. package/dist/utils/schemaNamespace.d.ts.map +1 -1
  21. package/dist/utils/schemaTranslation.d.ts.map +1 -1
  22. package/dist/utils/sectionRevert.d.ts +24 -0
  23. package/dist/utils/sectionRevert.d.ts.map +1 -0
  24. package/dist/utils/sectionValidate.d.ts.map +1 -1
  25. package/dist/widgets/ArrayWidget.d.ts.map +1 -1
  26. package/dist/widgets/BooleanWidget.d.ts.map +1 -1
  27. package/dist/widgets/CheckboxWidget.d.ts.map +1 -1
  28. package/dist/widgets/CurrencyInputWidget.d.ts.map +1 -1
  29. package/dist/widgets/DateInputWidget.d.ts.map +1 -1
  30. package/dist/widgets/DateTimeInputWidget.d.ts.map +1 -1
  31. package/dist/widgets/DialogTableWidget.d.ts.map +1 -1
  32. package/dist/widgets/FileInputWidget.d.ts.map +1 -1
  33. package/dist/widgets/IterableAccordionWidget.d.ts.map +1 -1
  34. package/dist/widgets/MultiSelectWidget.d.ts +7 -0
  35. package/dist/widgets/MultiSelectWidget.d.ts.map +1 -0
  36. package/dist/widgets/NumberInputWidget.d.ts.map +1 -1
  37. package/dist/widgets/PhoneInputWidget.d.ts.map +1 -1
  38. package/dist/widgets/RadioWidget.d.ts.map +1 -1
  39. package/dist/widgets/RegisterLookupWidget.d.ts.map +1 -1
  40. package/dist/widgets/SelectWidget.d.ts.map +1 -1
  41. package/dist/widgets/TextAreaWidget.d.ts.map +1 -1
  42. package/dist/widgets/TextInputWidget.d.ts.map +1 -1
  43. package/dist/widgets/index.d.ts +1 -0
  44. package/dist/widgets/index.d.ts.map +1 -1
  45. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -403,6 +403,18 @@ const createZodSchema = (validation, required = false) => {
403
403
  return schema;
404
404
  };
405
405
 
406
+ const normalizeBooleanLike = (val) => {
407
+ if (val === true || val === 1)
408
+ return true;
409
+ if (val === false || val === 0 || val === null || val === undefined || val === '') {
410
+ return false;
411
+ }
412
+ if (typeof val === 'string') {
413
+ const normalized = val.trim().toLowerCase();
414
+ return normalized === 'true' || normalized === 'yes' || normalized === '1';
415
+ }
416
+ return Boolean(val);
417
+ };
406
418
  /**
407
419
  * Evaluate condition against field value
408
420
  */
@@ -411,6 +423,9 @@ const evaluateCondition = (condition, allValues) => {
411
423
  const { operator, value } = condition;
412
424
  switch (operator) {
413
425
  case 'equals':
426
+ if (typeof value === 'boolean' || typeof fieldValue === 'boolean') {
427
+ return normalizeBooleanLike(fieldValue) === normalizeBooleanLike(value);
428
+ }
414
429
  return fieldValue === value;
415
430
  case 'notEquals':
416
431
  return fieldValue !== value;
@@ -443,37 +458,62 @@ const evaluateCondition = (condition, allValues) => {
443
458
  }
444
459
  };
445
460
  /**
446
- * Check if widget should be visible based on conditions
461
+ * Normalize widget-data-options into a sequential list of action rules.
462
+ * Supports legacy single { action, condition } and new { actions: [...] }.
447
463
  */
448
- const shouldShowWidget = (options, allValues) => {
449
- if (!options?.condition) {
450
- return true;
464
+ const normalizeOptionRules = (options) => {
465
+ if (!options) {
466
+ return [];
451
467
  }
452
- const conditionResult = evaluateCondition(options.condition, allValues);
453
- if (options.action === 'show') {
454
- return conditionResult;
468
+ if (Array.isArray(options.actions) && options.actions.length > 0) {
469
+ return options.actions.filter((rule) => !!rule?.action);
455
470
  }
456
- if (options.action === 'hide') {
457
- return !conditionResult;
471
+ if (options.action && options.condition) {
472
+ return [{ action: options.action, condition: options.condition }];
458
473
  }
459
- return true;
474
+ return [];
475
+ };
476
+ const hasVisibilityRules = (options) => {
477
+ return normalizeOptionRules(options).some((rule) => rule.action === 'show' || rule.action === 'hide');
460
478
  };
461
479
  /**
462
- * Check if widget should be enabled based on conditions
480
+ * Evaluate widget-data-options rules sequentially.
481
+ * show/hide and enable/disable only affect visibility and enabled state.
482
+ * require is independent: required = widget-required OR require-condition-match.
463
483
  */
464
- const shouldEnableWidget = (options, allValues) => {
465
- if (!options?.condition) {
466
- return true;
467
- }
468
- const conditionResult = evaluateCondition(options.condition, allValues);
469
- if (options.action === 'enable') {
470
- return conditionResult;
471
- }
472
- if (options.action === 'disable') {
473
- return !conditionResult;
484
+ const evaluateWidgetConditions = (options, allValues, baseRequired = false) => {
485
+ let visible = true;
486
+ let enabled = true;
487
+ let required = baseRequired;
488
+ const rules = normalizeOptionRules(options);
489
+ for (const rule of rules) {
490
+ if (!rule.condition) {
491
+ continue;
492
+ }
493
+ const match = evaluateCondition(rule.condition, allValues);
494
+ switch (rule.action) {
495
+ case 'show':
496
+ visible = match;
497
+ break;
498
+ case 'hide':
499
+ visible = !match;
500
+ break;
501
+ case 'enable':
502
+ enabled = match;
503
+ break;
504
+ case 'disable':
505
+ enabled = !match;
506
+ break;
507
+ case 'require':
508
+ required = baseRequired || match;
509
+ break;
510
+ }
474
511
  }
475
- return true;
512
+ return { visible, enabled, required };
476
513
  };
514
+ const shouldShowWidget = (options, allValues) => evaluateWidgetConditions(options, allValues).visible;
515
+ const shouldEnableWidget = (options, allValues) => evaluateWidgetConditions(options, allValues).enabled;
516
+ const shouldRequireWidget = (options, allValues, baseRequired = false) => evaluateWidgetConditions(options, allValues, baseRequired).required;
477
517
 
478
518
  /**
479
519
  * Format number with thousand and decimal separators
@@ -1937,6 +1977,98 @@ const WidgetProvider = ({ store, dataSourceRequestHandler, schemaData, translate
1937
1977
  return content;
1938
1978
  };
1939
1979
 
1980
+ /**
1981
+ * Custom hook for widget translations
1982
+ * Provides translation function with widget-specific namespace and fallback support
1983
+ */
1984
+ const useWidgetTranslation = () => {
1985
+ const { translate: translateFunction } = useWidgetContext();
1986
+ /**
1987
+ * Translate a key with flexible namespace support
1988
+ * Supports translation keys in various formats and direct strings
1989
+ *
1990
+ * Translation key formats supported:
1991
+ * - "widgets:common.addItem" - Namespaced key (for widget-specific translations)
1992
+ * - "Name" - Direct string (will be looked up in flat translation structure)
1993
+ * - "sections.personalDetails" - Nested key (for backward compatibility)
1994
+ *
1995
+ * With flat translation structure, direct strings like "Name" are automatically
1996
+ * translated by looking them up in the translation resources.
1997
+ *
1998
+ * @param keyOrString - Translation key (e.g., "widgets:common.addItem") or direct string (e.g., "Name")
1999
+ * @param options - Translation options (interpolation values, default value, etc.)
2000
+ * @returns Translated string or original string if translation not found
2001
+ */
2002
+ const translate = (keyOrString, options) => {
2003
+ if (!keyOrString) {
2004
+ return options?.defaultValue || '';
2005
+ }
2006
+ // Use the provided translation function or fallback to the key
2007
+ if (translateFunction) {
2008
+ return translateFunction(keyOrString, options) || options?.defaultValue || keyOrString;
2009
+ }
2010
+ // Fallback to key if no translation function available
2011
+ return options?.defaultValue || keyOrString;
2012
+ };
2013
+ /**
2014
+ * Translate widget config property
2015
+ * Attempts to translate the value, but if translation is not found,
2016
+ * returns the original value as-is (graceful fallback)
2017
+ *
2018
+ * This function will:
2019
+ * - Try to translate any string value
2020
+ * - If translation exists, use the translated value
2021
+ * - If translation doesn't exist (returns same value or throws), use original value
2022
+ * - This prevents errors when literal strings like "XXXX-XXXX-XXXX" are used
2023
+ */
2024
+ const translateConfig = (value, fallback) => {
2025
+ if (!value) {
2026
+ return fallback || '';
2027
+ }
2028
+ // Try to translate the value
2029
+ if (translateFunction) {
2030
+ try {
2031
+ // Pass defaultValue to ensure we get the original value if translation fails
2032
+ const translated = translateFunction(value, { defaultValue: value });
2033
+ // If translation returns empty, null, undefined, or the exact same value,
2034
+ // it means no translation was found - return the original value
2035
+ if (!translated || translated === value) {
2036
+ return value;
2037
+ }
2038
+ // Translation found, return it
2039
+ return translated;
2040
+ }
2041
+ catch (error) {
2042
+ // If translation throws an error (e.g., missing key warning), return original value
2043
+ return value;
2044
+ }
2045
+ }
2046
+ // No translation function available, return value as-is
2047
+ return value;
2048
+ };
2049
+ // No need of this getLanguage and changeLanguage functions
2050
+ /**
2051
+ * Get current language
2052
+ */
2053
+ // const getLanguage = (): string => {
2054
+ // return i18n.language || 'en';
2055
+ // };
2056
+ /**
2057
+ * Change language
2058
+ */
2059
+ // const changeLanguage = (lng: string): Promise<void> => {
2060
+ // return i18n.changeLanguage(lng).then(() => undefined);
2061
+ // };
2062
+ return {
2063
+ t: translate,
2064
+ translate,
2065
+ translateConfig,
2066
+ // getLanguage,
2067
+ // changeLanguage,
2068
+ // i18n: null,
2069
+ };
2070
+ };
2071
+
1940
2072
  /**
1941
2073
  * Geo Hierarchy Builder
1942
2074
  * Manages geo hierarchy state and builds hierarchy JSON structure
@@ -2136,6 +2268,42 @@ function isUpstreamGeoAncestor(changedWidgetId, widgetId, immediateParentWidgetI
2136
2268
  }
2137
2269
  return false;
2138
2270
  }
2271
+ /** Group id for geo widgets sharing the same register prefix (e.g. `{registerId}`). */
2272
+ function getGeoGroupId(dataPath) {
2273
+ if (typeof dataPath === 'string' && dataPath.includes('.')) {
2274
+ return dataPath.split('.').slice(0, -1).join('.');
2275
+ }
2276
+ return 'default';
2277
+ }
2278
+ /**
2279
+ * Resolve the human-readable label for a geo level from persisted hierarchy JSON.
2280
+ * Used in readonly mode when API options are not loaded.
2281
+ */
2282
+ function resolveGeoWidgetLevelLabel(values, widgetId, dataPath, geoConfig) {
2283
+ if (!dataPath || typeof dataPath !== 'string') {
2284
+ return undefined;
2285
+ }
2286
+ const stored = getWidgetValue(values, dataPath, widgetId);
2287
+ const hierarchy = stored?.hierarchy || stored?.geo_code_hierarchy_json?.hierarchy;
2288
+ if (!Array.isArray(hierarchy)) {
2289
+ return undefined;
2290
+ }
2291
+ const levelData = hierarchy.find((l) => l.level === geoConfig.level);
2292
+ if (levelData?.level_value_mnemonic) {
2293
+ return String(levelData.level_value_mnemonic);
2294
+ }
2295
+ return undefined;
2296
+ }
2297
+ /** All registered geo widgets that are descendants of ancestorWidgetId. */
2298
+ function getGeoDescendantWidgetIds(ancestorWidgetId) {
2299
+ const descendants = [];
2300
+ for (const [childId, parentId] of geoWidgetParentRegistry.entries()) {
2301
+ if (isUpstreamGeoAncestor(ancestorWidgetId, childId, parentId)) {
2302
+ descendants.push(childId);
2303
+ }
2304
+ }
2305
+ return descendants;
2306
+ }
2139
2307
  function readStoredHierarchyLevels(values, dataPath, widgetId) {
2140
2308
  const stored = getWidgetValue(values, dataPath, widgetId);
2141
2309
  const hierarchy = stored?.hierarchy || stored?.geo_code_hierarchy_json?.hierarchy;
@@ -2186,6 +2354,7 @@ const useBaseWidget = (options) => {
2186
2354
  const dispatch = reactRedux.useDispatch();
2187
2355
  const context = useWidgetContext();
2188
2356
  const eventBus = useWidgetEventBus();
2357
+ const { translateConfig } = useWidgetTranslation();
2189
2358
  const widgetId = config['widget-id'];
2190
2359
  // Fall back to WidgetContext for dataSourceRequestHandler
2191
2360
  const dataSourceRequestHandler = propHandler || context.dataSourceRequestHandler;
@@ -2356,6 +2525,15 @@ const useBaseWidget = (options) => {
2356
2525
  }
2357
2526
  // eslint-disable-next-line react-hooks/exhaustive-deps
2358
2527
  }, [isLayoutWidget]); // Only run once on mount
2528
+ const resolveIsRequired = React.useCallback((currentValues) => {
2529
+ if (isLayoutWidget) {
2530
+ return false;
2531
+ }
2532
+ if (config['widget-readonly']) {
2533
+ return false;
2534
+ }
2535
+ return evaluateWidgetConditions(config['widget-data-options'], currentValues, config['widget-required'] ?? false).required;
2536
+ }, [config, isLayoutWidget]);
2359
2537
  // Handle value change
2360
2538
  // CRITICAL: Don't include 'values' in dependency array - it causes the callback to be recreated
2361
2539
  // every time values change, which can lead to stale closures and double dispatches
@@ -2399,29 +2577,26 @@ const useBaseWidget = (options) => {
2399
2577
  lastDispatchedValueRef.current = newValue;
2400
2578
  dispatch(setValue({ widgetId, value: newValue }));
2401
2579
  }
2580
+ else if (config['widget-geo-config']) {
2581
+ // Geo widgets: hierarchy dataPath is managed by useGeoWidgetCascade
2582
+ getGeoDescendantWidgetIds(widgetId).forEach((descendantId) => {
2583
+ dispatch(setValue({ widgetId: descendantId, value: GEO_LEVEL_CLEARED }));
2584
+ dispatch(setDataSource({ widgetId: descendantId, data: [] }));
2585
+ });
2586
+ dispatch(setValue({ widgetId, value: newValue }));
2587
+ }
2402
2588
  else {
2403
- // Has dataPath: update both widgetId and dataPath
2404
- // CRITICAL: For geo widgets, we do NOT want to overwrite the shared hierarchy dataPath
2405
- // with a primitive value (the selected ID). The hierarchy object is managed by useGeoWidgetCascade.
2406
- if (config['widget-geo-config']) {
2407
- dispatch(setValue({ widgetId, value: newValue }));
2408
- return;
2409
- }
2410
- // For non-geo widgets, update both widgetId and dataPath
2411
- // CRITICAL: Create updated values object with newValue already set
2412
- // This prevents setWidgetValue from reading stale values
2589
+ // Non-geo widgets: update both widgetId and dataPath
2413
2590
  const currentValuesWithUpdate = {
2414
2591
  ...valuesRef.current,
2415
- [widgetId]: newValue, // Ensure widgetId has the new value
2592
+ [widgetId]: newValue,
2416
2593
  };
2417
2594
  const updatedValues = setWidgetValue(currentValuesWithUpdate, config['widget-data-path'], widgetId, newValue);
2418
- // setWidgetValue returns the complete updated structure with all existing data preserved
2419
- // Use setValues to update the entire state with deep merge
2420
2595
  dispatch(setValues(updatedValues));
2421
2596
  }
2422
2597
  // Validate if needed
2423
2598
  if (validate) {
2424
- const validationErrors = validateWidget(newValue, config['widget-data-validation'], config['widget-required']);
2599
+ const validationErrors = validateWidget(newValue, config['widget-data-validation'], resolveIsRequired(currentValues));
2425
2600
  dispatch(setError({ widgetId, errors: validationErrors }));
2426
2601
  }
2427
2602
  // Call custom onChange if provided
@@ -2440,13 +2615,12 @@ const useBaseWidget = (options) => {
2440
2615
  timestamp: Date.now(),
2441
2616
  });
2442
2617
  }
2443
- }, [config, widgetId, dispatch, onValueChange, eventBus] // Removed 'values' to prevent stale closures
2618
+ }, [config, widgetId, dispatch, onValueChange, eventBus, resolveIsRequired] // Removed 'values' to prevent stale closures
2444
2619
  );
2445
2620
  // Handle blur
2446
2621
  const handleBlur = React.useCallback(() => {
2447
2622
  dispatch(setTouched({ widgetId, touched: true }));
2448
- // Validate on blur
2449
- const validationErrors = validateWidget(currentValue, config['widget-data-validation'], config['widget-required']);
2623
+ const validationErrors = validateWidget(currentValue, config['widget-data-validation'], resolveIsRequired(valuesRef.current));
2450
2624
  dispatch(setError({ widgetId, errors: validationErrors }));
2451
2625
  // Publish widget:blur event
2452
2626
  if (eventBus) {
@@ -2457,7 +2631,7 @@ const useBaseWidget = (options) => {
2457
2631
  timestamp: Date.now(),
2458
2632
  });
2459
2633
  }
2460
- }, [currentValue, config, widgetId, dispatch, eventBus]);
2634
+ }, [currentValue, config, widgetId, dispatch, eventBus, resolveIsRequired]);
2461
2635
  // Get field value helper
2462
2636
  const getFieldValue = React.useCallback((path) => {
2463
2637
  return getWidgetValue(values, path, '');
@@ -2465,7 +2639,7 @@ const useBaseWidget = (options) => {
2465
2639
  // Conditional visibility and enablement
2466
2640
  const isVisible = React.useMemo(() => {
2467
2641
  // Layout widgets are always visible unless explicitly hidden
2468
- if (isLayoutWidget && !config['widget-data-options']?.condition) {
2642
+ if (isLayoutWidget && !hasVisibilityRules(config['widget-data-options'])) {
2469
2643
  return true;
2470
2644
  }
2471
2645
  return shouldShowWidget(config['widget-data-options'], values);
@@ -2480,6 +2654,7 @@ const useBaseWidget = (options) => {
2480
2654
  }
2481
2655
  return shouldEnableWidget(config['widget-data-options'], values);
2482
2656
  }, [config['widget-readonly'], config['widget-data-options'], values, isLayoutWidget]);
2657
+ const isRequired = React.useMemo(() => resolveIsRequired(values), [resolveIsRequired, values]);
2483
2658
  // Format value for display
2484
2659
  const formattedValue = React.useMemo(() => {
2485
2660
  if (!config['widget-data-format']) {
@@ -2526,10 +2701,9 @@ const useBaseWidget = (options) => {
2526
2701
  if (!dataSource) {
2527
2702
  return;
2528
2703
  }
2529
- // For API data sources, check if widget is readonly
2530
- // According to PRD: "Level 1 geo widgets load on widget mount or when entering edit mode"
2531
- // So we should only load API data sources when widget is NOT readonly
2532
- if (dataSource.type === 'api' && isReadonly) {
2704
+ // Non-geo readonly widgets skip API loads. Geo widgets still load in readonly so
2705
+ // labels can be resolved and translated on initial page view (not only after Edit).
2706
+ if (dataSource.type === 'api' && isReadonly && !geoConfig) {
2533
2707
  return;
2534
2708
  }
2535
2709
  // For widgets with dependencies, check if dependency value exists
@@ -2623,15 +2797,24 @@ const useBaseWidget = (options) => {
2623
2797
  // Use configKey and dependencyValue to ensure effect runs only when relevant state changes
2624
2798
  // eslint-disable-next-line react-hooks/exhaustive-deps
2625
2799
  }, [configKey, dependencyValue, dataSourceRequestHandler, schemaData, widgetId, dispatch]);
2800
+ const geoDisplayLabel = React.useMemo(() => {
2801
+ if (!geoConfig) {
2802
+ return undefined;
2803
+ }
2804
+ const rawLabel = resolveGeoWidgetLevelLabel(values, widgetId, config['widget-data-path'], geoConfig);
2805
+ return rawLabel ? translateConfig(rawLabel) : undefined;
2806
+ }, [values, widgetId, config, geoConfig, translateConfig]);
2626
2807
  return {
2627
2808
  widgetId,
2628
2809
  value: currentValue,
2810
+ geoDisplayLabel,
2629
2811
  formattedValue,
2630
2812
  error: errors,
2631
2813
  touched,
2632
2814
  loading,
2633
2815
  isVisible,
2634
2816
  isEnabled,
2817
+ isRequired,
2635
2818
  onChange: handleChange,
2636
2819
  onBlur: handleBlur,
2637
2820
  setError: (errors) => dispatch(setError({ widgetId, errors })),
@@ -2722,6 +2905,7 @@ const useGeoWidgetCascade = (options) => {
2722
2905
  const valuesRef = React.useRef(values);
2723
2906
  const handlerRef = React.useRef(dataSourceRequestHandler);
2724
2907
  const lastCascadePublishRef = React.useRef(undefined);
2908
+ const lastDirectParentValueRef = React.useRef(undefined);
2725
2909
  // Keep refs updated
2726
2910
  React.useEffect(() => {
2727
2911
  valuesRef.current = values;
@@ -2791,6 +2975,13 @@ const useGeoWidgetCascade = (options) => {
2791
2975
  event.value === null ||
2792
2976
  event.value === '' ||
2793
2977
  event.value === GEO_LEVEL_CLEARED;
2978
+ const isFirstParentEvent = lastDirectParentValueRef.current === undefined;
2979
+ const parentValueChanged = !isFirstParentEvent &&
2980
+ lastDirectParentValueRef.current !== event.value;
2981
+ lastDirectParentValueRef.current = event.value;
2982
+ if (!parentCleared && !parentValueChanged && !isFirstParentEvent) {
2983
+ return;
2984
+ }
2794
2985
  let parentValue = event.value;
2795
2986
  if (!parentCleared && (parentValue === undefined || parentValue === null)) {
2796
2987
  parentValue = currentValues[parentWidgetId];
@@ -2898,17 +3089,7 @@ const useGeoWidgetCascade = (options) => {
2898
3089
  if (dataPath && geoHierarchyBuilder.buildHierarchyJson(groupId)) {
2899
3090
  dispatch(setValues(applySharedGeoHierarchyToValues(valuesRef.current, groupId, dataPath, widgetId)));
2900
3091
  }
2901
- // Notify descendants when this level changes via hierarchy/rehydration (handleChange may not run).
2902
- if (!isLastLevel && eventBus && lastCascadePublishRef.current !== level_value_id) {
2903
- lastCascadePublishRef.current = level_value_id;
2904
- eventBus.publish({
2905
- type: 'widget:change',
2906
- widgetId,
2907
- value: level_value_id,
2908
- timestamp: Date.now(),
2909
- });
2910
- }
2911
- }, [geoConfig, currentValue, widgetId, dataPath, dispatch, dataSourceOptions, eventBus, groupId]);
3092
+ }, [geoConfig, currentValue, widgetId, dataPath, dispatch, dataSourceOptions, groupId]);
2912
3093
  };
2913
3094
 
2914
3095
  class WidgetRegistry {
@@ -3033,98 +3214,6 @@ const WidgetRenderer = ({ config, dataSourceRequestHandler: propDataSourceReques
3033
3214
  return (jsxRuntimeExports.jsx("div", { className: "widget-container", "data-widget-id": widgetContext.widgetId, style: { marginBottom: 0 }, children: widgetRegistry.render(config, widgetContext, defaultComponent) }));
3034
3215
  };
3035
3216
 
3036
- /**
3037
- * Custom hook for widget translations
3038
- * Provides translation function with widget-specific namespace and fallback support
3039
- */
3040
- const useWidgetTranslation = () => {
3041
- const { translate: translateFunction } = useWidgetContext();
3042
- /**
3043
- * Translate a key with flexible namespace support
3044
- * Supports translation keys in various formats and direct strings
3045
- *
3046
- * Translation key formats supported:
3047
- * - "widgets:common.addItem" - Namespaced key (for widget-specific translations)
3048
- * - "Name" - Direct string (will be looked up in flat translation structure)
3049
- * - "sections.personalDetails" - Nested key (for backward compatibility)
3050
- *
3051
- * With flat translation structure, direct strings like "Name" are automatically
3052
- * translated by looking them up in the translation resources.
3053
- *
3054
- * @param keyOrString - Translation key (e.g., "widgets:common.addItem") or direct string (e.g., "Name")
3055
- * @param options - Translation options (interpolation values, default value, etc.)
3056
- * @returns Translated string or original string if translation not found
3057
- */
3058
- const translate = (keyOrString, options) => {
3059
- if (!keyOrString) {
3060
- return options?.defaultValue || '';
3061
- }
3062
- // Use the provided translation function or fallback to the key
3063
- if (translateFunction) {
3064
- return translateFunction(keyOrString, options) || options?.defaultValue || keyOrString;
3065
- }
3066
- // Fallback to key if no translation function available
3067
- return options?.defaultValue || keyOrString;
3068
- };
3069
- /**
3070
- * Translate widget config property
3071
- * Attempts to translate the value, but if translation is not found,
3072
- * returns the original value as-is (graceful fallback)
3073
- *
3074
- * This function will:
3075
- * - Try to translate any string value
3076
- * - If translation exists, use the translated value
3077
- * - If translation doesn't exist (returns same value or throws), use original value
3078
- * - This prevents errors when literal strings like "XXXX-XXXX-XXXX" are used
3079
- */
3080
- const translateConfig = (value, fallback) => {
3081
- if (!value) {
3082
- return fallback || '';
3083
- }
3084
- // Try to translate the value
3085
- if (translateFunction) {
3086
- try {
3087
- // Pass defaultValue to ensure we get the original value if translation fails
3088
- const translated = translateFunction(value, { defaultValue: value });
3089
- // If translation returns empty, null, undefined, or the exact same value,
3090
- // it means no translation was found - return the original value
3091
- if (!translated || translated === value) {
3092
- return value;
3093
- }
3094
- // Translation found, return it
3095
- return translated;
3096
- }
3097
- catch (error) {
3098
- // If translation throws an error (e.g., missing key warning), return original value
3099
- return value;
3100
- }
3101
- }
3102
- // No translation function available, return value as-is
3103
- return value;
3104
- };
3105
- // No need of this getLanguage and changeLanguage functions
3106
- /**
3107
- * Get current language
3108
- */
3109
- // const getLanguage = (): string => {
3110
- // return i18n.language || 'en';
3111
- // };
3112
- /**
3113
- * Change language
3114
- */
3115
- // const changeLanguage = (lng: string): Promise<void> => {
3116
- // return i18n.changeLanguage(lng).then(() => undefined);
3117
- // };
3118
- return {
3119
- t: translate,
3120
- translate,
3121
- translateConfig,
3122
- // getLanguage,
3123
- // changeLanguage,
3124
- // i18n: null,
3125
- };
3126
- };
3127
-
3128
3217
  /**
3129
3218
  * Renders a panel with its nested panels or widgets
3130
3219
  *
@@ -3243,6 +3332,16 @@ const PanelRenderer = ({ panel, dataSourceRequestHandler, schemaData, onValueCha
3243
3332
  return (jsxRuntimeExports.jsx("div", { className: `panel panel-${orientation}`, "data-panel-id": panel['panel-id'], style: orientation === 'horizontal' ? { width: '100%' } : {}, children: content }));
3244
3333
  };
3245
3334
 
3335
+ /**
3336
+ * Field label: long text truncates with ellipsis; required asterisk always stays visible.
3337
+ */
3338
+ const WidgetFieldLabel = ({ label, required = false, className = '', title, }) => {
3339
+ const { translateConfig } = useWidgetTranslation();
3340
+ const translatedLabel = translateConfig(label);
3341
+ const tooltip = title !== undefined ? translateConfig(title) : translatedLabel;
3342
+ return (jsxRuntimeExports.jsxs("label", { className: `flex items-baseline min-w-0 max-w-full ${className}`, style: { fontFamily: 'Roboto, sans-serif' }, title: tooltip, children: [jsxRuntimeExports.jsx("span", { className: "min-w-0 truncate", children: translatedLabel }), required && jsxRuntimeExports.jsx("span", { className: "ml-1 shrink-0 text-red-500", children: "*" })] }));
3343
+ };
3344
+
3246
3345
  /**
3247
3346
  * Utility functions for file preview functionality
3248
3347
  */
@@ -3592,7 +3691,7 @@ const deserializeValue = (value) => {
3592
3691
  };
3593
3692
 
3594
3693
  const FileInputWidget = ({ config }) => {
3595
- const { value, error, touched, isEnabled, onChange, onBlur, config: widgetConfig, } = useBaseWidget({ config });
3694
+ const { value, error, touched, isEnabled, isRequired, onChange, onBlur, config: widgetConfig, } = useBaseWidget({ config });
3596
3695
  const { translate, translateConfig } = useWidgetTranslation();
3597
3696
  const accept = widgetConfig['widget-data-options']?.accept;
3598
3697
  const multiple = widgetConfig['widget-data-options']?.multiple || false;
@@ -3832,7 +3931,7 @@ const FileInputWidget = ({ config }) => {
3832
3931
  setPreviewFile(null);
3833
3932
  } })] }));
3834
3933
  }
3835
- return (jsxRuntimeExports.jsxs("div", { className: "mb-[10px]", children: [jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start", children: [jsxRuntimeExports.jsxs("label", { className: "text-base font-medium text-gray-700 md:min-w-[120px] sm:pr-4 sm:pt-1 mb-1 sm:mb-0", style: { fontFamily: 'Roboto, sans-serif' }, title: translateConfig(widgetConfig['widget-label']), children: [translateConfig(widgetConfig['widget-label']), widgetConfig['widget-required'] && (jsxRuntimeExports.jsx("span", { className: "text-red-500 ml-1", children: "*" }))] }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-center gap-2 sm:space-x-4", children: [jsxRuntimeExports.jsxs("label", { className: `cursor-pointer inline-flex items-center justify-between gap-2 border border-gray-300 shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 ${!isEnabled
3934
+ return (jsxRuntimeExports.jsxs("div", { className: "mb-[10px]", children: [jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start", children: [jsxRuntimeExports.jsx(WidgetFieldLabel, { className: "text-base font-medium text-gray-700 md:min-w-[120px] sm:pr-4 sm:pt-1 mb-1 sm:mb-0", label: translateConfig(widgetConfig['widget-label']), required: isRequired }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-center gap-2 sm:space-x-4", children: [jsxRuntimeExports.jsxs("label", { className: `cursor-pointer inline-flex items-center justify-between gap-2 border border-gray-300 shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 ${!isEnabled
3836
3935
  ? 'opacity-50 cursor-not-allowed'
3837
3936
  : ''}`, style: {
3838
3937
  width: '100%',
@@ -3893,6 +3992,13 @@ const namespaceWidgetConfig = (widgetConfig, namespace) => {
3893
3992
  if (namespaced['widget-data-path']) {
3894
3993
  namespaced['widget-data-path'] = namespaceDataPath(namespaced['widget-data-path'], namespace);
3895
3994
  }
3995
+ // Namespace geo parent references so cascade events match namespaced widget-id
3996
+ if (namespaced['widget-geo-config']?.parentWidgetId) {
3997
+ namespaced['widget-geo-config'] = {
3998
+ ...namespaced['widget-geo-config'],
3999
+ parentWidgetId: `${namespace}__${namespaced['widget-geo-config'].parentWidgetId}`,
4000
+ };
4001
+ }
3896
4002
  // Recursively namespace nested widgets (for layout widgets)
3897
4003
  if (namespaced.widgets && Array.isArray(namespaced.widgets)) {
3898
4004
  namespaced.widgets = namespaced.widgets.map((widget) => namespaceWidgetConfig(widget, namespace));
@@ -4097,6 +4203,9 @@ const sectionValidate = (section, currentSchemaData, dispatch, skipRequired = fa
4097
4203
  const isVisible = shouldShowWidget(widget['widget-data-options'], currentSchemaData);
4098
4204
  if (!isVisible)
4099
4205
  continue;
4206
+ const isEnabled = shouldEnableWidget(widget['widget-data-options'], currentSchemaData);
4207
+ if (!isEnabled)
4208
+ continue;
4100
4209
  const widgetId = widget['widget-id'];
4101
4210
  if (isTableLikeWidget(widget)) {
4102
4211
  const tableValid = validateTableLikeWidget(widget, currentSchemaData, dispatch, skipRequired);
@@ -4106,7 +4215,8 @@ const sectionValidate = (section, currentSchemaData, dispatch, skipRequired = fa
4106
4215
  continue;
4107
4216
  }
4108
4217
  const value = getWidgetValue(currentSchemaData, widget['widget-data-path'], widgetId);
4109
- const errors = validateWidget(value, widget['widget-data-validation'], widget['widget-required'], skipRequired);
4218
+ const isRequired = shouldRequireWidget(widget['widget-data-options'], currentSchemaData, widget['widget-required'] ?? false);
4219
+ const errors = validateWidget(value, widget['widget-data-validation'], isRequired, skipRequired);
4110
4220
  if (errors.length > 0) {
4111
4221
  isValid = false;
4112
4222
  dispatch(setTouched({ widgetId, touched: true }));
@@ -4139,6 +4249,108 @@ const sectionValidate = (section, currentSchemaData, dispatch, skipRequired = fa
4139
4249
  return isValid;
4140
4250
  };
4141
4251
 
4252
+ const cloneValue = (value) => {
4253
+ if (value === undefined) {
4254
+ return undefined;
4255
+ }
4256
+ try {
4257
+ return structuredClone(value);
4258
+ }
4259
+ catch {
4260
+ return JSON.parse(JSON.stringify(value));
4261
+ }
4262
+ };
4263
+ const resolveNamespacedWidgetId = (widgetId, namespace) => namespace ? `${namespace}__${widgetId}` : widgetId;
4264
+ const resolveStoreDataPath = (dataPath, namespace) => {
4265
+ if (!dataPath) {
4266
+ return dataPath;
4267
+ }
4268
+ if (!namespace) {
4269
+ return dataPath;
4270
+ }
4271
+ if (typeof dataPath === 'string') {
4272
+ return `${namespace}.${dataPath}`;
4273
+ }
4274
+ return Object.fromEntries(Object.entries(dataPath).map(([key, path]) => [key, `${namespace}.${path}`]));
4275
+ };
4276
+ /**
4277
+ * Capture Redux widget values for a section at edit entry.
4278
+ * Used to restore exact pre-edit state on Cancel (schemaData may be stale or shared with Redux).
4279
+ */
4280
+ function captureSectionEditSnapshot(values, section, options) {
4281
+ const { namespace, sectionId, supportingDocuments = [] } = options ?? {};
4282
+ const dataPaths = [];
4283
+ const processedPaths = new Set();
4284
+ const widgetIds = {};
4285
+ const addPath = (path) => {
4286
+ if (!path || processedPaths.has(path)) {
4287
+ return;
4288
+ }
4289
+ processedPaths.add(path);
4290
+ dataPaths.push({
4291
+ path,
4292
+ value: cloneValue(getValueByPath(values, path)),
4293
+ });
4294
+ if (path.endsWith('.geo_code_hierarchy_json')) {
4295
+ const prefix = path.slice(0, -'.geo_code_hierarchy_json'.length);
4296
+ addPath(`${prefix}.geo_lowest_level_value_id`);
4297
+ }
4298
+ };
4299
+ collectWidgets(section.panels).forEach((widget) => {
4300
+ const widgetId = resolveNamespacedWidgetId(widget['widget-id'], namespace);
4301
+ const storeDataPath = resolveStoreDataPath(widget['widget-data-path'], namespace);
4302
+ if (Object.prototype.hasOwnProperty.call(values, widgetId)) {
4303
+ widgetIds[widgetId] = { present: true, value: cloneValue(values[widgetId]) };
4304
+ }
4305
+ else {
4306
+ widgetIds[widgetId] = { present: false };
4307
+ }
4308
+ if (typeof storeDataPath === 'string') {
4309
+ addPath(storeDataPath);
4310
+ }
4311
+ else if (storeDataPath && typeof storeDataPath === 'object') {
4312
+ Object.values(storeDataPath).forEach((path) => {
4313
+ if (typeof path === 'string') {
4314
+ addPath(path);
4315
+ }
4316
+ });
4317
+ }
4318
+ });
4319
+ supportingDocuments.forEach((doc, index) => {
4320
+ const widgetId = `supporting-doc-${sectionId ?? 'section'}-${index}`;
4321
+ const storeDataPath = namespace && doc['document-data-path']
4322
+ ? `${namespace}.${doc['document-data-path']}`
4323
+ : doc['document-data-path'];
4324
+ if (Object.prototype.hasOwnProperty.call(values, widgetId)) {
4325
+ widgetIds[widgetId] = { present: true, value: cloneValue(values[widgetId]) };
4326
+ }
4327
+ else {
4328
+ widgetIds[widgetId] = { present: false };
4329
+ }
4330
+ if (typeof storeDataPath === 'string') {
4331
+ addPath(storeDataPath);
4332
+ }
4333
+ });
4334
+ return { dataPaths, widgetIds };
4335
+ }
4336
+ /** Apply a section edit snapshot back onto the full Redux values object. */
4337
+ function applySectionEditSnapshot(currentValues, snapshot) {
4338
+ let result = currentValues;
4339
+ for (const { path, value } of snapshot.dataPaths) {
4340
+ result = setValueByPath(result, path, cloneValue(value));
4341
+ }
4342
+ for (const [widgetId, entry] of Object.entries(snapshot.widgetIds)) {
4343
+ if (entry.present) {
4344
+ result = { ...result, [widgetId]: cloneValue(entry.value) };
4345
+ }
4346
+ else {
4347
+ const { [widgetId]: _removed, ...rest } = result;
4348
+ result = rest;
4349
+ }
4350
+ }
4351
+ return result;
4352
+ }
4353
+
4142
4354
  /** Root class on readonly label/value rows; SectionRenderer scopes overflow/ellipsis rules here. */
4143
4355
  const READONLY_VALUE_ROW_ROOT_CLASSES = [
4144
4356
  'TextDisplayWidget',
@@ -4350,6 +4562,8 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
4350
4562
  }, [forceExitEdit]); // eslint-disable-line react-hooks/exhaustive-deps
4351
4563
  const [isDocumentsExpanded, setIsDocumentsExpanded] = React.useState(true);
4352
4564
  const sectionRef = React.useRef(null);
4565
+ const baselineSnapshotRef = React.useRef(null);
4566
+ const editEntrySnapshotRef = React.useRef(null);
4353
4567
  const [sectionHeight, setSectionHeight] = React.useState(null);
4354
4568
  const [editSectionPosition, setEditSectionPosition] = React.useState(null);
4355
4569
  // Capture section position when entering edit mode and update on scroll
@@ -4414,6 +4628,18 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
4414
4628
  panels: makePanelsEditable(sectionToRender.panels, widgetsEditable),
4415
4629
  };
4416
4630
  }, [sectionToRender, widgetsEditable]);
4631
+ const effectiveHideEditButton = hideEditButton ||
4632
+ section['section-hide-edit-button'] === true ||
4633
+ !collectWidgets(section.panels || []).some((w) => section['section-editable'] === true || w['widget-readonly'] !== true);
4634
+ const captureEditEntrySnapshot = React.useCallback(() => {
4635
+ const currentValues = store.getState().widget.values;
4636
+ const supportingDocuments = section['section-supporting-documents'] || [];
4637
+ editEntrySnapshotRef.current = captureSectionEditSnapshot(currentValues, section, {
4638
+ namespace,
4639
+ sectionId,
4640
+ supportingDocuments: hasSupportingDocuments ? supportingDocuments : [],
4641
+ });
4642
+ }, [store, section, namespace, sectionId, hasSupportingDocuments]);
4417
4643
  // Handle edit button click
4418
4644
  const handleEdit = () => {
4419
4645
  // Capture height BEFORE entering edit mode to preserve space
@@ -4421,6 +4647,7 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
4421
4647
  const height = sectionRef.current.offsetHeight;
4422
4648
  setSectionHeight(height);
4423
4649
  }
4650
+ captureEditEntrySnapshot();
4424
4651
  setIsEditMode(true);
4425
4652
  onEditModeChange?.(originalSectionId, true);
4426
4653
  };
@@ -4595,8 +4822,6 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
4595
4822
  }
4596
4823
  return { records, files };
4597
4824
  }, [originalSection, hasSupportingDocuments]);
4598
- // Capture baseline when entering edit mode (used for isDirty comparison)
4599
- const baselineSnapshotRef = React.useRef(null);
4600
4825
  // IntakeForm only: increment when baseline is updated after save - forces badge to update (refs don't trigger re-renders)
4601
4826
  const [intakeFormBaselineTrigger, setIntakeFormBaselineTrigger] = React.useState(0);
4602
4827
  // IntakeForm: treat as edit mode for dirty tracking when isDraft. RegistryView: use isEditMode.
@@ -4614,6 +4839,9 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
4614
4839
  // Set baseline when entering edit mode; clear when leaving (baseline captured only on entry)
4615
4840
  React.useEffect(() => {
4616
4841
  if (effectiveEditModeForDirty) {
4842
+ if (!editEntrySnapshotRef.current) {
4843
+ captureEditEntrySnapshot();
4844
+ }
4617
4845
  const oldSchemaData = schemaData || contextSchemaData || {};
4618
4846
  if (namespace) {
4619
4847
  const namespacedSchema = getValueByPath(oldSchemaData, namespace);
@@ -4622,11 +4850,12 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
4622
4850
  : buildSectionSnapshot(oldSchemaData);
4623
4851
  }
4624
4852
  else {
4625
- baselineSnapshotRef.current = buildSectionSnapshot(oldSchemaData);
4853
+ baselineSnapshotRef.current = buildSectionSnapshot(store.getState().widget.values, namespace);
4626
4854
  }
4627
4855
  }
4628
4856
  else {
4629
4857
  baselineSnapshotRef.current = null;
4858
+ editEntrySnapshotRef.current = null;
4630
4859
  onSectionDirtyChange?.(sectionId, false);
4631
4860
  }
4632
4861
  // eslint-disable-next-line react-hooks/exhaustive-deps -- baseline must be captured only when effectiveEditModeForDirty toggles
@@ -4652,56 +4881,103 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
4652
4881
  // and handleCancel.
4653
4882
  const revertToOriginalValues = React.useCallback(() => {
4654
4883
  const sectionWidgets = collectWidgets(originalSection.panels);
4655
- const oldSchemaData = schemaData || contextSchemaData;
4656
4884
  const currentStoreValues = store.getState().widget.values;
4885
+ const snapshot = editEntrySnapshotRef.current;
4657
4886
  let newStoreValues = currentStoreValues;
4658
- sectionWidgets.forEach(widget => {
4659
- const originalWidgetId = widget['widget-id'];
4660
- const namespacedWidgetId = namespace ? `${namespace}__${originalWidgetId}` : originalWidgetId;
4661
- const widgetId = namespacedWidgetId;
4662
- const originalDataPath = widget['widget-data-path'];
4663
- const storeDataPath = namespace && originalDataPath
4664
- ? (typeof originalDataPath === 'string'
4665
- ? `${namespace}.${originalDataPath}`
4666
- : Object.fromEntries(Object.entries(originalDataPath).map(([key, path]) => [key, `${namespace}.${path}`])))
4667
- : originalDataPath;
4668
- if (widgetId && originalDataPath) {
4669
- let oldValue;
4670
- if (typeof originalDataPath === 'object') {
4671
- oldValue = {};
4672
- Object.entries(originalDataPath).forEach(([key, path]) => {
4673
- if (typeof path === 'string') {
4674
- oldValue[key] = getValueByPath(oldSchemaData, path);
4887
+ if (snapshot) {
4888
+ newStoreValues = applySectionEditSnapshot(currentStoreValues, snapshot);
4889
+ }
4890
+ else {
4891
+ const oldSchemaData = schemaData || contextSchemaData;
4892
+ const processedGeoGroups = new Set();
4893
+ sectionWidgets.forEach((widget) => {
4894
+ const originalWidgetId = widget['widget-id'];
4895
+ const namespacedWidgetId = namespace ? `${namespace}__${originalWidgetId}` : originalWidgetId;
4896
+ const widgetId = namespacedWidgetId;
4897
+ const originalDataPath = widget['widget-data-path'];
4898
+ const storeDataPath = namespace && originalDataPath
4899
+ ? (typeof originalDataPath === 'string'
4900
+ ? `${namespace}.${originalDataPath}`
4901
+ : Object.fromEntries(Object.entries(originalDataPath).map(([key, path]) => [key, `${namespace}.${path}`])))
4902
+ : originalDataPath;
4903
+ const geoConfig = widget['widget-geo-config'];
4904
+ if (widgetId && originalDataPath) {
4905
+ let oldValue;
4906
+ if (typeof originalDataPath === 'object') {
4907
+ oldValue = {};
4908
+ Object.entries(originalDataPath).forEach(([key, path]) => {
4909
+ if (typeof path === 'string') {
4910
+ oldValue[key] = getValueByPath(oldSchemaData, path);
4911
+ }
4912
+ });
4913
+ }
4914
+ else if (typeof originalDataPath === 'string') {
4915
+ oldValue = getValueByPath(oldSchemaData, originalDataPath);
4916
+ }
4917
+ if (oldValue !== undefined) {
4918
+ newStoreValues = setWidgetValue(newStoreValues, storeDataPath, widgetId, oldValue);
4919
+ if (geoConfig && typeof storeDataPath === 'string') {
4920
+ const groupId = getGeoGroupId(storeDataPath);
4921
+ const levelValue = resolveGeoWidgetLevelValue(newStoreValues, widgetId, storeDataPath, geoConfig);
4922
+ if (levelValue !== undefined && levelValue !== null && levelValue !== '') {
4923
+ newStoreValues = { ...newStoreValues, [widgetId]: levelValue };
4924
+ }
4925
+ else {
4926
+ const { [widgetId]: _removed, ...rest } = newStoreValues;
4927
+ newStoreValues = rest;
4928
+ }
4929
+ if (!processedGeoGroups.has(groupId)) {
4930
+ resetAndSeedGeoHierarchyFromValues(newStoreValues, storeDataPath, widgetId, groupId);
4931
+ processedGeoGroups.add(groupId);
4932
+ }
4933
+ if (geoConfig.parentWidgetId) {
4934
+ dispatch(setDataSource({ widgetId, data: [] }));
4935
+ }
4675
4936
  }
4676
- });
4677
- }
4678
- else if (typeof originalDataPath === 'string') {
4679
- oldValue = getValueByPath(oldSchemaData, originalDataPath);
4937
+ else {
4938
+ newStoreValues = { ...newStoreValues, [widgetId]: oldValue };
4939
+ }
4940
+ }
4680
4941
  }
4681
- if (oldValue !== undefined) {
4942
+ });
4943
+ if (hasSupportingDocuments) {
4944
+ const originalSupportingDocuments = originalSection['section-supporting-documents'] || [];
4945
+ originalSupportingDocuments.forEach((doc, index) => {
4946
+ const widgetId = `supporting-doc-${sectionId}-${index}`;
4947
+ const originalDataPath = doc['document-data-path'];
4948
+ const storeDataPath = namespace && originalDataPath
4949
+ ? `${namespace}.${originalDataPath}`
4950
+ : originalDataPath;
4951
+ const oldValue = getValueByPath(oldSchemaData, originalDataPath);
4682
4952
  newStoreValues = setWidgetValue(newStoreValues, storeDataPath, widgetId, oldValue);
4683
- // Also revert the widgetId-based entry — useBaseWidget.handleChange
4684
- // sets values[widgetId] during editing, and useBaseWidget.currentValue
4685
- // reads values[widgetId] first before falling through to the dataPath.
4686
- newStoreValues = { ...newStoreValues, [widgetId]: oldValue };
4687
- }
4953
+ });
4688
4954
  }
4689
- });
4690
- if (hasSupportingDocuments) {
4691
- const originalSupportingDocuments = originalSection['section-supporting-documents'] || [];
4692
- originalSupportingDocuments.forEach((doc, index) => {
4693
- const widgetId = `supporting-doc-${sectionId}-${index}`;
4694
- const originalDataPath = doc['document-data-path'];
4695
- const storeDataPath = namespace && originalDataPath
4696
- ? `${namespace}.${originalDataPath}`
4697
- : originalDataPath;
4698
- const oldValue = getValueByPath(oldSchemaData, originalDataPath);
4699
- newStoreValues = setWidgetValue(newStoreValues, storeDataPath, widgetId, oldValue);
4700
- });
4701
- }
4702
- if (newStoreValues !== currentStoreValues) {
4703
- dispatch(setValues(newStoreValues));
4704
4955
  }
4956
+ const processedGeoGroups = new Set();
4957
+ sectionWidgets.forEach((widget) => {
4958
+ const geoConfig = widget['widget-geo-config'];
4959
+ if (!geoConfig) {
4960
+ return;
4961
+ }
4962
+ const originalWidgetId = widget['widget-id'];
4963
+ const widgetId = namespace ? `${namespace}__${originalWidgetId}` : originalWidgetId;
4964
+ const originalDataPath = widget['widget-data-path'];
4965
+ const storeDataPath = namespace && typeof originalDataPath === 'string'
4966
+ ? `${namespace}.${originalDataPath}`
4967
+ : originalDataPath;
4968
+ if (typeof storeDataPath !== 'string') {
4969
+ return;
4970
+ }
4971
+ const groupId = getGeoGroupId(storeDataPath);
4972
+ if (!processedGeoGroups.has(groupId)) {
4973
+ resetAndSeedGeoHierarchyFromValues(newStoreValues, storeDataPath, widgetId, groupId);
4974
+ processedGeoGroups.add(groupId);
4975
+ }
4976
+ if (geoConfig.parentWidgetId) {
4977
+ dispatch(setDataSource({ widgetId, data: [] }));
4978
+ }
4979
+ });
4980
+ dispatch(setValues(newStoreValues));
4705
4981
  }, [originalSection, schemaData, contextSchemaData, store, namespace, hasSupportingDocuments, sectionId, dispatch]);
4706
4982
  // Handle save button click
4707
4983
  const handleSave = async () => {
@@ -5197,7 +5473,7 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
5197
5473
  color: changeRequestType === 'new' ? 'var(--owt-color-bg, #FFFFFF)' : 'var(--owt-color-error, #B91C1C)',
5198
5474
  whiteSpace: 'nowrap',
5199
5475
  boxShadow: changeRequestType === 'new' ? '0 2px 4px rgba(40, 167, 69, 0.3)' : 'none',
5200
- }, children: changeRequestType === 'new' ? 'New' : 'Old' }))] })), jsxRuntimeExports.jsxs("div", { id: gridId, className: "section-panels", style: mode === 'RegistryView' && hideEditButton ? { paddingBottom: '30px' } : {}, children: [editableSection.panels.map((panel, index) => (jsxRuntimeExports.jsx("div", { className: "panel-wrapper", children: jsxRuntimeExports.jsx(PanelRenderer, { panel: panel, dataSourceRequestHandler: dataSourceRequestHandler, schemaData: namespacedSchemaData, onValueChange: onValueChange }) }, panel['panel-id'] || `section-panel-${index}`))), mode === 'CRView' && crViewData && (jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [jsxRuntimeExports.jsx("hr", { className: "w-full", style: { height: '1px', marginTop: '20px', marginBottom: '0px', border: 'none', backgroundColor: 'var(--owt-color-border, #C4C4C4)' } }), jsxRuntimeExports.jsxs("div", { className: "cr-view-container", style: {
5476
+ }, children: changeRequestType === 'new' ? 'New' : 'Old' }))] })), jsxRuntimeExports.jsxs("div", { id: gridId, className: "section-panels", style: mode === 'RegistryView' && effectiveHideEditButton ? { paddingBottom: '30px' } : {}, children: [editableSection.panels.map((panel, index) => (jsxRuntimeExports.jsx("div", { className: "panel-wrapper", children: jsxRuntimeExports.jsx(PanelRenderer, { panel: panel, dataSourceRequestHandler: dataSourceRequestHandler, schemaData: namespacedSchemaData, onValueChange: onValueChange }) }, panel['panel-id'] || `section-panel-${index}`))), mode === 'CRView' && crViewData && (jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [jsxRuntimeExports.jsx("hr", { className: "w-full", style: { height: '1px', marginTop: '20px', marginBottom: '0px', border: 'none', backgroundColor: 'var(--owt-color-border, #C4C4C4)' } }), jsxRuntimeExports.jsxs("div", { className: "cr-view-container", style: {
5201
5477
  marginTop: '20px',
5202
5478
  paddingBottom: '30px',
5203
5479
  display: 'flex',
@@ -5245,7 +5521,7 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
5245
5521
  fontSize: '14px',
5246
5522
  color: 'var(--owt-color-text, #011627)',
5247
5523
  fontWeight: 'normal',
5248
- }, children: crViewData.approvedDate }))] })] })] })), mode === 'RegistryView' && !hideEditButton && (jsxRuntimeExports.jsx("hr", { className: "w-full", style: { height: '1px', marginTop: !isEditMode ? '10px' : 0, marginBottom: '14px', border: 'none', backgroundColor: 'var(--owt-color-border, #C4C4C4)' } })), mode === 'RegistryView' && !isEditMode && !hideEditButton && (jsxRuntimeExports.jsx("div", { className: "flex justify-center items-center", style: { marginBottom: '20px' }, children: jsxRuntimeExports.jsxs("button", { onClick: handleEdit, className: "font-normal inline-flex items-center gap-2 bg-transparent border-0 p-0 cursor-pointer hover:opacity-80", style: {
5524
+ }, children: crViewData.approvedDate }))] })] })] })), mode === 'RegistryView' && !effectiveHideEditButton && (jsxRuntimeExports.jsx("hr", { className: "w-full", style: { height: '1px', marginTop: !isEditMode ? '10px' : 0, marginBottom: '14px', border: 'none', backgroundColor: 'var(--owt-color-border, #C4C4C4)' } })), mode === 'RegistryView' && !isEditMode && !effectiveHideEditButton && (jsxRuntimeExports.jsx("div", { className: "flex justify-center items-center", style: { marginBottom: '20px' }, children: jsxRuntimeExports.jsxs("button", { onClick: handleEdit, className: "font-normal inline-flex items-center gap-2 bg-transparent border-0 p-0 cursor-pointer hover:opacity-80", style: {
5249
5525
  fontFamily: 'Roboto, sans-serif',
5250
5526
  fontSize: '16px',
5251
5527
  color: 'var(--owt-color-text-muted, #727474)',
@@ -6051,7 +6327,7 @@ const JSONEditorPanel = ({ section, onChange, onReset, context = 'section', }) =
6051
6327
  'widget-data-format.dateConstraint': ['any', 'past-only', 'future-only'],
6052
6328
  'widget-data-format.dateTimeConstraint': ['any', 'past-only', 'future-only'],
6053
6329
  // Widget options
6054
- 'widget-data-options.action': ['show', 'hide', 'enable', 'disable'],
6330
+ 'widget-data-options.action': ['show', 'hide', 'enable', 'disable', 'require'],
6055
6331
  'widget-data-options.condition.operator': CONDITION_OPERATORS,
6056
6332
  };
6057
6333
  }, []);
@@ -7436,7 +7712,7 @@ const removeMask = (value, mask) => {
7436
7712
  };
7437
7713
 
7438
7714
  const TextInputWidget = ({ config }) => {
7439
- const { value, formattedValue, error, touched, isEnabled, onChange, onBlur, config: widgetConfig, } = useBaseWidget({ config });
7715
+ const { value, formattedValue, error, touched, isEnabled, isRequired, onChange, onBlur, config: widgetConfig, } = useBaseWidget({ config });
7440
7716
  const { translate, translateConfig } = useWidgetTranslation();
7441
7717
  // Track raw value separately for masking (to preserve unmasked value internally)
7442
7718
  const formatConfig = widgetConfig['widget-data-format'];
@@ -7573,7 +7849,7 @@ const TextInputWidget = ({ config }) => {
7573
7849
  const label = translateConfig(widgetConfig['widget-label']);
7574
7850
  return (jsxRuntimeExports.jsxs("div", { className: "mb-[10px] TextDisplayWidget flex flex-col sm:flex-row sm:items-start", children: [label && (jsxRuntimeExports.jsxs("div", { className: "text-base text-gray-600 font-medium md:min-w-[120px] sm:pr-4 mb-1 sm:mb-0", style: { fontFamily: 'Roboto, sans-serif' }, title: label, children: [label, ":"] })), jsxRuntimeExports.jsx("div", { className: "flex-1", children: jsxRuntimeExports.jsx("div", { className: "text-base text-gray-900 font-medium", title: String(displayValue ?? ''), children: displayValue }) })] }));
7575
7851
  }
7576
- return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start", children: [jsxRuntimeExports.jsxs("label", { className: "text-base font-medium text-gray-700 md:min-w-[120px] sm:pr-4 sm:pt-1 mb-1 sm:mb-0", style: { fontFamily: 'Roboto, sans-serif' }, title: translateConfig(widgetConfig['widget-label']), children: [translateConfig(widgetConfig['widget-label']), widgetConfig['widget-required'] && (jsxRuntimeExports.jsx("span", { className: "text-red-500 ml-1", children: "*" }))] }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 mb-1", children: [jsxRuntimeExports.jsx("input", { type: getInputType(), value: displayValue, onChange: handleChange, onBlur: onBlur, disabled: !isEnabled || widgetConfig['widget-readonly'], placeholder: placeholder, maxLength: formatConfig?.mask ? undefined : maxLength, inputMode: formatConfig?.currency
7852
+ return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start", children: [jsxRuntimeExports.jsx(WidgetFieldLabel, { className: "text-base font-medium text-gray-700 md:min-w-[120px] sm:pr-4 sm:pt-1 mb-1 sm:mb-0", label: translateConfig(widgetConfig['widget-label']), required: isRequired }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 mb-1", children: [jsxRuntimeExports.jsx("input", { type: getInputType(), value: displayValue, onChange: handleChange, onBlur: onBlur, disabled: !isEnabled || widgetConfig['widget-readonly'], placeholder: placeholder, maxLength: formatConfig?.mask ? undefined : maxLength, inputMode: formatConfig?.currency
7577
7853
  ? 'decimal'
7578
7854
  : formatConfig?.characterType === 'numeric' || formatConfig?.characterType === 'numeric-decimal'
7579
7855
  ? 'numeric'
@@ -7596,7 +7872,7 @@ const NumberInputWidget = ({ config }) => {
7596
7872
  }
7597
7873
  return { ...config, 'widget-data-default': normalizedDefault };
7598
7874
  }, [config]);
7599
- const { value, formattedValue, error, touched, isEnabled, onChange, onBlur, config: widgetConfig, } = useBaseWidget({ config: resolvedConfig });
7875
+ const { value, formattedValue, error, touched, isEnabled, isRequired, onChange, onBlur, config: widgetConfig, } = useBaseWidget({ config: resolvedConfig });
7600
7876
  const { translate, translateConfig } = useWidgetTranslation();
7601
7877
  const formatConfig = widgetConfig['widget-data-format'];
7602
7878
  const validationConfig = widgetConfig['widget-data-validation'];
@@ -7727,7 +8003,7 @@ const NumberInputWidget = ({ config }) => {
7727
8003
  const display = numValue !== null ? formatNumber(numValue, formatConfig) : '';
7728
8004
  return (jsxRuntimeExports.jsxs("div", { className: "mb-[10px] NumberDisplayWidget flex flex-col sm:flex-row sm:items-start", children: [label && (jsxRuntimeExports.jsxs("div", { className: "text-base text-gray-600 font-medium md:min-w-[120px] sm:pr-4 mb-1 sm:mb-0", style: { fontFamily: 'Roboto, sans-serif' }, title: label, children: [label, ":"] })), jsxRuntimeExports.jsx("div", { className: "flex-1", children: jsxRuntimeExports.jsx("div", { className: `text-base text-gray-900 font-medium ${textAlignClass}`, title: String(display ?? ''), children: display }) })] }));
7729
8005
  }
7730
- return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start", children: [jsxRuntimeExports.jsxs("label", { className: "text-base font-medium text-gray-700 md:min-w-[120px] sm:pr-4 sm:pt-1 mb-1 sm:mb-0", style: { fontFamily: 'Roboto, sans-serif' }, title: translateConfig(widgetConfig['widget-label']), children: [translateConfig(widgetConfig['widget-label']), widgetConfig['widget-required'] && (jsxRuntimeExports.jsx("span", { className: "text-red-500 ml-1", children: "*" }))] }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between mb-1", children: [jsxRuntimeExports.jsx("input", { type: "text", inputMode: "decimal", value: displayValue, onChange: handleChange, onBlur: handleBlur, onFocus: handleFocus, onKeyDown: handleKeyDown, disabled: !isEnabled || widgetConfig['widget-readonly'], placeholder: placeholder, maxLength: maxLength, className: `w-full sm:w-[180px] max-w-full h-[30px] px-3 border shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 ${textAlignClass} ${(touched && error.length > 0) || (widgetConfig['widget-required'] && (value === null || value === undefined || value === ''))
8006
+ return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start", children: [jsxRuntimeExports.jsx(WidgetFieldLabel, { className: "text-base font-medium text-gray-700 md:min-w-[120px] sm:pr-4 sm:pt-1 mb-1 sm:mb-0", label: translateConfig(widgetConfig['widget-label']), required: isRequired }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between mb-1", children: [jsxRuntimeExports.jsx("input", { type: "text", inputMode: "decimal", value: displayValue, onChange: handleChange, onBlur: handleBlur, onFocus: handleFocus, onKeyDown: handleKeyDown, disabled: !isEnabled || widgetConfig['widget-readonly'], placeholder: placeholder, maxLength: maxLength, className: `w-full sm:w-[180px] max-w-full h-[30px] px-3 border shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 ${textAlignClass} ${(touched && error.length > 0) || (widgetConfig['widget-required'] && (value === null || value === undefined || value === ''))
7731
8007
  ? 'border-red-500 focus:ring-red-500 focus:border-red-500'
7732
8008
  : 'border-gray-300'} ${!isEnabled || widgetConfig['widget-readonly'] ? 'bg-gray-100 cursor-not-allowed' : 'bg-white'}`, style: { borderRadius: '10px' }, title: translateConfig(widgetConfig['widget-data-tooltip']) }), maxLength && (jsxRuntimeExports.jsxs("span", { className: `text-xs ml-2 flex-shrink-0 ${currentLength > maxLength
7733
8009
  ? 'text-red-500'
@@ -7735,7 +8011,7 @@ const NumberInputWidget = ({ config }) => {
7735
8011
  };
7736
8012
 
7737
8013
  const BooleanWidget = ({ config }) => {
7738
- const { value, error, touched, isEnabled, onChange, onBlur, config: widgetConfig, } = useBaseWidget({ config });
8014
+ const { value, error, touched, isEnabled, isRequired, onChange, onBlur, config: widgetConfig, } = useBaseWidget({ config });
7739
8015
  const { translate, translateConfig } = useWidgetTranslation();
7740
8016
  const formatConfig = widgetConfig['widget-data-format'];
7741
8017
  const representation = formatConfig?.booleanRepresentation || 'true-false';
@@ -7804,7 +8080,7 @@ const BooleanWidget = ({ config }) => {
7804
8080
  }
7805
8081
  // Render based on control type
7806
8082
  if (controlType === 'checkbox') {
7807
- return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-baseline", children: [jsxRuntimeExports.jsxs("label", { className: "text-base font-medium leading-normal text-gray-700 md:min-w-[120px] sm:pr-4 mb-1 sm:mb-0 sm:pt-0.5", style: { fontFamily: 'Roboto, sans-serif' }, title: translateConfig(widgetConfig['widget-label']), children: [translateConfig(widgetConfig['widget-label']), widgetConfig['widget-required'] && (jsxRuntimeExports.jsx("span", { className: "text-red-500 ml-1", children: "*" }))] }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntimeExports.jsxs("label", { className: "inline-flex items-baseline cursor-pointer gap-2", children: [jsxRuntimeExports.jsx("input", { type: "checkbox", checked: currentValue === true, onChange: handleCheckboxChange, onBlur: onBlur, disabled: !isEnabled || widgetConfig['widget-readonly'], className: "relative top-[0.2em] h-4 w-4 shrink-0 text-blue-600 focus:ring-blue-500 border-gray-300 rounded" }), (currentValue === true || currentValue === false) && (jsxRuntimeExports.jsx("span", { className: "text-base text-gray-700 leading-normal", children: currentValue === true ? trueLabel : falseLabel }))] }), touched && error.length > 0 && (jsxRuntimeExports.jsx("p", { className: "text-red-500 text-sm mt-1", children: error[0] }))] })] }) }));
8083
+ return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-baseline", children: [jsxRuntimeExports.jsx(WidgetFieldLabel, { className: "text-base font-medium leading-normal text-gray-700 md:min-w-[120px] sm:pr-4 mb-1 sm:mb-0 sm:pt-0.5", label: translateConfig(widgetConfig['widget-label']), required: isRequired }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntimeExports.jsxs("label", { className: "inline-flex items-baseline cursor-pointer gap-2", children: [jsxRuntimeExports.jsx("input", { type: "checkbox", checked: currentValue === true, onChange: handleCheckboxChange, onBlur: onBlur, disabled: !isEnabled || widgetConfig['widget-readonly'], className: "relative top-[0.2em] h-4 w-4 shrink-0 text-blue-600 focus:ring-blue-500 border-gray-300 rounded" }), (currentValue === true || currentValue === false) && (jsxRuntimeExports.jsx("span", { className: "text-base text-gray-700 leading-normal", children: currentValue === true ? trueLabel : falseLabel }))] }), touched && error.length > 0 && (jsxRuntimeExports.jsx("p", { className: "text-red-500 text-sm mt-1", children: error[0] }))] })] }) }));
7808
8084
  }
7809
8085
  if (controlType === 'radio') {
7810
8086
  const containerClass = orientation === 'horizontal'
@@ -7812,10 +8088,10 @@ const BooleanWidget = ({ config }) => {
7812
8088
  : 'flex flex-col items-start gap-2';
7813
8089
  const radioDisabled = !isEnabled || widgetConfig['widget-readonly'];
7814
8090
  const optionDisabledClass = radioDisabled ? 'opacity-50 cursor-not-allowed' : '';
7815
- return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-baseline", children: [jsxRuntimeExports.jsxs("label", { className: "text-base font-medium leading-normal text-gray-700 md:min-w-[120px] sm:pr-4 mb-1 sm:mb-0 sm:pt-0.5", style: { fontFamily: 'Roboto, sans-serif' }, title: translateConfig(widgetConfig['widget-label']), children: [translateConfig(widgetConfig['widget-label']), widgetConfig['widget-required'] && (jsxRuntimeExports.jsx("span", { className: "text-red-500 ml-1", children: "*" }))] }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntimeExports.jsxs("div", { className: containerClass, onBlur: onBlur, children: [allowUnset && (jsxRuntimeExports.jsxs("label", { className: `inline-flex items-baseline gap-2 cursor-pointer ${optionDisabledClass}`, children: [jsxRuntimeExports.jsx("input", { type: "radio", name: radioGroupName, checked: currentValue === null, onChange: () => handleRadioChange(null), disabled: radioDisabled, className: "relative top-[0.2em] h-4 w-4 shrink-0 text-blue-600 focus:ring-blue-500 border-gray-300" }), jsxRuntimeExports.jsx("span", { className: "text-base text-gray-700 leading-normal", children: unsetLabel })] })), jsxRuntimeExports.jsxs("label", { className: `inline-flex items-baseline gap-2 cursor-pointer ${optionDisabledClass}`, children: [jsxRuntimeExports.jsx("input", { type: "radio", name: radioGroupName, checked: currentValue === true, onChange: () => handleRadioChange(true), disabled: radioDisabled, className: "relative top-[0.2em] h-4 w-4 shrink-0 text-blue-600 focus:ring-blue-500 border-gray-300" }), jsxRuntimeExports.jsx("span", { className: "text-base text-gray-700 leading-normal", children: trueLabel })] }), jsxRuntimeExports.jsxs("label", { className: `inline-flex items-baseline gap-2 cursor-pointer ${optionDisabledClass}`, children: [jsxRuntimeExports.jsx("input", { type: "radio", name: radioGroupName, checked: currentValue === false, onChange: () => handleRadioChange(false), disabled: radioDisabled, className: "relative top-[0.2em] h-4 w-4 shrink-0 text-blue-600 focus:ring-blue-500 border-gray-300" }), jsxRuntimeExports.jsx("span", { className: "text-base text-gray-700 leading-normal", children: falseLabel })] })] }), touched && error.length > 0 && (jsxRuntimeExports.jsx("p", { className: "text-red-500 text-sm mt-1", children: error[0] }))] })] }) }));
8091
+ return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-baseline", children: [jsxRuntimeExports.jsx(WidgetFieldLabel, { className: "text-base font-medium leading-normal text-gray-700 md:min-w-[120px] sm:pr-4 mb-1 sm:mb-0 sm:pt-0.5", label: translateConfig(widgetConfig['widget-label']), required: isRequired }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntimeExports.jsxs("div", { className: containerClass, onBlur: onBlur, children: [allowUnset && (jsxRuntimeExports.jsxs("label", { className: `inline-flex items-baseline gap-2 cursor-pointer ${optionDisabledClass}`, children: [jsxRuntimeExports.jsx("input", { type: "radio", name: radioGroupName, checked: currentValue === null, onChange: () => handleRadioChange(null), disabled: radioDisabled, className: "relative top-[0.2em] h-4 w-4 shrink-0 text-blue-600 focus:ring-blue-500 border-gray-300" }), jsxRuntimeExports.jsx("span", { className: "text-base text-gray-700 leading-normal", children: unsetLabel })] })), jsxRuntimeExports.jsxs("label", { className: `inline-flex items-baseline gap-2 cursor-pointer ${optionDisabledClass}`, children: [jsxRuntimeExports.jsx("input", { type: "radio", name: radioGroupName, checked: currentValue === true, onChange: () => handleRadioChange(true), disabled: radioDisabled, className: "relative top-[0.2em] h-4 w-4 shrink-0 text-blue-600 focus:ring-blue-500 border-gray-300" }), jsxRuntimeExports.jsx("span", { className: "text-base text-gray-700 leading-normal", children: trueLabel })] }), jsxRuntimeExports.jsxs("label", { className: `inline-flex items-baseline gap-2 cursor-pointer ${optionDisabledClass}`, children: [jsxRuntimeExports.jsx("input", { type: "radio", name: radioGroupName, checked: currentValue === false, onChange: () => handleRadioChange(false), disabled: radioDisabled, className: "relative top-[0.2em] h-4 w-4 shrink-0 text-blue-600 focus:ring-blue-500 border-gray-300" }), jsxRuntimeExports.jsx("span", { className: "text-base text-gray-700 leading-normal", children: falseLabel })] })] }), touched && error.length > 0 && (jsxRuntimeExports.jsx("p", { className: "text-red-500 text-sm mt-1", children: error[0] }))] })] }) }));
7816
8092
  }
7817
8093
  // Toggle/switch control type
7818
- return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-baseline", children: [jsxRuntimeExports.jsxs("label", { className: "text-base font-medium leading-normal text-gray-700 sm:min-w-[150px] sm:pr-4 mb-1 sm:mb-0 sm:pt-0.5", style: { fontFamily: 'Roboto, sans-serif' }, title: translateConfig(widgetConfig['widget-label']), children: [translateConfig(widgetConfig['widget-label']), widgetConfig['widget-required'] && (jsxRuntimeExports.jsx("span", { className: "text-red-500 ml-1", children: "*" }))] }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntimeExports.jsxs("div", { className: "flex flex-wrap items-center gap-3", onBlur: onBlur, children: [allowUnset && (jsxRuntimeExports.jsx("button", { type: "button", onClick: () => handleChange(null), disabled: !isEnabled || widgetConfig['widget-readonly'], className: `px-3 py-1 text-sm border ${currentValue === null
8094
+ return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-baseline", children: [jsxRuntimeExports.jsx(WidgetFieldLabel, { className: "text-base font-medium leading-normal text-gray-700 sm:min-w-[150px] sm:pr-4 mb-1 sm:mb-0 sm:pt-0.5", label: translateConfig(widgetConfig['widget-label']), required: isRequired }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntimeExports.jsxs("div", { className: "flex flex-wrap items-center gap-3", onBlur: onBlur, children: [allowUnset && (jsxRuntimeExports.jsx("button", { type: "button", onClick: () => handleChange(null), disabled: !isEnabled || widgetConfig['widget-readonly'], className: `px-3 py-1 text-sm border ${currentValue === null
7819
8095
  ? 'bg-blue-600 text-white border-blue-600'
7820
8096
  : 'bg-white text-gray-700 border-gray-300'} ${!isEnabled || widgetConfig['widget-readonly'] ? 'opacity-50 cursor-not-allowed' : 'hover:bg-gray-50'}`, style: { borderRadius: '15px' }, children: unsetLabel })), jsxRuntimeExports.jsx("button", { type: "button", onClick: () => handleChange(true), disabled: !isEnabled || widgetConfig['widget-readonly'], className: `px-3 py-1 text-sm border ${currentValue === true
7821
8097
  ? 'bg-blue-600 text-white border-blue-600'
@@ -7825,7 +8101,7 @@ const BooleanWidget = ({ config }) => {
7825
8101
  };
7826
8102
 
7827
8103
  const DateInputWidget = ({ config }) => {
7828
- const { value, error, touched, isEnabled, onChange, onBlur, setError, config: widgetConfig, } = useBaseWidget({ config });
8104
+ const { value, error, touched, isEnabled, isRequired, onChange, onBlur, setError, config: widgetConfig, } = useBaseWidget({ config });
7829
8105
  const formValues = reactRedux.useSelector((state) => state.widget.values);
7830
8106
  const { translateConfig } = useWidgetTranslation();
7831
8107
  const formatConfig = widgetConfig['widget-data-format'];
@@ -8026,7 +8302,7 @@ const DateInputWidget = ({ config }) => {
8026
8302
  }
8027
8303
  return (jsxRuntimeExports.jsxs("div", { className: "mb-[10px] DateDisplayWidget flex flex-col sm:flex-row sm:items-start", children: [label && (jsxRuntimeExports.jsxs("div", { className: "text-base text-gray-600 font-medium md:min-w-[120px] sm:pr-4 mb-1 sm:mb-0", style: { fontFamily: 'Roboto, sans-serif' }, title: label, children: [label, ":"] })), jsxRuntimeExports.jsx("div", { className: "flex-1", children: jsxRuntimeExports.jsx("div", { className: "text-base text-gray-900 font-medium", title: String(displayValue ?? ''), children: displayValue }) })] }));
8028
8304
  }
8029
- return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start", children: [jsxRuntimeExports.jsxs("label", { className: "text-base font-medium text-gray-700 md:min-w-[120px] sm:pr-4 sm:pt-1 mb-1 sm:mb-0", style: { fontFamily: 'Roboto, sans-serif' }, title: translateConfig(widgetConfig['widget-label']), children: [translateConfig(widgetConfig['widget-label']), widgetConfig['widget-required'] && jsxRuntimeExports.jsx("span", { className: "text-red-500 ml-1", children: "*" })] }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntimeExports.jsx("input", { type: inputType, value: getDisplayValue(), onChange: handleChange, onBlur: handleBlur, onFocus: handleFocus, disabled: !isEnabled || widgetConfig['widget-readonly'], placeholder: placeholder, min: inputMethod === 'picker' ? effectiveMinDate : undefined, max: inputMethod === 'picker' ? effectiveMaxDate : undefined, className: `w-full sm:w-[180px] max-w-full h-[30px] px-3 border shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 ${showValidationError || showRequiredError
8305
+ return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start", children: [jsxRuntimeExports.jsx(WidgetFieldLabel, { className: "text-base font-medium text-gray-700 md:min-w-[120px] sm:pr-4 sm:pt-1 mb-1 sm:mb-0", label: translateConfig(widgetConfig['widget-label']), required: isRequired }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntimeExports.jsx("input", { type: inputType, value: getDisplayValue(), onChange: handleChange, onBlur: handleBlur, onFocus: handleFocus, disabled: !isEnabled || widgetConfig['widget-readonly'], placeholder: placeholder, min: inputMethod === 'picker' ? effectiveMinDate : undefined, max: inputMethod === 'picker' ? effectiveMaxDate : undefined, className: `w-full sm:w-[180px] max-w-full h-[30px] px-3 border shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 ${showValidationError || showRequiredError
8030
8306
  ? 'border-red-500 focus:ring-red-500 focus:border-red-500'
8031
8307
  : 'border-gray-300'} ${!isEnabled || widgetConfig['widget-readonly'] ? 'bg-gray-100 cursor-not-allowed' : 'bg-white'}`, style: { borderRadius: '10px' }, title: translateConfig(widgetConfig['widget-data-tooltip']) }), showValidationError && jsxRuntimeExports.jsx("p", { className: "text-red-500 text-sm mt-1", children: error[0] })] })] }) }));
8032
8308
  };
@@ -8316,7 +8592,7 @@ const validateDateTimeConstraints = (date, minDateTime, maxDateTime, constraint)
8316
8592
  };
8317
8593
 
8318
8594
  const DateTimeInputWidget = ({ config }) => {
8319
- const { value, formattedValue, error, touched, isEnabled, onChange, onBlur, config: widgetConfig, } = useBaseWidget({ config });
8595
+ const { value, formattedValue, error, touched, isEnabled, isRequired, onChange, onBlur, config: widgetConfig, } = useBaseWidget({ config });
8320
8596
  const { translate, translateConfig } = useWidgetTranslation();
8321
8597
  const formatConfig = widgetConfig['widget-data-format'];
8322
8598
  const optionsConfig = widgetConfig['widget-data-options'];
@@ -8469,29 +8745,31 @@ const DateTimeInputWidget = ({ config }) => {
8469
8745
  }
8470
8746
  return (jsxRuntimeExports.jsxs("div", { className: "mb-[10px] DateTimeDisplayWidget flex flex-col sm:flex-row sm:items-start", children: [label && (jsxRuntimeExports.jsxs("div", { className: "text-base text-gray-600 font-medium md:min-w-[120px] sm:pr-4 mb-1 sm:mb-0", style: { fontFamily: 'Roboto, sans-serif' }, title: label, children: [label, ":"] })), jsxRuntimeExports.jsx("div", { className: "flex-1", children: jsxRuntimeExports.jsx("div", { className: "text-base text-gray-900 font-medium", title: String(displayValue ?? ''), children: displayValue }) })] }));
8471
8747
  }
8472
- return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start", children: [jsxRuntimeExports.jsxs("label", { className: "text-base font-medium text-gray-700 md:min-w-[120px] sm:pr-4 sm:pt-1 mb-1 sm:mb-0", style: { fontFamily: 'Roboto, sans-serif' }, title: translateConfig(widgetConfig['widget-label']), children: [translateConfig(widgetConfig['widget-label']), widgetConfig['widget-required'] && (jsxRuntimeExports.jsx("span", { className: "text-red-500 ml-1", children: "*" }))] }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntimeExports.jsx("input", { type: inputType, value: getDisplayValue(), onChange: handleChange, onBlur: handleBlur, onFocus: handleFocus, disabled: !isEnabled || widgetConfig['widget-readonly'], placeholder: placeholder, min: inputMethod === 'picker' ? effectiveMinDateTime : undefined, max: inputMethod === 'picker' ? effectiveMaxDateTime : undefined, className: `w-full sm:w-[180px] max-w-full h-[30px] px-3 border shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 ${(touched && error.length > 0) || (widgetConfig['widget-required'] && (!value || value === ''))
8748
+ return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start", children: [jsxRuntimeExports.jsx(WidgetFieldLabel, { className: "text-base font-medium text-gray-700 md:min-w-[120px] sm:pr-4 sm:pt-1 mb-1 sm:mb-0", label: translateConfig(widgetConfig['widget-label']), required: isRequired }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntimeExports.jsx("input", { type: inputType, value: getDisplayValue(), onChange: handleChange, onBlur: handleBlur, onFocus: handleFocus, disabled: !isEnabled || widgetConfig['widget-readonly'], placeholder: placeholder, min: inputMethod === 'picker' ? effectiveMinDateTime : undefined, max: inputMethod === 'picker' ? effectiveMaxDateTime : undefined, className: `w-full sm:w-[180px] max-w-full h-[30px] px-3 border shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 ${(touched && error.length > 0) || (widgetConfig['widget-required'] && (!value || value === ''))
8473
8749
  ? 'border-red-500 focus:ring-red-500 focus:border-red-500'
8474
8750
  : 'border-gray-300'} ${!isEnabled || widgetConfig['widget-readonly'] ? 'bg-gray-100 cursor-not-allowed' : 'bg-white'}`, style: { borderRadius: '10px' }, title: translateConfig(widgetConfig['widget-data-tooltip']) }), touched && error.length > 0 && (jsxRuntimeExports.jsx("p", { className: "text-red-500 text-sm mt-1", children: error[0] }))] })] }) }));
8475
8751
  };
8476
8752
 
8477
8753
  const SelectWidget = ({ config }) => {
8478
- const { value, error, touched, isEnabled, onChange, onBlur, dataSourceOptions, loading, config: widgetConfig, } = useBaseWidget({ config });
8754
+ const { value, geoDisplayLabel, error, touched, isEnabled, isRequired, onChange, onBlur, dataSourceOptions, loading, config: widgetConfig, } = useBaseWidget({ config });
8479
8755
  const { translate, translateConfig } = useWidgetTranslation();
8480
8756
  // For readonly mode, render as display text showing only the selected label
8481
8757
  if (widgetConfig['widget-readonly']) {
8482
8758
  const label = translateConfig(widgetConfig['widget-label']);
8483
8759
  // Find the selected option's label
8484
8760
  const selectedOption = dataSourceOptions.find((option) => option.value === value);
8485
- const displayValue = selectedOption ? selectedOption.label : (value || '-');
8761
+ const displayValue = selectedOption
8762
+ ? translateConfig(selectedOption.label)
8763
+ : (geoDisplayLabel || (value != null && value !== '' ? translateConfig(String(value)) : '-'));
8486
8764
  return (jsxRuntimeExports.jsxs("div", { className: "mb-[10px] SelectDisplayWidget flex flex-col sm:flex-row sm:items-start", children: [label && (jsxRuntimeExports.jsxs("div", { className: "text-base text-gray-600 font-medium md:min-w-[120px] sm:pr-4 mb-1 sm:mb-0", style: { fontFamily: 'Roboto, sans-serif' }, title: label, children: [label, ":"] })), jsxRuntimeExports.jsx("div", { className: "flex-1", children: jsxRuntimeExports.jsx("div", { className: "text-base text-gray-900 font-medium", title: String(displayValue ?? ''), children: displayValue }) })] }));
8487
8765
  }
8488
- return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start", children: [jsxRuntimeExports.jsxs("label", { className: "text-base font-medium text-gray-700 md:min-w-[120px] sm:pr-4 sm:pt-1 mb-1 sm:mb-0", style: { fontFamily: 'Roboto, sans-serif' }, title: translateConfig(widgetConfig['widget-label']), children: [translateConfig(widgetConfig['widget-label']), widgetConfig['widget-required'] && (jsxRuntimeExports.jsx("span", { className: "text-red-500 ml-1", children: "*" }))] }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntimeExports.jsxs("select", { value: value || '', onChange: (e) => onChange(e.target.value === '' ? undefined : e.target.value), onBlur: onBlur, disabled: !isEnabled || loading || widgetConfig['widget-readonly'], className: `w-full sm:w-[180px] max-w-full h-[30px] px-3 border shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 ${(touched && error.length > 0) || (widgetConfig['widget-required'] && (!value || value === ''))
8766
+ return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start", children: [jsxRuntimeExports.jsx(WidgetFieldLabel, { className: "text-base font-medium text-gray-700 md:min-w-[120px] sm:pr-4 sm:pt-1 mb-1 sm:mb-0", label: translateConfig(widgetConfig['widget-label']), required: isRequired }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntimeExports.jsxs("select", { value: value || '', onChange: (e) => onChange(e.target.value === '' ? undefined : e.target.value), onBlur: onBlur, disabled: !isEnabled || loading || widgetConfig['widget-readonly'], className: `w-full sm:w-[180px] max-w-full h-[30px] px-3 border shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 ${(touched && error.length > 0) || (widgetConfig['widget-required'] && (!value || value === ''))
8489
8767
  ? 'border-red-500 focus:ring-red-500 focus:border-red-500'
8490
- : 'border-gray-300'} ${!isEnabled || loading || widgetConfig['widget-readonly'] ? 'bg-gray-100 cursor-not-allowed' : 'bg-white'}`, style: { borderRadius: '10px' }, title: translateConfig(widgetConfig['widget-data-tooltip']), children: [jsxRuntimeExports.jsx("option", { value: "", children: translate('common.select') }), dataSourceOptions.map((option) => (jsxRuntimeExports.jsx("option", { value: option.value, children: option.label }, option.value)))] }), loading && (jsxRuntimeExports.jsx("p", { className: "text-sm text-gray-500 mt-1", children: translate('common.loadingOptions') })), touched && error.length > 0 && (jsxRuntimeExports.jsx("p", { className: "text-red-500 text-sm mt-1", children: error[0] }))] })] }) }));
8768
+ : 'border-gray-300'} ${!isEnabled || loading || widgetConfig['widget-readonly'] ? 'bg-gray-100 cursor-not-allowed' : 'bg-white'}`, style: { borderRadius: '10px' }, title: translateConfig(widgetConfig['widget-data-tooltip']), children: [jsxRuntimeExports.jsx("option", { value: "", children: translate('common.select') }), dataSourceOptions.map((option) => (jsxRuntimeExports.jsx("option", { value: option.value, children: translateConfig(option.label) }, option.value)))] }), loading && (jsxRuntimeExports.jsx("p", { className: "text-sm text-gray-500 mt-1", children: translate('common.loadingOptions') })), touched && error.length > 0 && (jsxRuntimeExports.jsx("p", { className: "text-red-500 text-sm mt-1", children: error[0] }))] })] }) }));
8491
8769
  };
8492
8770
 
8493
8771
  const RadioWidget = ({ config }) => {
8494
- const { value, error, touched, isEnabled, onChange, onBlur, dataSourceOptions, loading, config: widgetConfig, } = useBaseWidget({ config });
8772
+ const { value, error, touched, isEnabled, isRequired, onChange, onBlur, dataSourceOptions, loading, config: widgetConfig, } = useBaseWidget({ config });
8495
8773
  const { translate, translateConfig } = useWidgetTranslation();
8496
8774
  const formatConfig = widgetConfig['widget-data-format'];
8497
8775
  const layout = formatConfig?.layout || widgetConfig['widget-orientation'] || 'vertical';
@@ -8554,14 +8832,16 @@ const RadioWidget = ({ config }) => {
8554
8832
  if (widgetConfig['widget-readonly']) {
8555
8833
  const label = translateConfig(widgetConfig['widget-label']);
8556
8834
  const selectedOption = processedOptions.find(opt => opt.value === currentValue);
8557
- const displayValue = selectedOption ? selectedOption.label : (allowUnset && currentValue === null ? '-' : '');
8835
+ const displayValue = selectedOption
8836
+ ? translateConfig(selectedOption.label)
8837
+ : (allowUnset && currentValue === null ? '-' : '');
8558
8838
  return (jsxRuntimeExports.jsxs("div", { className: "mb-[10px] RadioDisplayWidget flex flex-col sm:flex-row sm:items-start", children: [label && (jsxRuntimeExports.jsxs("div", { className: "text-base text-gray-600 font-medium md:min-w-[120px] sm:pr-4 mb-1 sm:mb-0", style: { fontFamily: 'Roboto, sans-serif' }, title: label, children: [label, ":"] })), jsxRuntimeExports.jsx("div", { className: "flex-1", children: jsxRuntimeExports.jsx("div", { className: "text-base text-gray-900 font-medium", title: String(displayValue ?? ''), children: displayValue }) })] }));
8559
8839
  }
8560
- return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start", children: [jsxRuntimeExports.jsxs("label", { className: "text-base font-medium text-gray-700 md:min-w-[120px] sm:pr-4 sm:pt-1 mb-1 sm:mb-0", style: { fontFamily: 'Roboto, sans-serif' }, title: translateConfig(widgetConfig['widget-label']), children: [translateConfig(widgetConfig['widget-label']), widgetConfig['widget-required'] && (jsxRuntimeExports.jsx("span", { className: "text-red-500 ml-1", children: "*" }))] }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntimeExports.jsx("div", { className: layoutConfig.className, style: layoutConfig.style, onBlur: onBlur, children: loading ? (jsxRuntimeExports.jsx("p", { className: "text-sm text-gray-500", children: translate('common.loading') })) : (jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [allowUnset && (jsxRuntimeExports.jsxs("label", { className: `flex items-center cursor-pointer ${!isEnabled || widgetConfig['widget-readonly'] ? 'opacity-50 cursor-not-allowed' : ''}`, children: [jsxRuntimeExports.jsx("input", { type: "radio", name: widgetConfig['widget-id'], checked: currentValue === null, onChange: handleUnset, disabled: !isEnabled || widgetConfig['widget-readonly'], className: "mr-2 h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300" }), jsxRuntimeExports.jsx("span", { className: "text-sm text-gray-700", children: "-" })] })), processedOptions.map((option) => (jsxRuntimeExports.jsxs("label", { className: `flex items-center cursor-pointer ${!isEnabled || widgetConfig['widget-readonly'] ? 'opacity-50 cursor-not-allowed' : ''}`, children: [jsxRuntimeExports.jsx("input", { type: "radio", name: widgetConfig['widget-id'], value: option.value, checked: currentValue === option.value, onChange: (e) => handleChange(option.value), disabled: !isEnabled || widgetConfig['widget-readonly'], className: "mr-2 h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300" }), jsxRuntimeExports.jsx("span", { className: "text-sm text-gray-700", children: translateConfig(option.label) })] }, option.value)))] })) }), touched && error.length > 0 && (jsxRuntimeExports.jsx("p", { className: "text-red-500 text-sm mt-1", children: error[0] }))] })] }) }));
8840
+ return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start", children: [jsxRuntimeExports.jsx(WidgetFieldLabel, { className: "text-base font-medium text-gray-700 md:min-w-[120px] sm:pr-4 sm:pt-1 mb-1 sm:mb-0", label: translateConfig(widgetConfig['widget-label']), required: isRequired }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntimeExports.jsx("div", { className: layoutConfig.className, style: layoutConfig.style, onBlur: onBlur, children: loading ? (jsxRuntimeExports.jsx("p", { className: "text-sm text-gray-500", children: translate('common.loading') })) : (jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [allowUnset && (jsxRuntimeExports.jsxs("label", { className: `flex items-center cursor-pointer ${!isEnabled || widgetConfig['widget-readonly'] ? 'opacity-50 cursor-not-allowed' : ''}`, children: [jsxRuntimeExports.jsx("input", { type: "radio", name: widgetConfig['widget-id'], checked: currentValue === null, onChange: handleUnset, disabled: !isEnabled || widgetConfig['widget-readonly'], className: "mr-2 h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300" }), jsxRuntimeExports.jsx("span", { className: "text-sm text-gray-700", children: "-" })] })), processedOptions.map((option) => (jsxRuntimeExports.jsxs("label", { className: `flex items-center cursor-pointer ${!isEnabled || widgetConfig['widget-readonly'] ? 'opacity-50 cursor-not-allowed' : ''}`, children: [jsxRuntimeExports.jsx("input", { type: "radio", name: widgetConfig['widget-id'], value: option.value, checked: currentValue === option.value, onChange: (e) => handleChange(option.value), disabled: !isEnabled || widgetConfig['widget-readonly'], className: "mr-2 h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300" }), jsxRuntimeExports.jsx("span", { className: "text-sm text-gray-700", children: translateConfig(option.label) })] }, option.value)))] })) }), touched && error.length > 0 && (jsxRuntimeExports.jsx("p", { className: "text-red-500 text-sm mt-1", children: error[0] }))] })] }) }));
8561
8841
  };
8562
8842
 
8563
8843
  const CheckboxWidget = ({ config }) => {
8564
- const { value, error, touched, isEnabled, onChange, onBlur, dataSourceOptions, loading, config: widgetConfig, } = useBaseWidget({ config });
8844
+ const { value, error, touched, isEnabled, isRequired, onChange, onBlur, dataSourceOptions, loading, config: widgetConfig, } = useBaseWidget({ config });
8565
8845
  const { translate, translateConfig } = useWidgetTranslation();
8566
8846
  const hasDataSource = !!widgetConfig['widget-data-source'];
8567
8847
  const formatConfig = widgetConfig['widget-data-format'];
@@ -8576,7 +8856,7 @@ const CheckboxWidget = ({ config }) => {
8576
8856
  const displayValue = isChecked ? 'Yes' : 'No';
8577
8857
  return (jsxRuntimeExports.jsxs("div", { className: "mb-[10px] CheckboxDisplayWidget flex flex-col sm:flex-row sm:items-start", children: [label && (jsxRuntimeExports.jsxs("div", { className: "text-base text-gray-600 font-medium md:min-w-[120px] sm:pr-4 mb-1 sm:mb-0", style: { fontFamily: 'Roboto, sans-serif' }, title: label, children: [label, ":"] })), jsxRuntimeExports.jsx("div", { className: "flex-1", children: jsxRuntimeExports.jsx("div", { className: "text-base text-gray-900 font-medium", title: String(displayValue ?? ''), children: displayValue }) })] }));
8578
8858
  }
8579
- return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-baseline", children: [jsxRuntimeExports.jsxs("label", { className: "text-base font-medium leading-normal text-gray-700 md:min-w-[120px] sm:pr-4 mb-1 sm:mb-0 sm:pt-0.5", style: { fontFamily: 'Roboto, sans-serif' }, title: translateConfig(widgetConfig['widget-label']), children: [translateConfig(widgetConfig['widget-label']), widgetConfig['widget-required'] && (jsxRuntimeExports.jsx("span", { className: "text-red-500 ml-1", children: "*" }))] }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntimeExports.jsxs("label", { className: "inline-flex cursor-pointer items-baseline gap-2", children: [jsxRuntimeExports.jsx("input", { type: "checkbox", checked: isChecked, onChange: (e) => onChange(e.target.checked), onBlur: onBlur, disabled: !isEnabled || widgetConfig['widget-readonly'], className: "relative top-[0.2em] h-4 w-4 shrink-0 text-blue-600 focus:ring-blue-500 border-gray-300 rounded" }), jsxRuntimeExports.jsx("span", { className: "text-base leading-normal text-gray-700", children: isChecked ? 'Yes' : 'No' })] }), touched && error.length > 0 && (jsxRuntimeExports.jsx("p", { className: "text-red-500 text-sm mt-1", children: error[0] }))] })] }) }));
8859
+ return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-baseline", children: [jsxRuntimeExports.jsx(WidgetFieldLabel, { className: "text-base font-medium leading-normal text-gray-700 md:min-w-[120px] sm:pr-4 mb-1 sm:mb-0 sm:pt-0.5", label: translateConfig(widgetConfig['widget-label']), required: isRequired }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntimeExports.jsxs("label", { className: "inline-flex cursor-pointer items-baseline gap-2", children: [jsxRuntimeExports.jsx("input", { type: "checkbox", checked: isChecked, onChange: (e) => onChange(e.target.checked), onBlur: onBlur, disabled: !isEnabled || widgetConfig['widget-readonly'], className: "relative top-[0.2em] h-4 w-4 shrink-0 text-blue-600 focus:ring-blue-500 border-gray-300 rounded" }), jsxRuntimeExports.jsx("span", { className: "text-base leading-normal text-gray-700", children: isChecked ? 'Yes' : 'No' })] }), touched && error.length > 0 && (jsxRuntimeExports.jsx("p", { className: "text-red-500 text-sm mt-1", children: error[0] }))] })] }) }));
8580
8860
  }
8581
8861
  // Multiple checkboxes (with data source) - for array values
8582
8862
  // Process and sort options if needed
@@ -8646,7 +8926,7 @@ const CheckboxWidget = ({ config }) => {
8646
8926
  : '-';
8647
8927
  return (jsxRuntimeExports.jsxs("div", { className: "mb-3 CheckboxDisplayWidget flex flex-col sm:flex-row sm:items-start", children: [label && (jsxRuntimeExports.jsxs("div", { className: "text-sm text-gray-600 font-medium md:min-w-[120px] sm:pr-4 mb-1 sm:mb-0", title: label, children: [label, ":"] })), jsxRuntimeExports.jsx("div", { className: "flex-1", children: jsxRuntimeExports.jsx("div", { className: "text-base text-gray-900 font-medium", title: String(displayValue ?? ''), children: displayValue }) })] }));
8648
8928
  }
8649
- return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-baseline", children: [jsxRuntimeExports.jsxs("label", { className: "text-base font-medium leading-normal text-gray-700 md:min-w-[120px] sm:pr-4 mb-1 sm:mb-0 sm:pt-0.5", style: { fontFamily: 'Roboto, sans-serif' }, title: translateConfig(widgetConfig['widget-label']), children: [translateConfig(widgetConfig['widget-label']), widgetConfig['widget-required'] && (jsxRuntimeExports.jsx("span", { className: "text-red-500 ml-1", children: "*" }))] }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntimeExports.jsx("div", { className: layoutConfig.className, style: layoutConfig.style, onBlur: onBlur, children: loading ? (jsxRuntimeExports.jsx("p", { className: "text-sm text-gray-500", children: translate('common.loading') })) : (processedOptions.map((option) => (jsxRuntimeExports.jsxs("label", { className: `inline-flex cursor-pointer items-baseline gap-2 ${!isEnabled || widgetConfig['widget-readonly'] ? 'opacity-50 cursor-not-allowed' : ''}`, children: [jsxRuntimeExports.jsx("input", { type: "checkbox", value: option.value, checked: selectedValues.includes(option.value), onChange: (e) => handleCheckboxChange(option.value, e.target.checked), disabled: !isEnabled || widgetConfig['widget-readonly'], className: "relative top-[0.2em] h-4 w-4 shrink-0 text-blue-600 focus:ring-blue-500 border-gray-300 rounded" }), jsxRuntimeExports.jsx("span", { className: "text-base leading-normal text-gray-700", children: translateConfig(option.label) })] }, option.value)))) }), touched && error.length > 0 && (jsxRuntimeExports.jsx("p", { className: "text-red-500 text-sm mt-1", children: error[0] }))] })] }) }));
8929
+ return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-baseline", children: [jsxRuntimeExports.jsx(WidgetFieldLabel, { className: "text-base font-medium leading-normal text-gray-700 md:min-w-[120px] sm:pr-4 mb-1 sm:mb-0 sm:pt-0.5", label: translateConfig(widgetConfig['widget-label']), required: isRequired }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntimeExports.jsx("div", { className: layoutConfig.className, style: layoutConfig.style, onBlur: onBlur, children: loading ? (jsxRuntimeExports.jsx("p", { className: "text-sm text-gray-500", children: translate('common.loading') })) : (processedOptions.map((option) => (jsxRuntimeExports.jsxs("label", { className: `inline-flex cursor-pointer items-baseline gap-2 ${!isEnabled || widgetConfig['widget-readonly'] ? 'opacity-50 cursor-not-allowed' : ''}`, children: [jsxRuntimeExports.jsx("input", { type: "checkbox", value: option.value, checked: selectedValues.includes(option.value), onChange: (e) => handleCheckboxChange(option.value, e.target.checked), disabled: !isEnabled || widgetConfig['widget-readonly'], className: "relative top-[0.2em] h-4 w-4 shrink-0 text-blue-600 focus:ring-blue-500 border-gray-300 rounded" }), jsxRuntimeExports.jsx("span", { className: "text-base leading-normal text-gray-700", children: translateConfig(option.label) })] }, option.value)))) }), touched && error.length > 0 && (jsxRuntimeExports.jsx("p", { className: "text-red-500 text-sm mt-1", children: error[0] }))] })] }) }));
8650
8930
  };
8651
8931
 
8652
8932
  const SimpleTableWidget = ({ config }) => {
@@ -8697,7 +8977,7 @@ const SimpleTableWidget = ({ config }) => {
8697
8977
  };
8698
8978
 
8699
8979
  const ArrayWidget = ({ config }) => {
8700
- const { value, error, touched, isEnabled, onChange, config: widgetConfig, } = useBaseWidget({ config });
8980
+ const { value, error, touched, isEnabled, isRequired, onChange, config: widgetConfig, } = useBaseWidget({ config });
8701
8981
  const { translate, translateConfig } = useWidgetTranslation();
8702
8982
  const items = Array.isArray(value) ? value : [];
8703
8983
  const itemConfig = widgetConfig['widget-item'];
@@ -8721,7 +9001,7 @@ const ArrayWidget = ({ config }) => {
8721
9001
  newItems[index] = newValue;
8722
9002
  onChange(newItems);
8723
9003
  };
8724
- return (jsxRuntimeExports.jsxs("div", { className: "mb-[10px]", children: [jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start mb-2", children: [jsxRuntimeExports.jsxs("label", { className: "text-base font-medium text-gray-700 md:min-w-[120px] sm:pr-4 sm:pt-1 mb-1 sm:mb-0", style: { fontFamily: 'Roboto, sans-serif' }, title: translateConfig(widgetConfig['widget-label']), children: [translateConfig(widgetConfig['widget-label']), widgetConfig['widget-required'] && (jsxRuntimeExports.jsx("span", { className: "text-red-500 ml-1", children: "*" }))] }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0 flex justify-between items-center", children: [jsxRuntimeExports.jsx("div", { className: "flex-1" }), operations.add && !isReadonly && isEnabled && (jsxRuntimeExports.jsx("button", { type: "button", onClick: addItem, className: "px-3 py-1 text-sm bg-blue-500 text-white hover:bg-blue-600", style: { borderRadius: '15px' }, children: addLabel }))] })] }), items.length === 0 ? (jsxRuntimeExports.jsxs("div", { className: "text-gray-500 text-sm py-4 text-center border border-gray-300 rounded", children: [translate('common.noItems'), ". ", operations.add && !isReadonly && translate('common.clickToAdd', { label: addLabel })] })) : (jsxRuntimeExports.jsx("div", { className: "space-y-2", children: items.map((itemValue, index) => {
9004
+ return (jsxRuntimeExports.jsxs("div", { className: "mb-[10px]", children: [jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start mb-2", children: [jsxRuntimeExports.jsx(WidgetFieldLabel, { className: "text-base font-medium text-gray-700 md:min-w-[120px] sm:pr-4 sm:pt-1 mb-1 sm:mb-0", label: translateConfig(widgetConfig['widget-label']), required: isRequired }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0 flex justify-between items-center", children: [jsxRuntimeExports.jsx("div", { className: "flex-1" }), operations.add && !isReadonly && isEnabled && (jsxRuntimeExports.jsx("button", { type: "button", onClick: addItem, className: "px-3 py-1 text-sm bg-blue-500 text-white hover:bg-blue-600", style: { borderRadius: '15px' }, children: addLabel }))] })] }), items.length === 0 ? (jsxRuntimeExports.jsxs("div", { className: "text-gray-500 text-sm py-4 text-center border border-gray-300 rounded", children: [translate('common.noItems'), ". ", operations.add && !isReadonly && translate('common.clickToAdd', { label: addLabel })] })) : (jsxRuntimeExports.jsx("div", { className: "space-y-2", children: items.map((itemValue, index) => {
8725
9005
  ({
8726
9006
  ...itemConfig,
8727
9007
  'widget-id': `${widgetConfig['widget-id']}-item-${index}`,
@@ -8732,7 +9012,7 @@ const ArrayWidget = ({ config }) => {
8732
9012
  };
8733
9013
 
8734
9014
  const IterableAccordionWidget = ({ config }) => {
8735
- const { value, error, touched, isEnabled, onChange, config: widgetConfig, } = useBaseWidget({ config });
9015
+ const { value, error, touched, isEnabled, isRequired, onChange, config: widgetConfig, } = useBaseWidget({ config });
8736
9016
  const { translate, translateConfig } = useWidgetTranslation();
8737
9017
  const items = Array.isArray(value) ? value : [];
8738
9018
  const itemConfig = widgetConfig['widget-item'];
@@ -8781,7 +9061,7 @@ const IterableAccordionWidget = ({ config }) => {
8781
9061
  newItems[index] = newValue;
8782
9062
  onChange(newItems);
8783
9063
  };
8784
- return (jsxRuntimeExports.jsxs("div", { className: "mb-[10px]", children: [jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start mb-2", children: [jsxRuntimeExports.jsxs("label", { className: "text-base font-medium text-gray-700 md:min-w-[120px] sm:pr-4 sm:pt-1 mb-1 sm:mb-0", style: { fontFamily: 'Roboto, sans-serif' }, title: translateConfig(widgetConfig['widget-label']), children: [translateConfig(widgetConfig['widget-label']), widgetConfig['widget-required'] && (jsxRuntimeExports.jsx("span", { className: "text-red-500 ml-1", children: "*" }))] }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0 flex justify-between items-center", children: [jsxRuntimeExports.jsx("div", { className: "flex-1" }), operations.add && !isReadonly && isEnabled && (jsxRuntimeExports.jsx("button", { type: "button", onClick: addItem, className: "px-3 py-1 text-sm bg-blue-500 text-white hover:bg-blue-600", style: { borderRadius: '15px' }, children: addLabel }))] })] }), items.length === 0 ? (jsxRuntimeExports.jsxs("div", { className: "text-gray-500 text-sm py-4 text-center border border-gray-300 rounded", children: [translate('common.noItems'), ". ", operations.add && !isReadonly && translate('common.clickToAdd', { label: addLabel })] })) : (jsxRuntimeExports.jsx("div", { className: "space-y-2", children: items.map((itemValue, index) => {
9064
+ return (jsxRuntimeExports.jsxs("div", { className: "mb-[10px]", children: [jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start mb-2", children: [jsxRuntimeExports.jsx(WidgetFieldLabel, { className: "text-base font-medium text-gray-700 md:min-w-[120px] sm:pr-4 sm:pt-1 mb-1 sm:mb-0", label: translateConfig(widgetConfig['widget-label']), required: isRequired }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0 flex justify-between items-center", children: [jsxRuntimeExports.jsx("div", { className: "flex-1" }), operations.add && !isReadonly && isEnabled && (jsxRuntimeExports.jsx("button", { type: "button", onClick: addItem, className: "px-3 py-1 text-sm bg-blue-500 text-white hover:bg-blue-600", style: { borderRadius: '15px' }, children: addLabel }))] })] }), items.length === 0 ? (jsxRuntimeExports.jsxs("div", { className: "text-gray-500 text-sm py-4 text-center border border-gray-300 rounded", children: [translate('common.noItems'), ". ", operations.add && !isReadonly && translate('common.clickToAdd', { label: addLabel })] })) : (jsxRuntimeExports.jsx("div", { className: "space-y-2", children: items.map((itemValue, index) => {
8785
9065
  const isCollapsed = collapsedItems[index] ?? defaultCollapsed;
8786
9066
  const parentPath = widgetConfig['widget-data-path'];
8787
9067
  const childPath = itemConfig['widget-data-path'];
@@ -8820,7 +9100,7 @@ const IterableAccordionWidget = ({ config }) => {
8820
9100
  };
8821
9101
 
8822
9102
  const PhoneInputWidget = ({ config }) => {
8823
- const { value, formattedValue, error, touched, isEnabled, onChange, onBlur, config: widgetConfig, } = useBaseWidget({ config });
9103
+ const { value, formattedValue, error, touched, isEnabled, isRequired, onChange, onBlur, config: widgetConfig, } = useBaseWidget({ config });
8824
9104
  const { translate, translateConfig } = useWidgetTranslation();
8825
9105
  // Use formatted value if available, otherwise raw value
8826
9106
  const displayValue = formattedValue !== undefined && formattedValue !== value
@@ -8831,13 +9111,13 @@ const PhoneInputWidget = ({ config }) => {
8831
9111
  const label = translateConfig(widgetConfig['widget-label']);
8832
9112
  return (jsxRuntimeExports.jsxs("div", { className: "mb-[10px] PhoneDisplayWidget flex flex-col sm:flex-row sm:items-start", children: [label && (jsxRuntimeExports.jsxs("div", { className: "text-base text-gray-600 font-medium md:min-w-[120px] sm:pr-4 mb-1 sm:mb-0", style: { fontFamily: 'Roboto, sans-serif' }, title: label, children: [label, ":"] })), jsxRuntimeExports.jsx("div", { className: "flex-1", children: jsxRuntimeExports.jsx("div", { className: "text-base text-gray-900 font-medium", title: String(displayValue || ''), children: displayValue || '-' }) })] }));
8833
9113
  }
8834
- return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start", children: [jsxRuntimeExports.jsxs("label", { className: "text-base font-medium text-gray-700 md:min-w-[120px] sm:pr-4 sm:pt-1 mb-1 sm:mb-0", style: { fontFamily: 'Roboto, sans-serif' }, title: translateConfig(widgetConfig['widget-label']), children: [translateConfig(widgetConfig['widget-label']), widgetConfig['widget-required'] && (jsxRuntimeExports.jsx("span", { className: "text-red-500 ml-1", children: "*" }))] }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntimeExports.jsx("input", { type: "tel", value: displayValue, onChange: (e) => onChange(e.target.value), onBlur: onBlur, disabled: !isEnabled || widgetConfig['widget-readonly'], placeholder: translateConfig(widgetConfig['widget-data-placeholder']), className: `w-full sm:w-[180px] max-w-full h-[30px] px-3 border shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 ${(touched && error.length > 0) || (widgetConfig['widget-required'] && (!value || value === ''))
9114
+ return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start", children: [jsxRuntimeExports.jsx(WidgetFieldLabel, { className: "text-base font-medium text-gray-700 md:min-w-[120px] sm:pr-4 sm:pt-1 mb-1 sm:mb-0", label: translateConfig(widgetConfig['widget-label']), required: isRequired }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntimeExports.jsx("input", { type: "tel", value: displayValue, onChange: (e) => onChange(e.target.value), onBlur: onBlur, disabled: !isEnabled || widgetConfig['widget-readonly'], placeholder: translateConfig(widgetConfig['widget-data-placeholder']), className: `w-full sm:w-[180px] max-w-full h-[30px] px-3 border shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 ${(touched && error.length > 0) || (widgetConfig['widget-required'] && (!value || value === ''))
8835
9115
  ? 'border-red-500 focus:ring-red-500 focus:border-red-500'
8836
9116
  : 'border-gray-300'} ${!isEnabled || widgetConfig['widget-readonly'] ? 'bg-gray-100 cursor-not-allowed' : 'bg-white'}`, style: { borderRadius: '10px' }, title: translateConfig(widgetConfig['widget-data-tooltip']) }), touched && error.length > 0 && (jsxRuntimeExports.jsx("p", { className: "text-red-500 text-sm mt-1", children: error[0] }))] })] }) }));
8837
9117
  };
8838
9118
 
8839
9119
  const CurrencyInputWidget = ({ config }) => {
8840
- const { value, formattedValue, error, touched, isEnabled, onChange, onBlur, config: widgetConfig, } = useBaseWidget({ config });
9120
+ const { value, formattedValue, error, touched, isEnabled, isRequired, onChange, onBlur, config: widgetConfig, } = useBaseWidget({ config });
8841
9121
  const { translate, translateConfig } = useWidgetTranslation();
8842
9122
  // For input, use raw numeric value; formatted value is for display only
8843
9123
  const numericValue = typeof value === 'number' ? value : (value ? parseFloat(String(value)) : '');
@@ -8859,7 +9139,7 @@ const CurrencyInputWidget = ({ config }) => {
8859
9139
  const display = formattedValue || (value !== null && value !== undefined ? String(value) : '-');
8860
9140
  return (jsxRuntimeExports.jsxs("div", { className: "mb-[10px] CurrencyDisplayWidget flex flex-col sm:flex-row sm:items-start", children: [label && (jsxRuntimeExports.jsxs("div", { className: "text-base text-gray-600 font-medium md:min-w-[120px] sm:pr-4 mb-1 sm:mb-0", style: { fontFamily: 'Roboto, sans-serif' }, title: label, children: [label, ":"] })), jsxRuntimeExports.jsx("div", { className: "flex-1", children: jsxRuntimeExports.jsx("div", { className: "text-base text-gray-900 font-medium", title: String(display ?? ''), children: display }) })] }));
8861
9141
  }
8862
- return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start", children: [jsxRuntimeExports.jsxs("label", { className: "text-base font-medium text-gray-700 md:min-w-[120px] sm:pr-4 sm:pt-1 mb-1 sm:mb-0", style: { fontFamily: 'Roboto, sans-serif' }, title: translateConfig(widgetConfig['widget-label']), children: [translateConfig(widgetConfig['widget-label']), widgetConfig['widget-required'] && (jsxRuntimeExports.jsx("span", { className: "text-red-500 ml-1", children: "*" }))] }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntimeExports.jsxs("div", { className: "relative", children: [jsxRuntimeExports.jsx("input", { type: "text", inputMode: "decimal", value: numericValue, onChange: handleChange, onBlur: onBlur, disabled: !isEnabled || widgetConfig['widget-readonly'], placeholder: translateConfig(widgetConfig['widget-data-placeholder']), className: `w-full sm:w-[180px] max-w-full h-[30px] px-3 border shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 ${(touched && error.length > 0) || (widgetConfig['widget-required'] && (value === null || value === undefined || value === ''))
9142
+ return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start", children: [jsxRuntimeExports.jsx(WidgetFieldLabel, { className: "text-base font-medium text-gray-700 md:min-w-[120px] sm:pr-4 sm:pt-1 mb-1 sm:mb-0", label: translateConfig(widgetConfig['widget-label']), required: isRequired }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntimeExports.jsxs("div", { className: "relative", children: [jsxRuntimeExports.jsx("input", { type: "text", inputMode: "decimal", value: numericValue, onChange: handleChange, onBlur: onBlur, disabled: !isEnabled || widgetConfig['widget-readonly'], placeholder: translateConfig(widgetConfig['widget-data-placeholder']), className: `w-full sm:w-[180px] max-w-full h-[30px] px-3 border shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 ${(touched && error.length > 0) || (widgetConfig['widget-required'] && (value === null || value === undefined || value === ''))
8863
9143
  ? 'border-red-500 focus:ring-red-500 focus:border-red-500'
8864
9144
  : 'border-gray-300'} ${!isEnabled || widgetConfig['widget-readonly'] ? 'bg-gray-100 cursor-not-allowed' : 'bg-white'}`, style: { borderRadius: '10px' }, title: translateConfig(widgetConfig['widget-data-tooltip']) }), formattedValue && formattedValue !== String(value) && (jsxRuntimeExports.jsx("span", { className: "absolute right-3 top-2 text-gray-500 text-sm", children: formattedValue }))] }), touched && error.length > 0 && (jsxRuntimeExports.jsx("p", { className: "text-red-500 text-sm mt-1", children: error[0] }))] })] }) }));
8865
9145
  };
@@ -9702,6 +9982,7 @@ const TableWidget = ({ config }) => {
9702
9982
  }, children: translate('common.cancel') || 'Cancel' })] }) })] }))] })] }) }), touched && error.length > 0 && (jsxRuntimeExports.jsx("p", { className: "text-sm mt-1", style: { color: 'var(--owt-widget-error-color, #B91C1C)' }, children: error[0] }))] })] }));
9703
9983
  };
9704
9984
 
9985
+ const isUnsetRowValue = (value) => value === null || value === undefined || value === '';
9705
9986
  // Display select value label in view mode
9706
9987
  const SelectDisplayValue = ({ config, value }) => {
9707
9988
  const { dataSourceOptions, loading } = useBaseWidget({ config });
@@ -9734,7 +10015,6 @@ const DialogTableWidget = ({ config }) => {
9734
10015
  const { value, error, touched, isEnabled, onChange, config: widgetConfig } = useBaseWidget({ config });
9735
10016
  const { translate, translateConfig } = useWidgetTranslation();
9736
10017
  const dispatch = reactRedux.useDispatch();
9737
- const storeValues = reactRedux.useSelector((state) => state.widget?.values ?? {});
9738
10018
  const rows = Array.isArray(value) ? value : [];
9739
10019
  const columns = widgetConfig['widget-data-columns'] || [];
9740
10020
  const operations = widgetConfig['widget-data-operations'] || {};
@@ -9766,7 +10046,12 @@ const DialogTableWidget = ({ config }) => {
9766
10046
  const emptyRow = {};
9767
10047
  columns.forEach((col) => {
9768
10048
  const key = col['column-key'];
9769
- emptyRow[key] = col['widget-data-default'] ?? '';
10049
+ if (col['widget-data-default'] !== undefined) {
10050
+ emptyRow[key] = col['widget-data-default'];
10051
+ }
10052
+ else if (col.widget === 'checkbox') {
10053
+ emptyRow[key] = false;
10054
+ }
9770
10055
  });
9771
10056
  return emptyRow;
9772
10057
  }, [columns]);
@@ -9819,17 +10104,48 @@ const DialogTableWidget = ({ config }) => {
9819
10104
  const updateField = React.useCallback((columnKey, newValue) => {
9820
10105
  setFormData((prev) => ({ ...prev, [columnKey]: newValue }));
9821
10106
  }, []);
9822
- const collectMergedRowPayload = React.useCallback(() => {
9823
- const merged = { ...formData };
10107
+ const membersWidgetId = widgetConfig['widget-id'];
10108
+ const dialogStoreValues = reactRedux.useSelector((state) => {
10109
+ if (dialogSessionId <= 0) {
10110
+ return {};
10111
+ }
10112
+ const values = state.widget?.values ?? {};
10113
+ const row = {};
10114
+ columns.forEach((col) => {
10115
+ const k = col['column-key'];
10116
+ const wid = `${membersWidgetId}-dlg-${dialogSessionId}-${k}`;
10117
+ if (values[wid] !== undefined) {
10118
+ row[k] = values[wid];
10119
+ }
10120
+ });
10121
+ return row;
10122
+ }, (a, b) => JSON.stringify(a) === JSON.stringify(b));
10123
+ const buildDialogRowValues = React.useCallback((storeSlice) => {
10124
+ const row = { ...formData };
9824
10125
  columns.forEach((col) => {
9825
10126
  const k = col['column-key'];
9826
- const wid = dialogFieldWidgetId(k);
9827
- const fromStore = storeValues[wid];
9828
- if (fromStore !== undefined)
9829
- merged[k] = fromStore;
10127
+ if (storeSlice[k] !== undefined) {
10128
+ row[k] = storeSlice[k];
10129
+ }
9830
10130
  });
9831
- return merged;
9832
- }, [formData, columns, storeValues, dialogFieldWidgetId]);
10131
+ return row;
10132
+ }, [formData, columns]);
10133
+ const dialogRowValues = React.useMemo(() => buildDialogRowValues(dialogStoreValues), [buildDialogRowValues, dialogStoreValues]);
10134
+ const collectMergedRowPayload = React.useCallback(() => buildDialogRowValues(dialogStoreValues), [buildDialogRowValues, dialogStoreValues]);
10135
+ const finalizeDialogRowPayload = React.useCallback((raw) => {
10136
+ const result = {};
10137
+ columns.forEach((col) => {
10138
+ const key = col['column-key'];
10139
+ if (!shouldShowWidget(col['widget-data-options'], raw)) {
10140
+ return;
10141
+ }
10142
+ const val = raw[key];
10143
+ if (!isUnsetRowValue(val)) {
10144
+ result[key] = val;
10145
+ }
10146
+ });
10147
+ return result;
10148
+ }, [columns]);
9833
10149
  const saveDialog = React.useCallback(() => {
9834
10150
  const payload = collectMergedRowPayload();
9835
10151
  let hasErrors = false;
@@ -9839,8 +10155,11 @@ const DialogTableWidget = ({ config }) => {
9839
10155
  const isColReadonly = isReadonly || col['widget-readonly'] === true;
9840
10156
  if (isColReadonly)
9841
10157
  return;
10158
+ if (!shouldShowWidget(col['widget-data-options'], payload))
10159
+ return;
9842
10160
  const cellValue = payload[key];
9843
- const validationErrors = validateWidget(cellValue, col['widget-data-validation'], col['widget-required']);
10161
+ const isRequired = shouldRequireWidget(col['widget-data-options'], payload, col['widget-required']);
10162
+ const validationErrors = validateWidget(cellValue, col['widget-data-validation'], isRequired);
9844
10163
  if (validationErrors && validationErrors.length > 0) {
9845
10164
  hasErrors = true;
9846
10165
  dispatch(setError({ widgetId: cellWidgetId, errors: validationErrors }));
@@ -9853,8 +10172,9 @@ const DialogTableWidget = ({ config }) => {
9853
10172
  if (hasErrors) {
9854
10173
  return;
9855
10174
  }
10175
+ const cleaned = finalizeDialogRowPayload(payload);
9856
10176
  if (dialogMode === 'add') {
9857
- const savedRow = { ...payload, edit_action: 'ADD' };
10177
+ const savedRow = { ...cleaned, edit_action: 'ADD' };
9858
10178
  onChange([...rows, savedRow]);
9859
10179
  closeDialog();
9860
10180
  return;
@@ -9864,11 +10184,18 @@ const DialogTableWidget = ({ config }) => {
9864
10184
  const currentRow = newRows[activeRowIndex] || {};
9865
10185
  const wasDeleted = currentRow.edit_action === 'DELETE';
9866
10186
  const editAction = wasDeleted ? 'UPDATE' : (currentRow.edit_action ?? 'UPDATE');
9867
- newRows[activeRowIndex] = { ...currentRow, ...payload, edit_action: editAction };
10187
+ const merged = { ...currentRow, ...cleaned, edit_action: editAction };
10188
+ columns.forEach((col) => {
10189
+ const key = col['column-key'];
10190
+ if (!(key in cleaned)) {
10191
+ delete merged[key];
10192
+ }
10193
+ });
10194
+ newRows[activeRowIndex] = merged;
9868
10195
  onChange(newRows);
9869
10196
  closeDialog();
9870
10197
  }
9871
- }, [collectMergedRowPayload, dialogMode, onChange, rows, closeDialog, activeRowIndex, columns, dialogFieldWidgetId, isReadonly, dispatch]);
10198
+ }, [collectMergedRowPayload, finalizeDialogRowPayload, dialogMode, onChange, rows, closeDialog, activeRowIndex, columns, dialogFieldWidgetId, isReadonly, dispatch]);
9872
10199
  const deleteRow = React.useCallback((rowIndex) => {
9873
10200
  const newRows = rows.filter((_, i) => i !== rowIndex);
9874
10201
  onChange(newRows);
@@ -9955,9 +10282,12 @@ const DialogTableWidget = ({ config }) => {
9955
10282
  lineHeight: 1,
9956
10283
  }, "aria-label": "Close", children: "\u00D7" })] }), jsxRuntimeExports.jsx("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4", style: { maxHeight: '70vh', overflow: 'auto' }, children: columns.map((col) => {
9957
10284
  const key = col['column-key'];
10285
+ if (!shouldShowWidget(col['widget-data-options'], dialogRowValues)) {
10286
+ return null;
10287
+ }
9958
10288
  const widgetType = col.widget || 'text';
9959
10289
  const cellWidgetId = dialogFieldWidgetId(key);
9960
- const initialValue = formData[key] ?? col['widget-data-default'] ?? '';
10290
+ const initialValue = formData[key] ?? col['widget-data-default'];
9961
10291
  const fieldConfig = {
9962
10292
  ...col,
9963
10293
  widget: widgetType,
@@ -9967,6 +10297,8 @@ const DialogTableWidget = ({ config }) => {
9967
10297
  'widget-readonly': isReadonly || col['widget-readonly'] === true,
9968
10298
  'widget-data-path': undefined,
9969
10299
  'widget-data-default': initialValue,
10300
+ 'widget-data-options': undefined,
10301
+ 'widget-required': shouldRequireWidget(col['widget-data-options'], dialogRowValues, col['widget-required']),
9970
10302
  };
9971
10303
  return (jsxRuntimeExports.jsx("div", { className: "min-w-0", children: jsxRuntimeExports.jsx(WidgetRenderer, { config: fieldConfig, schemaData: { [cellWidgetId]: initialValue }, onValueChange: (_widgetId, newValue) => updateField(key, newValue) }) }, `${dialogSessionId}-${key}`));
9972
10304
  }) }, `dialog-fields-${dialogSessionId}`), jsxRuntimeExports.jsxs("div", { className: "flex justify-end gap-3 mt-6", children: [jsxRuntimeExports.jsx("button", { type: "button", onClick: closeDialog, className: "px-4 py-2 text-sm font-medium", style: {
@@ -10164,7 +10496,7 @@ const ProfileWidget = ({ config }) => {
10164
10496
  const TextAreaWidget = ({ config }) => {
10165
10497
  // Check readonly early from original config
10166
10498
  const isReadonly = config['widget-readonly'] || false;
10167
- const { value, error, touched, isEnabled, onChange, onBlur, config: widgetConfig, } = useBaseWidget({ config });
10499
+ const { value, error, touched, isEnabled, isRequired, onChange, onBlur, config: widgetConfig, } = useBaseWidget({ config });
10168
10500
  const { translate, translateConfig } = useWidgetTranslation();
10169
10501
  const formatConfig = widgetConfig['widget-data-format'] || {};
10170
10502
  const validationConfig = widgetConfig['widget-data-validation'] || {};
@@ -10212,7 +10544,6 @@ const TextAreaWidget = ({ config }) => {
10212
10544
  ? translateConfig(widgetConfig['widget-label'])
10213
10545
  : '';
10214
10546
  // Check if required
10215
- const isRequired = widgetConfig['widget-required'] || false;
10216
10547
  // Error display
10217
10548
  const hasError = touched && error && error.length > 0;
10218
10549
  const errorMessage = hasError ? error[0] : '';
@@ -10230,7 +10561,7 @@ const TextAreaWidget = ({ config }) => {
10230
10561
  border: 'none',
10231
10562
  }, children: displayValue }) })] }));
10232
10563
  }
10233
- return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start", children: [jsxRuntimeExports.jsxs("label", { className: "text-base font-medium text-gray-700 md:min-w-[120px] sm:pr-4 sm:pt-1 mb-1 sm:mb-0", style: { fontFamily: 'Roboto, sans-serif' }, title: translateConfig(widgetConfig['widget-label']), children: [label, isRequired && (jsxRuntimeExports.jsx("span", { className: "text-red-500 ml-1", children: "*" }))] }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntimeExports.jsxs("div", { style: { position: 'relative' }, children: [jsxRuntimeExports.jsx("textarea", { id: widgetConfig['widget-id'], rows: rows, value: getStringValue(), onChange: handleChange, onBlur: onBlur, disabled: !isEnabled, placeholder: placeholder, className: `w-full px-3 py-2 border shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 ${hasError
10564
+ return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start", children: [jsxRuntimeExports.jsx(WidgetFieldLabel, { className: "text-base font-medium text-gray-700 md:min-w-[120px] sm:pr-4 sm:pt-1 mb-1 sm:mb-0", label: label, required: isRequired }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntimeExports.jsxs("div", { style: { position: 'relative' }, children: [jsxRuntimeExports.jsx("textarea", { id: widgetConfig['widget-id'], rows: rows, value: getStringValue(), onChange: handleChange, onBlur: onBlur, disabled: !isEnabled, placeholder: placeholder, className: `w-full px-3 py-2 border shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 ${hasError
10234
10565
  ? 'border-red-500 focus:ring-red-500 focus:border-red-500'
10235
10566
  : 'border-gray-300'} ${!isEnabled ? 'bg-gray-100 cursor-not-allowed' : 'bg-white'}`, style: {
10236
10567
  borderRadius: '10px',
@@ -11708,7 +12039,7 @@ const ResultsTable = ({ rows, selectedRowKey, onRowClick, onRowDoubleClick, }) =
11708
12039
  }) })] }) }));
11709
12040
  };
11710
12041
  const RegisterLookupWidget = ({ config }) => {
11711
- const { value, error, touched, isEnabled, onChange, onBlur, config: widgetConfig, } = useBaseWidget({ config });
12042
+ const { value, error, touched, isEnabled, isRequired, onChange, onBlur, config: widgetConfig, } = useBaseWidget({ config });
11712
12043
  const { translate, translateConfig } = useWidgetTranslation();
11713
12044
  const { dataSourceRequestHandler } = useWidgetContext();
11714
12045
  const dataSource = widgetConfig['widget-data-source'];
@@ -11871,7 +12202,7 @@ const RegisterLookupWidget = ({ config }) => {
11871
12202
  onChange(null);
11872
12203
  setAppliedRecord(null);
11873
12204
  setPendingRow(null);
11874
- }, className: "text-sm underline text-red-500 p-0 border-0 bg-transparent cursor-pointer focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-1 rounded", children: translate('common.remove', { defaultValue: 'Remove' }) })] })), !isReadonly && touched && error.length > 0 && jsxRuntimeExports.jsx("p", { className: "text-red-500 text-sm mt-1", children: error[0] })] })) : !isReadonly ? (jsxRuntimeExports.jsxs("div", { className: "w-full min-w-0", children: [jsxRuntimeExports.jsxs("button", { type: "button", disabled: !isEnabled, onClick: openLookup, title: actionLabel, className: `flex items-center gap-2 w-full sm:w-[180px] max-w-full px-3 h-[30px] text-sm border rounded-[10px] shadow-sm transition-colors ${hasError ? 'border-red-500 text-gray-700' : 'border-gray-300 text-gray-700'} ${!isEnabled ? 'bg-gray-100 text-gray-400 cursor-not-allowed' : 'bg-white cursor-pointer'}`, children: [jsxRuntimeExports.jsx("img", { src: img$g, alt: "", className: "w-4 h-4 opacity-50 flex-shrink-0" }), jsxRuntimeExports.jsx("span", { className: "truncate", children: actionLabel }), widgetConfig['widget-required'] && jsxRuntimeExports.jsx("span", { className: "text-red-500 ml-1", children: "*" })] }), touched && error.length > 0 && jsxRuntimeExports.jsx("p", { className: "text-red-500 text-sm mt-1", children: error[0] })] })) : null, !isReadonly && isOpen && (jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [jsxRuntimeExports.jsx("div", { className: "fixed inset-0 z-50", style: { backgroundColor: 'rgba(0,0,0,0.5)' }, onClick: () => { setIsOpen(false); onBlur(); } }), jsxRuntimeExports.jsxs("div", { className: "flex flex-col overflow-hidden", style: {
12205
+ }, className: "text-sm underline text-red-500 p-0 border-0 bg-transparent cursor-pointer focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-1 rounded", children: translate('common.remove', { defaultValue: 'Remove' }) })] })), !isReadonly && touched && error.length > 0 && jsxRuntimeExports.jsx("p", { className: "text-red-500 text-sm mt-1", children: error[0] })] })) : !isReadonly ? (jsxRuntimeExports.jsxs("div", { className: "w-full min-w-0", children: [jsxRuntimeExports.jsxs("button", { type: "button", disabled: !isEnabled, onClick: openLookup, title: actionLabel, className: `flex items-center gap-2 w-full sm:w-[180px] max-w-full px-3 h-[30px] text-sm border rounded-[10px] shadow-sm transition-colors ${hasError ? 'border-red-500 text-gray-700' : 'border-gray-300 text-gray-700'} ${!isEnabled ? 'bg-gray-100 text-gray-400 cursor-not-allowed' : 'bg-white cursor-pointer'}`, children: [jsxRuntimeExports.jsx("img", { src: img$g, alt: "", className: "w-4 h-4 opacity-50 flex-shrink-0" }), jsxRuntimeExports.jsx("span", { className: "min-w-0 truncate", children: actionLabel }), isRequired && jsxRuntimeExports.jsx("span", { className: "shrink-0 text-red-500", children: "*" })] }), touched && error.length > 0 && jsxRuntimeExports.jsx("p", { className: "text-red-500 text-sm mt-1", children: error[0] })] })) : null, !isReadonly && isOpen && (jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [jsxRuntimeExports.jsx("div", { className: "fixed inset-0 z-50", style: { backgroundColor: 'rgba(0,0,0,0.5)' }, onClick: () => { setIsOpen(false); onBlur(); } }), jsxRuntimeExports.jsxs("div", { className: "flex flex-col overflow-hidden", style: {
11875
12206
  position: 'fixed',
11876
12207
  top: modalPos.y,
11877
12208
  left: modalPos.x,
@@ -11900,6 +12231,288 @@ const RegisterLookupWidget = ({ config }) => {
11900
12231
  : translate('common.searchHint', { defaultValue: 'Type and press Enter or click search' }) })) : (jsxRuntimeExports.jsx(ResultsTable, { rows: searchResults, selectedRowKey: pendingRow?.internal_record_id ?? null, onRowClick: setPendingRow, onRowDoubleClick: applySelection })) }), jsxRuntimeExports.jsxs("div", { className: `flex-shrink-0 flex flex-wrap items-center gap-3 px-5 py-3 border-t border-gray-200 ${totalCount !== null ? 'justify-between' : 'justify-end'}`, children: [totalCount !== null && (jsxRuntimeExports.jsx(PaginationFooter, { embedded: true, currentPage: currentPage, totalPages: totalPages, totalCount: totalCount, pageSize: pageSize, onPageChange: (page) => runSearch(searchText, page), onPrev: () => currentPage > 1 && runSearch(searchText, currentPage - 1), onNext: () => currentPage < totalPages && runSearch(searchText, currentPage + 1), translate: translate })), jsxRuntimeExports.jsx("button", { type: "button", onClick: () => pendingRow && applySelection(pendingRow), disabled: !pendingRow, className: "px-4 h-9 text-sm font-medium rounded-[10px] text-white disabled:opacity-40 disabled:cursor-not-allowed flex-shrink-0", style: { backgroundColor: 'var(--owt-color-info, #2563eb)' }, children: selectRecordLabel })] })] })] }))] }));
11901
12232
  };
11902
12233
 
12234
+ const MultiSelectWidget = ({ config }) => {
12235
+ const { value, error, touched, isEnabled, isRequired, onChange, onBlur, dataSourceOptions, loading, config: widgetConfig, } = useBaseWidget({ config });
12236
+ const { translate, translateConfig } = useWidgetTranslation();
12237
+ const [isOpen, setIsOpen] = React.useState(false);
12238
+ const [isListPopupOpen, setIsListPopupOpen] = React.useState(false);
12239
+ const [searchQuery, setSearchQuery] = React.useState('');
12240
+ const [dropdownPosition, setDropdownPosition] = React.useState(null);
12241
+ const [listPopupPosition, setListPopupPosition] = React.useState(null);
12242
+ const [mounted, setMounted] = React.useState(false);
12243
+ const containerRef = React.useRef(null);
12244
+ const triggerRef = React.useRef(null);
12245
+ const dropdownRef = React.useRef(null);
12246
+ const listPopupRef = React.useRef(null);
12247
+ const moreButtonRef = React.useRef(null);
12248
+ const searchInputRef = React.useRef(null);
12249
+ const formatConfig = widgetConfig['widget-data-format'];
12250
+ const sortOptions = formatConfig?.sortOptions ?? false;
12251
+ React.useEffect(() => {
12252
+ setMounted(true);
12253
+ }, []);
12254
+ const updateDropdownPosition = React.useCallback(() => {
12255
+ const trigger = triggerRef.current;
12256
+ if (!trigger)
12257
+ return;
12258
+ const rect = trigger.getBoundingClientRect();
12259
+ const gap = 4;
12260
+ const spaceBelow = window.innerHeight - rect.bottom - gap;
12261
+ const spaceAbove = rect.top - gap;
12262
+ const openDown = spaceBelow >= 160 || spaceBelow >= spaceAbove;
12263
+ const availableSpace = openDown ? spaceBelow : spaceAbove;
12264
+ const maxHeight = Math.min(320, Math.max(160, availableSpace - 8));
12265
+ setDropdownPosition(openDown
12266
+ ? {
12267
+ top: rect.bottom + gap,
12268
+ left: rect.left,
12269
+ width: rect.width,
12270
+ maxHeight,
12271
+ placement: 'bottom',
12272
+ }
12273
+ : {
12274
+ bottom: window.innerHeight - rect.top + gap,
12275
+ left: rect.left,
12276
+ width: rect.width,
12277
+ maxHeight,
12278
+ placement: 'top',
12279
+ });
12280
+ }, []);
12281
+ const updateListPopupPosition = React.useCallback(() => {
12282
+ const anchor = moreButtonRef.current;
12283
+ if (!anchor)
12284
+ return;
12285
+ const rect = anchor.getBoundingClientRect();
12286
+ const gap = 4;
12287
+ const spaceBelow = window.innerHeight - rect.bottom - gap;
12288
+ const spaceAbove = rect.top - gap;
12289
+ const openDown = spaceBelow >= 120 || spaceBelow >= spaceAbove;
12290
+ const availableSpace = openDown ? spaceBelow : spaceAbove;
12291
+ const maxHeight = Math.min(280, Math.max(120, availableSpace - 8));
12292
+ setListPopupPosition(openDown
12293
+ ? {
12294
+ top: rect.bottom + gap,
12295
+ left: rect.left,
12296
+ width: Math.max(rect.width, 220),
12297
+ maxHeight,
12298
+ placement: 'bottom',
12299
+ }
12300
+ : {
12301
+ bottom: window.innerHeight - rect.top + gap,
12302
+ left: rect.left,
12303
+ width: Math.max(rect.width, 220),
12304
+ maxHeight,
12305
+ placement: 'top',
12306
+ });
12307
+ }, []);
12308
+ React.useEffect(() => {
12309
+ if (!isOpen) {
12310
+ setDropdownPosition(null);
12311
+ setSearchQuery('');
12312
+ return;
12313
+ }
12314
+ updateDropdownPosition();
12315
+ const handleResize = () => updateDropdownPosition();
12316
+ window.addEventListener('resize', handleResize);
12317
+ return () => {
12318
+ window.removeEventListener('resize', handleResize);
12319
+ };
12320
+ }, [isOpen, updateDropdownPosition]);
12321
+ React.useEffect(() => {
12322
+ if (!isListPopupOpen) {
12323
+ setListPopupPosition(null);
12324
+ return;
12325
+ }
12326
+ updateListPopupPosition();
12327
+ const handleResize = () => updateListPopupPosition();
12328
+ window.addEventListener('resize', handleResize);
12329
+ return () => {
12330
+ window.removeEventListener('resize', handleResize);
12331
+ };
12332
+ }, [isListPopupOpen, updateListPopupPosition]);
12333
+ React.useEffect(() => {
12334
+ if (!isOpen && !isListPopupOpen)
12335
+ return;
12336
+ const handleScroll = (event) => {
12337
+ const target = event.target;
12338
+ if (dropdownRef.current?.contains(target))
12339
+ return;
12340
+ if (listPopupRef.current?.contains(target))
12341
+ return;
12342
+ if (isOpen)
12343
+ setIsOpen(false);
12344
+ if (isListPopupOpen)
12345
+ setIsListPopupOpen(false);
12346
+ };
12347
+ window.addEventListener('scroll', handleScroll, true);
12348
+ return () => window.removeEventListener('scroll', handleScroll, true);
12349
+ }, [isOpen, isListPopupOpen]);
12350
+ React.useEffect(() => {
12351
+ if (!isOpen && !isListPopupOpen)
12352
+ return;
12353
+ const handleClickOutside = (event) => {
12354
+ const target = event.target;
12355
+ if (isOpen) {
12356
+ if (containerRef.current?.contains(target))
12357
+ return;
12358
+ if (dropdownRef.current?.contains(target))
12359
+ return;
12360
+ setIsOpen(false);
12361
+ }
12362
+ if (isListPopupOpen) {
12363
+ if (listPopupRef.current?.contains(target))
12364
+ return;
12365
+ if (moreButtonRef.current?.contains(target))
12366
+ return;
12367
+ setIsListPopupOpen(false);
12368
+ }
12369
+ };
12370
+ document.addEventListener('mousedown', handleClickOutside);
12371
+ return () => document.removeEventListener('mousedown', handleClickOutside);
12372
+ }, [isOpen, isListPopupOpen]);
12373
+ React.useEffect(() => {
12374
+ if (isOpen && searchInputRef.current) {
12375
+ searchInputRef.current.focus();
12376
+ }
12377
+ }, [isOpen]);
12378
+ const processedOptions = React.useMemo(() => {
12379
+ let options = dataSourceOptions.map((opt) => {
12380
+ const rawLabel = String(opt.label ?? opt.value ?? '');
12381
+ return {
12382
+ value: opt.value,
12383
+ label: translateConfig(rawLabel),
12384
+ rawLabel,
12385
+ };
12386
+ });
12387
+ if (sortOptions) {
12388
+ options.sort((a, b) => a.label.localeCompare(b.label));
12389
+ }
12390
+ return options;
12391
+ }, [dataSourceOptions, sortOptions, translateConfig]);
12392
+ const filteredOptions = React.useMemo(() => {
12393
+ if (!searchQuery.trim())
12394
+ return processedOptions;
12395
+ const q = searchQuery.trim().toLowerCase();
12396
+ return processedOptions.filter((opt) => opt.label.toLowerCase().includes(q) ||
12397
+ opt.rawLabel.toLowerCase().includes(q));
12398
+ }, [processedOptions, searchQuery]);
12399
+ const selectedValues = React.useMemo(() => {
12400
+ if (value === null || value === undefined)
12401
+ return [];
12402
+ if (Array.isArray(value))
12403
+ return value;
12404
+ return [value];
12405
+ }, [value]);
12406
+ const allFilteredSelected = React.useMemo(() => {
12407
+ if (filteredOptions.length === 0)
12408
+ return false;
12409
+ return filteredOptions.every((opt) => selectedValues.includes(opt.value));
12410
+ }, [filteredOptions, selectedValues]);
12411
+ const handleToggle = React.useCallback((optionValue, checked) => {
12412
+ if (checked) {
12413
+ onChange([...selectedValues, optionValue]);
12414
+ }
12415
+ else {
12416
+ onChange(selectedValues.filter((v) => v !== optionValue));
12417
+ }
12418
+ }, [selectedValues, onChange]);
12419
+ const handleSelectAll = React.useCallback(() => {
12420
+ const filteredVals = filteredOptions.map((o) => o.value);
12421
+ const merged = Array.from(new Set([...selectedValues, ...filteredVals]));
12422
+ onChange(merged);
12423
+ }, [filteredOptions, selectedValues, onChange]);
12424
+ const handleClearAll = React.useCallback(() => {
12425
+ onChange([]);
12426
+ }, [onChange]);
12427
+ const selectedLabels = React.useMemo(() => {
12428
+ return selectedValues.map((val) => {
12429
+ const opt = processedOptions.find((o) => o.value === val);
12430
+ return opt ? opt.label : translateConfig(String(val));
12431
+ });
12432
+ }, [selectedValues, processedOptions, translateConfig]);
12433
+ const fullSelectionText = selectedLabels.join(', ');
12434
+ const visibleLabels = selectedLabels.slice(0, 5);
12435
+ const overflowCount = Math.max(0, selectedLabels.length - 10);
12436
+ const disabled = !isEnabled || loading || widgetConfig['widget-readonly'];
12437
+ const renderSelectedLabels = (options) => {
12438
+ if (selectedLabels.length === 0)
12439
+ return null;
12440
+ const readonly = options?.readonly ?? false;
12441
+ return (jsxRuntimeExports.jsxs("div", { className: `flex flex-wrap gap-1 ${readonly ? '' : 'mt-1.5'}`, children: [visibleLabels.map((label, index) => (jsxRuntimeExports.jsxs("span", { className: "inline-flex max-w-full items-center gap-1 rounded-md bg-blue-50 px-2 py-0.5 text-xs text-blue-800", title: label, children: [jsxRuntimeExports.jsx("span", { className: "truncate", children: label }), !readonly && !disabled && (jsxRuntimeExports.jsx("button", { type: "button", onClick: () => handleToggle(selectedValues[index], false), className: "shrink-0 text-blue-600 hover:text-blue-900 focus:outline-none", "aria-label": translate('common.removeItem', {
12442
+ label,
12443
+ defaultValue: `Remove ${label}`,
12444
+ }), children: "\u00D7" }))] }, `${selectedValues[index]}-${label}`))), overflowCount > 0 && (jsxRuntimeExports.jsx("button", { ref: moreButtonRef, type: "button", onClick: () => setIsListPopupOpen((prev) => !prev), className: "inline-flex items-center rounded-md bg-gray-100 px-2 py-0.5 text-xs font-medium text-gray-700 hover:bg-gray-200 focus:outline-none focus:ring-1 focus:ring-blue-500", children: translate('common.moreSelected', {
12445
+ count: overflowCount,
12446
+ defaultValue: `+${overflowCount} more`,
12447
+ }) }))] }));
12448
+ };
12449
+ const listPopupPanel = isListPopupOpen && listPopupPosition && mounted ? (jsxRuntimeExports.jsxs("div", { ref: listPopupRef, className: "fixed z-[201] bg-white border border-gray-300 shadow-lg", style: {
12450
+ ...(listPopupPosition.placement === 'bottom'
12451
+ ? { top: listPopupPosition.top }
12452
+ : { bottom: listPopupPosition.bottom }),
12453
+ left: listPopupPosition.left,
12454
+ width: listPopupPosition.width,
12455
+ maxWidth: '320px',
12456
+ maxHeight: listPopupPosition.maxHeight,
12457
+ borderRadius: '10px',
12458
+ display: 'flex',
12459
+ flexDirection: 'column',
12460
+ }, children: [jsxRuntimeExports.jsx("div", { className: "px-3 py-2 text-xs font-semibold text-gray-500 shrink-0", style: { borderBottom: '1px solid #e5e7eb' }, children: translate('common.allSelected', {
12461
+ count: selectedLabels.length,
12462
+ defaultValue: `All selected (${selectedLabels.length})`,
12463
+ }) }), jsxRuntimeExports.jsx("div", { className: "overflow-y-auto flex-1 min-h-0 py-1 overscroll-contain", children: selectedLabels.map((label, index) => (jsxRuntimeExports.jsx("div", { className: "px-3 py-1.5 text-sm text-gray-700", title: label, children: label }, `${selectedValues[index]}-${label}`))) })] })) : null;
12464
+ if (widgetConfig['widget-readonly']) {
12465
+ const fieldLabel = widgetConfig['widget-label'];
12466
+ return (jsxRuntimeExports.jsxs("div", { className: "mb-[10px] MultiSelectDisplayWidget flex flex-col sm:flex-row sm:items-start", children: [fieldLabel && (jsxRuntimeExports.jsx(WidgetFieldLabel, { className: "text-base text-gray-600 font-medium md:min-w-[120px] sm:pr-4 mb-1 sm:mb-0", label: fieldLabel })), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [selectedLabels.length === 0 ? (jsxRuntimeExports.jsx("div", { className: "text-base text-gray-900 font-medium", children: "-" })) : (renderSelectedLabels({ readonly: true })), mounted && listPopupPanel && typeof document !== 'undefined'
12467
+ ? reactDom.createPortal(listPopupPanel, document.body)
12468
+ : null] })] }));
12469
+ }
12470
+ const optionsMaxHeight = dropdownPosition
12471
+ ? Math.min(280, dropdownPosition.maxHeight - 100)
12472
+ : 280;
12473
+ const dropdownPanel = isOpen && dropdownPosition && mounted ? (jsxRuntimeExports.jsxs("div", { ref: dropdownRef, className: "fixed z-[200] bg-white border border-gray-300 shadow-lg", style: {
12474
+ ...(dropdownPosition.placement === 'bottom'
12475
+ ? { top: dropdownPosition.top }
12476
+ : { bottom: dropdownPosition.bottom }),
12477
+ left: dropdownPosition.left,
12478
+ width: dropdownPosition.width,
12479
+ maxWidth: '280px',
12480
+ maxHeight: dropdownPosition.maxHeight,
12481
+ borderRadius: '10px',
12482
+ display: 'flex',
12483
+ flexDirection: 'column',
12484
+ }, children: [jsxRuntimeExports.jsx("div", { className: "px-3 pt-2 pb-1 shrink-0", style: { borderBottom: '1px solid #e5e7eb' }, children: jsxRuntimeExports.jsx("input", { ref: searchInputRef, type: "text", placeholder: translate('common.searchPlaceholder', { defaultValue: 'Search...' }), value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), className: "w-full h-[28px] px-2 text-sm border border-gray-300 bg-gray-50 focus:outline-none focus:ring-1 focus:ring-blue-500 focus:border-blue-500", style: { borderRadius: '6px' } }) }), jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between px-3 py-1 shrink-0", style: { borderBottom: '1px solid #e5e7eb' }, children: [jsxRuntimeExports.jsx("button", { type: "button", onClick: allFilteredSelected ? () => {
12485
+ const filteredVals = new Set(filteredOptions.map((o) => o.value));
12486
+ onChange(selectedValues.filter((v) => !filteredVals.has(v)));
12487
+ } : handleSelectAll, className: "text-xs font-medium text-blue-600 hover:text-blue-800 focus:outline-none", children: allFilteredSelected
12488
+ ? translate('common.deselectAll', { defaultValue: 'Deselect All' })
12489
+ : translate('common.selectAll', { defaultValue: 'Select All' }) }), selectedValues.length > 0 && (jsxRuntimeExports.jsx("button", { type: "button", onClick: handleClearAll, className: "text-xs font-medium text-red-500 hover:text-red-700 focus:outline-none", children: translate('common.clearAll', { defaultValue: 'Clear All' }) }))] }), jsxRuntimeExports.jsx("div", { className: "overflow-y-auto flex-1 min-h-0 py-1 overscroll-contain", style: { maxHeight: `${Math.max(80, optionsMaxHeight)}px` }, children: loading ? (jsxRuntimeExports.jsx("p", { className: "text-sm text-gray-500 px-3 py-2", children: translate('common.loading') })) : filteredOptions.length === 0 ? (jsxRuntimeExports.jsx("p", { className: "text-sm text-gray-400 px-3 py-2", children: translate('common.noOptionsFound', { defaultValue: 'No options found' }) })) : (filteredOptions.map((option) => {
12490
+ const isChecked = selectedValues.includes(option.value);
12491
+ return (jsxRuntimeExports.jsxs("label", { className: `flex items-center gap-2 px-3 py-1 cursor-pointer hover:bg-blue-50 ${isChecked ? 'bg-blue-50/60' : ''}`, children: [jsxRuntimeExports.jsx("input", { type: "checkbox", checked: isChecked, onChange: (e) => handleToggle(option.value, e.target.checked), className: "h-4 w-4 shrink-0 text-blue-600 focus:ring-blue-500 border-gray-300 rounded" }), jsxRuntimeExports.jsx("span", { className: "text-sm text-gray-700 leading-normal select-none", children: option.label })] }, option.value));
12492
+ })) })] })) : null;
12493
+ return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start", children: [jsxRuntimeExports.jsx(WidgetFieldLabel, { className: "text-base font-medium text-gray-700 md:min-w-[120px] sm:pr-4 sm:pt-1 mb-1 sm:mb-0", label: widgetConfig['widget-label'] ?? '', required: isRequired }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", ref: containerRef, children: [jsxRuntimeExports.jsxs("button", { ref: triggerRef, type: "button", onClick: () => {
12494
+ if (!disabled)
12495
+ setIsOpen((prev) => !prev);
12496
+ }, onBlur: () => {
12497
+ if (!isOpen)
12498
+ onBlur();
12499
+ }, disabled: disabled, className: `w-full sm:w-[280px] max-w-full h-[30px] px-3 border shadow-sm text-left flex items-center justify-between gap-2 ${(touched && error.length > 0) ||
12500
+ (widgetConfig['widget-required'] && selectedValues.length === 0)
12501
+ ? 'border-red-500 focus:ring-red-500 focus:border-red-500'
12502
+ : 'border-gray-300'} ${disabled ? 'bg-gray-100 cursor-not-allowed' : 'bg-white cursor-pointer'} focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500`, style: { borderRadius: '10px' }, title: selectedValues.length > 0
12503
+ ? fullSelectionText
12504
+ : translateConfig(widgetConfig['widget-data-tooltip']), children: [jsxRuntimeExports.jsx("span", { className: `truncate text-sm ${selectedValues.length === 0 ? 'text-gray-400' : 'text-gray-900'}`, children: selectedLabels.length === 0
12505
+ ? translate('common.select', { defaultValue: 'Select...' })
12506
+ : translate('common.selectedCount', {
12507
+ count: selectedLabels.length,
12508
+ defaultValue: `${selectedLabels.length} selected`,
12509
+ }) }), jsxRuntimeExports.jsx("svg", { className: `w-4 h-4 flex-shrink-0 text-gray-500 transition-transform ${isOpen ? 'rotate-180' : ''}`, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: jsxRuntimeExports.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 9l-7 7-7-7" }) })] }), mounted && dropdownPanel && typeof document !== 'undefined'
12510
+ ? reactDom.createPortal(dropdownPanel, document.body)
12511
+ : null, selectedLabels.length > 0 && renderSelectedLabels(), mounted && listPopupPanel && typeof document !== 'undefined'
12512
+ ? reactDom.createPortal(listPopupPanel, document.body)
12513
+ : null, touched && error.length > 0 && (jsxRuntimeExports.jsx("p", { className: "text-red-500 text-sm mt-1", children: error[0] })), loading && (jsxRuntimeExports.jsx("p", { className: "text-sm text-gray-500 mt-1", children: translate('common.loadingOptions') }))] })] }) }));
12514
+ };
12515
+
11903
12516
  /**
11904
12517
  * Register all default/generic widgets
11905
12518
  * This is called automatically when the package is imported
@@ -11949,6 +12562,8 @@ const registerDefaultWidgets = () => {
11949
12562
  widgetRegistry.register({ widget: 'id-authentication', component: IdAuthenticationWidget });
11950
12563
  // Register lookup widget — searchable popup to select a record from any register
11951
12564
  widgetRegistry.register({ widget: 'register-lookup', component: RegisterLookupWidget });
12565
+ // Multi-select widget — searchable dropdown with checkbox-style options, select all, and clear all
12566
+ widgetRegistry.register({ widget: 'multi-select', component: MultiSelectWidget });
11952
12567
  };
11953
12568
  // Auto-register on import
11954
12569
  registerDefaultWidgets();
@@ -12012,6 +12627,14 @@ var enTranslations = {
12012
12627
  "common.sectionModified": "Modified and not saved",
12013
12628
  "common.supportedDocuments": "Supported Documents",
12014
12629
  "common.searchPlaceholder": "Search...",
12630
+ "common.selectAll": "Select All",
12631
+ "common.deselectAll": "Deselect All",
12632
+ "common.clearAll": "Clear All",
12633
+ "common.noOptionsFound": "No options found",
12634
+ "common.allSelected": "All selected ({{count}})",
12635
+ "common.moreSelected": "+{{count}} more",
12636
+ "common.selectedCount": "{{count}} selected",
12637
+ "common.removeItem": "Remove {{label}}",
12015
12638
  "common.selectAction": "Select {{label}}",
12016
12639
  "common.selectTitle": "Select {{label}}",
12017
12640
  "common.change": "Change",
@@ -12264,13 +12887,10 @@ const translateWidgetConfig = (widgetConfig, translate) => {
12264
12887
  ...dataSource,
12265
12888
  options: dataSource.options.map((option) => {
12266
12889
  if (option.label && typeof option.label === 'string') {
12267
- const optionLabel = option.label;
12268
- if (isTranslationKey(optionLabel)) {
12269
- return {
12270
- ...option,
12271
- label: translate(optionLabel, { defaultValue: optionLabel }),
12272
- };
12273
- }
12890
+ return {
12891
+ ...option,
12892
+ label: translate(option.label, { defaultValue: option.label }),
12893
+ };
12274
12894
  }
12275
12895
  return option;
12276
12896
  }),
@@ -12338,6 +12958,7 @@ exports.HeaderSectionWidget = HeaderSectionWidget;
12338
12958
  exports.IdAuthenticationWidget = IdAuthenticationWidget;
12339
12959
  exports.IterableAccordionWidget = IterableAccordionWidget;
12340
12960
  exports.JSONEditorPanel = JSONEditorPanel;
12961
+ exports.MultiSelectWidget = MultiSelectWidget;
12341
12962
  exports.NumberInputWidget = NumberInputWidget;
12342
12963
  exports.PanelRenderer = PanelRenderer;
12343
12964
  exports.PhoneInputWidget = PhoneInputWidget;
@@ -12367,6 +12988,7 @@ exports.createWidgetStore = createWidgetStore;
12367
12988
  exports.createZodSchema = createZodSchema;
12368
12989
  exports.defaultTheme = defaultTheme;
12369
12990
  exports.evaluateCondition = evaluateCondition;
12991
+ exports.evaluateWidgetConditions = evaluateWidgetConditions;
12370
12992
  exports.filterByCharacterType = filterByCharacterType;
12371
12993
  exports.formatCurrency = formatCurrency;
12372
12994
  exports.formatDate = formatDate;
@@ -12376,14 +12998,18 @@ exports.formatValue = formatValue;
12376
12998
  exports.geoHierarchyBuilder = geoHierarchyBuilder;
12377
12999
  exports.getApiDataSource = getApiDataSource;
12378
13000
  exports.getFormattedNumberLength = getFormattedNumberLength;
13001
+ exports.getGeoDescendantWidgetIds = getGeoDescendantWidgetIds;
13002
+ exports.getGeoGroupId = getGeoGroupId;
12379
13003
  exports.getSchemaDataSource = getSchemaDataSource;
12380
13004
  exports.getStaticDataSource = getStaticDataSource;
12381
13005
  exports.getValueByPath = getValueByPath;
12382
13006
  exports.getWidgetValue = getWidgetValue;
13007
+ exports.hasVisibilityRules = hasVisibilityRules;
12383
13008
  exports.initI18n = initI18n;
12384
13009
  exports.isAllowedKey = isAllowedKey;
12385
13010
  exports.isUpstreamGeoAncestor = isUpstreamGeoAncestor;
12386
13011
  exports.normalizeNumericDefault = normalizeNumericDefault;
13012
+ exports.normalizeOptionRules = normalizeOptionRules;
12387
13013
  exports.parseDataPath = parseDataPath;
12388
13014
  exports.parseNumber = parseNumber;
12389
13015
  exports.registerDefaultWidgets = registerDefaultWidgets;
@@ -12392,6 +13018,7 @@ exports.removeMask = removeMask;
12392
13018
  exports.resetAll = resetAll;
12393
13019
  exports.resetAndSeedGeoHierarchyFromValues = resetAndSeedGeoHierarchyFromValues;
12394
13020
  exports.resetWidget = resetWidget;
13021
+ exports.resolveGeoWidgetLevelLabel = resolveGeoWidgetLevelLabel;
12395
13022
  exports.resolveGeoWidgetLevelValue = resolveGeoWidgetLevelValue;
12396
13023
  exports.resolveTheme = resolveTheme;
12397
13024
  exports.resolveWidgetIdValue = resolveWidgetIdValue;
@@ -12405,6 +13032,7 @@ exports.setValueByPath = setValueByPath;
12405
13032
  exports.setValues = setValues;
12406
13033
  exports.setWidgetValue = setWidgetValue;
12407
13034
  exports.shouldEnableWidget = shouldEnableWidget;
13035
+ exports.shouldRequireWidget = shouldRequireWidget;
12408
13036
  exports.shouldShowWidget = shouldShowWidget;
12409
13037
  exports.transformDataSourceOptions = transformDataSourceOptions;
12410
13038
  exports.translatePanelConfig = translatePanelConfig;