@openg2p/registry-widgets 1.1.0-dev.10 → 1.1.0-dev.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -3947,7 +3947,7 @@ function scopedClassSelectors(sectionClassId, classNames) {
3947
3947
  * - Panels wrap when they exceed available width
3948
3948
  * - Sections can sit side-by-side if there's space
3949
3949
  */
3950
- const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequestHandler, schemaData, onValueChange, gridColumnSpan, onSectionSave, hideEditButton = false, mode = 'RegistryView', namespace, changeRequestType, showChangeRequestLabel = true, dbSectionId, sectionRegisterId, onSectionDirtyChange, sectionIndex, sectionCount, expandedSectionIndex, onExpandSection, onSectionSaveSuccess, onPreviousSection, isDraft, onEditModeChange, forceExitEdit, }) => {
3950
+ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequestHandler, schemaData, onValueChange, gridColumnSpan, onSectionSave, hideEditButton = false, mode = 'RegistryView', namespace, changeRequestType, showChangeRequestLabel = true, dbSectionId, sectionRegisterId, onSectionDirtyChange, sectionIndex, sectionCount, expandedSectionIndex, onExpandSection, onSectionSaveSuccess, onPreviousSection, isDraft, isAccessible = false, onEditModeChange, forceExitEdit, }) => {
3951
3951
  const { translateConfig, translate } = useWidgetTranslation();
3952
3952
  const resolvedTheme = useWidgetTheme();
3953
3953
  const portalCSSVariables = React.useMemo(() => themeToCSSVariables(resolvedTheme), [resolvedTheme]);
@@ -4013,17 +4013,25 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
4013
4013
  const isExpandedFromContainer = typeof sectionIndex === 'number' && expandedSectionIndex === sectionIndex;
4014
4014
  const isExpandedStandalone = sectionIndex === undefined && standaloneExpanded;
4015
4015
  const isExpanded = mode === 'IntakeForm' && (isExpandedFromContainer || isExpandedStandalone);
4016
- // When the section is managed by SectionsContainer (sectionIndex is set), header clicks are
4017
- // disabled navigation is only allowed via the Next / Previous buttons.
4018
- // Standalone usage (sectionIndex undefined) keeps the classic toggle behaviour.
4016
+ // IntakeForm only: tracks whether the user has clicked Next on this section at least once.
4017
+ // Used to unlock the accordion header so the user can navigate back to a visited section.
4018
+ const [hasBeenSavedByUser, setHasBeenSavedByUser] = React.useState(false);
4019
+ // Accordion header click behaviour in IntakeForm mode:
4020
+ // - Standalone (no sectionIndex): always toggleable.
4021
+ // - Managed by SectionsContainer: toggleable only when isAccessible is true
4022
+ // (i.e. the section has been visited OR is the immediate next one).
4023
+ // Sections beyond that remain locked.
4019
4024
  const handleAccordionToggle = React.useCallback(() => {
4020
4025
  if (mode !== 'IntakeForm')
4021
4026
  return;
4022
4027
  if (sectionIndex === undefined) {
4023
4028
  setStandaloneExpanded(prev => !prev);
4024
4029
  }
4025
- // Intentionally no-op when sectionIndex is set (managed by SectionsContainer)
4026
- }, [mode, sectionIndex]);
4030
+ else if (isAccessible && onExpandSection) {
4031
+ onExpandSection(sectionIndex);
4032
+ }
4033
+ // Intentionally no-op for locked sections (isAccessible === false)
4034
+ }, [mode, sectionIndex, isAccessible, onExpandSection]);
4027
4035
  // Recursively count all vertical panels, especially those nested inside horizontal panels
4028
4036
  // Typically: horizontal panels at first level contain vertical panels at second level
4029
4037
  // Accounts for panel-column-span: a panel with column-span 3 counts as 3 columns
@@ -4358,8 +4366,6 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
4358
4366
  const baselineSnapshotRef = React.useRef(null);
4359
4367
  // IntakeForm only: increment when baseline is updated after save - forces badge to update (refs don't trigger re-renders)
4360
4368
  const [intakeFormBaselineTrigger, setIntakeFormBaselineTrigger] = React.useState(0);
4361
- // IntakeForm only: tracks whether the user has actually saved this section (prevents "Saved" badge on initial load)
4362
- const [hasBeenSavedByUser, setHasBeenSavedByUser] = React.useState(false);
4363
4369
  // IntakeForm: treat as edit mode for dirty tracking when isDraft. RegistryView: use isEditMode.
4364
4370
  const effectiveEditModeForDirty = mode === 'IntakeForm' ? (isDraft !== false) : isEditMode;
4365
4371
  // Compute isDirty: compare current store state to baseline (only when in edit mode)
@@ -4592,6 +4598,11 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
4592
4598
  }
4593
4599
  onSectionDirtyChange?.(sectionId, false);
4594
4600
  }
