@openg2p/registry-widgets 1.1.3 → 1.1.4-dev.1

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 (36) hide show
  1. package/dist/components/SectionRenderer/utils/executeSectionSave.d.ts.map +1 -1
  2. package/dist/components/SectionRenderer.d.ts +61 -0
  3. package/dist/components/SectionRenderer.d.ts.map +1 -0
  4. package/dist/hooks/useGeoHierarchy.d.ts +1 -0
  5. package/dist/hooks/useGeoHierarchy.d.ts.map +1 -1
  6. package/dist/hooks/useGeoWidgetCascade.d.ts +12 -0
  7. package/dist/hooks/useGeoWidgetCascade.d.ts.map +1 -0
  8. package/dist/hooks/useWidgetTranslation.d.ts +16 -0
  9. package/dist/hooks/useWidgetTranslation.d.ts.map +1 -0
  10. package/dist/i18n/config.d.ts +21 -0
  11. package/dist/i18n/config.d.ts.map +1 -0
  12. package/dist/index.d.ts +9 -1
  13. package/dist/index.esm.js +141 -25
  14. package/dist/index.esm.js.map +1 -1
  15. package/dist/index.js +141 -25
  16. package/dist/index.js.map +1 -1
  17. package/dist/types/index.d.ts +9 -1
  18. package/dist/types/index.d.ts.map +1 -1
  19. package/dist/utils/buildSectionChanges.d.ts +11 -0
  20. package/dist/utils/buildSectionChanges.d.ts.map +1 -0
  21. package/dist/utils/geoHierarchy.d.ts +13 -0
  22. package/dist/utils/geoHierarchy.d.ts.map +1 -1
  23. package/dist/utils/schemaNamespace.d.ts +12 -0
  24. package/dist/utils/schemaNamespace.d.ts.map +1 -0
  25. package/dist/widgets/ArrayWidget.d.ts +29 -0
  26. package/dist/widgets/ArrayWidget.d.ts.map +1 -0
  27. package/dist/widgets/CurrencyInputWidget.d.ts +24 -0
  28. package/dist/widgets/CurrencyInputWidget.d.ts.map +1 -0
  29. package/dist/widgets/DocsWidget.d.ts.map +1 -1
  30. package/dist/widgets/GeoHierarchyWidget.d.ts.map +1 -1
  31. package/dist/widgets/HeaderSectionWidget.d.ts.map +1 -1
  32. package/dist/widgets/IterableAccordionWidget.d.ts +30 -0
  33. package/dist/widgets/IterableAccordionWidget.d.ts.map +1 -0
  34. package/dist/widgets/SimpleTableWidget.d.ts +32 -0
  35. package/dist/widgets/SimpleTableWidget.d.ts.map +1 -0
  36. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"executeSectionSave.d.ts","sourceRoot":"","sources":["../../../../src/components/SectionRenderer/utils/executeSectionSave.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAI/C,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAG1C,MAAM,WAAW,wBAAwB;IACvC,KAAK,EAAE;QAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;KAAE,CAAC;IACnC,QAAQ,EAAE,QAAQ,CAAC;IACnB,OAAO,EAAE,aAAa,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,iBAAiB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC5C,sBAAsB,EAAE,OAAO,CAAC;IAChC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;CACnE;AAED,MAAM,WAAW,wBAAwB;IACvC,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,OAAO,CAAC;IACf,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC5C;AAiGD,eAAO,MAAM,kBAAkB,GAAU,qIAUtC,wBAAwB,KAAG,OAAO,CAAC,wBAAwB,CAwD7D,CAAC"}
