@openg2p/registry-widgets 1.1.0-dev.9 → 1.1.0
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/components/SectionRenderer.d.ts +7 -1
- package/dist/components/SectionRenderer.d.ts.map +1 -1
- package/dist/components/SectionsContainer.d.ts.map +1 -1
- package/dist/hooks/useBaseWidget.d.ts.map +1 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.esm.js +53 -30
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +53 -30
- package/dist/index.js.map +1 -1
- package/dist/utils/dataSource.d.ts.map +1 -1
- package/dist/widgets/TextAreaWidget.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1078,16 +1078,12 @@ const getApiDataSource = async (dataSource, allValues, dataSourceRequestHandler,
|
|
|
1078
1078
|
console.error('[getApiDataSource] API data source missing endpoint. Use "endpoint" field to specify the operation (e.g., "get_g2p_geo_level_values")');
|
|
1079
1079
|
return [];
|
|
1080
1080
|
}
|
|
1081
|
-
let
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
}
|
|
1087
|
-
catch (error) {
|
|
1088
|
-
console.error('[getApiDataSource] Handler error:', error);
|
|
1089
|
-
throw error;
|
|
1090
|
-
}
|
|
1081
|
+
// Call handler — let any throw propagate to the outer catch so it is logged once
|
|
1082
|
+
// by useBaseWidget rather than double-logged here (which can cascade when
|
|
1083
|
+
// intercept-console-error.js converts console.error calls into thrown errors).
|
|
1084
|
+
const response = await dataSourceRequestHandler(service, endpoint, method, requestParams, {
|
|
1085
|
+
headers: dataSource.headers,
|
|
1086
|
+
});
|
|
1091
1087
|
// Handle OpenG2P response format (response_body.response_payload)
|
|
1092
1088
|
if (response && typeof response === 'object') {
|
|
1093
1089
|
if (response.response_body?.response_payload && Array.isArray(response.response_body.response_payload)) {
|
|
@@ -1110,8 +1106,8 @@ const getApiDataSource = async (dataSource, allValues, dataSourceRequestHandler,
|
|
|
1110
1106
|
return [];
|
|
1111
1107
|
}
|
|
1112
1108
|
catch (error) {
|
|
1113
|
-
|
|
1114
|
-
|
|
1109
|
+
// Rethrow so useBaseWidget's catch can log it with full widget context
|
|
1110
|
+
throw error;
|
|
1115
1111
|
}
|
|
1116
1112
|
};
|
|
1117
1113
|
/**
|
|
@@ -2305,7 +2301,7 @@ const useBaseWidget = (options) => {
|
|
|
2305
2301
|
dispatch(setDataSource({ widgetId, data: transformed }));
|
|
2306
2302
|
}
|
|
2307
2303
|
catch (error) {
|
|
2308
|
-
console.error(`[useBaseWidget] ERROR loading data source for ${widgetId}:`, error);
|
|
2304
|
+
console.error(`[useBaseWidget] ERROR loading data source for widget "${widgetId}" (type="${dataSource.type}"):`, error, '\nWidget config:', config, '\ndataSourceRequestHandler provided:', Boolean(dataSourceRequestHandler));
|
|
2309
2305
|
dispatch(setDataSource({ widgetId, data: [] }));
|
|
2310
2306
|
}
|
|
2311
2307
|
finally {
|
|
@@ -3947,7 +3943,7 @@ function scopedClassSelectors(sectionClassId, classNames) {
|
|
|
3947
3943
|
* - Panels wrap when they exceed available width
|
|
3948
3944
|
* - Sections can sit side-by-side if there's space
|
|
3949
3945
|
*/
|
|
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, }) => {
|
|
3946
|
+
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
3947
|
const { translateConfig, translate } = useWidgetTranslation();
|
|
3952
3948
|
const resolvedTheme = useWidgetTheme();
|
|
3953
3949
|
const portalCSSVariables = React.useMemo(() => themeToCSSVariables(resolvedTheme), [resolvedTheme]);
|
|
@@ -4013,16 +4009,25 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
|
|
|
4013
4009
|
const isExpandedFromContainer = typeof sectionIndex === 'number' && expandedSectionIndex === sectionIndex;
|
|
4014
4010
|
const isExpandedStandalone = sectionIndex === undefined && standaloneExpanded;
|
|
4015
4011
|
const isExpanded = mode === 'IntakeForm' && (isExpandedFromContainer || isExpandedStandalone);
|
|
4012
|
+
// IntakeForm only: tracks whether the user has clicked Next on this section at least once.
|
|
4013
|
+
// Used to unlock the accordion header so the user can navigate back to a visited section.
|
|
4014
|
+
const [hasBeenSavedByUser, setHasBeenSavedByUser] = React.useState(false);
|
|
4015
|
+
// Accordion header click behaviour in IntakeForm mode:
|
|
4016
|
+
// - Standalone (no sectionIndex): always toggleable.
|
|
4017
|
+
// - Managed by SectionsContainer: toggleable only when isAccessible is true
|
|
4018
|
+
// (i.e. the section has been visited OR is the immediate next one).
|
|
4019
|
+
// Sections beyond that remain locked.
|
|
4016
4020
|
const handleAccordionToggle = React.useCallback(() => {
|
|
4017
4021
|
if (mode !== 'IntakeForm')
|
|
4018
4022
|
return;
|
|
4019
|
-
if (
|
|
4020
|
-
onExpandSection(sectionIndex);
|
|
4021
|
-
}
|
|
4022
|
-
else if (sectionIndex === undefined) {
|
|
4023
|
+
if (sectionIndex === undefined) {
|
|
4023
4024
|
setStandaloneExpanded(prev => !prev);
|
|
4024
4025
|
}
|
|
4025
|
-
|
|
4026
|
+
else if (isAccessible && onExpandSection) {
|
|
4027
|
+
onExpandSection(sectionIndex);
|
|
4028
|
+
}
|
|
4029
|
+
// Intentionally no-op for locked sections (isAccessible === false)
|
|
4030
|
+
}, [mode, sectionIndex, isAccessible, onExpandSection]);
|
|
4026
4031
|
// Recursively count all vertical panels, especially those nested inside horizontal panels
|
|
4027
4032
|
// Typically: horizontal panels at first level contain vertical panels at second level
|
|
4028
4033
|
// Accounts for panel-column-span: a panel with column-span 3 counts as 3 columns
|
|
@@ -4357,8 +4362,6 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
|
|
|
4357
4362
|
const baselineSnapshotRef = React.useRef(null);
|
|
4358
4363
|
// IntakeForm only: increment when baseline is updated after save - forces badge to update (refs don't trigger re-renders)
|
|
4359
4364
|
const [intakeFormBaselineTrigger, setIntakeFormBaselineTrigger] = React.useState(0);
|
|
4360
|
-
// IntakeForm only: tracks whether the user has actually saved this section (prevents "Saved" badge on initial load)
|
|
4361
|
-
const [hasBeenSavedByUser, setHasBeenSavedByUser] = React.useState(false);
|
|
4362
4365
|
// IntakeForm: treat as edit mode for dirty tracking when isDraft. RegistryView: use isEditMode.
|
|
4363
4366
|
const effectiveEditModeForDirty = mode === 'IntakeForm' ? (isDraft !== false) : isEditMode;
|
|
4364
4367
|
// Compute isDirty: compare current store state to baseline (only when in edit mode)
|
|
@@ -4591,6 +4594,11 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
|
|
|
4591
4594
|
}
|
|
4592
4595
|
onSectionDirtyChange?.(sectionId, false);
|
|
4593
4596
|
}
|
|
4597
|
+
else if (mode === 'IntakeForm') {
|
|
4598
|
+
// No onSectionSave provided, but still mark section as visited so the
|
|
4599
|
+
// user can navigate back to it by clicking the accordion header.
|
|
4600
|
+
setHasBeenSavedByUser(true);
|
|
4601
|
+
}
|
|
4594
4602
|
// Always navigate to the next section
|
|
4595
4603
|
onSectionSaveSuccess?.(sectionIndex);
|
|
4596
4604
|
}, [store, onSectionSave, onSectionSaveSuccess, sectionIndex, originalSection, schemaData, contextSchemaData, namespace, hasSupportingDocuments, dbSectionId, sectionRegisterId, dispatch, buildSectionSnapshot, sectionId, onSectionDirtyChange, mode, isDraft]);
|
|
@@ -4809,13 +4817,17 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
|
|
|
4809
4817
|
.${sectionClassId}.intake-form-accordion-item .intake-form-accordion-header h2 {
|
|
4810
4818
|
color: var(--owt-color-primary-dark, #F07B1A);
|
|
4811
4819
|
}
|
|
4812
|
-
|
|
4820
|
+
/* Hover / focus only shown when the header is actually interactive (standalone mode) */
|
|
4821
|
+
.${sectionClassId}.intake-form-accordion-item .intake-form-accordion-header[data-interactive="true"]:hover {
|
|
4813
4822
|
opacity: 0.85;
|
|
4814
4823
|
}
|
|
4815
|
-
.${sectionClassId}.intake-form-accordion-item .intake-form-accordion-header:focus-visible {
|
|
4824
|
+
.${sectionClassId}.intake-form-accordion-item .intake-form-accordion-header[data-interactive="true"]:focus-visible {
|
|
4816
4825
|
outline: 2px solid var(--owt-color-primary, #F5BB1A);
|
|
4817
4826
|
outline-offset: 2px;
|
|
4818
4827
|
}
|
|
4828
|
+
.${sectionClassId}.intake-form-accordion-item .intake-form-accordion-header[data-interactive="false"]:focus-visible {
|
|
4829
|
+
outline: none;
|
|
4830
|
+
}
|
|
4819
4831
|
.${sectionClassId}.intake-form-accordion-item .intake-form-accordion-content {
|
|
4820
4832
|
padding-top: 8px;
|
|
4821
4833
|
padding-bottom: 0px;
|
|
@@ -4858,7 +4870,7 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
|
|
|
4858
4870
|
}),
|
|
4859
4871
|
}, children: mode === 'IntakeForm' ? (
|
|
4860
4872
|
/* IntakeForm: accordion layout - header always visible, content only when expanded */
|
|
4861
|
-
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, style: {
|
|
4873
|
+
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: {
|
|
4862
4874
|
width: '100%',
|
|
4863
4875
|
display: 'flex',
|
|
4864
4876
|
alignItems: 'flex-start',
|
|
@@ -4868,7 +4880,7 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
|
|
|
4868
4880
|
marginBottom: 0,
|
|
4869
4881
|
background: 'none',
|
|
4870
4882
|
border: 'none',
|
|
4871
|
-
cursor: 'pointer',
|
|
4883
|
+
cursor: sectionIndex === undefined || isAccessible ? 'pointer' : 'default',
|
|
4872
4884
|
textAlign: 'left',
|
|
4873
4885
|
fontFamily: 'Roboto, sans-serif',
|
|
4874
4886
|
}, 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']
|
|
@@ -5166,6 +5178,11 @@ const SectionsContainer = ({ sections, dataSourceRequestHandler: propDataSourceR
|
|
|
5166
5178
|
const dataSourceRequestHandler = propDataSourceRequestHandler || contextDataSourceRequestHandler;
|
|
5167
5179
|
// IntakeForm mode: accordion state - which section is expanded (null = none; first expanded by default)
|
|
5168
5180
|
const [expandedSectionIndex, setExpandedSectionIndex] = React.useState(0);
|
|
5181
|
+
// IntakeForm mode: high-water mark of the furthest section the user has clicked Next on.
|
|
5182
|
+
// A section at index i is accessible when i <= maxVisitedIndex + 1
|
|
5183
|
+
// (i.e. every visited section plus the one immediately after it).
|
|
5184
|
+
// Starts at -1 so only section 0 is accessible before any Next is clicked.
|
|
5185
|
+
const [maxVisitedIndex, setMaxVisitedIndex] = React.useState(-1);
|
|
5169
5186
|
// RegistryView: track which section is currently in edit mode (by section-id); null = none
|
|
5170
5187
|
const [editingSectionId, setEditingSectionId] = React.useState(null);
|
|
5171
5188
|
const handleEditModeChange = React.useCallback((sectionId, editing) => {
|
|
@@ -5190,8 +5207,9 @@ const SectionsContainer = ({ sections, dataSourceRequestHandler: propDataSourceR
|
|
|
5190
5207
|
const handleExpandSection = React.useCallback((index) => {
|
|
5191
5208
|
setExpandedSectionIndex(prev => (prev === index ? null : index));
|
|
5192
5209
|
}, []);
|
|
5193
|
-
// IntakeForm mode: called after section save - collapse current, expand next
|
|
5210
|
+
// IntakeForm mode: called after section save - advance high-water mark, collapse current, expand next
|
|
5194
5211
|
const handleSectionSaveSuccess = React.useCallback((index) => {
|
|
5212
|
+
setMaxVisitedIndex(prev => Math.max(prev, index));
|
|
5195
5213
|
if (index + 1 < safeSections.length) {
|
|
5196
5214
|
setExpandedSectionIndex(index + 1);
|
|
5197
5215
|
}
|
|
@@ -5385,6 +5403,8 @@ const SectionsContainer = ({ sections, dataSourceRequestHandler: propDataSourceR
|
|
|
5385
5403
|
onSectionSaveSuccess: handleSectionSaveSuccess,
|
|
5386
5404
|
onPreviousSection: handlePreviousSection,
|
|
5387
5405
|
isDraft,
|
|
5406
|
+
// Accessible = every visited section + the one immediately after
|
|
5407
|
+
isAccessible: index <= maxVisitedIndex + 1,
|
|
5388
5408
|
}
|
|
5389
5409
|
: {};
|
|
5390
5410
|
// RegistryView: single-edit coordination props
|
|
@@ -7367,7 +7387,7 @@ const NumberInputWidget = ({ config }) => {
|
|
|
7367
7387
|
if (parsed === null) {
|
|
7368
7388
|
// Allow empty input or partial input (e.g., "-", ".")
|
|
7369
7389
|
if (inputValue === '' || inputValue === '-' || inputValue === '.') {
|
|
7370
|
-
onChange(
|
|
7390
|
+
onChange(null);
|
|
7371
7391
|
}
|
|
7372
7392
|
// Don't update if invalid - let user continue typing
|
|
7373
7393
|
return;
|
|
@@ -9731,10 +9751,13 @@ const TextAreaWidget = ({ config }) => {
|
|
|
9731
9751
|
// For readonly mode, render as preformatted text using <pre> tag
|
|
9732
9752
|
if (isReadonly) {
|
|
9733
9753
|
const displayValue = getStringValue() || '-';
|
|
9734
|
-
return (jsxRuntimeExports.jsxs("div", { className: "mb-[10px] TextAreaDisplayWidget 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("
|
|
9754
|
+
return (jsxRuntimeExports.jsxs("div", { className: "mb-[10px] TextAreaDisplayWidget 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", { title: String(displayValue), className: "text-base text-gray-900 font-medium overflow-y-auto whitespace-pre-wrap break-words", style: {
|
|
9735
9755
|
fontFamily: 'Roboto, sans-serif',
|
|
9736
|
-
|
|
9737
|
-
|
|
9756
|
+
height: '56px',
|
|
9757
|
+
minHeight: '56px',
|
|
9758
|
+
maxHeight: '56px',
|
|
9759
|
+
lineHeight: '20px',
|
|
9760
|
+
padding: '8px 0',
|
|
9738
9761
|
backgroundColor: 'transparent',
|
|
9739
9762
|
border: 'none',
|
|
9740
9763
|
}, children: displayValue }) })] }));
|