4601
+ else if (mode === 'IntakeForm') {
4602
+ // No onSectionSave provided, but still mark section as visited so the
4603
+ // user can navigate back to it by clicking the accordion header.
4604
+ setHasBeenSavedByUser(true);
4605
+ }
4595
4606
  // Always navigate to the next section
4596
4607
  onSectionSaveSuccess?.(sectionIndex);
4597
4608
  }, [store, onSectionSave, onSectionSaveSuccess, sectionIndex, originalSection, schemaData, contextSchemaData, namespace, hasSupportingDocuments, dbSectionId, sectionRegisterId, dispatch, buildSectionSnapshot, sectionId, onSectionDirtyChange, mode, isDraft]);
@@ -4863,7 +4874,7 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
4863
4874
  }),
4864
4875
  }, children: mode === 'IntakeForm' ? (
4865
4876
  /* IntakeForm: accordion layout - header always visible, content only when expanded */
4866
- jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [jsxRuntimeExports.jsxs("button", { type: "button", id: `intake-form-accordion-header-${sectionId}`, className: "intake-form-accordion-header", onClick: handleAccordionToggle, "aria-expanded": isExpanded, "aria-controls": isExpanded ? `intake-form-accordion-content-${sectionId}` : undefined, "data-interactive": sectionIndex === undefined ? 'true' : 'false', style: {
4877
+ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [jsxRuntimeExports.jsxs("button", { type: "button", id: `intake-form-accordion-header-${sectionId}`, className: "intake-form-accordion-header", onClick: handleAccordionToggle, "aria-expanded": isExpanded, "aria-controls": isExpanded ? `intake-form-accordion-content-${sectionId}` : undefined, "data-interactive": sectionIndex === undefined || isAccessible ? 'true' : 'false', style: {
4867
4878
  width: '100%',
4868
4879
  display: 'flex',
4869
4880
  alignItems: 'flex-start',
@@ -4873,7 +4884,7 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
4873
4884
  marginBottom: 0,
4874
4885
  background: 'none',
4875
4886
  border: 'none',
4876
- cursor: sectionIndex === undefined ? 'pointer' : 'default',
4887
+ cursor: sectionIndex === undefined || isAccessible ? 'pointer' : 'default',
4877
4888
  textAlign: 'left',
4878
4889
  fontFamily: 'Roboto, sans-serif',
4879
4890
  }, children: [jsxRuntimeExports.jsxs("div", { style: { flex: 1, display: 'flex', alignItems: 'center', gap: '12px', minWidth: 0 }, children: [jsxRuntimeExports.jsx("h2", { className: "text-xl font-semibold", style: { margin: 0 }, children: sectionToRender['section-title']
@@ -5171,6 +5182,11 @@ const SectionsContainer = ({ sections, dataSourceRequestHandler: propDataSourceR
5171
5182
  const dataSourceRequestHandler = propDataSourceRequestHandler || contextDataSourceRequestHandler;
5172
5183
  // IntakeForm mode: accordion state - which section is expanded (null = none; first expanded by default)
5173
5184
  const [expandedSectionIndex, setExpandedSectionIndex] = React.useState(0);
5185
+ // IntakeForm mode: high-water mark of the furthest section the user has clicked Next on.
5186
+ // A section at index i is accessible when i <= maxVisitedIndex + 1
5187
+ // (i.e. every visited section plus the one immediately after it).
5188
+ // Starts at -1 so only section 0 is accessible before any Next is clicked.
5189
+ const [maxVisitedIndex, setMaxVisitedIndex] = React.useState(-1);
5174
5190
  // RegistryView: track which section is currently in edit mode (by section-id); null = none
5175
5191
  const [editingSectionId, setEditingSectionId] = React.useState(null);
5176
5192
  const handleEditModeChange = React.useCallback((sectionId, editing) => {
@@ -5195,8 +5211,9 @@ const SectionsContainer = ({ sections, dataSourceRequestHandler: propDataSourceR
5195
5211
  const handleExpandSection = React.useCallback((index) => {
5196
5212
  setExpandedSectionIndex(prev => (prev === index ? null : index));
5197
5213
  }, []);
5198
- // IntakeForm mode: called after section save - collapse current, expand next
5214
+ // IntakeForm mode: called after section save - advance high-water mark, collapse current, expand next
5199
5215
  const handleSectionSaveSuccess = React.useCallback((index) => {
5216
+ setMaxVisitedIndex(prev => Math.max(prev, index));
5200
5217
  if (index + 1 < safeSections.length) {
5201
5218
  setExpandedSectionIndex(index + 1);
5202
5219
  }
@@ -5390,6 +5407,8 @@ const SectionsContainer = ({ sections, dataSourceRequestHandler: propDataSourceR
5390
5407
  onSectionSaveSuccess: handleSectionSaveSuccess,
5391
5408
  onPreviousSection: handlePreviousSection,
5392
5409
  isDraft,
5410
+ // Accessible = every visited section + the one immediately after
5411
+ isAccessible: index <= maxVisitedIndex + 1,
5393
5412
  }
5394
5413
  : {};
5395
5414
  // RegistryView: single-edit coordination props