1
+ {"version":3,"file":"executeSectionSave.d.ts","sourceRoot":"","sources":["../../../../src/components/SectionRenderer/utils/executeSectionSave.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAO/C,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAG1C,MAAM,WAAW,wBAAwB;IACvC,KAAK,EAAE;QAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;KAAE,CAAC;IACnC,QAAQ,EAAE,QAAQ,CAAC;IACnB,OAAO,EAAE,aAAa,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,iBAAiB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC5C,sBAAsB,EAAE,OAAO,CAAC;IAChC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;CACnE;AAED,MAAM,WAAW,wBAAwB;IACvC,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,OAAO,CAAC;IACf,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC5C;AAwGD,eAAO,MAAM,kBAAkB,GAAU,qIAUtC,wBAAwB,KAAG,OAAO,CAAC,wBAAwB,CAwD7D,CAAC"}
@@ -0,0 +1,61 @@
1
+ import { SectionConfig } from '../types';
2
+ import { UseBaseWidgetOptions } from '../hooks/useBaseWidget';
3
+ import { SectionMode } from './SectionsContainer';
4
+ export interface SectionChanges {
5
+ section_id?: string;
6
+ section_register_id?: string;
7
+ records: unknown[];
8
+ files?: unknown[];
9
+ image?: File | null;
10
+ }
11
+ export interface SectionRendererProps {
12
+ section: SectionConfig;
13
+ dataSourceRequestHandler?: UseBaseWidgetOptions['dataSourceRequestHandler'];
14
+ schemaData?: UseBaseWidgetOptions['schemaData'];
15
+ onValueChange?: UseBaseWidgetOptions['onValueChange'];
16
+ gridColumnSpan?: number;
17
+ onSectionSave?: (changes: SectionChanges) => Promise<void> | void;
18
+ hideEditButton?: boolean;
19
+ mode?: SectionMode;
20
+ namespace?: string;
21
+ changeRequestType?: 'new' | 'old';
22
+ showChangeRequestLabel?: boolean;
23
+ dbSectionId?: string;
24
+ sectionRegisterId?: string;
25
+ /** Called when the section's dirty (has unsaved changes) status changes. Only fires while in edit mode. */
26
+ onSectionDirtyChange?: (sectionId: string, isDirty: boolean) => void;
27
+ /** IntakeForm mode: 0-based index of this section within the form */
28
+ sectionIndex?: number;
29
+ /** IntakeForm mode: total number of sections in the form */
30
+ sectionCount?: number;
31
+ /** IntakeForm mode: index of the currently expanded section (null = all collapsed) */
32
+ expandedSectionIndex?: number | null;
33
+ /** IntakeForm mode: called when user requests to expand a section (e.g. clicks accordion header) */
34
+ onExpandSection?: (index: number) => void;
35
+ /** IntakeForm mode: called when section save completes successfully (collapse current, expand next) */
36
+ onSectionSaveSuccess?: (index: number) => void;
37
+ /** IntakeForm mode: called when user clicks Previous (collapse current, expand previous) */
38
+ onPreviousSection?: (index: number) => void;
39
+ /** IntakeForm mode: when true or undefined, sections are editable; when false, sections are readonly */
40
+ isDraft?: boolean;
41
+ /**
42
+ * IntakeForm mode: when true the accordion header is clickable so the user can open/close this
43
+ * section freely. False (default) locks the header — navigation is only via Next/Previous.
44
+ * SectionsContainer sets this to true for every visited section plus the one immediately after.
45
+ */
46
+ isAccessible?: boolean;
47
+ /** RegistryView: called when this section enters or exits edit mode. Used by SectionsContainer to enforce single-edit. */
48
+ onEditModeChange?: (sectionId: string, editing: boolean) => void;
49
+ /** RegistryView: when true, forces this section out of edit mode (another section took over). */
50
+ forceExitEdit?: boolean;
51
+ }
52
+ /**
53
+ * Renders a section with its panels
54
+ *
55
+ * Layout behavior:
56
+ * - Section width is based on max 3 panels (or more on high resolution)
57
+ * - Panels wrap when they exceed available width
58
+ * - Sections can sit side-by-side if there's space
59
+ */
60
+ export declare const SectionRenderer: ({ section, dataSourceRequestHandler: propDataSourceRequestHandler, schemaData, onValueChange, gridColumnSpan, onSectionSave, hideEditButton, mode, namespace, changeRequestType, showChangeRequestLabel, dbSectionId, sectionRegisterId, onSectionDirtyChange, sectionIndex, sectionCount, expandedSectionIndex, onExpandSection, onSectionSaveSuccess, onPreviousSection, isDraft, isAccessible, onEditModeChange, forceExitEdit, }: SectionRendererProps) => import("react/jsx-runtime").JSX.Element;
61
+ //# sourceMappingURL=SectionRenderer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SectionRenderer.d.ts","sourceRoot":"","sources":["../../src/components/SectionRenderer.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAE,aAAa,EAAyC,MAAM,UAAU,CAAC;AAChF,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAQ9D,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAuDlD,MAAM,WAAW,cAAc;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,OAAO,EAAE,OAAO,EAAE,CAAC;IACnB,KAAK,CAAC,EAAE,OAAO,EAAE,CAAC;IAClB,KAAK,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;CACrB;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,aAAa,CAAC;IACvB,wBAAwB,CAAC,EAAE,oBAAoB,CAAC,0BAA0B,CAAC,CAAC;IAC5E,UAAU,CAAC,EAAE,oBAAoB,CAAC,YAAY,CAAC,CAAC;IAChD,aAAa,CAAC,EAAE,oBAAoB,CAAC,eAAe,CAAC,CAAC;IACtD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAClE,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iBAAiB,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC;IAClC,sBAAsB,CAAC,EAAE,OAAO,CAAC;IAGjC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAE3B,2GAA2G;IAC3G,oBAAoB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IAErE,qEAAqE;IACrE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,4DAA4D;IAC5D,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,sFAAsF;IACtF,oBAAoB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,oGAAoG;IACpG,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,uGAAuG;IACvG,oBAAoB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/C,4FAA4F;IAC5F,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,wGAAwG;IACxG,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;OAIG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IAEvB,0HAA0H;IAC1H,gBAAgB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IACjE,iGAAiG;IACjG,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAGD;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe,GAAI,saAyB7B,oBAAoB,4CAqlDtB,CAAC"}
@@ -9,6 +9,7 @@ export declare function useGeoHierarchy({ config }: UseGeoHierarchyOptions): {
9
9
  options: Record<string, GeoSelectOption[]>;
10
10
  resolvedLabels: Record<string, string>;
11
11
  columnCounts: number[];
12
+ columnSpan: number;
12
13
  visibleColumns: {
13
14
  index: number;
14
15
  levels: GeoLevel[];
@@ -1 +1 @@
1
- {"version":3,"file":"useGeoHierarchy.d.ts","sourceRoot":"","sources":["../../src/hooks/useGeoHierarchy.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAA4B,MAAM,UAAU,CAAC;AAKtE,OAAO,EACL,QAAQ,EAER,eAAe,EAMf,gBAAgB,EASjB,MAAM,uBAAuB,CAAC;AAE/B,UAAU,sBAAsB;IAC9B,MAAM,EAAE,gBAAgB,CAAC;CAC1B;AA+BD,wBAAgB,eAAe,CAAC,EAAE,MAAM,EAAE,EAAE,sBAAsB;;;;;;;;;;;;;;oCAmU3C,MAAM,aAAa,MAAM,GAAG,SAAS;iCA2E3B,MAAM;;;;;;;;;;;;;;;;;;;;;;;EAGtC"}
1
+ {"version":3,"file":"useGeoHierarchy.d.ts","sourceRoot":"","sources":["../../src/hooks/useGeoHierarchy.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAA4B,MAAM,UAAU,CAAC;AAKtE,OAAO,EACL,QAAQ,EAER,eAAe,EAQf,gBAAgB,EASjB,MAAM,uBAAuB,CAAC;AAE/B,UAAU,sBAAsB;IAC9B,MAAM,EAAE,gBAAgB,CAAC;CAC1B;AA+BD,wBAAgB,eAAe,CAAC,EAAE,MAAM,EAAE,EAAE,sBAAsB;;;;;;;;;;;;;;;oCA0V3C,MAAM,aAAa,MAAM,GAAG,SAAS;iCAuF3B,MAAM;;;;;;;;;;;;;;;;;;;;;;;EAGtC"}
@@ -0,0 +1,12 @@
1
+ import { BaseWidgetConfig, DataSourceRequestHandler } from '../types';
2
+ export interface UseGeoWidgetCascadeOptions {
3
+ config: BaseWidgetConfig;
4
+ dataSourceRequestHandler?: DataSourceRequestHandler;
5
+ values: Record<string, any>;
6
+ }
7
+ /**
8
+ * Hook for geo widget cascade functionality
9
+ * Handles geo hierarchy building and cascade behavior
10
+ */
11
+ export declare const useGeoWidgetCascade: (options: UseGeoWidgetCascadeOptions) => void;
12
+ //# sourceMappingURL=useGeoWidgetCascade.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useGeoWidgetCascade.d.ts","sourceRoot":"","sources":["../../src/hooks/useGeoWidgetCascade.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,gBAAgB,EAAE,wBAAwB,EAAE,MAAM,UAAU,CAAC;AAmBtE,MAAM,WAAW,0BAA0B;IACzC,MAAM,EAAE,gBAAgB,CAAC;IACzB,wBAAwB,CAAC,EAAE,wBAAwB,CAAC;IACpD,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC7B;AAGD;;;GAGG;AACH,eAAO,MAAM,mBAAmB,GAAI,SAAS,0BAA0B,SAwMtE,CAAC"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Custom hook for widget translations
3
+ * Provides translation function with widget-specific namespace and fallback support
4
+ */
5
+ export declare const useWidgetTranslation: () => {
6
+ t: (keyOrString: string | undefined | null, options?: {
7
+ defaultValue?: string;
8
+ [key: string]: any;
9
+ }) => string;
10
+ translate: (keyOrString: string | undefined | null, options?: {
11
+ defaultValue?: string;
12
+ [key: string]: any;
13
+ }) => string;
14
+ translateConfig: (value: string | undefined | null, fallback?: string) => string;
15
+ };
16
+ //# sourceMappingURL=useWidgetTranslation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useWidgetTranslation.d.ts","sourceRoot":"","sources":["../../src/hooks/useWidgetTranslation.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,eAAO,MAAM,oBAAoB;qBAoBhB,MAAM,GAAG,SAAS,GAAG,IAAI,YAC5B;QACR,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;KACpB,KACA,MAAM;6BALM,MAAM,GAAG,SAAS,GAAG,IAAI,YAC5B;QACR,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;KACpB,KACA,MAAM;6BA0BA,MAAM,GAAG,SAAS,GAAG,IAAI,aACrB,MAAM,KAChB,MAAM;CAqDV,CAAC"}
@@ -0,0 +1,21 @@
1
+ import i18n from 'i18next';
2
+ /**
3
+ * Initialize i18n for the widget library
4
+ * Can be customized by passing custom resources or configuration
5
+ *
6
+ * If no resources are provided, it will attempt to automatically load translations
7
+ * from the consuming project's locale directory.
8
+ *
9
+ * If options are provided and the default instance is already initialized,
10
+ * creates a new i18n instance to avoid reinitialization issues.
11
+ */
12
+ export declare const initI18n: (options?: {
13
+ resources?: Record<string, any>;
14
+ lng?: string;
15
+ fallbackLng?: string;
16
+ debug?: boolean;
17
+ loadPath?: string | string[];
18
+ autoLoad?: boolean;
19
+ }) => Promise<import("i18next").i18n>;
20
+ export default i18n;
21
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/i18n/config.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,SAAS,CAAC;AA6C3B;;;;;;;;;GASG;AACH,eAAO,MAAM,QAAQ,GAAU,UAAU;IACvC,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAChC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC7B,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,oCAuEA,CAAC;AAwBF,eAAe,IAAI,CAAC"}
package/dist/index.d.ts CHANGED
@@ -57,7 +57,14 @@ interface DocsWidgetDocumentConfig {
57
57
  }
58
58
  interface GeoHierarchyLayout {
59
59
  distribution?: 'fixed';
60
+ /** Explicit levels-per-column counts, e.g. [3, 3] for a 6-level hierarchy in 2 columns. */
60
61
  columns?: number[];
62
+ /**
63
+ * How many section columns this hierarchy occupies (1–3), matching panel-column-span.
64
+ * Levels are distributed evenly across these columns.
65
+ * When omitted, derived from level count: ≤3 → 1, 4–5 → 2, ≥6 → 3.
66
+ */
67
+ columnSpan?: number;
61
68
  columnIndex?: number;
62
69
  }
63
70
  /** Optional read path for edit-mode hydration (defaults from widget-data-path). */
@@ -180,7 +187,8 @@ interface BaseWidgetConfig {
180
187
  /** Column layout for geo-hierarchy widget (e.g. 3 levels per column). */
181
188
  'widget-geo-layout'?: GeoHierarchyLayout;
182
189
  /**
183
- * Read path for geo_code_hierarchy_json (geo-hierarchy widget, edit-mode hydrate).
190
+ * Read path for geo_code_hierarchy_json (hydrate/display).
191
+ * To also persist draft hierarchy on edit/save, use object widget-data-path with a hierarchy key.
184
192
  */
185
193
  'widget-geo-hierarchy-path'?: string;
186
194
  widgets?: BaseWidgetConfig[];
package/dist/index.esm.js CHANGED
@@ -2988,6 +2988,15 @@ const extractProfileImage = (records) => {
2988
2988
  profileImage = value;
2989
2989
  copy[key] = '';
2990
2990
  }
2991
+ else if (isSerializedFile(value)) {
2992
+ try {
2993
+ profileImage = deserializeFile(value);
2994
+ copy[key] = '';
2995
+ }
2996
+ catch (err) {
2997
+ console.error('Failed to deserialize profile image:', err);
2998
+ }
2999
+ }
2991
3000
  }
2992
3001
  return copy;
2993
3002
  });
@@ -8695,10 +8704,6 @@ const DEFAULT_LABELS = {
8695
8704
  lastApprovedBy: 'Last Approved by',
8696
8705
  lastApprovedAt: 'Last Approved at',
8697
8706
  };
8698
- // Options are loaded in both view and edit modes because view mode
8699
- // needs them to resolve display labels (e.g. "active" → "Active").
8700
- // For API data sources, loading is deferred to edit mode to avoid
8701
- // unnecessary network calls when only labels are needed.
8702
8707
  function useFieldDataSource(fieldKey, fieldConfig, isReadonly) {
8703
8708
  const [options, setOptions] = useState([]);
8704
8709
  const { dataSourceRequestHandler, schemaData } = useWidgetContext();
@@ -8802,11 +8807,15 @@ const HeaderSectionWidget = ({ config }) => {
8802
8807
  const imageUrlVal = findValue('imageUrl');
8803
8808
  const [previewUrl, setPreviewUrl] = useState(null);
8804
8809
  useEffect(() => {
8805
- if (imageUrlVal instanceof File) {
8810
+ if (isFile(imageUrlVal)) {
8806
8811
  const url = URL.createObjectURL(imageUrlVal);
8807
8812
  setPreviewUrl(url);
8808
8813
  return () => URL.revokeObjectURL(url);
8809
8814
  }
8815
+ if (isSerializedFile(imageUrlVal) && imageUrlVal.data) {
8816
+ setPreviewUrl(`data:${imageUrlVal.type};base64,${imageUrlVal.data}`);
8817
+ return;
8818
+ }
8810
8819
  setPreviewUrl(null);
8811
8820
  }, [imageUrlVal]);
8812
8821
  const displayImageUrl = previewUrl || (typeof imageUrlVal === 'string' && imageUrlVal ? imageUrlVal : null);
@@ -8910,16 +8919,25 @@ const HeaderSectionWidget = ({ config }) => {
8910
8919
  }, [statusValue, statusOptions]);
8911
8920
  const statusColor = statusColors[String(statusValue).toLowerCase()] || 'var(--owt-color-text-muted, #6B7280)';
8912
8921
  const fileInputRef = useRef(null);
8913
- const handleImageUpload = useCallback((e) => {
8922
+ const handleImageUpload = useCallback(async (e) => {
8914
8923
  const file = e.target.files?.[0];
8915
8924
  if (!file || !paths.imageUrl)
8916
8925
  return;
8917
- dispatch(setValues(setValueByPath({ ...values }, paths.imageUrl, file)));
8918
- e.target.value = '';
8926
+ try {
8927
+ const serialized = await serializeFile(file);
8928
+ dispatch(setValues(setValueByPath({ ...values }, paths.imageUrl, serialized)));
8929
+ }
8930
+ catch (err) {
8931
+ console.error('[HeaderSectionWidget] Error serializing image:', err);
8932
+ }
8933
+ finally {
8934
+ e.target.value = '';
8935
+ }
8919
8936
  }, [paths.imageUrl, values, dispatch]);
8920
8937
  const handleImageDelete = useCallback(() => {
8921
8938
  if (!paths.imageUrl)
8922
8939
  return;
8940
+ setPreviewUrl(null);
8923
8941
  dispatch(setValues(setValueByPath({ ...values }, paths.imageUrl, null)));
8924
8942
  }, [paths.imageUrl, values, dispatch]);
8925
8943
  const cls = `header-section-widget-${widgetConfig['widget-id']}`;
@@ -10596,6 +10614,33 @@ function buildOrderedLevels(flat) {
10596
10614
  return ordered;
10597
10615
  }
10598
10616
  const GEO_SECTION_COLUMN_SLOTS = 3;
10617
+ /**
10618
+ * Suggested panel/widget column span from hierarchy depth:
10619
+ * ≤3 levels → 1, 4–5 → 2, ≥6 → 3.
10620
+ */
10621
+ function suggestedGeoColumnSpan(levelCount) {
10622
+ if (levelCount <= 0) {
10623
+ return 1;
10624
+ }
10625
+ if (levelCount <= 3) {
10626
+ return 1;
10627
+ }
10628
+ if (levelCount < 6) {
10629
+ return 2;
10630
+ }
10631
+ return GEO_SECTION_COLUMN_SLOTS;
10632
+ }
10633
+ /** Clamp span to 1…GEO_SECTION_COLUMN_SLOTS and at most the level count. */
10634
+ function resolveGeoColumnSpan(levelCount, explicitSpan) {
10635
+ const preferred = explicitSpan !== undefined && explicitSpan !== null
10636
+ ? explicitSpan
10637
+ : suggestedGeoColumnSpan(levelCount);
10638
+ const clamped = Math.min(GEO_SECTION_COLUMN_SLOTS, Math.max(1, Math.floor(preferred)));
10639
+ if (levelCount <= 0) {
10640
+ return clamped;
10641
+ }
10642
+ return Math.min(clamped, levelCount);
10643
+ }
10599
10644
  /** Slice ordered levels into section columns per counts, e.g. [3, 2, 0]. */
10600
10645
  function distributeLevelsToColumns(levels, columnCounts) {
10601
10646
  const columns = columnCounts.map(() => []);
@@ -10626,19 +10671,22 @@ function resolveGeoLevelColumns(levels, layout) {
10626
10671
  const columnCounts = layout.distribution === 'fixed'
10627
10672
  ? padColumnCounts(layout.columns, GEO_SECTION_COLUMN_SLOTS)
10628
10673
  : layout.columns;
10674
+ const columns = distributeLevelsToColumns(levels, columnCounts);
10675
+ const occupied = columns.filter((column) => column.length > 0).length;
10629
10676
  return {
10630
10677
  columnCounts,
10631
- columns: distributeLevelsToColumns(levels, columnCounts),
10678
+ columns,
10679
+ columnSpan: resolveGeoColumnSpan(levels.length, layout.columnSpan ?? occupied),
10632
10680
  };
10633
10681
  }
10634
- // Default (no explicit layout): fill top-to-bottom across up to
10635
- // GEO_SECTION_COLUMN_SLOTS columns, each holding a contiguous block of levels
10636
- // so the order stays correct when columns stack on small screens.
10637
- const columnCount = Math.min(GEO_SECTION_COLUMN_SLOTS, Math.max(levels.length, 1));
10638
- const columns = distributeLevelsContiguous(levels, columnCount);
10682
+ // Distribute top-to-bottom across the allotted column span so order stays
10683
+ // correct when columns stack on small screens.
10684
+ const columnSpan = resolveGeoColumnSpan(levels.length, layout?.columnSpan);
10685
+ const columns = distributeLevelsContiguous(levels, columnSpan);
10639
10686
  return {
10640
10687
  columnCounts: columns.map((column) => column.length),
10641
10688
  columns,
10689
+ columnSpan,
10642
10690
  };
10643
10691
  }
10644
10692
  function padColumnCounts(counts, slotCount) {
@@ -10781,6 +10829,42 @@ function getDeepestSelectedValue(orderedLevels, selectedValues) {
10781
10829
  }
10782
10830
  return deepest;
10783
10831
  }
10832
+ /** Build geo_code_hierarchy_json document from current selections (for save payload). */
10833
+ function buildHierarchyJson(orderedLevels, selectedValues, options, resolvedLabels = {}) {
10834
+ const hierarchy = [];
10835
+ for (const level of orderedLevels) {
10836
+ const levelValueId = selectedValues[level.level_id];
10837
+ if (!levelValueId) {
10838
+ break;
10839
+ }
10840
+ const optionLabel = options[level.level_id]?.find((item) => item.value === levelValueId)?.label;
10841
+ const mnemonic = optionLabel || resolvedLabels[levelValueId] || levelValueId;
10842
+ hierarchy.push({
10843
+ level_id: level.level_id,
10844
+ level: level.level_mnemonic,
10845
+ level_mnemonic: level.level_mnemonic,
10846
+ level_value_id: levelValueId,
10847
+ level_value_mnemonic: mnemonic,
10848
+ });
10849
+ }
10850
+ if (hierarchy.length === 0) {
10851
+ return null;
10852
+ }
10853
+ return {
10854
+ hierarchy,
10855
+ lowest_level_value_id: hierarchy[hierarchy.length - 1].level_value_id,
10856
+ };
10857
+ }
10858
+ /** Preserve string vs object storage shape used by the existing hierarchy field. */
10859
+ function formatHierarchyForPersist(document, previous) {
10860
+ if (document === null) {
10861
+ return typeof previous === 'string' ? '' : null;
10862
+ }
10863
+ if (typeof previous === 'string') {
10864
+ return JSON.stringify(document);
10865
+ }
10866
+ return document;
10867
+ }
10784
10868
  function clearDescendants(orderedLevels, fromIndex, selectedValues, options) {
10785
10869
  const nextSelected = { ...selectedValues };
10786
10870
  const nextOptions = { ...options };
@@ -10856,7 +10940,10 @@ function useGeoHierarchy({ config }) {
10856
10940
  const geoLayout = config['widget-geo-layout'];
10857
10941
  const hierarchyJsonPath = useMemo(() => resolveHierarchyPath(config), [config]);
10858
10942
  const dataPath = useMemo(() => resolveDataPath(config), [config]);
10859
- /** Read-only base: geo_code_hierarchy_json is never written — schema first, then store. */
10943
+ /**
10944
+ * Approved hierarchy for hydrate/display: schema first, then store.
10945
+ * Edits persist draft hierarchy into store values for save; schema keeps approved.
10946
+ */
10860
10947
  const baseHierarchyJson = useMemo(() => {
10861
10948
  if (!hierarchyJsonPath) {
10862
10949
  return null;
@@ -10956,17 +11043,23 @@ function useGeoHierarchy({ config }) {
10956
11043
  });
10957
11044
  return transformGeoValueOptions(payload);
10958
11045
  }, [fetchRawValues]);
10959
- /** Write only geo_lowest_level_value_id never geo_code_hierarchy_json. */
10960
- const persistDeepestValue = useCallback((nextSelectedValues, orderedLevels) => {
11046
+ /** Persist leaf id + current hierarchy structure for save (schema approved value stays preferred for read). */
11047
+ const persistDeepestValue = useCallback((nextSelectedValues, orderedLevels, nextOptions, nextResolvedLabels) => {
10961
11048
  if (initializingRef.current || hydratingRef.current || isReadonly) {
10962
11049
  return;
10963
11050
  }
10964
11051
  const deepest = getDeepestSelectedValue(orderedLevels, nextSelectedValues);
10965
11052
  selfPersistedValueRef.current = deepest ? String(deepest) : '';
11053
+ const hierarchyDocument = buildHierarchyJson(orderedLevels, nextSelectedValues, nextOptions, nextResolvedLabels);
11054
+ const hierarchyPayload = formatHierarchyForPersist(hierarchyDocument, baseHierarchyRef.current);
10966
11055
  const rawDataPath = config['widget-data-path'];
10967
11056
  const nextValue = deepest ?? null;
10968
11057
  if (rawDataPath && typeof rawDataPath === 'object') {
10969
- base.onChange({ value: nextValue });
11058
+ const payload = { value: nextValue };
11059
+ if ('hierarchy' in rawDataPath) {
11060
+ payload.hierarchy = hierarchyPayload;
11061
+ }
11062
+ base.onChange(payload);
10970
11063
  }
10971
11064
  else {
10972
11065
  base.onChange(nextValue);
@@ -11108,9 +11201,7 @@ function useGeoHierarchy({ config }) {
11108
11201
  nextSelectedValues = cleared.selectedValues;
11109
11202
  setSelectedValues(nextSelectedValues);
11110
11203
  setOptions(cleared.options);
11111
- if (nextValue || Object.values(nextSelectedValues).some((value) => value)) {
11112
- persistDeepestValue(nextSelectedValues, levels);
11113
- }
11204
+ persistDeepestValue(nextSelectedValues, levels, cleared.options, resolvedLabels);
11114
11205
  if (!nextValue || levelIndex >= levels.length - 1) {
11115
11206
  return;
11116
11207
  }
@@ -11122,9 +11213,17 @@ function useGeoHierarchy({ config }) {
11122
11213
  const message = error instanceof Error ? error.message : 'Failed to load child geo level values';
11123
11214
  setGeoError(message);
11124
11215
  }
11125
- }, [isReadonly, levels, selectedValues, options, persistDeepestValue, loadOptionsForLevel]);
11216
+ }, [
11217
+ isReadonly,
11218
+ levels,
11219
+ selectedValues,
11220
+ options,
11221
+ resolvedLabels,
11222
+ persistDeepestValue,
11223
+ loadOptionsForLevel,
11224
+ ]);
11126
11225
  const readonlyPath = useMemo(() => buildReadonlyPath(levels, selectedValues, options, resolvedLabels), [levels, selectedValues, options, resolvedLabels]);
11127
- const { columnCounts, columns: levelColumns } = useMemo(() => resolveGeoLevelColumns(levels, geoLayout), [levels, geoLayout]);
11226
+ const { columnCounts, columns: levelColumns, columnSpan } = useMemo(() => resolveGeoLevelColumns(levels, geoLayout), [levels, geoLayout]);
11128
11227
  const visibleColumns = useMemo(() => {
11129
11228
  const columnIndex = geoLayout?.columnIndex;
11130
11229
  if (columnIndex === undefined || columnIndex === null) {
@@ -11142,6 +11241,7 @@ function useGeoHierarchy({ config }) {
11142
11241
  options,
11143
11242
  resolvedLabels,
11144
11243
  columnCounts,
11244
+ columnSpan,
11145
11245
  visibleColumns,
11146
11246
  loadingLevels,
11147
11247
  loadingLevelId,
@@ -11211,11 +11311,12 @@ const GeoHierarchyWidget = ({ config }) => {
11211
11311
  isLevelEnabled,
11212
11312
  formatLevelLabel,
11213
11313
  };
11314
+ const layoutColumnCount = Math.max(visibleColumns.length, 1);
11214
11315
  const content = visibleColumns.length <= 1 ? (renderLevelRows({
11215
11316
  ...rowProps,
11216
11317
  columnLevels: visibleColumns[0]?.levels ?? levels,
11217
11318
  })) : (jsxRuntimeExports.jsx("div", { className: "flex flex-col lg:grid w-full", style: {
11218
- gridTemplateColumns: `repeat(${visibleColumns.length}, minmax(200px, 1fr))`,
11319
+ gridTemplateColumns: `repeat(${layoutColumnCount}, minmax(200px, 1fr))`,
11219
11320
  }, children: visibleColumns.map((column, position) => {
11220
11321
  const isLast = position === visibleColumns.length - 1;
11221
11322
  const columnClassName = [
@@ -11343,7 +11444,22 @@ const DocsWidget = ({ config }) => {
11343
11444
  fileInputRefs.current[docKey] = el;
11344
11445
  } })] })), hasFile && (jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [jsxRuntimeExports.jsx("button", { type: "button", onClick: (e) => handlePreview(file, e), className: "inline-flex items-center px-3 py-1 rounded-md border border-gray-300 text-sm font-medium text-gray-900 bg-gray-50 focus:outline-none", title: fileName, children: t?.('common.view') ?? 'View' }), jsxRuntimeExports.jsx("button", { type: "button", onClick: () => handleRemove(docKey), className: "inline-flex items-center px-3 py-1 rounded-md border border-gray-300 text-sm font-medium text-gray-900 bg-gray-50 focus:outline-none", title: "Remove", children: t?.('common.remove') ?? 'Remove' })] }))] }) })] }) }, docKey));
11345
11446
  };
11346
- return (jsxRuntimeExports.jsxs("div", { className: isReadonly ? 'DocsDisplayWidget mb-[10px]' : 'DocsWidget mb-[10px]', children: [jsxRuntimeExports.jsx("div", { className: "flex flex-col lg:grid gap-x-8 gap-y-0", style: { gridTemplateColumns: `repeat(${docColumns.length || 1}, minmax(0, 1fr))` }, children: docColumns.map((column, columnIndex) => (jsxRuntimeExports.jsx("div", { className: "flex flex-col", children: column.map((doc) => renderSlot(doc)) }, `docs-column-${columnIndex}`))) }), jsxRuntimeExports.jsx(FilePreviewModal, { file: previewFile, isOpen: isPreviewOpen && !!previewFile, onClose: () => {
11447
+ return (jsxRuntimeExports.jsxs("div", { className: isReadonly ? 'DocsDisplayWidget mb-[10px]' : 'DocsWidget mb-[10px]', children: [jsxRuntimeExports.jsx("div", { className: "flex flex-col lg:grid w-full", style: { gridTemplateColumns: `repeat(${docColumns.length || 1}, minmax(0, 1fr))` }, children: docColumns.map((column, columnIndex) => {
11448
+ const isLast = columnIndex === docColumns.length - 1;
11449
+ const columnClassName = [
11450
+ 'flex flex-col min-w-0 relative',
11451
+ columnIndex > 0 ? 'lg:pl-10' : '',
11452
+ isLast ? '' : 'lg:pr-10',
11453
+ ]
11454
+ .filter(Boolean)
11455
+ .join(' ');
11456
+ return (jsxRuntimeExports.jsxs("div", { className: columnClassName, children: [!isLast && (jsxRuntimeExports.jsx("div", { className: "hidden lg:block absolute right-0 top-0 w-px", style: {
11457
+ bottom: '5px',
11458
+ backgroundColor: isReadonly
11459
+ ? 'var(--owt-panel-divider-color, #C4C4C4)'
11460
+ : 'var(--owt-color-primary, #F5BB1A)',
11461
+ } })), column.map((doc) => renderSlot(doc))] }, `docs-column-${columnIndex}`));
11462
+ }) }), jsxRuntimeExports.jsx(FilePreviewModal, { file: previewFile, isOpen: isPreviewOpen && !!previewFile, onClose: () => {
11347
11463
  setIsPreviewOpen(false);
11348
11464
  setPreviewFile(null);
11349
11465
  } })] }));