@openg2p/registry-widgets 1.1.2-dev.7 → 1.1.2-dev.9
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.map +1 -1
- package/dist/hooks/useBaseWidget.d.ts.map +1 -1
- package/dist/hooks/useGeoWidgetCascade.d.ts.map +1 -1
- package/dist/index.d.ts +42 -4
- package/dist/index.esm.js +285 -181
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +293 -180
- package/dist/index.js.map +1 -1
- package/dist/utils/dataSource.d.ts +6 -0
- package/dist/utils/dataSource.d.ts.map +1 -1
- package/dist/utils/geoHierarchy.d.ts +33 -0
- package/dist/utils/geoHierarchy.d.ts.map +1 -1
- package/dist/widgets/DialogTableWidget.d.ts.map +1 -1
- package/dist/widgets/SelectWidget.d.ts.map +1 -1
- package/dist/widgets/TableWidget.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.esm.js
CHANGED
|
@@ -1082,6 +1082,67 @@ const formatValue = (value, format, widgetType) => {
|
|
|
1082
1082
|
return value?.toString() || '';
|
|
1083
1083
|
};
|
|
1084
1084
|
|
|
1085
|
+
const apiDataSourceCache = new Map();
|
|
1086
|
+
const apiDataSourceInflight = new Map();
|
|
1087
|
+
function buildApiRequestContext(dataSource, allValues, levelId) {
|
|
1088
|
+
let depValue = null;
|
|
1089
|
+
if (dataSource.dependsOn) {
|
|
1090
|
+
if (dataSource.dependsOn.includes('.')) {
|
|
1091
|
+
depValue = getValueByPath(allValues, dataSource.dependsOn);
|
|
1092
|
+
}
|
|
1093
|
+
else {
|
|
1094
|
+
depValue = resolveWidgetIdValue(allValues, dataSource.dependsOn);
|
|
1095
|
+
}
|
|
1096
|
+
if (depValue === null || depValue === undefined || depValue === '') {
|
|
1097
|
+
return null;
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
const method = dataSource.method || 'GET';
|
|
1101
|
+
const staticParams = { ...dataSource.params };
|
|
1102
|
+
const standardFields = ['type', 'service', 'endpoint', 'url', 'method', 'dependsOn', 'valueKey', 'labelKey', 'headers', 'body', 'params', 'level_id'];
|
|
1103
|
+
for (const [key, value] of Object.entries(dataSource)) {
|
|
1104
|
+
if (!standardFields.includes(key) && value !== undefined && value !== null) {
|
|
1105
|
+
staticParams[key] = value;
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
if (levelId) {
|
|
1109
|
+
staticParams.level_id = levelId;
|
|
1110
|
+
}
|
|
1111
|
+
const requestParams = { ...staticParams };
|
|
1112
|
+
if (dataSource.dependsOn && depValue !== null && depValue !== undefined) {
|
|
1113
|
+
const parentValueId = typeof depValue === 'object' && depValue !== null
|
|
1114
|
+
? (depValue.level_value_id || depValue.id || depValue.value || depValue)
|
|
1115
|
+
: depValue;
|
|
1116
|
+
if (staticParams.level_id) {
|
|
1117
|
+
requestParams.parent_level_value_id = parentValueId;
|
|
1118
|
+
}
|
|
1119
|
+
else {
|
|
1120
|
+
const paramKey = dataSource.dependsOn.split('.').pop() || 'filter';
|
|
1121
|
+
requestParams[paramKey] = parentValueId;
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
else if (staticParams.level_id) {
|
|
1125
|
+
requestParams.parent_level_value_id = '';
|
|
1126
|
+
}
|
|
1127
|
+
const service = dataSource.service;
|
|
1128
|
+
const endpoint = dataSource.endpoint;
|
|
1129
|
+
if (!service || !endpoint) {
|
|
1130
|
+
return null;
|
|
1131
|
+
}
|
|
1132
|
+
return { service, endpoint, method, requestParams };
|
|
1133
|
+
}
|
|
1134
|
+
function buildApiDataSourceCacheKey(service, endpoint, method, requestParams) {
|
|
1135
|
+
return `${service}|${endpoint}|${method}|${JSON.stringify(requestParams)}`;
|
|
1136
|
+
}
|
|
1137
|
+
/** Return cached API options when already fetched (e.g. duplicate table cells). */
|
|
1138
|
+
function getCachedApiDataSource(dataSource, allValues, levelId) {
|
|
1139
|
+
const context = buildApiRequestContext(dataSource, allValues, levelId);
|
|
1140
|
+
if (!context) {
|
|
1141
|
+
return undefined;
|
|
1142
|
+
}
|
|
1143
|
+
const cacheKey = buildApiDataSourceCacheKey(context.service, context.endpoint, context.method, context.requestParams);
|
|
1144
|
+
return apiDataSourceCache.get(cacheKey);
|
|
1145
|
+
}
|
|
1085
1146
|
/**
|
|
1086
1147
|
* Get static data source options
|
|
1087
1148
|
*/
|
|
@@ -1099,98 +1160,33 @@ const getApiDataSource = async (dataSource, allValues, dataSourceRequestHandler,
|
|
|
1099
1160
|
return [];
|
|
1100
1161
|
}
|
|
1101
1162
|
try {
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
let depValue = null;
|
|
1105
|
-
if (dataSource.dependsOn) {
|
|
1106
|
-
if (dataSource.dependsOn.includes('.')) {
|
|
1107
|
-
depValue = getValueByPath(allValues, dataSource.dependsOn);
|
|
1108
|
-
}
|
|
1109
|
-
else {
|
|
1110
|
-
depValue = resolveWidgetIdValue(allValues, dataSource.dependsOn);
|
|
1111
|
-
}
|
|
1112
|
-
if (depValue === null || depValue === undefined || depValue === '') {
|
|
1113
|
-
// If dependency is empty, return empty array
|
|
1114
|
-
return [];
|
|
1115
|
-
}
|
|
1116
|
-
}
|
|
1117
|
-
// Build request parameters
|
|
1118
|
-
const method = dataSource.method || 'GET';
|
|
1119
|
-
// Extract static params from dataSource
|
|
1120
|
-
// Include explicit params object and any additional fields (like level_id)
|
|
1121
|
-
const staticParams = { ...dataSource.params };
|
|
1122
|
-
// Extract additional fields that aren't part of the standard ApiDataSource interface
|
|
1123
|
-
// These are fields like level_id that might be directly on the dataSource
|
|
1124
|
-
// BUT: level_id should come from widget-geo-config.level, not from dataSource
|
|
1125
|
-
const standardFields = ['type', 'service', 'endpoint', 'url', 'method', 'dependsOn', 'valueKey', 'labelKey', 'headers', 'body', 'params', 'level_id'];
|
|
1126
|
-
for (const [key, value] of Object.entries(dataSource)) {
|
|
1127
|
-
if (!standardFields.includes(key) && value !== undefined && value !== null) {
|
|
1128
|
-
staticParams[key] = value;
|
|
1129
|
-
}
|
|
1130
|
-
}
|
|
1131
|
-
// If levelId is provided (from widget-geo-config.level), use it instead of any level_id in dataSource
|
|
1132
|
-
if (levelId) {
|
|
1133
|
-
staticParams.level_id = levelId;
|
|
1134
|
-
}
|
|
1135
|
-
// Build request params object
|
|
1136
|
-
const requestParams = { ...staticParams };
|
|
1137
|
-
// Add dependency value to params
|
|
1138
|
-
if (dataSource.dependsOn && depValue !== null && depValue !== undefined) {
|
|
1139
|
-
// Extract the actual value ID if depValue is an object
|
|
1140
|
-
const parentValueId = typeof depValue === 'object' && depValue !== null
|
|
1141
|
-
? (depValue.level_value_id || depValue.id || depValue.value || depValue)
|
|
1142
|
-
: depValue;
|
|
1143
|
-
// For geo APIs, use parent_level_value_id
|
|
1144
|
-
if (staticParams.level_id) {
|
|
1145
|
-
requestParams.parent_level_value_id = parentValueId;
|
|
1146
|
-
}
|
|
1147
|
-
else {
|
|
1148
|
-
// For other APIs, use the dependency field name as param key
|
|
1149
|
-
const paramKey = dataSource.dependsOn.split('.').pop() || 'filter';
|
|
1150
|
-
requestParams[paramKey] = parentValueId;
|
|
1151
|
-
}
|
|
1152
|
-
}
|
|
1153
|
-
else if (staticParams.level_id) {
|
|
1154
|
-
// First level has no parent, send empty string as many OpenG2P APIs expect it
|
|
1155
|
-
requestParams.parent_level_value_id = "";
|
|
1156
|
-
}
|
|
1157
|
-
// Get service mnemonic and endpoint (required)
|
|
1158
|
-
const service = dataSource.service;
|
|
1159
|
-
const endpoint = dataSource.endpoint;
|
|
1160
|
-
if (!service) {
|
|
1161
|
-
console.error('[getApiDataSource] API data source missing service mnemonic. Use "service" field instead of "url"');
|
|
1162
|
-
return [];
|
|
1163
|
-
}
|
|
1164
|
-
if (!endpoint) {
|
|
1165
|
-
console.error('[getApiDataSource] API data source missing endpoint. Use "endpoint" field to specify the operation (e.g., "get_g2p_geo_level_values")');
|
|
1163
|
+
const context = buildApiRequestContext(dataSource, allValues, levelId);
|
|
1164
|
+
if (!context) {
|
|
1166
1165
|
return [];
|
|
1167
1166
|
}
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
}
|
|
1174
|
-
|
|
1175
|
-
if (
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
return
|
|
1167
|
+
const { service, endpoint, method, requestParams } = context;
|
|
1168
|
+
const cacheKey = buildApiDataSourceCacheKey(service, endpoint, method, requestParams);
|
|
1169
|
+
const cached = apiDataSourceCache.get(cacheKey);
|
|
1170
|
+
if (cached) {
|
|
1171
|
+
return cached;
|
|
1172
|
+
}
|
|
1173
|
+
const inflight = apiDataSourceInflight.get(cacheKey);
|
|
1174
|
+
if (inflight) {
|
|
1175
|
+
return inflight;
|
|
1176
|
+
}
|
|
1177
|
+
const fetchPromise = (async () => {
|
|
1178
|
+
const response = await dataSourceRequestHandler(service, endpoint, method, requestParams, { headers: dataSource.headers });
|
|
1179
|
+
const parsed = Array.isArray(response) ? response : [];
|
|
1180
|
+
apiDataSourceCache.set(cacheKey, parsed);
|
|
1181
|
+
return parsed;
|
|
1182
|
+
})();
|
|
1183
|
+
apiDataSourceInflight.set(cacheKey, fetchPromise);
|
|
1184
|
+
try {
|
|
1185
|
+
return await fetchPromise;
|
|
1183
1186
|
}
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
if (response.data && Array.isArray(response.data)) {
|
|
1187
|
-
return response.data;
|
|
1188
|
-
}
|
|
1189
|
-
if (response.results && Array.isArray(response.results)) {
|
|
1190
|
-
return response.results;
|
|
1191
|
-
}
|
|
1187
|
+
finally {
|
|
1188
|
+
apiDataSourceInflight.delete(cacheKey);
|
|
1192
1189
|
}
|
|
1193
|
-
return [];
|
|
1194
1190
|
}
|
|
1195
1191
|
catch (error) {
|
|
1196
1192
|
// Rethrow so useBaseWidget's catch can log it with full widget context
|
|
@@ -2247,12 +2243,145 @@ const geoHierarchyBuilder = new GeoHierarchyBuilder();
|
|
|
2247
2243
|
/** Sentinel written to Redux when a geo level is cleared by cascade (distinct from "never set"). */
|
|
2248
2244
|
const GEO_LEVEL_CLEARED = null;
|
|
2249
2245
|
const geoWidgetParentRegistry = new Map();
|
|
2246
|
+
const geoWidgetConfigRegistry = new Map();
|
|
2250
2247
|
function registerGeoWidgetParent(widgetId, parentWidgetId) {
|
|
2251
2248
|
geoWidgetParentRegistry.set(widgetId, parentWidgetId || null);
|
|
2252
2249
|
}
|
|
2253
2250
|
function unregisterGeoWidgetParent(widgetId) {
|
|
2254
2251
|
geoWidgetParentRegistry.delete(widgetId);
|
|
2255
2252
|
}
|
|
2253
|
+
function registerGeoWidget(widgetId, geoConfig, dataPath) {
|
|
2254
|
+
if (typeof dataPath !== 'string') {
|
|
2255
|
+
return;
|
|
2256
|
+
}
|
|
2257
|
+
const parentWidgetId = geoConfig.parentWidgetId?.trim() ? geoConfig.parentWidgetId : null;
|
|
2258
|
+
registerGeoWidgetParent(widgetId, parentWidgetId);
|
|
2259
|
+
geoWidgetConfigRegistry.set(widgetId, {
|
|
2260
|
+
widgetId,
|
|
2261
|
+
parentWidgetId,
|
|
2262
|
+
level: geoConfig.level,
|
|
2263
|
+
geoConfig,
|
|
2264
|
+
dataPath,
|
|
2265
|
+
groupId: getGeoGroupId(dataPath),
|
|
2266
|
+
});
|
|
2267
|
+
}
|
|
2268
|
+
function unregisterGeoWidget(widgetId) {
|
|
2269
|
+
unregisterGeoWidgetParent(widgetId);
|
|
2270
|
+
geoWidgetConfigRegistry.delete(widgetId);
|
|
2271
|
+
}
|
|
2272
|
+
function orderGeoWidgetRegistrations(registrations) {
|
|
2273
|
+
if (registrations.length <= 1) {
|
|
2274
|
+
return registrations;
|
|
2275
|
+
}
|
|
2276
|
+
const roots = registrations.filter((entry) => !entry.parentWidgetId);
|
|
2277
|
+
if (roots.length === 0) {
|
|
2278
|
+
return registrations;
|
|
2279
|
+
}
|
|
2280
|
+
const ordered = [];
|
|
2281
|
+
let current = roots[0];
|
|
2282
|
+
const visited = new Set();
|
|
2283
|
+
while (current && !visited.has(current.widgetId)) {
|
|
2284
|
+
visited.add(current.widgetId);
|
|
2285
|
+
ordered.push(current);
|
|
2286
|
+
current = registrations.find((entry) => entry.parentWidgetId === current.widgetId);
|
|
2287
|
+
}
|
|
2288
|
+
return ordered.length > 0 ? ordered : registrations;
|
|
2289
|
+
}
|
|
2290
|
+
function resolveLevelValueId(rawValue) {
|
|
2291
|
+
if (rawValue === null || rawValue === undefined || rawValue === '') {
|
|
2292
|
+
return null;
|
|
2293
|
+
}
|
|
2294
|
+
if (typeof rawValue === 'string' || typeof rawValue === 'number') {
|
|
2295
|
+
return String(rawValue);
|
|
2296
|
+
}
|
|
2297
|
+
if (typeof rawValue === 'object') {
|
|
2298
|
+
const id = rawValue.level_value_id || rawValue.id || rawValue.value;
|
|
2299
|
+
return id != null && id !== '' ? String(id) : null;
|
|
2300
|
+
}
|
|
2301
|
+
return null;
|
|
2302
|
+
}
|
|
2303
|
+
function resolveStoredMnemonic(values, registration, valueId) {
|
|
2304
|
+
const stored = getWidgetValue(values, registration.dataPath, registration.widgetId);
|
|
2305
|
+
const hierarchy = stored?.hierarchy || stored?.geo_code_hierarchy_json?.hierarchy;
|
|
2306
|
+
if (!Array.isArray(hierarchy)) {
|
|
2307
|
+
return undefined;
|
|
2308
|
+
}
|
|
2309
|
+
const levelData = hierarchy.find((entry) => entry.level === registration.level);
|
|
2310
|
+
if (levelData && String(levelData.level_value_id) === String(valueId)) {
|
|
2311
|
+
return levelData.level_value_mnemonic ? String(levelData.level_value_mnemonic) : undefined;
|
|
2312
|
+
}
|
|
2313
|
+
return undefined;
|
|
2314
|
+
}
|
|
2315
|
+
/** Resolve display mnemonic from cached dropdown options, then stored hierarchy. */
|
|
2316
|
+
function createGeoLevelMnemonicResolver(values, dataSources) {
|
|
2317
|
+
return (registration, valueId) => {
|
|
2318
|
+
const options = dataSources[registration.widgetId];
|
|
2319
|
+
const option = options?.find((entry) => String(entry.value) === String(valueId));
|
|
2320
|
+
if (option?.label) {
|
|
2321
|
+
return option.label;
|
|
2322
|
+
}
|
|
2323
|
+
return resolveStoredMnemonic(values, registration, valueId);
|
|
2324
|
+
};
|
|
2325
|
+
}
|
|
2326
|
+
/** Rebuild group hierarchy from widget values in parent→child order; stop at first missing level. */
|
|
2327
|
+
function rebuildGeoHierarchyFromRegistrations(groupId, values, registrations, resolveMnemonic) {
|
|
2328
|
+
const ordered = orderGeoWidgetRegistrations(registrations.filter((entry) => entry.groupId === groupId));
|
|
2329
|
+
geoHierarchyBuilder.clear(groupId);
|
|
2330
|
+
for (const registration of ordered) {
|
|
2331
|
+
const rawValue = resolveGeoWidgetLevelValue(values, registration.widgetId, registration.dataPath, registration.geoConfig);
|
|
2332
|
+
const valueId = resolveLevelValueId(rawValue);
|
|
2333
|
+
if (!valueId) {
|
|
2334
|
+
break;
|
|
2335
|
+
}
|
|
2336
|
+
const mnemonic = resolveMnemonic?.(registration, valueId) ??
|
|
2337
|
+
resolveStoredMnemonic(values, registration, valueId) ??
|
|
2338
|
+
valueId;
|
|
2339
|
+
geoHierarchyBuilder.addLevel(registration.level, valueId, mnemonic, groupId);
|
|
2340
|
+
}
|
|
2341
|
+
return geoHierarchyBuilder.buildHierarchyJson(groupId) !== null;
|
|
2342
|
+
}
|
|
2343
|
+
function collectGeoWidgetRegistrationsFromWidgets(widgets, namespace) {
|
|
2344
|
+
return widgets
|
|
2345
|
+
.filter((widget) => widget['widget-geo-config'] && typeof widget['widget-data-path'] === 'string')
|
|
2346
|
+
.map((widget) => {
|
|
2347
|
+
const originalWidgetId = widget['widget-id'];
|
|
2348
|
+
const originalDataPath = widget['widget-data-path'];
|
|
2349
|
+
const geoConfig = widget['widget-geo-config'];
|
|
2350
|
+
const widgetId = namespace ? `${namespace}__${originalWidgetId}` : originalWidgetId;
|
|
2351
|
+
const parentWidgetId = geoConfig.parentWidgetId?.trim()
|
|
2352
|
+
? (namespace ? `${namespace}__${geoConfig.parentWidgetId}` : geoConfig.parentWidgetId)
|
|
2353
|
+
: null;
|
|
2354
|
+
const dataPath = namespace ? `${namespace}.${originalDataPath}` : originalDataPath;
|
|
2355
|
+
return {
|
|
2356
|
+
widgetId,
|
|
2357
|
+
parentWidgetId,
|
|
2358
|
+
level: geoConfig.level,
|
|
2359
|
+
geoConfig,
|
|
2360
|
+
dataPath,
|
|
2361
|
+
groupId: getGeoGroupId(dataPath),
|
|
2362
|
+
};
|
|
2363
|
+
});
|
|
2364
|
+
}
|
|
2365
|
+
/** Reconcile all geo groups in section values before save. */
|
|
2366
|
+
function reconcileGeoHierarchiesInValues(values, registrations, dataSources = {}) {
|
|
2367
|
+
const groupIds = [...new Set(registrations.map((entry) => entry.groupId))];
|
|
2368
|
+
let updatedValues = values;
|
|
2369
|
+
const resolveMnemonic = createGeoLevelMnemonicResolver(updatedValues, dataSources);
|
|
2370
|
+
for (const groupId of groupIds) {
|
|
2371
|
+
const groupRegistrations = registrations.filter((entry) => entry.groupId === groupId);
|
|
2372
|
+
const dataPath = groupRegistrations[0]?.dataPath;
|
|
2373
|
+
const widgetId = groupRegistrations[0]?.widgetId;
|
|
2374
|
+
if (!dataPath || !widgetId) {
|
|
2375
|
+
continue;
|
|
2376
|
+
}
|
|
2377
|
+
rebuildGeoHierarchyFromRegistrations(groupId, updatedValues, groupRegistrations, resolveMnemonic);
|
|
2378
|
+
updatedValues = applySharedGeoHierarchyToValues(updatedValues, groupId, dataPath, widgetId);
|
|
2379
|
+
}
|
|
2380
|
+
return updatedValues;
|
|
2381
|
+
}
|
|
2382
|
+
function getGeoWidgetRegistrationsInGroup(groupId) {
|
|
2383
|
+
return orderGeoWidgetRegistrations([...geoWidgetConfigRegistry.values()].filter((entry) => entry.groupId === groupId));
|
|
2384
|
+
}
|
|
2256
2385
|
/** True when changedWidgetId is any upstream geo parent of widgetId (not only immediate parent). */
|
|
2257
2386
|
function isUpstreamGeoAncestor(changedWidgetId, widgetId, immediateParentWidgetId) {
|
|
2258
2387
|
if (changedWidgetId === widgetId) {
|
|
@@ -2347,7 +2476,7 @@ function resetAndSeedGeoHierarchyFromValues(values, dataPath, widgetId, groupId)
|
|
|
2347
2476
|
|
|
2348
2477
|
// Define stable empty arrays to avoid selector reference issues
|
|
2349
2478
|
const EMPTY_ERRORS = [];
|
|
2350
|
-
const EMPTY_DATA_SOURCE
|
|
2479
|
+
const EMPTY_DATA_SOURCE = [];
|
|
2351
2480
|
const useBaseWidget = (options) => {
|
|
2352
2481
|
const { config, dataSourceRequestHandler: propHandler, schemaData, onValueChange } = options;
|
|
2353
2482
|
const dispatch = useDispatch();
|
|
@@ -2362,7 +2491,7 @@ const useBaseWidget = (options) => {
|
|
|
2362
2491
|
const errors = useSelector((state) => state.widget.errors[widgetId] ?? EMPTY_ERRORS);
|
|
2363
2492
|
const touched = useSelector((state) => state.widget.touched[widgetId] || false);
|
|
2364
2493
|
const loading = useSelector((state) => state.widget.loading[widgetId] || false);
|
|
2365
|
-
const dataSourceOptions = useSelector((state) => state.widget.dataSources[widgetId] ?? EMPTY_DATA_SOURCE
|
|
2494
|
+
const dataSourceOptions = useSelector((state) => state.widget.dataSources[widgetId] ?? EMPTY_DATA_SOURCE);
|
|
2366
2495
|
// Skip value handling for layout widgets (they don't store data values)
|
|
2367
2496
|
// Infer layout from widget-type
|
|
2368
2497
|
const isLayoutWidget = config['widget-type'] === 'layout';
|
|
@@ -2700,9 +2829,9 @@ const useBaseWidget = (options) => {
|
|
|
2700
2829
|
if (!dataSource) {
|
|
2701
2830
|
return;
|
|
2702
2831
|
}
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
if (dataSource.type === 'api' && isReadonly && !
|
|
2832
|
+
const loadApiInReadonly = !!geoConfig ||
|
|
2833
|
+
['select', 'radio', 'checkbox', 'multi-select'].includes(config.widget);
|
|
2834
|
+
if (dataSource.type === 'api' && isReadonly && !loadApiInReadonly) {
|
|
2706
2835
|
return;
|
|
2707
2836
|
}
|
|
2708
2837
|
// For widgets with dependencies, check if dependency value exists
|
|
@@ -2744,6 +2873,30 @@ const useBaseWidget = (options) => {
|
|
|
2744
2873
|
// React will call this effect again when the handler is ready
|
|
2745
2874
|
return;
|
|
2746
2875
|
}
|
|
2876
|
+
const resolveOptionKeys = () => {
|
|
2877
|
+
if (dataSource.type === 'static') {
|
|
2878
|
+
return { valueKey: undefined, labelKey: undefined };
|
|
2879
|
+
}
|
|
2880
|
+
if (geoConfig) {
|
|
2881
|
+
return {
|
|
2882
|
+
valueKey: dataSource.valueKey || 'level_value_id',
|
|
2883
|
+
labelKey: dataSource.labelKey || 'level_value_mnemonic',
|
|
2884
|
+
};
|
|
2885
|
+
}
|
|
2886
|
+
return { valueKey: dataSource.valueKey, labelKey: dataSource.labelKey };
|
|
2887
|
+
};
|
|
2888
|
+
if (dataSource.type === 'api') {
|
|
2889
|
+
const levelId = geoConfig?.level;
|
|
2890
|
+
const cached = getCachedApiDataSource(dataSource, valuesRef.current, levelId);
|
|
2891
|
+
if (cached) {
|
|
2892
|
+
const { valueKey, labelKey } = resolveOptionKeys();
|
|
2893
|
+
dispatch(setDataSource({
|
|
2894
|
+
widgetId,
|
|
2895
|
+
data: transformDataSourceOptions(cached, valueKey, labelKey),
|
|
2896
|
+
}));
|
|
2897
|
+
return;
|
|
2898
|
+
}
|
|
2899
|
+
}
|
|
2747
2900
|
dispatch(setLoading({ widgetId, loading: true }));
|
|
2748
2901
|
let data = [];
|
|
2749
2902
|
if (dataSource.type === 'static') {
|
|
@@ -2756,31 +2909,13 @@ const useBaseWidget = (options) => {
|
|
|
2756
2909
|
dispatch(setDataSource({ widgetId, data: [] }));
|
|
2757
2910
|
return;
|
|
2758
2911
|
}
|
|
2759
|
-
// Extract level_id from widget-geo-config.level if available
|
|
2760
2912
|
const levelId = geoConfig?.level;
|
|
2761
2913
|
data = await getApiDataSource(dataSource, valuesRef.current, currentHandler, levelId);
|
|
2762
2914
|
}
|
|
2763
2915
|
else if (dataSource.type === 'schema') {
|
|
2764
2916
|
data = getSchemaDataSource(dataSource, schemaData || {});
|
|
2765
2917
|
}
|
|
2766
|
-
|
|
2767
|
-
// For geo widgets, default to level_value_id and level_value_mnemonic
|
|
2768
|
-
let valueKey;
|
|
2769
|
-
let labelKey;
|
|
2770
|
-
if (dataSource.type === 'static') {
|
|
2771
|
-
valueKey = undefined;
|
|
2772
|
-
labelKey = undefined;
|
|
2773
|
-
}
|
|
2774
|
-
else if (geoConfig) {
|
|
2775
|
-
// Geo widgets: default to level_value_id and level_value_mnemonic
|
|
2776
|
-
valueKey = dataSource.valueKey || 'level_value_id';
|
|
2777
|
-
labelKey = dataSource.labelKey || 'level_value_mnemonic';
|
|
2778
|
-
}
|
|
2779
|
-
else {
|
|
2780
|
-
// Non-geo widgets: use specified keys or undefined
|
|
2781
|
-
valueKey = dataSource.valueKey;
|
|
2782
|
-
labelKey = dataSource.labelKey;
|
|
2783
|
-
}
|
|
2918
|
+
const { valueKey, labelKey } = resolveOptionKeys();
|
|
2784
2919
|
const transformed = transformDataSourceOptions(data, valueKey, labelKey);
|
|
2785
2920
|
dispatch(setDataSource({ widgetId, data: transformed }));
|
|
2786
2921
|
}
|
|
@@ -2884,8 +3019,6 @@ const useWidgetCascade = (options) => {
|
|
|
2884
3019
|
}, [cascadeConfig, eventBus, dataSource, widgetId, dispatch]);
|
|
2885
3020
|
};
|
|
2886
3021
|
|
|
2887
|
-
// Define stable empty array to avoid selector reference issues
|
|
2888
|
-
const EMPTY_DATA_SOURCE = [];
|
|
2889
3022
|
/**
|
|
2890
3023
|
* Hook for geo widget cascade functionality
|
|
2891
3024
|
* Handles geo hierarchy building and cascade behavior
|
|
@@ -2915,15 +3048,15 @@ const useGeoWidgetCascade = (options) => {
|
|
|
2915
3048
|
? resolveGeoWidgetLevelValue(state.widget.values, widgetId, dataPath, geoConfig)
|
|
2916
3049
|
: state.widget.values[widgetId]);
|
|
2917
3050
|
// Memoize selector to avoid returning new array reference
|
|
2918
|
-
const
|
|
3051
|
+
const allDataSources = useSelector((state) => state.widget.dataSources);
|
|
2919
3052
|
// Register parent chain for upstream-ancestor detection (grandparent → grandchild reset)
|
|
2920
3053
|
useEffect(() => {
|
|
2921
|
-
if (!geoConfig) {
|
|
3054
|
+
if (!geoConfig || typeof dataPath !== 'string') {
|
|
2922
3055
|
return;
|
|
2923
3056
|
}
|
|
2924
|
-
|
|
2925
|
-
return () =>
|
|
2926
|
-
}, [widgetId, geoConfig]);
|
|
3057
|
+
registerGeoWidget(widgetId, geoConfig, dataPath);
|
|
3058
|
+
return () => unregisterGeoWidget(widgetId);
|
|
3059
|
+
}, [widgetId, geoConfig, dataPath]);
|
|
2927
3060
|
// Keep in-memory builder in sync with persisted hierarchy (reload, cancel→re-edit, etc.)
|
|
2928
3061
|
useEffect(() => {
|
|
2929
3062
|
if (!geoConfig || typeof dataPath !== 'string') {
|
|
@@ -3015,18 +3148,20 @@ const useGeoWidgetCascade = (options) => {
|
|
|
3015
3148
|
}, [geoConfig, eventBus, dataSource, widgetId, dataPath, dispatch, groupId]);
|
|
3016
3149
|
// Handle value changes to build hierarchy
|
|
3017
3150
|
useEffect(() => {
|
|
3018
|
-
if (!geoConfig) {
|
|
3151
|
+
if (!geoConfig || typeof dataPath !== 'string') {
|
|
3019
3152
|
return;
|
|
3020
3153
|
}
|
|
3021
|
-
|
|
3154
|
+
const { level, isLastLevel } = geoConfig;
|
|
3155
|
+
const groupRegistrations = getGeoWidgetRegistrationsInGroup(groupId);
|
|
3156
|
+
const applyGroupRebuild = () => {
|
|
3157
|
+
const resolveMnemonic = createGeoLevelMnemonicResolver(valuesRef.current, allDataSources);
|
|
3158
|
+
rebuildGeoHierarchyFromRegistrations(groupId, valuesRef.current, groupRegistrations, resolveMnemonic);
|
|
3159
|
+
dispatch(setValues(applySharedGeoHierarchyToValues(valuesRef.current, groupId, dataPath, widgetId)));
|
|
3160
|
+
};
|
|
3022
3161
|
// ONLY clear hierarchy if the value is explicitly null or empty string (user action)
|
|
3023
3162
|
if (currentValue === null || currentValue === '') {
|
|
3024
|
-
const { level, isLastLevel } = geoConfig;
|
|
3025
3163
|
geoHierarchyBuilder.removeLevelAndBelow(level, groupId);
|
|
3026
|
-
|
|
3027
|
-
if (dataPath) {
|
|
3028
|
-
dispatch(setValues(applySharedGeoHierarchyToValues(valuesRef.current, groupId, dataPath, widgetId)));
|
|
3029
|
-
}
|
|
3164
|
+
applyGroupRebuild();
|
|
3030
3165
|
if (!isLastLevel && eventBus && lastCascadePublishRef.current !== GEO_LEVEL_CLEARED) {
|
|
3031
3166
|
lastCascadePublishRef.current = GEO_LEVEL_CLEARED;
|
|
3032
3167
|
eventBus.publish({
|
|
@@ -3039,56 +3174,13 @@ const useGeoWidgetCascade = (options) => {
|
|
|
3039
3174
|
return;
|
|
3040
3175
|
}
|
|
3041
3176
|
if (currentValue === undefined) {
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
// Check if hierarchy is already built to prevent endless loops
|
|
3046
|
-
if (dataPath) {
|
|
3047
|
-
const currentHierarchy = getWidgetValue(valuesRef.current, dataPath, widgetId);
|
|
3048
|
-
// If hierarchy JSON is already set and matches current value, skip rebuilding
|
|
3049
|
-
if (currentHierarchy && typeof currentHierarchy === 'object') {
|
|
3050
|
-
// Check if this specific level's value matches the hierarchy
|
|
3051
|
-
const hierarchyArray = currentHierarchy.hierarchy || currentHierarchy.geo_code_hierarchy_json?.hierarchy;
|
|
3052
|
-
if (Array.isArray(hierarchyArray)) {
|
|
3053
|
-
const currentLevelValue = typeof currentValue === 'object'
|
|
3054
|
-
? (currentValue.level_value_id || currentValue.id || currentValue.value)
|
|
3055
|
-
: currentValue;
|
|
3056
|
-
const levelData = hierarchyArray.find((l) => l.level === geoConfig.level);
|
|
3057
|
-
// If this level is already correctly represented in the hierarchy, skip rebuilding
|
|
3058
|
-
// String conversion ensures comparison works for mixed types
|
|
3059
|
-
if (levelData && String(levelData.level_value_id) === String(currentLevelValue)) {
|
|
3060
|
-
return;
|
|
3061
|
-
}
|
|
3062
|
-
}
|
|
3177
|
+
const hasOwnValue = Object.prototype.hasOwnProperty.call(valuesRef.current, widgetId);
|
|
3178
|
+
if (!hasOwnValue) {
|
|
3179
|
+
return;
|
|
3063
3180
|
}
|
|
3064
3181
|
}
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
let level_value_id;
|
|
3068
|
-
let level_value_mnemonic;
|
|
3069
|
-
if (typeof currentValue === 'string' || typeof currentValue === 'number') {
|
|
3070
|
-
// Value is just the ID, need to find mnemonic from data source
|
|
3071
|
-
level_value_id = String(currentValue);
|
|
3072
|
-
// Try to get mnemonic from data source options
|
|
3073
|
-
const option = dataSourceOptions.find((opt) => opt.value === currentValue);
|
|
3074
|
-
level_value_mnemonic = option?.label || String(currentValue);
|
|
3075
|
-
}
|
|
3076
|
-
else if (currentValue && typeof currentValue === 'object') {
|
|
3077
|
-
level_value_id = currentValue.level_value_id || currentValue.id || currentValue.value;
|
|
3078
|
-
level_value_mnemonic = currentValue.level_value_mnemonic || currentValue.name || currentValue.label;
|
|
3079
|
-
}
|
|
3080
|
-
else {
|
|
3081
|
-
return;
|
|
3082
|
-
}
|
|
3083
|
-
// When a widget's own value changes, remove this level and all below from hierarchy first
|
|
3084
|
-
geoHierarchyBuilder.removeLevelAndBelow(level, groupId);
|
|
3085
|
-
// Add level to hierarchy
|
|
3086
|
-
geoHierarchyBuilder.addLevel(level, level_value_id, level_value_mnemonic, groupId);
|
|
3087
|
-
// Build and store hierarchy JSON on every change
|
|
3088
|
-
if (dataPath && geoHierarchyBuilder.buildHierarchyJson(groupId)) {
|
|
3089
|
-
dispatch(setValues(applySharedGeoHierarchyToValues(valuesRef.current, groupId, dataPath, widgetId)));
|
|
3090
|
-
}
|
|
3091
|
-
}, [geoConfig, currentValue, widgetId, dataPath, dispatch, dataSourceOptions, groupId]);
|
|
3182
|
+
applyGroupRebuild();
|
|
3183
|
+
}, [geoConfig, currentValue, widgetId, dataPath, dispatch, allDataSources, groupId, eventBus]);
|
|
3092
3184
|
};
|
|
3093
3185
|
|
|
3094
3186
|
class WidgetRegistry {
|
|
@@ -4990,7 +5082,12 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
|
|
|
4990
5082
|
// This ensures we use the original widget IDs and data paths
|
|
4991
5083
|
const sectionWidgets = collectWidgets(originalSection.panels);
|
|
4992
5084
|
const currentState = store.getState().widget;
|
|
4993
|
-
|
|
5085
|
+
let currentSchemaData = currentState.values || {};
|
|
5086
|
+
const geoRegistrations = collectGeoWidgetRegistrationsFromWidgets(sectionWidgets, namespace);
|
|
5087
|
+
if (geoRegistrations.length > 0) {
|
|
5088
|
+
currentSchemaData = reconcileGeoHierarchiesInValues(currentSchemaData, geoRegistrations, currentState.dataSources || {});
|
|
5089
|
+
dispatch(setValues(currentSchemaData));
|
|
5090
|
+
}
|
|
4994
5091
|
const isSectionValid = sectionValidate(originalSection, currentSchemaData, dispatch, true);
|
|
4995
5092
|
if (!isSectionValid) {
|
|
4996
5093
|
return;
|
|
@@ -5056,7 +5153,12 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
|
|
|
5056
5153
|
if (isDraft !== false && store && onSectionSave) {
|
|
5057
5154
|
const sectionWidgets = collectWidgets(originalSection.panels);
|
|
5058
5155
|
const currentState = store.getState().widget;
|
|
5059
|
-
|
|
5156
|
+
let currentSchemaData = currentState.values || {};
|
|
5157
|
+
const geoRegistrations = collectGeoWidgetRegistrationsFromWidgets(sectionWidgets, namespace);
|
|
5158
|
+
if (geoRegistrations.length > 0) {
|
|
5159
|
+
currentSchemaData = reconcileGeoHierarchiesInValues(currentSchemaData, geoRegistrations, currentState.dataSources || {});
|
|
5160
|
+
dispatch(setValues(currentSchemaData));
|
|
5161
|
+
}
|
|
5060
5162
|
const isSectionValid = sectionValidate(originalSection, currentSchemaData, dispatch, true);
|
|
5061
5163
|
if (!isSectionValid)
|
|
5062
5164
|
return;
|
|
@@ -8756,10 +8858,12 @@ const SelectWidget = ({ config }) => {
|
|
|
8756
8858
|
if (widgetConfig['widget-readonly']) {
|
|
8757
8859
|
const label = translateConfig(widgetConfig['widget-label']);
|
|
8758
8860
|
// Find the selected option's label
|
|
8759
|
-
const selectedOption = dataSourceOptions.find((option) => option.value === value);
|
|
8861
|
+
const selectedOption = dataSourceOptions.find((option) => option.value === value || String(option.value) === String(value));
|
|
8760
8862
|
const displayValue = selectedOption
|
|
8761
8863
|
? translateConfig(selectedOption.label)
|
|
8762
|
-
:
|
|
8864
|
+
: loading
|
|
8865
|
+
? (geoDisplayLabel || '-')
|
|
8866
|
+
: (geoDisplayLabel || (value != null && value !== '' ? translateConfig(String(value)) : '-'));
|
|
8763
8867
|
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 }) })] }));
|
|
8764
8868
|
}
|
|
8765
8869
|
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 === ''))
|
|
@@ -9258,7 +9362,7 @@ const SelectDisplayValue$1 = ({ config, value }) => {
|
|
|
9258
9362
|
if (value === null || value === undefined || value === '') {
|
|
9259
9363
|
return jsxRuntimeExports.jsx("span", { children: "-" });
|
|
9260
9364
|
}
|
|
9261
|
-
const selectedOption = dataSourceOptions.find((option) => option.value === value);
|
|
9365
|
+
const selectedOption = dataSourceOptions.find((option) => option.value === value || String(option.value) === String(value));
|
|
9262
9366
|
return jsxRuntimeExports.jsx("span", { children: selectedOption ? selectedOption.label : String(value) });
|
|
9263
9367
|
};
|
|
9264
9368
|
const TableCellText = ({ config, value, onValueChange }) => {
|
|
@@ -9989,7 +10093,7 @@ const SelectDisplayValue = ({ config, value }) => {
|
|
|
9989
10093
|
return jsxRuntimeExports.jsx("span", { children: "-" });
|
|
9990
10094
|
if (value === null || value === undefined || value === '')
|
|
9991
10095
|
return jsxRuntimeExports.jsx("span", { children: "-" });
|
|
9992
|
-
const selectedOption = dataSourceOptions.find((option) => option.value === value);
|
|
10096
|
+
const selectedOption = dataSourceOptions.find((option) => option.value === value || String(option.value) === String(value));
|
|
9993
10097
|
return jsxRuntimeExports.jsx("span", { children: selectedOption ? selectedOption.label : String(value) });
|
|
9994
10098
|
};
|
|
9995
10099
|
/**
|
|
@@ -12942,5 +13046,5 @@ const translateUISchema = (schema, translate) => {
|
|
|
12942
13046
|
};
|
|
12943
13047
|
};
|
|
12944
13048
|
|
|
12945
|
-
export { ArrayWidget, BooleanWidget, CheckboxWidget, CurrencyInputWidget, DateInputWidget, DateTimeInputWidget, DialogTableWidget, DisplayWidget, FileInputWidget, GEO_LEVEL_CLEARED, HeaderSectionWidget, IdAuthenticationWidget, IterableAccordionWidget, JSONEditorPanel, MultiSelectWidget, NumberInputWidget, PanelRenderer, PhoneInputWidget, ProfileWidget, PropertyEditor, RadioWidget, RegisterLookupWidget, ScoresDisplayWidget, SectionBuilder, SectionRenderer, SectionTree, SectionsContainer, SelectWidget, SimpleTableWidget, TableWidget, TextAreaWidget, TextInputWidget, VisualBuilderPanel, WidgetEventBus, WidgetProvider, WidgetRenderer, applyCaseControl, applyDecimalPrecision, applyMask, applySharedGeoHierarchyToValues, createWidgetStore, createZodSchema, defaultTheme, evaluateCondition, evaluateWidgetConditions, filterByCharacterType, formatCurrency, formatDate, formatNumber, formatPhone, formatValue, geoHierarchyBuilder, getApiDataSource, getFormattedNumberLength, getGeoDescendantWidgetIds, getGeoGroupId, getSchemaDataSource, getStaticDataSource, getValueByPath, getWidgetValue, hasVisibilityRules, initI18n, isAllowedKey, isUpstreamGeoAncestor, normalizeNumericDefault, normalizeOptionRules, parseDataPath, parseNumber, registerDefaultWidgets, registerGeoWidgetParent, removeMask, resetAll, resetAndSeedGeoHierarchyFromValues, resetWidget, resolveGeoWidgetLevelLabel, resolveGeoWidgetLevelValue, resolveTheme, resolveWidgetIdValue, seedGeoHierarchyFromValues, setDataSource, setError, setLoading, setTouched, setValue, setValueByPath, setValues, setWidgetValue, shouldEnableWidget, shouldRequireWidget, shouldShowWidget, transformDataSourceOptions, translatePanelConfig, translateUISchema, translateWidgetConfig, unregisterGeoWidgetParent, useBaseWidget, useGeoWidgetCascade, useWidgetCascade, useWidgetContext, useWidgetEventBus, useWidgetTheme, useWidgetTranslation, validateNumericValue, validateWidget, widgetRegistry };
|
|
13049
|
+
export { ArrayWidget, BooleanWidget, CheckboxWidget, CurrencyInputWidget, DateInputWidget, DateTimeInputWidget, DialogTableWidget, DisplayWidget, FileInputWidget, GEO_LEVEL_CLEARED, HeaderSectionWidget, IdAuthenticationWidget, IterableAccordionWidget, JSONEditorPanel, MultiSelectWidget, NumberInputWidget, PanelRenderer, PhoneInputWidget, ProfileWidget, PropertyEditor, RadioWidget, RegisterLookupWidget, ScoresDisplayWidget, SectionBuilder, SectionRenderer, SectionTree, SectionsContainer, SelectWidget, SimpleTableWidget, TableWidget, TextAreaWidget, TextInputWidget, VisualBuilderPanel, WidgetEventBus, WidgetProvider, WidgetRenderer, applyCaseControl, applyDecimalPrecision, applyMask, applySharedGeoHierarchyToValues, collectGeoWidgetRegistrationsFromWidgets, createGeoLevelMnemonicResolver, createWidgetStore, createZodSchema, defaultTheme, evaluateCondition, evaluateWidgetConditions, filterByCharacterType, formatCurrency, formatDate, formatNumber, formatPhone, formatValue, geoHierarchyBuilder, getApiDataSource, getCachedApiDataSource, getFormattedNumberLength, getGeoDescendantWidgetIds, getGeoGroupId, getGeoWidgetRegistrationsInGroup, getSchemaDataSource, getStaticDataSource, getValueByPath, getWidgetValue, hasVisibilityRules, initI18n, isAllowedKey, isUpstreamGeoAncestor, normalizeNumericDefault, normalizeOptionRules, orderGeoWidgetRegistrations, parseDataPath, parseNumber, rebuildGeoHierarchyFromRegistrations, reconcileGeoHierarchiesInValues, registerDefaultWidgets, registerGeoWidget, registerGeoWidgetParent, removeMask, resetAll, resetAndSeedGeoHierarchyFromValues, resetWidget, resolveGeoWidgetLevelLabel, resolveGeoWidgetLevelValue, resolveTheme, resolveWidgetIdValue, seedGeoHierarchyFromValues, setDataSource, setError, setLoading, setTouched, setValue, setValueByPath, setValues, setWidgetValue, shouldEnableWidget, shouldRequireWidget, shouldShowWidget, transformDataSourceOptions, translatePanelConfig, translateUISchema, translateWidgetConfig, unregisterGeoWidget, unregisterGeoWidgetParent, useBaseWidget, useGeoWidgetCascade, useWidgetCascade, useWidgetContext, useWidgetEventBus, useWidgetTheme, useWidgetTranslation, validateNumericValue, validateWidget, widgetRegistry };
|
|
12946
13050
|
//# sourceMappingURL=index.esm.js.map
|