jupiter-dynamic-forms 1.20.6 â 1.20.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/add-column-dialog.d.ts +31 -2
- package/dist/core/add-column-dialog.d.ts.map +1 -1
- package/dist/core/dynamic-form.d.ts +6 -0
- package/dist/core/dynamic-form.d.ts.map +1 -1
- package/dist/core/form-field.d.ts.map +1 -1
- package/dist/core/form-section.d.ts +28 -2
- package/dist/core/form-section.d.ts.map +1 -1
- package/dist/index.js +312 -152
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +993 -296
- package/dist/index.mjs.map +1 -1
- package/dist/schema/types.d.ts +18 -0
- package/dist/schema/types.d.ts.map +1 -1
- package/dist/schema/xbrl-types.d.ts +20 -0
- package/dist/schema/xbrl-types.d.ts.map +1 -1
- package/dist/utils/formula-precondition-evaluator.d.ts +4 -1
- package/dist/utils/formula-precondition-evaluator.d.ts.map +1 -1
- package/dist/utils/formula-resolution-context.d.ts +29 -0
- package/dist/utils/formula-resolution-context.d.ts.map +1 -1
- package/dist/utils/formula-variable-resolver.d.ts +96 -4
- package/dist/utils/formula-variable-resolver.d.ts.map +1 -1
- package/dist/utils/type-input-mapping.d.ts +4 -4
- package/dist/utils/type-input-mapping.d.ts.map +1 -1
- package/dist/utils/xbrl-form-builder.d.ts.map +1 -1
- package/dist/utils/xbrl-validation.d.ts +9 -0
- package/dist/utils/xbrl-validation.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -989,7 +989,9 @@ class XBRLFormBuilder {
|
|
|
989
989
|
axisId: dimension.id,
|
|
990
990
|
axisLabel,
|
|
991
991
|
typedMemberId: dimension.typedMember.id,
|
|
992
|
-
memberLabel: axisLabel
|
|
992
|
+
memberLabel: axisLabel,
|
|
993
|
+
type: dimension.typedMember.type,
|
|
994
|
+
validation: dimension.typedMember.validation
|
|
993
995
|
});
|
|
994
996
|
}
|
|
995
997
|
return result.length > 0 ? result : null;
|
|
@@ -1181,6 +1183,8 @@ class XBRLFormBuilder {
|
|
|
1181
1183
|
axisId: dimension.id,
|
|
1182
1184
|
axisLabel: axisLabel2,
|
|
1183
1185
|
typedMemberId: dimension.typedMember.id,
|
|
1186
|
+
type: dimension.typedMember.type,
|
|
1187
|
+
validation: dimension.typedMember.validation,
|
|
1184
1188
|
dimensionKey: `${axisLabel2} | [Typed Input]`,
|
|
1185
1189
|
dimensionIdKey: `${dimension.id}|[typed]`
|
|
1186
1190
|
};
|
|
@@ -1207,7 +1211,9 @@ class XBRLFormBuilder {
|
|
|
1207
1211
|
axisId: dimensionInfo2.axisId,
|
|
1208
1212
|
axisLabel: dimensionInfo2.axisLabel,
|
|
1209
1213
|
typedMemberId: dimensionInfo2.typedMemberId,
|
|
1210
|
-
memberLabel: dimensionInfo2.axisLabel
|
|
1214
|
+
memberLabel: dimensionInfo2.axisLabel,
|
|
1215
|
+
type: dimensionInfo2.type,
|
|
1216
|
+
validation: dimensionInfo2.validation
|
|
1211
1217
|
}]
|
|
1212
1218
|
},
|
|
1213
1219
|
order: 0,
|
|
@@ -1237,7 +1243,9 @@ class XBRLFormBuilder {
|
|
|
1237
1243
|
axisId: dimensionInfo2.axisId,
|
|
1238
1244
|
axisLabel: dimensionInfo2.axisLabel,
|
|
1239
1245
|
typedMemberId: dimensionInfo2.typedMemberId,
|
|
1240
|
-
memberLabel: dimensionInfo2.axisLabel
|
|
1246
|
+
memberLabel: dimensionInfo2.axisLabel,
|
|
1247
|
+
type: dimensionInfo2.type,
|
|
1248
|
+
validation: dimensionInfo2.validation
|
|
1241
1249
|
}]
|
|
1242
1250
|
},
|
|
1243
1251
|
order: 0,
|
|
@@ -1267,7 +1275,9 @@ class XBRLFormBuilder {
|
|
|
1267
1275
|
axisId: dimensionInfo2.axisId,
|
|
1268
1276
|
axisLabel: dimensionInfo2.axisLabel,
|
|
1269
1277
|
typedMemberId: dimensionInfo2.typedMemberId,
|
|
1270
|
-
memberLabel: dimensionInfo2.axisLabel
|
|
1278
|
+
memberLabel: dimensionInfo2.axisLabel,
|
|
1279
|
+
type: dimensionInfo2.type,
|
|
1280
|
+
validation: dimensionInfo2.validation
|
|
1271
1281
|
}]
|
|
1272
1282
|
},
|
|
1273
1283
|
order: 0,
|
|
@@ -1297,7 +1307,9 @@ class XBRLFormBuilder {
|
|
|
1297
1307
|
axisId: dimensionInfo2.axisId,
|
|
1298
1308
|
axisLabel: dimensionInfo2.axisLabel,
|
|
1299
1309
|
typedMemberId: dimensionInfo2.typedMemberId,
|
|
1300
|
-
memberLabel: dimensionInfo2.axisLabel
|
|
1310
|
+
memberLabel: dimensionInfo2.axisLabel,
|
|
1311
|
+
type: dimensionInfo2.type,
|
|
1312
|
+
validation: dimensionInfo2.validation
|
|
1301
1313
|
}]
|
|
1302
1314
|
},
|
|
1303
1315
|
order: 0,
|
|
@@ -1442,6 +1454,8 @@ class XBRLFormBuilder {
|
|
|
1442
1454
|
axisLabel,
|
|
1443
1455
|
isTyped: true,
|
|
1444
1456
|
typedMemberId: dimension.typedMember.id,
|
|
1457
|
+
type: dimension.typedMember.type,
|
|
1458
|
+
validation: dimension.typedMember.validation,
|
|
1445
1459
|
members: [{
|
|
1446
1460
|
id: "[typed]",
|
|
1447
1461
|
label: `${axisLabel}`
|
|
@@ -1482,7 +1496,9 @@ class XBRLFormBuilder {
|
|
|
1482
1496
|
axisId: c2.axisId,
|
|
1483
1497
|
axisLabel: c2.axisLabel,
|
|
1484
1498
|
typedMemberId: c2.typedMemberId,
|
|
1485
|
-
memberLabel: c2.memberLabel
|
|
1499
|
+
memberLabel: c2.memberLabel,
|
|
1500
|
+
type: c2.type,
|
|
1501
|
+
validation: c2.validation
|
|
1486
1502
|
})),
|
|
1487
1503
|
hasTypedMembers: typedMembers.length > 0,
|
|
1488
1504
|
memberKey: columnTitle,
|
|
@@ -1537,7 +1553,9 @@ class XBRLFormBuilder {
|
|
|
1537
1553
|
memberId: member.id,
|
|
1538
1554
|
memberLabel: member.label,
|
|
1539
1555
|
isTyped: dimensionInfos[0].isTyped || false,
|
|
1540
|
-
typedMemberId: dimensionInfos[0].typedMemberId
|
|
1556
|
+
typedMemberId: dimensionInfos[0].typedMemberId,
|
|
1557
|
+
type: dimensionInfos[0].type,
|
|
1558
|
+
validation: dimensionInfos[0].validation
|
|
1541
1559
|
}]);
|
|
1542
1560
|
}
|
|
1543
1561
|
const [firstDimension, ...restDimensions] = dimensionInfos;
|
|
@@ -1551,7 +1569,9 @@ class XBRLFormBuilder {
|
|
|
1551
1569
|
memberId: member.id,
|
|
1552
1570
|
memberLabel: member.label,
|
|
1553
1571
|
isTyped: firstDimension.isTyped || false,
|
|
1554
|
-
typedMemberId: firstDimension.typedMemberId
|
|
1572
|
+
typedMemberId: firstDimension.typedMemberId,
|
|
1573
|
+
type: firstDimension.type,
|
|
1574
|
+
validation: firstDimension.validation
|
|
1555
1575
|
}]);
|
|
1556
1576
|
} else {
|
|
1557
1577
|
restCombinations.forEach((restCombination) => {
|
|
@@ -1562,7 +1582,9 @@ class XBRLFormBuilder {
|
|
|
1562
1582
|
memberId: member.id,
|
|
1563
1583
|
memberLabel: member.label,
|
|
1564
1584
|
isTyped: firstDimension.isTyped || false,
|
|
1565
|
-
typedMemberId: firstDimension.typedMemberId
|
|
1585
|
+
typedMemberId: firstDimension.typedMemberId,
|
|
1586
|
+
type: firstDimension.type,
|
|
1587
|
+
validation: firstDimension.validation
|
|
1566
1588
|
},
|
|
1567
1589
|
...restCombination
|
|
1568
1590
|
]);
|
|
@@ -1783,7 +1805,8 @@ const admin$1 = {
|
|
|
1783
1805
|
const validation$1 = {
|
|
1784
1806
|
summary: "Please fix the following errors before submitting:",
|
|
1785
1807
|
errorsFound: "errors found",
|
|
1786
|
-
unitRequired: "Unit is required. Please select a unit via the period icon."
|
|
1808
|
+
unitRequired: "Unit is required. Please select a unit via the period icon.",
|
|
1809
|
+
typedMemberRequired: "This field is required because data has been entered in this column."
|
|
1787
1810
|
};
|
|
1788
1811
|
const xbrlValidation$1 = {
|
|
1789
1812
|
patternMismatch: "Value does not match required pattern: {{pattern}}",
|
|
@@ -1989,7 +2012,8 @@ const admin = {
|
|
|
1989
2012
|
const validation = {
|
|
1990
2013
|
summary: "Corrigeer de volgende fouten voordat u indient:",
|
|
1991
2014
|
errorsFound: "fouten gevonden",
|
|
1992
|
-
unitRequired: "Eenheid is verplicht. Selecteer een eenheid via het periodeicoon."
|
|
2015
|
+
unitRequired: "Eenheid is verplicht. Selecteer een eenheid via het periodeicoon.",
|
|
2016
|
+
typedMemberRequired: "Dit veld is verplicht omdat er gegevens zijn ingevoerd in deze kolom."
|
|
1993
2017
|
};
|
|
1994
2018
|
const xbrlValidation = {
|
|
1995
2019
|
patternMismatch: "Waarde voldoet niet aan het vereiste patroon: {{pattern}}",
|
|
@@ -2766,13 +2790,24 @@ function indexConceptTree(concept, conceptByQName, conceptById, conceptIdByQName
|
|
|
2766
2790
|
);
|
|
2767
2791
|
children.forEach((child) => indexConceptTree(child, conceptByQName, conceptById, conceptIdByQName, conceptIdByQNameHasChildren, childrenIndex));
|
|
2768
2792
|
}
|
|
2769
|
-
function
|
|
2793
|
+
function collectMemberSubtreeIds(member) {
|
|
2794
|
+
const ids = [member.id];
|
|
2795
|
+
(member.children || []).forEach((child) => ids.push(...collectMemberSubtreeIds(child)));
|
|
2796
|
+
return ids;
|
|
2797
|
+
}
|
|
2798
|
+
function indexMemberTree(member, memberQNameToId, memberChildrenByQName, memberDescendantOrSelfIdsByQName, memberIdsWithParent) {
|
|
2770
2799
|
if (!memberQNameToId.has(member.conceptName))
|
|
2771
2800
|
memberQNameToId.set(member.conceptName, member.id);
|
|
2772
2801
|
if (!memberChildrenByQName.has(member.conceptName)) {
|
|
2773
2802
|
memberChildrenByQName.set(member.conceptName, (member.children || []).map((child) => child.id));
|
|
2774
2803
|
}
|
|
2775
|
-
(member.
|
|
2804
|
+
if (!memberDescendantOrSelfIdsByQName.has(member.conceptName)) {
|
|
2805
|
+
memberDescendantOrSelfIdsByQName.set(member.conceptName, collectMemberSubtreeIds(member));
|
|
2806
|
+
}
|
|
2807
|
+
(member.children || []).forEach((child) => {
|
|
2808
|
+
memberIdsWithParent.add(child.id);
|
|
2809
|
+
indexMemberTree(child, memberQNameToId, memberChildrenByQName, memberDescendantOrSelfIdsByQName, memberIdsWithParent);
|
|
2810
|
+
});
|
|
2776
2811
|
}
|
|
2777
2812
|
const PRESENTATION_TOTAL_GROUP_ACCESSORS = {
|
|
2778
2813
|
getId: (concept) => concept.id,
|
|
@@ -2802,16 +2837,35 @@ function buildFormulaResolutionContext(formData, columns, sections, hypercubeRol
|
|
|
2802
2837
|
if (section2.columns)
|
|
2803
2838
|
columnsByRole.set(section2.roleURI, section2.columns);
|
|
2804
2839
|
});
|
|
2840
|
+
const crossRoleChildrenByQName = /* @__PURE__ */ new Map();
|
|
2841
|
+
conceptIdByQNamePerRole.forEach((conceptIdByQName, roleURI) => {
|
|
2842
|
+
const childrenIndex = childrenByRole.get(roleURI);
|
|
2843
|
+
const totalGroupIndex = totalGroupChildrenByRole.get(roleURI);
|
|
2844
|
+
conceptIdByQName.forEach((conceptId, qname) => {
|
|
2845
|
+
var _a;
|
|
2846
|
+
const nestedChildren = (childrenIndex == null ? void 0 : childrenIndex.get(conceptId)) || [];
|
|
2847
|
+
const children = nestedChildren.length > 0 ? nestedChildren : (totalGroupIndex == null ? void 0 : totalGroupIndex.get(conceptId)) || [];
|
|
2848
|
+
if (children.length === 0)
|
|
2849
|
+
return;
|
|
2850
|
+
if (!crossRoleChildrenByQName.has(qname))
|
|
2851
|
+
crossRoleChildrenByQName.set(qname, /* @__PURE__ */ new Map());
|
|
2852
|
+
(_a = crossRoleChildrenByQName.get(qname)) == null ? void 0 : _a.set(roleURI, children);
|
|
2853
|
+
});
|
|
2854
|
+
});
|
|
2805
2855
|
const dimensionQNameToId = /* @__PURE__ */ new Map();
|
|
2806
2856
|
const memberQNameToId = /* @__PURE__ */ new Map();
|
|
2807
2857
|
const memberChildrenByQName = /* @__PURE__ */ new Map();
|
|
2858
|
+
const memberDescendantOrSelfIdsByQName = /* @__PURE__ */ new Map();
|
|
2859
|
+
const memberIdsWithParent = /* @__PURE__ */ new Set();
|
|
2808
2860
|
(hypercubeRoles || []).forEach((role) => {
|
|
2809
2861
|
role.items.forEach((item) => {
|
|
2810
2862
|
item.dimensions.forEach((dimension) => {
|
|
2811
2863
|
if (!dimensionQNameToId.has(dimension.conceptName)) {
|
|
2812
2864
|
dimensionQNameToId.set(dimension.conceptName, dimension.id);
|
|
2813
2865
|
}
|
|
2814
|
-
(dimension.members || []).forEach(
|
|
2866
|
+
(dimension.members || []).forEach(
|
|
2867
|
+
(member) => indexMemberTree(member, memberQNameToId, memberChildrenByQName, memberDescendantOrSelfIdsByQName, memberIdsWithParent)
|
|
2868
|
+
);
|
|
2815
2869
|
});
|
|
2816
2870
|
});
|
|
2817
2871
|
});
|
|
@@ -2825,9 +2879,12 @@ function buildFormulaResolutionContext(formData, columns, sections, hypercubeRol
|
|
|
2825
2879
|
conceptIdByQNamePerRole,
|
|
2826
2880
|
childrenByRole,
|
|
2827
2881
|
totalGroupChildrenByRole,
|
|
2882
|
+
crossRoleChildrenByQName,
|
|
2828
2883
|
dimensionQNameToId,
|
|
2829
2884
|
memberQNameToId,
|
|
2830
2885
|
memberChildrenByQName,
|
|
2886
|
+
memberDescendantOrSelfIdsByQName,
|
|
2887
|
+
memberIdsWithParent,
|
|
2831
2888
|
columnsByRole,
|
|
2832
2889
|
roleURIByConceptId,
|
|
2833
2890
|
allCandidates: collectAllCandidates(formData),
|
|
@@ -2951,6 +3008,21 @@ function resolveChildMemberAxisFilter(memberQName, axisId, universe, ctx, siblin
|
|
|
2951
3008
|
return [];
|
|
2952
3009
|
return universe.filter((candidate) => !directChildren.has(candidate.conceptId));
|
|
2953
3010
|
}
|
|
3011
|
+
function resolveDescendantOrSelfMemberAxisFilter(memberQName, axisId, universe, ctx) {
|
|
3012
|
+
const memberIds = ctx.memberDescendantOrSelfIdsByQName.get(memberQName) || [];
|
|
3013
|
+
if (memberIds.length === 0)
|
|
3014
|
+
return [];
|
|
3015
|
+
return universe.filter((candidate) => memberIds.some((id) => columnMatchesExplicitDimension(getColumn(ctx, candidate), axisId, id)));
|
|
3016
|
+
}
|
|
3017
|
+
function resolveCrossRoleChildren(anchorQName, ownLinkrole, ctx) {
|
|
3018
|
+
if (!anchorQName)
|
|
3019
|
+
return [];
|
|
3020
|
+
const candidatesByRole = ctx.crossRoleChildrenByQName.get(anchorQName);
|
|
3021
|
+
if (!candidatesByRole)
|
|
3022
|
+
return [];
|
|
3023
|
+
const otherRoles = Array.from(candidatesByRole.entries()).filter(([roleURI]) => roleURI !== ownLinkrole);
|
|
3024
|
+
return otherRoles.length === 1 ? otherRoles[0][1] : [];
|
|
3025
|
+
}
|
|
2954
3026
|
function matchFilter(filter2, universe, ctx, siblingFilters) {
|
|
2955
3027
|
var _a, _b;
|
|
2956
3028
|
switch (filter2.type) {
|
|
@@ -2983,6 +3055,9 @@ function matchFilter(filter2, universe, ctx, siblingFilters) {
|
|
|
2983
3055
|
} else {
|
|
2984
3056
|
const nestedChildren = childrenIndex.get(anchorConceptId) || [];
|
|
2985
3057
|
relatedConceptIds = nestedChildren.length > 0 ? nestedChildren : ((_b = ctx.totalGroupChildrenByRole.get(linkrole ?? "")) == null ? void 0 : _b.get(anchorConceptId)) || [];
|
|
3058
|
+
if (relatedConceptIds.length === 0) {
|
|
3059
|
+
relatedConceptIds = resolveCrossRoleChildren(anchorQName, linkrole, ctx);
|
|
3060
|
+
}
|
|
2986
3061
|
}
|
|
2987
3062
|
const relatedSet = new Set(relatedConceptIds);
|
|
2988
3063
|
return universe.filter((candidate) => relatedSet.has(candidate.conceptId));
|
|
@@ -2993,9 +3068,13 @@ function matchFilter(filter2, universe, ctx, siblingFilters) {
|
|
|
2993
3068
|
const axisId = dimensionQName ? ctx.dimensionQNameToId.get(dimensionQName) : void 0;
|
|
2994
3069
|
if (!axisId)
|
|
2995
3070
|
return [];
|
|
2996
|
-
|
|
3071
|
+
const memberAxis = filter2.attributes["member.axis"];
|
|
3072
|
+
if (memberQName && memberAxis === "child") {
|
|
2997
3073
|
return resolveChildMemberAxisFilter(memberQName, axisId, universe, ctx, siblingFilters);
|
|
2998
3074
|
}
|
|
3075
|
+
if (memberQName && memberAxis === "descendant-or-self") {
|
|
3076
|
+
return resolveDescendantOrSelfMemberAxisFilter(memberQName, axisId, universe, ctx);
|
|
3077
|
+
}
|
|
2999
3078
|
const memberId = memberQName ? ctx.memberQNameToId.get(memberQName) : void 0;
|
|
3000
3079
|
return universe.filter((candidate) => columnMatchesExplicitDimension(getColumn(ctx, candidate), axisId, memberId));
|
|
3001
3080
|
}
|
|
@@ -3015,8 +3094,17 @@ function matchFilter(filter2, universe, ctx, siblingFilters) {
|
|
|
3015
3094
|
return unionCandidates(filter2.children.map((child) => resolveFilter(child, universe, ctx, siblingFilters)));
|
|
3016
3095
|
case "ASPECT_COVER":
|
|
3017
3096
|
return universe;
|
|
3018
|
-
case "UNKNOWN":
|
|
3097
|
+
case "UNKNOWN": {
|
|
3098
|
+
const boundary = filter2.attributes["boundary"];
|
|
3099
|
+
if (boundary === "start" || boundary === "end") {
|
|
3100
|
+
const requiredRole = boundary === "start" ? "periodStartLabel" : "periodEndLabel";
|
|
3101
|
+
return universe.filter((candidate) => {
|
|
3102
|
+
var _a2;
|
|
3103
|
+
return ((_a2 = ctx.conceptById.get(candidate.conceptId)) == null ? void 0 : _a2.effectivePeriodRole) === requiredRole;
|
|
3104
|
+
});
|
|
3105
|
+
}
|
|
3019
3106
|
return universe;
|
|
3107
|
+
}
|
|
3020
3108
|
default:
|
|
3021
3109
|
return [];
|
|
3022
3110
|
}
|
|
@@ -3104,18 +3192,20 @@ function narrowToDirectChildOfDescendantAnchor(candidates, variable, ctx) {
|
|
|
3104
3192
|
return candidates;
|
|
3105
3193
|
return candidates.filter((candidate) => directChildren.has(candidate.conceptId));
|
|
3106
3194
|
}
|
|
3107
|
-
function
|
|
3108
|
-
|
|
3195
|
+
function resolveNarrowedCandidates(variable, ctx) {
|
|
3196
|
+
return narrowByPreferredOccurrence(
|
|
3109
3197
|
narrowToDirectChildOfDescendantAnchor(narrowToCurrentPeriod(resolveMatchedCandidates(variable, ctx), variable, ctx), variable, ctx),
|
|
3110
3198
|
ctx
|
|
3111
3199
|
);
|
|
3200
|
+
}
|
|
3201
|
+
function valuesFromCandidates(variable, matched, ctx) {
|
|
3112
3202
|
const values = matched.map((candidate) => {
|
|
3113
3203
|
var _a;
|
|
3114
3204
|
return resolveRawValue((_a = ctx.formData[candidate.conceptId]) == null ? void 0 : _a[candidate.columnId]);
|
|
3115
3205
|
}).filter((value) => value !== void 0);
|
|
3206
|
+
if (values.length === 0 && variable.fallbackValue === "()")
|
|
3207
|
+
return [];
|
|
3116
3208
|
if (variable.bindAsSequence) {
|
|
3117
|
-
if (values.length === 0 && variable.fallbackValue === "()")
|
|
3118
|
-
return [];
|
|
3119
3209
|
if (values.length === 0)
|
|
3120
3210
|
return void 0;
|
|
3121
3211
|
return values;
|
|
@@ -3129,6 +3219,186 @@ function resolveFormulaVariable(variable, ctx) {
|
|
|
3129
3219
|
}
|
|
3130
3220
|
return values[0];
|
|
3131
3221
|
}
|
|
3222
|
+
function uncoveredExplicitDimensionAxisIds(variable, ctx) {
|
|
3223
|
+
const axisIds = /* @__PURE__ */ new Set();
|
|
3224
|
+
for (const filter2 of variable.filters) {
|
|
3225
|
+
if (filter2.type !== "EXPLICIT_DIMENSION" || filter2.complement || filter2.cover)
|
|
3226
|
+
continue;
|
|
3227
|
+
if (filter2.attributes["member.qname"])
|
|
3228
|
+
continue;
|
|
3229
|
+
const axisId = ctx.dimensionQNameToId.get(filter2.attributes["dimension.qname"] ?? "");
|
|
3230
|
+
if (axisId)
|
|
3231
|
+
axisIds.add(axisId);
|
|
3232
|
+
}
|
|
3233
|
+
return axisIds;
|
|
3234
|
+
}
|
|
3235
|
+
function candidateMemberIdForAxis(ctx, candidate, axisId) {
|
|
3236
|
+
var _a;
|
|
3237
|
+
const column2 = getColumn(ctx, candidate);
|
|
3238
|
+
if (!(column2 == null ? void 0 : column2.dimensionData))
|
|
3239
|
+
return void 0;
|
|
3240
|
+
if (column2.dimensionData.axisId === axisId)
|
|
3241
|
+
return column2.dimensionData.memberId;
|
|
3242
|
+
return (_a = (column2.dimensionData.combinations || []).find((combo) => combo.axisId === axisId)) == null ? void 0 : _a.memberId;
|
|
3243
|
+
}
|
|
3244
|
+
function findUncoveredAxisAnchor(variables, axisId, candidatesByName, ctx) {
|
|
3245
|
+
for (const variable of variables) {
|
|
3246
|
+
if (variable.bindAsSequence)
|
|
3247
|
+
continue;
|
|
3248
|
+
if (!uncoveredExplicitDimensionAxisIds(variable, ctx).has(axisId))
|
|
3249
|
+
continue;
|
|
3250
|
+
const candidates = candidatesByName.get(variable.name) || [];
|
|
3251
|
+
const memberIds = new Set(
|
|
3252
|
+
candidates.map((candidate) => candidateMemberIdForAxis(ctx, candidate, axisId)).filter((id) => id !== void 0)
|
|
3253
|
+
);
|
|
3254
|
+
if (memberIds.size === 1)
|
|
3255
|
+
return { memberId: [...memberIds][0], variableName: variable.name, ambiguous: false };
|
|
3256
|
+
const rootMembers = [...memberIds].filter((id) => !ctx.memberIdsWithParent.has(id));
|
|
3257
|
+
if (rootMembers.length === 1)
|
|
3258
|
+
return { memberId: rootMembers[0], variableName: variable.name, ambiguous: true };
|
|
3259
|
+
}
|
|
3260
|
+
return void 0;
|
|
3261
|
+
}
|
|
3262
|
+
function resolveAssertionVariableBindings(variables, ctx, implicitFiltering) {
|
|
3263
|
+
const candidatesByName = /* @__PURE__ */ new Map();
|
|
3264
|
+
variables.forEach((variable) => candidatesByName.set(variable.name, resolveNarrowedCandidates(variable, ctx)));
|
|
3265
|
+
if (implicitFiltering) {
|
|
3266
|
+
const axisIds = /* @__PURE__ */ new Set();
|
|
3267
|
+
variables.forEach((variable) => uncoveredExplicitDimensionAxisIds(variable, ctx).forEach((axisId) => axisIds.add(axisId)));
|
|
3268
|
+
axisIds.forEach((axisId) => {
|
|
3269
|
+
const anchor = findUncoveredAxisAnchor(variables, axisId, candidatesByName, ctx);
|
|
3270
|
+
if (!anchor)
|
|
3271
|
+
return;
|
|
3272
|
+
variables.forEach((variable) => {
|
|
3273
|
+
const isNarrowableSequence = variable.bindAsSequence && uncoveredExplicitDimensionAxisIds(variable, ctx).has(axisId);
|
|
3274
|
+
const isAmbiguousAnchorItself = anchor.ambiguous && variable.name === anchor.variableName;
|
|
3275
|
+
if (!isNarrowableSequence && !isAmbiguousAnchorItself)
|
|
3276
|
+
return;
|
|
3277
|
+
const candidates = candidatesByName.get(variable.name) || [];
|
|
3278
|
+
candidatesByName.set(
|
|
3279
|
+
variable.name,
|
|
3280
|
+
candidates.filter((candidate) => candidateMemberIdForAxis(ctx, candidate, axisId) === anchor.memberId)
|
|
3281
|
+
);
|
|
3282
|
+
});
|
|
3283
|
+
});
|
|
3284
|
+
}
|
|
3285
|
+
const bindings = /* @__PURE__ */ new Map();
|
|
3286
|
+
variables.forEach((variable) => bindings.set(variable.name, valuesFromCandidates(variable, candidatesByName.get(variable.name) || [], ctx)));
|
|
3287
|
+
return bindings;
|
|
3288
|
+
}
|
|
3289
|
+
function filterSignature(filter2) {
|
|
3290
|
+
const attrs = Object.entries(filter2.attributes).filter(([key]) => key !== "id").sort(([a2], [b2]) => a2.localeCompare(b2));
|
|
3291
|
+
return JSON.stringify({ type: filter2.type, complement: filter2.complement, cover: filter2.cover, attrs });
|
|
3292
|
+
}
|
|
3293
|
+
function detectSumOfMembersOnAbstractShape(assertion) {
|
|
3294
|
+
if (assertion.type !== "VALUE_ASSERTION" || assertion.variables.length !== 2)
|
|
3295
|
+
return void 0;
|
|
3296
|
+
const scalarVariable = assertion.variables.find((v) => !v.bindAsSequence);
|
|
3297
|
+
const sequenceVariable = assertion.variables.find((v) => v.bindAsSequence);
|
|
3298
|
+
if (!scalarVariable || !sequenceVariable)
|
|
3299
|
+
return void 0;
|
|
3300
|
+
const isDescendantRelation = (f2) => f2.type === "CONCEPT_RELATION" && f2.attributes["axis"] === "descendant" && !f2.complement && !f2.cover;
|
|
3301
|
+
const scalarCR = scalarVariable.filters.find(isDescendantRelation);
|
|
3302
|
+
const seqCR = sequenceVariable.filters.find(isDescendantRelation);
|
|
3303
|
+
if (!scalarCR || !seqCR)
|
|
3304
|
+
return void 0;
|
|
3305
|
+
const anchorQName = scalarCR.attributes["qname"];
|
|
3306
|
+
const linkrole = scalarCR.attributes["linkrole"];
|
|
3307
|
+
if (!anchorQName || !linkrole)
|
|
3308
|
+
return void 0;
|
|
3309
|
+
if (seqCR.attributes["qname"] !== anchorQName || seqCR.attributes["linkrole"] !== linkrole)
|
|
3310
|
+
return void 0;
|
|
3311
|
+
const scalarED = scalarVariable.filters.find(
|
|
3312
|
+
(f2) => f2.type === "EXPLICIT_DIMENSION" && !f2.complement && f2.cover && !("member.axis" in f2.attributes)
|
|
3313
|
+
);
|
|
3314
|
+
const seqED = sequenceVariable.filters.find(
|
|
3315
|
+
(f2) => f2.type === "EXPLICIT_DIMENSION" && !f2.complement && f2.cover && f2.attributes["member.axis"] === "child"
|
|
3316
|
+
);
|
|
3317
|
+
if (!scalarED || !seqED)
|
|
3318
|
+
return void 0;
|
|
3319
|
+
const dimensionQName = scalarED.attributes["dimension.qname"];
|
|
3320
|
+
const memberQName = scalarED.attributes["member.qname"];
|
|
3321
|
+
if (!dimensionQName || !memberQName)
|
|
3322
|
+
return void 0;
|
|
3323
|
+
if (seqED.attributes["dimension.qname"] !== dimensionQName || seqED.attributes["member.qname"] !== memberQName)
|
|
3324
|
+
return void 0;
|
|
3325
|
+
const scalarOther = scalarVariable.filters.filter((f2) => f2 !== scalarCR && f2 !== scalarED).map(filterSignature).sort();
|
|
3326
|
+
const seqOther = sequenceVariable.filters.filter((f2) => f2 !== seqCR && f2 !== seqED).map(filterSignature).sort();
|
|
3327
|
+
if (scalarOther.length !== seqOther.length || scalarOther.some((sig, i2) => sig !== seqOther[i2]))
|
|
3328
|
+
return void 0;
|
|
3329
|
+
return { scalarVariable, sequenceVariable, anchorQName, linkrole, dimensionQName, memberQName };
|
|
3330
|
+
}
|
|
3331
|
+
function resolveSumOfMembersOnAbstractSatisfaction(shape, ctx) {
|
|
3332
|
+
var _a;
|
|
3333
|
+
const { scalarVariable, sequenceVariable, anchorQName, linkrole, dimensionQName, memberQName } = shape;
|
|
3334
|
+
const anchorConceptId = (_a = ctx.conceptIdByQNamePerRole.get(linkrole)) == null ? void 0 : _a.get(anchorQName);
|
|
3335
|
+
const childrenIndex = ctx.childrenByRole.get(linkrole);
|
|
3336
|
+
if (!anchorConceptId || !childrenIndex)
|
|
3337
|
+
return "not-applicable";
|
|
3338
|
+
const descendantConceptIds = collectDescendantConceptIds(childrenIndex, anchorConceptId);
|
|
3339
|
+
if (descendantConceptIds.length === 0)
|
|
3340
|
+
return "not-applicable";
|
|
3341
|
+
const axisId = ctx.dimensionQNameToId.get(dimensionQName);
|
|
3342
|
+
const rootMemberId = ctx.memberQNameToId.get(memberQName);
|
|
3343
|
+
const childMemberIds = ctx.memberChildrenByQName.get(memberQName) || [];
|
|
3344
|
+
if (!axisId || !rootMemberId || childMemberIds.length === 0)
|
|
3345
|
+
return "not-applicable";
|
|
3346
|
+
const sharedFilters = scalarVariable.filters.filter(
|
|
3347
|
+
(f2) => !(f2.type === "CONCEPT_RELATION" && f2.attributes["axis"] === "descendant") && !(f2.type === "EXPLICIT_DIMENSION" && f2.attributes["dimension.qname"] === dimensionQName)
|
|
3348
|
+
);
|
|
3349
|
+
const sharedPool = sharedFilters.length > 0 ? intersectCandidates(sharedFilters.map((f2) => resolveFilter(f2, ctx.allCandidates, ctx, sharedFilters))) : ctx.allCandidates;
|
|
3350
|
+
const descendantSet = new Set(descendantConceptIds);
|
|
3351
|
+
const poolByConceptId = /* @__PURE__ */ new Map();
|
|
3352
|
+
sharedPool.forEach((candidate) => {
|
|
3353
|
+
if (!descendantSet.has(candidate.conceptId))
|
|
3354
|
+
return;
|
|
3355
|
+
const list = poolByConceptId.get(candidate.conceptId);
|
|
3356
|
+
if (list)
|
|
3357
|
+
list.push(candidate);
|
|
3358
|
+
else
|
|
3359
|
+
poolByConceptId.set(candidate.conceptId, [candidate]);
|
|
3360
|
+
});
|
|
3361
|
+
const hasGenuineChildMemberData = Array.from(poolByConceptId.values()).some((candidates) => candidates.some((candidate) => childMemberIds.some((id) => columnMatchesExplicitDimension(getColumn(ctx, candidate), axisId, id))));
|
|
3362
|
+
if (!hasGenuineChildMemberData)
|
|
3363
|
+
return "not-applicable";
|
|
3364
|
+
let comparedAny = false;
|
|
3365
|
+
for (const conceptId of descendantConceptIds) {
|
|
3366
|
+
const candidates = poolByConceptId.get(conceptId);
|
|
3367
|
+
if (!candidates || candidates.length === 0)
|
|
3368
|
+
continue;
|
|
3369
|
+
const scalarCandidates = narrowToCurrentPeriod(
|
|
3370
|
+
candidates.filter((candidate) => columnMatchesExplicitDimension(getColumn(ctx, candidate), axisId, rootMemberId)),
|
|
3371
|
+
scalarVariable,
|
|
3372
|
+
ctx
|
|
3373
|
+
);
|
|
3374
|
+
const childCandidates = narrowToCurrentPeriod(
|
|
3375
|
+
candidates.filter((candidate) => childMemberIds.some((id) => columnMatchesExplicitDimension(getColumn(ctx, candidate), axisId, id))),
|
|
3376
|
+
sequenceVariable,
|
|
3377
|
+
ctx
|
|
3378
|
+
);
|
|
3379
|
+
const scalarValues = scalarCandidates.map((candidate) => {
|
|
3380
|
+
var _a2;
|
|
3381
|
+
return resolveRawValue((_a2 = ctx.formData[candidate.conceptId]) == null ? void 0 : _a2[candidate.columnId]);
|
|
3382
|
+
}).filter((value) => typeof value === "number");
|
|
3383
|
+
const childValues = childCandidates.map((candidate) => {
|
|
3384
|
+
var _a2;
|
|
3385
|
+
return resolveRawValue((_a2 = ctx.formData[candidate.conceptId]) == null ? void 0 : _a2[candidate.columnId]);
|
|
3386
|
+
}).filter((value) => typeof value === "number");
|
|
3387
|
+
if (scalarValues.length === 0 || childValues.length === 0)
|
|
3388
|
+
continue;
|
|
3389
|
+
if (scalarValues.length > 1) {
|
|
3390
|
+
console.warn(
|
|
3391
|
+
`[formula-variable-resolver] SumOfMembersOnAbstract concept "${conceptId}" matched ${scalarValues.length} aggregate-member facts; using the first by column order.`
|
|
3392
|
+
);
|
|
3393
|
+
}
|
|
3394
|
+
const scalarValue = scalarValues[0];
|
|
3395
|
+
const childrenSum = childValues.reduce((sum, value) => sum + value, 0);
|
|
3396
|
+
comparedAny = true;
|
|
3397
|
+
if (Math.abs(scalarValue - childrenSum) >= CALCULATION_TOLERANCE)
|
|
3398
|
+
return false;
|
|
3399
|
+
}
|
|
3400
|
+
return comparedAny ? true : "unresolvable";
|
|
3401
|
+
}
|
|
3132
3402
|
function resolveFormulaVariableMatchCount(variable, ctx) {
|
|
3133
3403
|
return resolveMatchedCandidates(variable, ctx).length;
|
|
3134
3404
|
}
|
|
@@ -3171,11 +3441,7 @@ function resolveVariableColumnsForConcept(variable, targetConceptId, ctx) {
|
|
|
3171
3441
|
return resolved.map((candidate) => candidate.columnId);
|
|
3172
3442
|
}
|
|
3173
3443
|
function buildAssertionBindings(assertion, ctx) {
|
|
3174
|
-
|
|
3175
|
-
for (const variable of assertion.variables) {
|
|
3176
|
-
bindings.set(variable.name, resolveFormulaVariable(variable, ctx));
|
|
3177
|
-
}
|
|
3178
|
-
return bindings;
|
|
3444
|
+
return resolveAssertionVariableBindings(assertion.variables, ctx, assertion.implicitFiltering);
|
|
3179
3445
|
}
|
|
3180
3446
|
function buildAssertionFallbackUsage(assertion, ctx) {
|
|
3181
3447
|
var _a;
|
|
@@ -3268,7 +3534,10 @@ class FormulaValidationService {
|
|
|
3268
3534
|
_evaluateAssertion(assertion, role, ctx, language) {
|
|
3269
3535
|
try {
|
|
3270
3536
|
const bindings = buildAssertionBindings(assertion, ctx);
|
|
3271
|
-
|
|
3537
|
+
const sumOfMembersShape = detectSumOfMembersOnAbstractShape(assertion);
|
|
3538
|
+
const sumOfMembersResult = sumOfMembersShape ? resolveSumOfMembersOnAbstractSatisfaction(sumOfMembersShape, ctx) : void 0;
|
|
3539
|
+
const usesSumOfMembersPath = sumOfMembersResult !== void 0 && sumOfMembersResult !== "not-applicable";
|
|
3540
|
+
if (assertion.type === "VALUE_ASSERTION" && !usesSumOfMembersPath) {
|
|
3272
3541
|
const bindingCheck = checkAllVariablesBound(assertion, bindings);
|
|
3273
3542
|
if (!bindingCheck.satisfied) {
|
|
3274
3543
|
return this._skippedResult(assertion, role, bindingCheck.skipReason ?? "unresolvable");
|
|
@@ -3284,6 +3553,12 @@ class FormulaValidationService {
|
|
|
3284
3553
|
const satisfied2 = evaluateExistenceTest(assertion.test, matchedFactCount);
|
|
3285
3554
|
return this._evaluatedResult(assertion, role, ctx, satisfied2, language, matchedFactCount);
|
|
3286
3555
|
}
|
|
3556
|
+
if (usesSumOfMembersPath) {
|
|
3557
|
+
if (sumOfMembersResult === "unresolvable") {
|
|
3558
|
+
return this._skippedResult(assertion, role, "unresolvable");
|
|
3559
|
+
}
|
|
3560
|
+
return this._evaluatedResult(assertion, role, ctx, sumOfMembersResult, language);
|
|
3561
|
+
}
|
|
3287
3562
|
const satisfied = evaluateFormulaTest(assertion.test, bindings, fallbackUsage);
|
|
3288
3563
|
return this._evaluatedResult(assertion, role, ctx, satisfied, language);
|
|
3289
3564
|
} catch (error2) {
|
|
@@ -3612,204 +3887,6 @@ class FactMatcher {
|
|
|
3612
3887
|
console.log(" Match Result:", match ? match.value : "No match");
|
|
3613
3888
|
}
|
|
3614
3889
|
}
|
|
3615
|
-
class XBRLValidator {
|
|
3616
|
-
/**
|
|
3617
|
-
* Validates a value against XBRL datatype validation rules
|
|
3618
|
-
* Only validates when value is not blank
|
|
3619
|
-
*
|
|
3620
|
-
* @param conceptType - The XBRL concept type (e.g., "nl-types:houseNumberNLItemType")
|
|
3621
|
-
* @param value - The input value to validate
|
|
3622
|
-
* @param datatypes - Array of XBRL datatypes from taxonomy
|
|
3623
|
-
* @returns Validation result with errors if any
|
|
3624
|
-
*/
|
|
3625
|
-
static validateConceptValue(conceptType, value, datatypes) {
|
|
3626
|
-
const errors = [];
|
|
3627
|
-
if (value === null || value === void 0 || value === "") {
|
|
3628
|
-
return { valid: true, errors: [] };
|
|
3629
|
-
}
|
|
3630
|
-
if (!conceptType || !datatypes || datatypes.length === 0) {
|
|
3631
|
-
return { valid: true, errors: [] };
|
|
3632
|
-
}
|
|
3633
|
-
const datatype = this.findDatatype(conceptType, datatypes);
|
|
3634
|
-
if (!datatype || !datatype.validation) {
|
|
3635
|
-
return { valid: true, errors: [] };
|
|
3636
|
-
}
|
|
3637
|
-
const validation2 = datatype.validation;
|
|
3638
|
-
const stringValue = String(value);
|
|
3639
|
-
if (validation2.pattern) {
|
|
3640
|
-
const patternError = this.validatePattern(stringValue, validation2.pattern);
|
|
3641
|
-
if (patternError) {
|
|
3642
|
-
errors.push(patternError);
|
|
3643
|
-
}
|
|
3644
|
-
}
|
|
3645
|
-
if (validation2.length !== void 0) {
|
|
3646
|
-
const lengthError = this.validateExactLength(stringValue, validation2.length);
|
|
3647
|
-
if (lengthError) {
|
|
3648
|
-
errors.push(lengthError);
|
|
3649
|
-
}
|
|
3650
|
-
}
|
|
3651
|
-
if (validation2.minLength !== void 0) {
|
|
3652
|
-
const minLengthError = this.validateMinLength(stringValue, validation2.minLength);
|
|
3653
|
-
if (minLengthError) {
|
|
3654
|
-
errors.push(minLengthError);
|
|
3655
|
-
}
|
|
3656
|
-
}
|
|
3657
|
-
if (validation2.maxLength !== void 0) {
|
|
3658
|
-
const maxLengthError = this.validateMaxLength(stringValue, validation2.maxLength);
|
|
3659
|
-
if (maxLengthError) {
|
|
3660
|
-
errors.push(maxLengthError);
|
|
3661
|
-
}
|
|
3662
|
-
}
|
|
3663
|
-
if (validation2.totalDigits !== void 0) {
|
|
3664
|
-
const totalDigitsError = this.validateTotalDigits(stringValue, validation2.totalDigits);
|
|
3665
|
-
if (totalDigitsError) {
|
|
3666
|
-
errors.push(totalDigitsError);
|
|
3667
|
-
}
|
|
3668
|
-
}
|
|
3669
|
-
return {
|
|
3670
|
-
valid: errors.length === 0,
|
|
3671
|
-
errors
|
|
3672
|
-
};
|
|
3673
|
-
}
|
|
3674
|
-
/**
|
|
3675
|
-
* Finds the datatype definition for a given concept type
|
|
3676
|
-
* Handles both exact matches and base type chain resolution
|
|
3677
|
-
*/
|
|
3678
|
-
static findDatatype(conceptType, datatypes) {
|
|
3679
|
-
let datatype = datatypes.find((dt) => dt.type === conceptType);
|
|
3680
|
-
if (datatype) {
|
|
3681
|
-
return datatype;
|
|
3682
|
-
}
|
|
3683
|
-
const visited = /* @__PURE__ */ new Set();
|
|
3684
|
-
let currentType = conceptType;
|
|
3685
|
-
while (currentType && !visited.has(currentType)) {
|
|
3686
|
-
visited.add(currentType);
|
|
3687
|
-
datatype = datatypes.find((dt) => dt.type === currentType);
|
|
3688
|
-
if (datatype) {
|
|
3689
|
-
if (datatype.validation) {
|
|
3690
|
-
return datatype;
|
|
3691
|
-
}
|
|
3692
|
-
currentType = datatype.basetype;
|
|
3693
|
-
} else {
|
|
3694
|
-
break;
|
|
3695
|
-
}
|
|
3696
|
-
}
|
|
3697
|
-
return void 0;
|
|
3698
|
-
}
|
|
3699
|
-
/**
|
|
3700
|
-
* Validates that value matches the regex pattern
|
|
3701
|
-
*/
|
|
3702
|
-
static validatePattern(value, pattern) {
|
|
3703
|
-
try {
|
|
3704
|
-
const regex = new RegExp(`^${pattern}$`);
|
|
3705
|
-
if (!regex.test(value)) {
|
|
3706
|
-
return {
|
|
3707
|
-
type: "pattern",
|
|
3708
|
-
message: I18n.t("xbrlValidation.patternMismatch", { pattern }),
|
|
3709
|
-
expectedValue: pattern,
|
|
3710
|
-
actualValue: value
|
|
3711
|
-
};
|
|
3712
|
-
}
|
|
3713
|
-
} catch (error2) {
|
|
3714
|
-
console.error(`Invalid regex pattern: ${pattern}`, error2);
|
|
3715
|
-
return {
|
|
3716
|
-
type: "pattern",
|
|
3717
|
-
message: I18n.t("xbrlValidation.invalidPattern", { pattern }),
|
|
3718
|
-
expectedValue: pattern,
|
|
3719
|
-
actualValue: value
|
|
3720
|
-
};
|
|
3721
|
-
}
|
|
3722
|
-
return null;
|
|
3723
|
-
}
|
|
3724
|
-
/**
|
|
3725
|
-
* Validates that value has exact length
|
|
3726
|
-
*/
|
|
3727
|
-
static validateExactLength(value, requiredLength) {
|
|
3728
|
-
if (value.length !== requiredLength) {
|
|
3729
|
-
return {
|
|
3730
|
-
type: "length",
|
|
3731
|
-
message: I18n.t("xbrlValidation.exactLength", { required: requiredLength, current: value.length }),
|
|
3732
|
-
expectedValue: requiredLength,
|
|
3733
|
-
actualValue: value.length
|
|
3734
|
-
};
|
|
3735
|
-
}
|
|
3736
|
-
return null;
|
|
3737
|
-
}
|
|
3738
|
-
/**
|
|
3739
|
-
* Validates minimum length
|
|
3740
|
-
*/
|
|
3741
|
-
static validateMinLength(value, minLength) {
|
|
3742
|
-
if (value.length < minLength) {
|
|
3743
|
-
return {
|
|
3744
|
-
type: "minLength",
|
|
3745
|
-
message: I18n.t("xbrlValidation.minLength", { required: minLength, current: value.length }),
|
|
3746
|
-
expectedValue: minLength,
|
|
3747
|
-
actualValue: value.length
|
|
3748
|
-
};
|
|
3749
|
-
}
|
|
3750
|
-
return null;
|
|
3751
|
-
}
|
|
3752
|
-
/**
|
|
3753
|
-
* Validates maximum length
|
|
3754
|
-
*/
|
|
3755
|
-
static validateMaxLength(value, maxLength) {
|
|
3756
|
-
if (value.length > maxLength) {
|
|
3757
|
-
return {
|
|
3758
|
-
type: "maxLength",
|
|
3759
|
-
message: I18n.t("xbrlValidation.maxLength", { required: maxLength, current: value.length }),
|
|
3760
|
-
expectedValue: maxLength,
|
|
3761
|
-
actualValue: value.length
|
|
3762
|
-
};
|
|
3763
|
-
}
|
|
3764
|
-
return null;
|
|
3765
|
-
}
|
|
3766
|
-
/**
|
|
3767
|
-
* Validates total digits (for numeric values)
|
|
3768
|
-
*/
|
|
3769
|
-
static validateTotalDigits(value, totalDigits) {
|
|
3770
|
-
const digitsOnly = value.replace(/[^\d]/g, "");
|
|
3771
|
-
if (digitsOnly.length > totalDigits) {
|
|
3772
|
-
return {
|
|
3773
|
-
type: "totalDigits",
|
|
3774
|
-
message: I18n.t("xbrlValidation.totalDigits", { required: totalDigits, current: digitsOnly.length }),
|
|
3775
|
-
expectedValue: totalDigits,
|
|
3776
|
-
actualValue: digitsOnly.length
|
|
3777
|
-
};
|
|
3778
|
-
}
|
|
3779
|
-
return null;
|
|
3780
|
-
}
|
|
3781
|
-
/**
|
|
3782
|
-
* Gets a human-readable summary of validation rules for a concept type
|
|
3783
|
-
* Useful for displaying validation hints to users
|
|
3784
|
-
*/
|
|
3785
|
-
static getValidationRulesSummary(conceptType, datatypes) {
|
|
3786
|
-
if (!conceptType || !datatypes) {
|
|
3787
|
-
return [];
|
|
3788
|
-
}
|
|
3789
|
-
const datatype = this.findDatatype(conceptType, datatypes);
|
|
3790
|
-
if (!datatype || !datatype.validation) {
|
|
3791
|
-
return [];
|
|
3792
|
-
}
|
|
3793
|
-
const validation2 = datatype.validation;
|
|
3794
|
-
const rules = [];
|
|
3795
|
-
if (validation2.pattern) {
|
|
3796
|
-
rules.push(I18n.t("xbrlValidation.ruleSummaryPattern", { pattern: validation2.pattern }));
|
|
3797
|
-
}
|
|
3798
|
-
if (validation2.length !== void 0) {
|
|
3799
|
-
rules.push(I18n.t("xbrlValidation.ruleSummaryExactLength", { required: validation2.length }));
|
|
3800
|
-
}
|
|
3801
|
-
if (validation2.minLength !== void 0) {
|
|
3802
|
-
rules.push(I18n.t("xbrlValidation.ruleSummaryMinLength", { required: validation2.minLength }));
|
|
3803
|
-
}
|
|
3804
|
-
if (validation2.maxLength !== void 0) {
|
|
3805
|
-
rules.push(I18n.t("xbrlValidation.ruleSummaryMaxLength", { required: validation2.maxLength }));
|
|
3806
|
-
}
|
|
3807
|
-
if (validation2.totalDigits !== void 0) {
|
|
3808
|
-
rules.push(I18n.t("xbrlValidation.ruleSummaryTotalDigits", { required: validation2.totalDigits }));
|
|
3809
|
-
}
|
|
3810
|
-
return rules;
|
|
3811
|
-
}
|
|
3812
|
-
}
|
|
3813
3890
|
const TYPE_INPUT_MAP = {
|
|
3814
3891
|
// ==========================================
|
|
3815
3892
|
// Dutch XBRL Types (nl-types namespace)
|
|
@@ -3846,14 +3923,21 @@ const TYPE_INPUT_MAP = {
|
|
|
3846
3923
|
htmlInputType: "text",
|
|
3847
3924
|
placeholder: I18n.t("field.enterText")
|
|
3848
3925
|
},
|
|
3849
|
-
// Date types
|
|
3926
|
+
// Date types â bounded to a sane 1900-2100 range. Browsers don't stop you from *typing* a
|
|
3927
|
+
// year outside this (see MDN: min/max restrict the calendar picker and validity state, not
|
|
3928
|
+
// keystrokes), but it does keep the calendar picker itself sane and correctly flags an
|
|
3929
|
+
// out-of-range typed value as :invalid without us clearing/rejecting anything ourselves.
|
|
3850
3930
|
"xbrli:dateItemType": {
|
|
3851
3931
|
fieldType: "date",
|
|
3852
|
-
htmlInputType: "date"
|
|
3932
|
+
htmlInputType: "date",
|
|
3933
|
+
min: "1900-01-01",
|
|
3934
|
+
max: "2100-12-31"
|
|
3853
3935
|
},
|
|
3854
3936
|
"xbrli:dateTimeItemType": {
|
|
3855
3937
|
fieldType: "datetime",
|
|
3856
|
-
htmlInputType: "datetime-local"
|
|
3938
|
+
htmlInputType: "datetime-local",
|
|
3939
|
+
min: "1900-01-01T00:00",
|
|
3940
|
+
max: "2100-12-31T23:59"
|
|
3857
3941
|
},
|
|
3858
3942
|
// Numeric types - integers
|
|
3859
3943
|
"xbrli:integerItemType": {
|
|
@@ -4013,12 +4097,16 @@ function getInputTypeForConceptType(conceptType, datatypes) {
|
|
|
4013
4097
|
if (lowerType.includes("time")) {
|
|
4014
4098
|
return {
|
|
4015
4099
|
fieldType: "datetime",
|
|
4016
|
-
htmlInputType: "datetime-local"
|
|
4100
|
+
htmlInputType: "datetime-local",
|
|
4101
|
+
min: "1900-01-01T00:00",
|
|
4102
|
+
max: "2100-12-31T23:59"
|
|
4017
4103
|
};
|
|
4018
4104
|
}
|
|
4019
4105
|
return {
|
|
4020
4106
|
fieldType: "date",
|
|
4021
|
-
htmlInputType: "date"
|
|
4107
|
+
htmlInputType: "date",
|
|
4108
|
+
min: "1900-01-01",
|
|
4109
|
+
max: "2100-12-31"
|
|
4022
4110
|
};
|
|
4023
4111
|
}
|
|
4024
4112
|
if (lowerType.includes("boolean") || lowerType.includes("bool")) {
|
|
@@ -4151,12 +4239,16 @@ function determineInputTypeFromBaseChain(baseTypeChain, datatypes) {
|
|
|
4151
4239
|
if (chainString.includes("datetime") || chainString.includes("time")) {
|
|
4152
4240
|
return {
|
|
4153
4241
|
fieldType: "datetime",
|
|
4154
|
-
htmlInputType: "datetime-local"
|
|
4242
|
+
htmlInputType: "datetime-local",
|
|
4243
|
+
min: "1900-01-01T00:00",
|
|
4244
|
+
max: "2100-12-31T23:59"
|
|
4155
4245
|
};
|
|
4156
4246
|
}
|
|
4157
4247
|
return {
|
|
4158
4248
|
fieldType: "date",
|
|
4159
|
-
htmlInputType: "date"
|
|
4249
|
+
htmlInputType: "date",
|
|
4250
|
+
min: "1900-01-01",
|
|
4251
|
+
max: "2100-12-31"
|
|
4160
4252
|
};
|
|
4161
4253
|
}
|
|
4162
4254
|
if (chainLower.some((type) => type.includes("boolean") || type.includes("bool"))) {
|
|
@@ -4187,48 +4279,262 @@ function determineInputTypeFromBaseChain(baseTypeChain, datatypes) {
|
|
|
4187
4279
|
placeholder: I18n.t("field.enterPercentage")
|
|
4188
4280
|
};
|
|
4189
4281
|
}
|
|
4190
|
-
if (chainLower.some((type) => type.includes("decimal") || type === "decimal")) {
|
|
4191
|
-
if (chainString.includes("monetary") || chainString.includes("currency")) {
|
|
4192
|
-
if (chainString.includes("nodecimals") || chainString.includes("no-decimals") || chainString.includes("nodecimals20")) {
|
|
4193
|
-
return {
|
|
4194
|
-
fieldType: "number",
|
|
4195
|
-
htmlInputType: "text",
|
|
4196
|
-
inputMode: "decimal",
|
|
4197
|
-
allowDecimals: false,
|
|
4198
|
-
step: 1,
|
|
4199
|
-
placeholder: I18n.t("field.enterAmountNoDecimals")
|
|
4200
|
-
};
|
|
4201
|
-
}
|
|
4282
|
+
if (chainLower.some((type) => type.includes("decimal") || type === "decimal")) {
|
|
4283
|
+
if (chainString.includes("monetary") || chainString.includes("currency")) {
|
|
4284
|
+
if (chainString.includes("nodecimals") || chainString.includes("no-decimals") || chainString.includes("nodecimals20")) {
|
|
4285
|
+
return {
|
|
4286
|
+
fieldType: "number",
|
|
4287
|
+
htmlInputType: "text",
|
|
4288
|
+
inputMode: "decimal",
|
|
4289
|
+
allowDecimals: false,
|
|
4290
|
+
step: 1,
|
|
4291
|
+
placeholder: I18n.t("field.enterAmountNoDecimals")
|
|
4292
|
+
};
|
|
4293
|
+
}
|
|
4294
|
+
return {
|
|
4295
|
+
fieldType: "currency",
|
|
4296
|
+
htmlInputType: "text",
|
|
4297
|
+
inputMode: "decimal",
|
|
4298
|
+
allowDecimals: true,
|
|
4299
|
+
step: 0.01,
|
|
4300
|
+
placeholder: I18n.t("field.enterCurrency")
|
|
4301
|
+
};
|
|
4302
|
+
}
|
|
4303
|
+
return {
|
|
4304
|
+
fieldType: "decimal",
|
|
4305
|
+
htmlInputType: "text",
|
|
4306
|
+
inputMode: "decimal",
|
|
4307
|
+
allowDecimals: true,
|
|
4308
|
+
step: "any",
|
|
4309
|
+
placeholder: I18n.t("field.enterDecimalValue")
|
|
4310
|
+
};
|
|
4311
|
+
}
|
|
4312
|
+
if (baseTypeChain.some((type) => type === "xbrli:stringItemType") || chainLower.some((type) => type === "string" || type.includes("stringitem"))) {
|
|
4313
|
+
return {
|
|
4314
|
+
fieldType: "text",
|
|
4315
|
+
htmlInputType: "text",
|
|
4316
|
+
placeholder: I18n.t("field.enterText")
|
|
4317
|
+
};
|
|
4318
|
+
}
|
|
4319
|
+
return {
|
|
4320
|
+
fieldType: "text",
|
|
4321
|
+
htmlInputType: "text",
|
|
4322
|
+
placeholder: I18n.t("field.enterValue")
|
|
4323
|
+
};
|
|
4324
|
+
}
|
|
4325
|
+
class XBRLValidator {
|
|
4326
|
+
/**
|
|
4327
|
+
* Validates a value against XBRL datatype validation rules
|
|
4328
|
+
* Only validates when value is not blank
|
|
4329
|
+
*
|
|
4330
|
+
* @param conceptType - The XBRL concept type (e.g., "nl-types:houseNumberNLItemType")
|
|
4331
|
+
* @param value - The input value to validate
|
|
4332
|
+
* @param datatypes - Array of XBRL datatypes from taxonomy
|
|
4333
|
+
* @returns Validation result with errors if any
|
|
4334
|
+
*/
|
|
4335
|
+
static validateConceptValue(conceptType, value, datatypes) {
|
|
4336
|
+
if (value === null || value === void 0 || value === "") {
|
|
4337
|
+
return { valid: true, errors: [] };
|
|
4338
|
+
}
|
|
4339
|
+
if (!conceptType || !datatypes || datatypes.length === 0) {
|
|
4340
|
+
return { valid: true, errors: [] };
|
|
4341
|
+
}
|
|
4342
|
+
const datatype = this.findDatatype(conceptType, datatypes);
|
|
4343
|
+
if (!datatype || !datatype.validation) {
|
|
4344
|
+
return { valid: true, errors: [] };
|
|
4345
|
+
}
|
|
4346
|
+
return this.validateAgainstRules(value, datatype.validation);
|
|
4347
|
+
}
|
|
4348
|
+
/**
|
|
4349
|
+
* Validates a value against an already-resolved validation rule set, skipping the
|
|
4350
|
+
* type-name â datatypes.json lookup `validateConceptValue` does. Use this when the caller
|
|
4351
|
+
* already has the applicable rules in hand â e.g. an XBRL typed-dimension member's own
|
|
4352
|
+
* `validation` block, embedded directly on the hypercube JSON's `typedMember` (a separate
|
|
4353
|
+
* source from, and not necessarily mirrored in, datatypes.json â many typed-member types
|
|
4354
|
+
* such as "nl-types:string255" never appear there at all).
|
|
4355
|
+
*/
|
|
4356
|
+
static validateAgainstRules(value, validation2) {
|
|
4357
|
+
const errors = [];
|
|
4358
|
+
if (value === null || value === void 0 || value === "") {
|
|
4359
|
+
return { valid: true, errors: [] };
|
|
4360
|
+
}
|
|
4361
|
+
if (!validation2) {
|
|
4362
|
+
return { valid: true, errors: [] };
|
|
4363
|
+
}
|
|
4364
|
+
const stringValue = String(value);
|
|
4365
|
+
if (validation2.pattern) {
|
|
4366
|
+
const patternError = this.validatePattern(stringValue, validation2.pattern);
|
|
4367
|
+
if (patternError) {
|
|
4368
|
+
errors.push(patternError);
|
|
4369
|
+
}
|
|
4370
|
+
}
|
|
4371
|
+
if (validation2.length !== void 0) {
|
|
4372
|
+
const lengthError = this.validateExactLength(stringValue, validation2.length);
|
|
4373
|
+
if (lengthError) {
|
|
4374
|
+
errors.push(lengthError);
|
|
4375
|
+
}
|
|
4376
|
+
}
|
|
4377
|
+
if (validation2.minLength !== void 0) {
|
|
4378
|
+
const minLengthError = this.validateMinLength(stringValue, validation2.minLength);
|
|
4379
|
+
if (minLengthError) {
|
|
4380
|
+
errors.push(minLengthError);
|
|
4381
|
+
}
|
|
4382
|
+
}
|
|
4383
|
+
if (validation2.maxLength !== void 0) {
|
|
4384
|
+
const maxLengthError = this.validateMaxLength(stringValue, validation2.maxLength);
|
|
4385
|
+
if (maxLengthError) {
|
|
4386
|
+
errors.push(maxLengthError);
|
|
4387
|
+
}
|
|
4388
|
+
}
|
|
4389
|
+
if (validation2.totalDigits !== void 0) {
|
|
4390
|
+
const totalDigitsError = this.validateTotalDigits(stringValue, validation2.totalDigits);
|
|
4391
|
+
if (totalDigitsError) {
|
|
4392
|
+
errors.push(totalDigitsError);
|
|
4393
|
+
}
|
|
4394
|
+
}
|
|
4395
|
+
return {
|
|
4396
|
+
valid: errors.length === 0,
|
|
4397
|
+
errors
|
|
4398
|
+
};
|
|
4399
|
+
}
|
|
4400
|
+
/**
|
|
4401
|
+
* Finds the datatype definition for a given concept type
|
|
4402
|
+
* Handles both exact matches and base type chain resolution
|
|
4403
|
+
*/
|
|
4404
|
+
static findDatatype(conceptType, datatypes) {
|
|
4405
|
+
let datatype = datatypes.find((dt) => dt.type === conceptType);
|
|
4406
|
+
if (datatype) {
|
|
4407
|
+
return datatype;
|
|
4408
|
+
}
|
|
4409
|
+
const visited = /* @__PURE__ */ new Set();
|
|
4410
|
+
let currentType = conceptType;
|
|
4411
|
+
while (currentType && !visited.has(currentType)) {
|
|
4412
|
+
visited.add(currentType);
|
|
4413
|
+
datatype = datatypes.find((dt) => dt.type === currentType);
|
|
4414
|
+
if (datatype) {
|
|
4415
|
+
if (datatype.validation) {
|
|
4416
|
+
return datatype;
|
|
4417
|
+
}
|
|
4418
|
+
currentType = datatype.basetype;
|
|
4419
|
+
} else {
|
|
4420
|
+
break;
|
|
4421
|
+
}
|
|
4422
|
+
}
|
|
4423
|
+
return void 0;
|
|
4424
|
+
}
|
|
4425
|
+
/**
|
|
4426
|
+
* Validates that value matches the regex pattern
|
|
4427
|
+
*/
|
|
4428
|
+
static validatePattern(value, pattern) {
|
|
4429
|
+
try {
|
|
4430
|
+
const regex = new RegExp(`^${pattern}$`);
|
|
4431
|
+
if (!regex.test(value)) {
|
|
4432
|
+
return {
|
|
4433
|
+
type: "pattern",
|
|
4434
|
+
message: I18n.t("xbrlValidation.patternMismatch", { pattern }),
|
|
4435
|
+
expectedValue: pattern,
|
|
4436
|
+
actualValue: value
|
|
4437
|
+
};
|
|
4438
|
+
}
|
|
4439
|
+
} catch (error2) {
|
|
4440
|
+
console.error(`Invalid regex pattern: ${pattern}`, error2);
|
|
4441
|
+
return {
|
|
4442
|
+
type: "pattern",
|
|
4443
|
+
message: I18n.t("xbrlValidation.invalidPattern", { pattern }),
|
|
4444
|
+
expectedValue: pattern,
|
|
4445
|
+
actualValue: value
|
|
4446
|
+
};
|
|
4447
|
+
}
|
|
4448
|
+
return null;
|
|
4449
|
+
}
|
|
4450
|
+
/**
|
|
4451
|
+
* Validates that value has exact length
|
|
4452
|
+
*/
|
|
4453
|
+
static validateExactLength(value, requiredLength) {
|
|
4454
|
+
if (value.length !== requiredLength) {
|
|
4455
|
+
return {
|
|
4456
|
+
type: "length",
|
|
4457
|
+
message: I18n.t("xbrlValidation.exactLength", { required: requiredLength, current: value.length }),
|
|
4458
|
+
expectedValue: requiredLength,
|
|
4459
|
+
actualValue: value.length
|
|
4460
|
+
};
|
|
4461
|
+
}
|
|
4462
|
+
return null;
|
|
4463
|
+
}
|
|
4464
|
+
/**
|
|
4465
|
+
* Validates minimum length
|
|
4466
|
+
*/
|
|
4467
|
+
static validateMinLength(value, minLength) {
|
|
4468
|
+
if (value.length < minLength) {
|
|
4469
|
+
return {
|
|
4470
|
+
type: "minLength",
|
|
4471
|
+
message: I18n.t("xbrlValidation.minLength", { required: minLength, current: value.length }),
|
|
4472
|
+
expectedValue: minLength,
|
|
4473
|
+
actualValue: value.length
|
|
4474
|
+
};
|
|
4475
|
+
}
|
|
4476
|
+
return null;
|
|
4477
|
+
}
|
|
4478
|
+
/**
|
|
4479
|
+
* Validates maximum length
|
|
4480
|
+
*/
|
|
4481
|
+
static validateMaxLength(value, maxLength) {
|
|
4482
|
+
if (value.length > maxLength) {
|
|
4202
4483
|
return {
|
|
4203
|
-
|
|
4204
|
-
|
|
4205
|
-
|
|
4206
|
-
|
|
4207
|
-
step: 0.01,
|
|
4208
|
-
placeholder: I18n.t("field.enterCurrency")
|
|
4484
|
+
type: "maxLength",
|
|
4485
|
+
message: I18n.t("xbrlValidation.maxLength", { required: maxLength, current: value.length }),
|
|
4486
|
+
expectedValue: maxLength,
|
|
4487
|
+
actualValue: value.length
|
|
4209
4488
|
};
|
|
4210
4489
|
}
|
|
4211
|
-
return
|
|
4212
|
-
fieldType: "decimal",
|
|
4213
|
-
htmlInputType: "text",
|
|
4214
|
-
inputMode: "decimal",
|
|
4215
|
-
allowDecimals: true,
|
|
4216
|
-
step: "any",
|
|
4217
|
-
placeholder: I18n.t("field.enterDecimalValue")
|
|
4218
|
-
};
|
|
4490
|
+
return null;
|
|
4219
4491
|
}
|
|
4220
|
-
|
|
4221
|
-
|
|
4222
|
-
|
|
4223
|
-
|
|
4224
|
-
|
|
4225
|
-
|
|
4492
|
+
/**
|
|
4493
|
+
* Validates total digits (for numeric values)
|
|
4494
|
+
*/
|
|
4495
|
+
static validateTotalDigits(value, totalDigits) {
|
|
4496
|
+
const digitsOnly = value.replace(/[^\d]/g, "");
|
|
4497
|
+
if (digitsOnly.length > totalDigits) {
|
|
4498
|
+
return {
|
|
4499
|
+
type: "totalDigits",
|
|
4500
|
+
message: I18n.t("xbrlValidation.totalDigits", { required: totalDigits, current: digitsOnly.length }),
|
|
4501
|
+
expectedValue: totalDigits,
|
|
4502
|
+
actualValue: digitsOnly.length
|
|
4503
|
+
};
|
|
4504
|
+
}
|
|
4505
|
+
return null;
|
|
4506
|
+
}
|
|
4507
|
+
/**
|
|
4508
|
+
* Gets a human-readable summary of validation rules for a concept type
|
|
4509
|
+
* Useful for displaying validation hints to users
|
|
4510
|
+
*/
|
|
4511
|
+
static getValidationRulesSummary(conceptType, datatypes) {
|
|
4512
|
+
if (!conceptType || !datatypes) {
|
|
4513
|
+
return [];
|
|
4514
|
+
}
|
|
4515
|
+
const datatype = this.findDatatype(conceptType, datatypes);
|
|
4516
|
+
if (!datatype || !datatype.validation) {
|
|
4517
|
+
return [];
|
|
4518
|
+
}
|
|
4519
|
+
const validation2 = datatype.validation;
|
|
4520
|
+
const rules = [];
|
|
4521
|
+
if (validation2.pattern) {
|
|
4522
|
+
rules.push(I18n.t("xbrlValidation.ruleSummaryPattern", { pattern: validation2.pattern }));
|
|
4523
|
+
}
|
|
4524
|
+
if (validation2.length !== void 0) {
|
|
4525
|
+
rules.push(I18n.t("xbrlValidation.ruleSummaryExactLength", { required: validation2.length }));
|
|
4526
|
+
}
|
|
4527
|
+
if (validation2.minLength !== void 0) {
|
|
4528
|
+
rules.push(I18n.t("xbrlValidation.ruleSummaryMinLength", { required: validation2.minLength }));
|
|
4529
|
+
}
|
|
4530
|
+
if (validation2.maxLength !== void 0) {
|
|
4531
|
+
rules.push(I18n.t("xbrlValidation.ruleSummaryMaxLength", { required: validation2.maxLength }));
|
|
4532
|
+
}
|
|
4533
|
+
if (validation2.totalDigits !== void 0) {
|
|
4534
|
+
rules.push(I18n.t("xbrlValidation.ruleSummaryTotalDigits", { required: validation2.totalDigits }));
|
|
4535
|
+
}
|
|
4536
|
+
return rules;
|
|
4226
4537
|
}
|
|
4227
|
-
return {
|
|
4228
|
-
fieldType: "text",
|
|
4229
|
-
htmlInputType: "text",
|
|
4230
|
-
placeholder: I18n.t("field.enterValue")
|
|
4231
|
-
};
|
|
4232
4538
|
}
|
|
4233
4539
|
var __defProp$7 = Object.defineProperty;
|
|
4234
4540
|
var __getOwnPropDesc$7 = Object.getOwnPropertyDescriptor;
|
|
@@ -4310,6 +4616,14 @@ let JupiterFormField = class extends LitElement {
|
|
|
4310
4616
|
}
|
|
4311
4617
|
_handleInput(event) {
|
|
4312
4618
|
const target = event.target;
|
|
4619
|
+
const isDateType = target instanceof HTMLInputElement && (target.type === "date" || target.type === "datetime-local");
|
|
4620
|
+
if (isDateType) {
|
|
4621
|
+
if (event.type !== "change") {
|
|
4622
|
+
return;
|
|
4623
|
+
}
|
|
4624
|
+
} else if (target.tagName === "INPUT" && target.type !== "checkbox" && event.type === "change") {
|
|
4625
|
+
return;
|
|
4626
|
+
}
|
|
4313
4627
|
const oldValue = this.value;
|
|
4314
4628
|
let value = target.value;
|
|
4315
4629
|
if (this._isNumericField()) {
|
|
@@ -4447,7 +4761,7 @@ let JupiterFormField = class extends LitElement {
|
|
|
4447
4761
|
return;
|
|
4448
4762
|
}
|
|
4449
4763
|
const typeConfig = getInputTypeForConceptType(this.conceptType, this.datatypes);
|
|
4450
|
-
const allowNegative = typeConfig.min === void 0 || typeConfig.min < 0;
|
|
4764
|
+
const allowNegative = typeConfig.min === void 0 || typeof typeConfig.min === "number" && typeConfig.min < 0;
|
|
4451
4765
|
if (event.key === "-" && allowNegative && !target.value.includes("-") && (target.selectionStart === 0 || target.value.length === 0)) {
|
|
4452
4766
|
return;
|
|
4453
4767
|
}
|
|
@@ -4941,6 +5255,33 @@ let JupiterFormField = class extends LitElement {
|
|
|
4941
5255
|
} else {
|
|
4942
5256
|
renderedInputValue = effectiveValue ?? "";
|
|
4943
5257
|
}
|
|
5258
|
+
if (inputType === "date" || inputType === "datetime-local") {
|
|
5259
|
+
return html`
|
|
5260
|
+
<div class="date-wrapper">
|
|
5261
|
+
<input
|
|
5262
|
+
id="${fieldId}"
|
|
5263
|
+
name="${fieldName}"
|
|
5264
|
+
type="${inputType}"
|
|
5265
|
+
inputmode="${typeConfig.inputMode || ""}"
|
|
5266
|
+
class="${cssClass}"
|
|
5267
|
+
.value="${renderedInputValue}"
|
|
5268
|
+
?disabled="${effectiveDisabled || this.field.disabled}"
|
|
5269
|
+
placeholder="${typeConfig.placeholder || this.field.placeholder || ""}"
|
|
5270
|
+
min="${min !== void 0 ? min : ""}"
|
|
5271
|
+
max="${max !== void 0 ? max : ""}"
|
|
5272
|
+
@input="${this._handleInput}"
|
|
5273
|
+
@change="${this._handleInput}"
|
|
5274
|
+
@focus="${this._handleFocus}"
|
|
5275
|
+
@blur="${this._handleBlur}"
|
|
5276
|
+
/>
|
|
5277
|
+
<svg class="calendar-icon-svg" viewBox="0 0 24 24">
|
|
5278
|
+
<path fill="currentColor" d="M19 4h-1V2h-2v2H8V2H6v2H5
|
|
5279
|
+
a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14
|
|
5280
|
+
a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2zm0 16H5V9h14z"/>
|
|
5281
|
+
</svg>
|
|
5282
|
+
</div>
|
|
5283
|
+
`;
|
|
5284
|
+
}
|
|
4944
5285
|
return html`
|
|
4945
5286
|
<input
|
|
4946
5287
|
id="${fieldId}"
|
|
@@ -4955,6 +5296,7 @@ let JupiterFormField = class extends LitElement {
|
|
|
4955
5296
|
min="${min !== void 0 ? min : ""}"
|
|
4956
5297
|
max="${max !== void 0 ? max : ""}"
|
|
4957
5298
|
@input="${this._handleInput}"
|
|
5299
|
+
@change="${this._handleInput}"
|
|
4958
5300
|
@focus="${this._handleFocus}"
|
|
4959
5301
|
@blur="${this._handleBlur}"
|
|
4960
5302
|
@keydown="${this._handleKeyDown}"
|
|
@@ -5007,6 +5349,8 @@ let JupiterFormField = class extends LitElement {
|
|
|
5007
5349
|
<div class="date-wrapper">
|
|
5008
5350
|
<input
|
|
5009
5351
|
type="date"
|
|
5352
|
+
min="1900-01-01"
|
|
5353
|
+
max="2100-12-31"
|
|
5010
5354
|
.value="${this.periodInstantDate || this.field.periodInstantDate || ""}"
|
|
5011
5355
|
@change="${(e2) => this._handlePeriodChange(e2, "instant")}"
|
|
5012
5356
|
?disabled="${this.disabled}"
|
|
@@ -5026,6 +5370,8 @@ let JupiterFormField = class extends LitElement {
|
|
|
5026
5370
|
<div class="date-wrapper">
|
|
5027
5371
|
<input
|
|
5028
5372
|
type="date"
|
|
5373
|
+
min="1900-01-01"
|
|
5374
|
+
max="2100-12-31"
|
|
5029
5375
|
.value="${this.periodStartDate || this.field.periodStartDate || ""}"
|
|
5030
5376
|
@change="${(e2) => this._handlePeriodChange(e2, "start")}"
|
|
5031
5377
|
?disabled="${this.disabled}"
|
|
@@ -5040,6 +5386,8 @@ let JupiterFormField = class extends LitElement {
|
|
|
5040
5386
|
<div class="date-wrapper">
|
|
5041
5387
|
<input
|
|
5042
5388
|
type="date"
|
|
5389
|
+
min="1900-01-01"
|
|
5390
|
+
max="2100-12-31"
|
|
5043
5391
|
.value="${this.periodEndDate || this.field.periodEndDate || ""}"
|
|
5044
5392
|
@change="${(e2) => this._handlePeriodChange(e2, "end")}"
|
|
5045
5393
|
?disabled="${this.disabled}"
|
|
@@ -5073,6 +5421,8 @@ let JupiterFormField = class extends LitElement {
|
|
|
5073
5421
|
<div class="date-wrapper">
|
|
5074
5422
|
<input
|
|
5075
5423
|
type="date"
|
|
5424
|
+
min="1900-01-01"
|
|
5425
|
+
max="2100-12-31"
|
|
5076
5426
|
.value="${this.periodInstantDate || this.field.periodInstantDate || ""}"
|
|
5077
5427
|
@change="${(e2) => this._handlePeriodChange(e2, "instant")}"
|
|
5078
5428
|
?disabled="${this.disabled}"
|
|
@@ -5090,6 +5440,8 @@ let JupiterFormField = class extends LitElement {
|
|
|
5090
5440
|
<div class="date-wrapper">
|
|
5091
5441
|
<input
|
|
5092
5442
|
type="date"
|
|
5443
|
+
min="1900-01-01"
|
|
5444
|
+
max="2100-12-31"
|
|
5093
5445
|
.value="${this.periodStartDate || this.field.periodStartDate || ""}"
|
|
5094
5446
|
@change="${(e2) => this._handlePeriodChange(e2, "start")}"
|
|
5095
5447
|
?disabled="${this.disabled}"
|
|
@@ -5108,6 +5460,8 @@ let JupiterFormField = class extends LitElement {
|
|
|
5108
5460
|
<div class="date-wrapper">
|
|
5109
5461
|
<input
|
|
5110
5462
|
type="date"
|
|
5463
|
+
min="1900-01-01"
|
|
5464
|
+
max="2100-12-31"
|
|
5111
5465
|
.value="${this.periodEndDate || this.field.periodEndDate || ""}"
|
|
5112
5466
|
@change="${(e2) => this._handlePeriodChange(e2, "end")}"
|
|
5113
5467
|
?disabled="${this.disabled}"
|
|
@@ -5338,6 +5692,11 @@ JupiterFormField.styles = css`
|
|
|
5338
5692
|
border-radius: 4px;
|
|
5339
5693
|
transition: background-color 0.2s;
|
|
5340
5694
|
opacity: 0.7;
|
|
5695
|
+
/* Keep this icon on the wrapper's right edge (same rail as every other field type)
|
|
5696
|
+
regardless of the sibling input's own width â the date-wrapper next to it sizes to
|
|
5697
|
+
its dd/mm/yyyy content, which is narrower than a full-width text/select input. */
|
|
5698
|
+
margin-left: auto;
|
|
5699
|
+
flex-shrink: 0;
|
|
5341
5700
|
}
|
|
5342
5701
|
|
|
5343
5702
|
.period-icon-btn:hover {
|
|
@@ -6600,6 +6959,7 @@ let JupiterAddColumnDialog = class extends LitElement {
|
|
|
6600
6959
|
this._instantDate = "";
|
|
6601
6960
|
this._selectedType = "duration";
|
|
6602
6961
|
this._selectedDimensions = /* @__PURE__ */ new Map();
|
|
6962
|
+
this._typedValueErrors = {};
|
|
6603
6963
|
}
|
|
6604
6964
|
updated(changedProperties) {
|
|
6605
6965
|
if (changedProperties.has("open") && this.open) {
|
|
@@ -6663,6 +7023,7 @@ let JupiterAddColumnDialog = class extends LitElement {
|
|
|
6663
7023
|
this._instantDate = e2.target.value;
|
|
6664
7024
|
}
|
|
6665
7025
|
_handleDimensionToggle(dimensionId, event) {
|
|
7026
|
+
var _a, _b;
|
|
6666
7027
|
const checkbox = event.target;
|
|
6667
7028
|
if (checkbox.checked) {
|
|
6668
7029
|
const dimension = this.availableDimensions.find((d2) => d2.id === dimensionId);
|
|
@@ -6670,7 +7031,9 @@ let JupiterAddColumnDialog = class extends LitElement {
|
|
|
6670
7031
|
const selection = {
|
|
6671
7032
|
axisId: dimension.id,
|
|
6672
7033
|
axisLabel: dimension.axisLabel,
|
|
6673
|
-
isTyped: !!(dimension.typedMember && (!dimension.members || dimension.members.length === 0))
|
|
7034
|
+
isTyped: !!(dimension.typedMember && (!dimension.members || dimension.members.length === 0)),
|
|
7035
|
+
dataType: (_a = dimension.typedMember) == null ? void 0 : _a.dataType,
|
|
7036
|
+
validation: (_b = dimension.typedMember) == null ? void 0 : _b.validation
|
|
6674
7037
|
};
|
|
6675
7038
|
this._selectedDimensions.set(dimensionId, selection);
|
|
6676
7039
|
}
|
|
@@ -6689,24 +7052,130 @@ let JupiterAddColumnDialog = class extends LitElement {
|
|
|
6689
7052
|
}
|
|
6690
7053
|
}
|
|
6691
7054
|
_handleTypedValueChange(dimensionId, event) {
|
|
6692
|
-
const
|
|
7055
|
+
const target = event.target;
|
|
7056
|
+
const value = target instanceof HTMLInputElement && target.type === "checkbox" ? String(target.checked) : target.value;
|
|
6693
7057
|
const existing = this._selectedDimensions.get(dimensionId);
|
|
6694
7058
|
if (existing) {
|
|
6695
|
-
existing.typedValue =
|
|
7059
|
+
existing.typedValue = value;
|
|
6696
7060
|
this._selectedDimensions.set(dimensionId, existing);
|
|
6697
7061
|
}
|
|
6698
7062
|
}
|
|
7063
|
+
/**
|
|
7064
|
+
* Validate a typed dimension's value against its XBRL datatype's `validation` constraints
|
|
7065
|
+
* (minLength/maxLength/length/pattern/totalDigits), reusing the exact same datatype-driven
|
|
7066
|
+
* validator concept row fields use on blur (`XBRLValidator.validateConceptValue` â see
|
|
7067
|
+
* `form-field.ts`'s `_validateXBRLDatatype` / `form-section.ts`'s `_validateTypedMemberValue`).
|
|
7068
|
+
*/
|
|
7069
|
+
_validateTypedValue(dimensionId, dataType, validation2, value) {
|
|
7070
|
+
const hasExistingEntry = dimensionId in this._typedValueErrors;
|
|
7071
|
+
if (!value) {
|
|
7072
|
+
if (hasExistingEntry) {
|
|
7073
|
+
const updated = { ...this._typedValueErrors };
|
|
7074
|
+
delete updated[dimensionId];
|
|
7075
|
+
this._typedValueErrors = updated;
|
|
7076
|
+
}
|
|
7077
|
+
return;
|
|
7078
|
+
}
|
|
7079
|
+
const result = validation2 ? XBRLValidator.validateAgainstRules(value, validation2) : XBRLValidator.validateConceptValue(dataType, value, this.datatypes);
|
|
7080
|
+
if (result.valid) {
|
|
7081
|
+
if (hasExistingEntry) {
|
|
7082
|
+
const updated = { ...this._typedValueErrors };
|
|
7083
|
+
delete updated[dimensionId];
|
|
7084
|
+
this._typedValueErrors = updated;
|
|
7085
|
+
}
|
|
7086
|
+
} else {
|
|
7087
|
+
this._typedValueErrors = { ...this._typedValueErrors, [dimensionId]: result.errors };
|
|
7088
|
+
}
|
|
7089
|
+
}
|
|
7090
|
+
_handleTypedValueBlur(dimensionId, dataType, validation2, event) {
|
|
7091
|
+
const target = event.target;
|
|
7092
|
+
const value = target instanceof HTMLInputElement && target.type === "checkbox" ? String(target.checked) : target.value;
|
|
7093
|
+
this._validateTypedValue(dimensionId, dataType, validation2, value);
|
|
7094
|
+
}
|
|
7095
|
+
/** Renders the `field-errors`-style message list for a typed dimension's value input, if any. */
|
|
7096
|
+
_renderTypedValueErrors(dimensionId) {
|
|
7097
|
+
const errors = this._typedValueErrors[dimensionId];
|
|
7098
|
+
if (!errors || errors.length === 0)
|
|
7099
|
+
return "";
|
|
7100
|
+
return html`
|
|
7101
|
+
<div class="typed-input-errors">
|
|
7102
|
+
${errors.map((error2) => html`<span class="typed-input-error">${error2.message}</span>`)}
|
|
7103
|
+
</div>
|
|
7104
|
+
`;
|
|
7105
|
+
}
|
|
7106
|
+
/**
|
|
7107
|
+
* Resolve the input-type configuration for a typed dimension's value input, reusing the same
|
|
7108
|
+
* XBRL type â HTML input mapping that drives concept row fields and the column-header typed-
|
|
7109
|
+
* member input (see `getInputTypeForConceptType` / `form-field.ts`'s `_renderInput` /
|
|
7110
|
+
* `form-section.ts`'s `_renderTypedMemberHeaderInput`).
|
|
7111
|
+
*/
|
|
7112
|
+
_getTypedValueInputConfig(dataType) {
|
|
7113
|
+
return getInputTypeForConceptType(dataType, this.datatypes);
|
|
7114
|
+
}
|
|
7115
|
+
/**
|
|
7116
|
+
* Render the typed dimension's value input, choosing select/checkbox/date/generic text-like
|
|
7117
|
+
* input based on the typed member's XBRL datatype instead of always rendering plain text.
|
|
7118
|
+
*/
|
|
7119
|
+
_renderTypedValueInput(dimensionId, dataType, validation2, currentValue) {
|
|
7120
|
+
var _a;
|
|
7121
|
+
const typeConfig = this._getTypedValueInputConfig(dataType);
|
|
7122
|
+
const hasValidationError = !!((_a = this._typedValueErrors[dimensionId]) == null ? void 0 : _a.length);
|
|
7123
|
+
const inputClass = `typed-input ${hasValidationError ? "has-error" : ""}`;
|
|
7124
|
+
const onChange = (e2) => this._handleTypedValueChange(dimensionId, e2);
|
|
7125
|
+
const onBlur = (e2) => this._handleTypedValueBlur(dimensionId, dataType, validation2, e2);
|
|
7126
|
+
if (typeConfig.fieldType === "select" || isSelectType(typeConfig.fieldType)) {
|
|
7127
|
+
const options = typeConfig.enumerations || [];
|
|
7128
|
+
return html`
|
|
7129
|
+
<select class="${inputClass}" .value="${currentValue}" @change="${onChange}" @blur="${onBlur}">
|
|
7130
|
+
<option value="">${typeConfig.placeholder || I18n.t("field.select")}</option>
|
|
7131
|
+
${options.map((option) => html`
|
|
7132
|
+
<option value="${option.value}" ?selected="${currentValue === option.value}">
|
|
7133
|
+
${option.label || option.value}
|
|
7134
|
+
</option>
|
|
7135
|
+
`)}
|
|
7136
|
+
</select>
|
|
7137
|
+
`;
|
|
7138
|
+
}
|
|
7139
|
+
if (typeConfig.fieldType === "boolean" || isCheckboxType(typeConfig.fieldType)) {
|
|
7140
|
+
return html`
|
|
7141
|
+
<input
|
|
7142
|
+
type="checkbox"
|
|
7143
|
+
class="${inputClass}"
|
|
7144
|
+
.checked="${currentValue === "true"}"
|
|
7145
|
+
@change="${onChange}"
|
|
7146
|
+
@blur="${onBlur}"
|
|
7147
|
+
/>
|
|
7148
|
+
`;
|
|
7149
|
+
}
|
|
7150
|
+
const inputType = typeConfig.htmlInputType || "text";
|
|
7151
|
+
return html`
|
|
7152
|
+
<input
|
|
7153
|
+
type="${inputType}"
|
|
7154
|
+
inputmode="${typeConfig.inputMode || ""}"
|
|
7155
|
+
class="${inputClass}"
|
|
7156
|
+
.value="${currentValue}"
|
|
7157
|
+
min="${typeConfig.min !== void 0 ? typeConfig.min : ""}"
|
|
7158
|
+
max="${typeConfig.max !== void 0 ? typeConfig.max : ""}"
|
|
7159
|
+
step="${typeConfig.step !== void 0 ? typeConfig.step : ""}"
|
|
7160
|
+
@input="${onChange}"
|
|
7161
|
+
@blur="${onBlur}"
|
|
7162
|
+
placeholder="${typeConfig.placeholder || I18n.t("column.enterValuePlaceholder")}"
|
|
7163
|
+
/>
|
|
7164
|
+
`;
|
|
7165
|
+
}
|
|
6699
7166
|
_resetForm() {
|
|
6700
7167
|
this._startDate = this.periodStartDate || "";
|
|
6701
7168
|
this._endDate = this.periodEndDate || "";
|
|
6702
7169
|
this._instantDate = this.periodEndDate || this.periodStartDate || "";
|
|
6703
7170
|
this._selectedType = this.periodType === "instant" ? "instant" : "duration";
|
|
6704
7171
|
this._selectedDimensions.clear();
|
|
7172
|
+
this._typedValueErrors = {};
|
|
6705
7173
|
this._autoSelectSingleMemberDimensions();
|
|
6706
7174
|
this.requestUpdate();
|
|
6707
7175
|
}
|
|
6708
7176
|
_autoSelectSingleMemberDimensions() {
|
|
6709
7177
|
this.availableDimensions.forEach((dimension) => {
|
|
7178
|
+
var _a, _b;
|
|
6710
7179
|
if (dimension.members && dimension.members.length > 0) {
|
|
6711
7180
|
const member = dimension.members[0];
|
|
6712
7181
|
const selection = {
|
|
@@ -6722,7 +7191,9 @@ let JupiterAddColumnDialog = class extends LitElement {
|
|
|
6722
7191
|
const selection = {
|
|
6723
7192
|
axisId: dimension.id,
|
|
6724
7193
|
axisLabel: dimension.axisLabel,
|
|
6725
|
-
isTyped: true
|
|
7194
|
+
isTyped: true,
|
|
7195
|
+
dataType: (_a = dimension.typedMember) == null ? void 0 : _a.dataType,
|
|
7196
|
+
validation: (_b = dimension.typedMember) == null ? void 0 : _b.validation
|
|
6726
7197
|
// Note: typedValue will be entered in column header, not here
|
|
6727
7198
|
};
|
|
6728
7199
|
this._selectedDimensions.set(dimension.id, selection);
|
|
@@ -6748,6 +7219,8 @@ let JupiterAddColumnDialog = class extends LitElement {
|
|
|
6748
7219
|
<div class="date-wrapper">
|
|
6749
7220
|
<input
|
|
6750
7221
|
type="date"
|
|
7222
|
+
min="1900-01-01"
|
|
7223
|
+
max="2100-12-31"
|
|
6751
7224
|
class="form-input"
|
|
6752
7225
|
.value="${this._instantDate}"
|
|
6753
7226
|
@change="${this._handleInstantDateChange}"
|
|
@@ -6767,6 +7240,8 @@ let JupiterAddColumnDialog = class extends LitElement {
|
|
|
6767
7240
|
<div class="date-wrapper">
|
|
6768
7241
|
<input
|
|
6769
7242
|
type="date"
|
|
7243
|
+
min="1900-01-01"
|
|
7244
|
+
max="2100-12-31"
|
|
6770
7245
|
class="form-input"
|
|
6771
7246
|
.value="${this._startDate}"
|
|
6772
7247
|
@change="${this._handleStartDateChange}"
|
|
@@ -6784,6 +7259,8 @@ let JupiterAddColumnDialog = class extends LitElement {
|
|
|
6784
7259
|
<div class="date-wrapper">
|
|
6785
7260
|
<input
|
|
6786
7261
|
type="date"
|
|
7262
|
+
min="1900-01-01"
|
|
7263
|
+
max="2100-12-31"
|
|
6787
7264
|
class="form-input"
|
|
6788
7265
|
.value="${this._endDate}"
|
|
6789
7266
|
@change="${this._handleEndDateChange}"
|
|
@@ -6806,6 +7283,7 @@ let JupiterAddColumnDialog = class extends LitElement {
|
|
|
6806
7283
|
<p class="form-description">${I18n.t("column.dimensionsDescription")}</p>
|
|
6807
7284
|
|
|
6808
7285
|
${this.availableDimensions.map((dimension) => {
|
|
7286
|
+
var _a, _b;
|
|
6809
7287
|
const isSelected = this._selectedDimensions.has(dimension.id);
|
|
6810
7288
|
const selection = this._selectedDimensions.get(dimension.id);
|
|
6811
7289
|
const isTypedDimension = !!(dimension.typedMember && (!dimension.members || dimension.members.length === 0));
|
|
@@ -6830,13 +7308,8 @@ let JupiterAddColumnDialog = class extends LitElement {
|
|
|
6830
7308
|
<!-- Typed Dimension Input -->
|
|
6831
7309
|
<div class="member-selection">
|
|
6832
7310
|
<label>${I18n.t("column.enterValue")} ${dimension.conceptName}:</label>
|
|
6833
|
-
|
|
6834
|
-
|
|
6835
|
-
class="typed-input"
|
|
6836
|
-
.value="${(selection == null ? void 0 : selection.typedValue) || ""}"
|
|
6837
|
-
@input="${(e2) => this._handleTypedValueChange(dimension.id, e2)}"
|
|
6838
|
-
placeholder="${I18n.t("column.enterValuePlaceholder")}"
|
|
6839
|
-
/>
|
|
7311
|
+
${this._renderTypedValueInput(dimension.id, (_a = dimension.typedMember) == null ? void 0 : _a.dataType, (_b = dimension.typedMember) == null ? void 0 : _b.validation, (selection == null ? void 0 : selection.typedValue) || "")}
|
|
7312
|
+
${this._renderTypedValueErrors(dimension.id)}
|
|
6840
7313
|
</div>
|
|
6841
7314
|
` : html`
|
|
6842
7315
|
<!-- Domain Members Selection -->
|
|
@@ -7153,6 +7626,20 @@ JupiterAddColumnDialog.styles = css`
|
|
|
7153
7626
|
font-size: 14px;
|
|
7154
7627
|
}
|
|
7155
7628
|
|
|
7629
|
+
.typed-input.has-error {
|
|
7630
|
+
border-color: var(--jupiter-error-color, #d32f2f);
|
|
7631
|
+
}
|
|
7632
|
+
|
|
7633
|
+
.typed-input-errors {
|
|
7634
|
+
margin-top: 4px;
|
|
7635
|
+
}
|
|
7636
|
+
|
|
7637
|
+
.typed-input-error {
|
|
7638
|
+
display: block;
|
|
7639
|
+
font-size: 12px;
|
|
7640
|
+
color: var(--jupiter-error-color, #d32f2f);
|
|
7641
|
+
}
|
|
7642
|
+
|
|
7156
7643
|
.member-list {
|
|
7157
7644
|
max-height: 120px;
|
|
7158
7645
|
overflow-y: auto;
|
|
@@ -7208,6 +7695,9 @@ __decorateClass$5([
|
|
|
7208
7695
|
__decorateClass$5([
|
|
7209
7696
|
n2({ type: String })
|
|
7210
7697
|
], JupiterAddColumnDialog.prototype, "periodEndDate", 2);
|
|
7698
|
+
__decorateClass$5([
|
|
7699
|
+
n2({ type: Array })
|
|
7700
|
+
], JupiterAddColumnDialog.prototype, "datatypes", 2);
|
|
7211
7701
|
__decorateClass$5([
|
|
7212
7702
|
r()
|
|
7213
7703
|
], JupiterAddColumnDialog.prototype, "_startDate", 2);
|
|
@@ -7223,6 +7713,9 @@ __decorateClass$5([
|
|
|
7223
7713
|
__decorateClass$5([
|
|
7224
7714
|
r()
|
|
7225
7715
|
], JupiterAddColumnDialog.prototype, "_selectedDimensions", 2);
|
|
7716
|
+
__decorateClass$5([
|
|
7717
|
+
r()
|
|
7718
|
+
], JupiterAddColumnDialog.prototype, "_typedValueErrors", 2);
|
|
7226
7719
|
JupiterAddColumnDialog = __decorateClass$5([
|
|
7227
7720
|
t$1("jupiter-add-column-dialog")
|
|
7228
7721
|
], JupiterAddColumnDialog);
|
|
@@ -7267,6 +7760,7 @@ let JupiterFormSection = class extends LitElement {
|
|
|
7267
7760
|
this._sectionPeriodType = "duration";
|
|
7268
7761
|
this._openMenuColumnId = null;
|
|
7269
7762
|
this._insertAfterColumnId = null;
|
|
7763
|
+
this._typedMemberErrors = {};
|
|
7270
7764
|
this._boundFieldBlur = (e2) => {
|
|
7271
7765
|
this._clearHighlights();
|
|
7272
7766
|
this._focusedConceptId = null;
|
|
@@ -7888,9 +8382,72 @@ let JupiterFormSection = class extends LitElement {
|
|
|
7888
8382
|
var _a;
|
|
7889
8383
|
return ((_a = this.typedMemberData[columnId]) == null ? void 0 : _a[axisId]) || "";
|
|
7890
8384
|
}
|
|
8385
|
+
/** True once any concept in this section has a non-empty value entered for the given column. */
|
|
8386
|
+
_columnHasAnyValue(columnId) {
|
|
8387
|
+
return Object.keys(this.formData).some((conceptId) => {
|
|
8388
|
+
var _a;
|
|
8389
|
+
const value = (_a = this.formData[conceptId]) == null ? void 0 : _a[columnId];
|
|
8390
|
+
return value !== void 0 && value !== null && value !== "";
|
|
8391
|
+
});
|
|
8392
|
+
}
|
|
8393
|
+
/**
|
|
8394
|
+
* Resolve the input-type configuration for a typed dimension's column-header input,
|
|
8395
|
+
* reusing the same XBRL type â HTML input mapping that drives concept row fields
|
|
8396
|
+
* (see `getInputTypeForConceptType` / `form-field.ts`'s `_renderInput`).
|
|
8397
|
+
*/
|
|
8398
|
+
_getTypedMemberInputConfig(typedMember) {
|
|
8399
|
+
return getInputTypeForConceptType(typedMember.type, this.datatypes);
|
|
8400
|
+
}
|
|
8401
|
+
_typedMemberErrorKey(columnId, axisId) {
|
|
8402
|
+
return `${columnId}::${axisId}`;
|
|
8403
|
+
}
|
|
8404
|
+
/**
|
|
8405
|
+
* Validate a typed-member header value against its XBRL datatype's `validation` constraints
|
|
8406
|
+
* (minLength/maxLength/length/pattern/totalDigits), reusing the exact same datatype-driven
|
|
8407
|
+
* validator concept row fields use on blur (`XBRLValidator.validateConceptValue` â see
|
|
8408
|
+
* `form-field.ts`'s `_validateXBRLDatatype`). Stores/clears the result in `_typedMemberErrors`.
|
|
8409
|
+
*/
|
|
8410
|
+
_validateTypedMemberValue(columnId, typedMember, value) {
|
|
8411
|
+
const key = this._typedMemberErrorKey(columnId, typedMember.axisId);
|
|
8412
|
+
const hasExistingEntry = key in this._typedMemberErrors;
|
|
8413
|
+
if (!value) {
|
|
8414
|
+
if (hasExistingEntry) {
|
|
8415
|
+
const updated = { ...this._typedMemberErrors };
|
|
8416
|
+
delete updated[key];
|
|
8417
|
+
this._typedMemberErrors = updated;
|
|
8418
|
+
}
|
|
8419
|
+
return;
|
|
8420
|
+
}
|
|
8421
|
+
const result = typedMember.validation ? XBRLValidator.validateAgainstRules(value, typedMember.validation) : XBRLValidator.validateConceptValue(typedMember.type, value, this.datatypes);
|
|
8422
|
+
if (result.valid) {
|
|
8423
|
+
if (hasExistingEntry) {
|
|
8424
|
+
const updated = { ...this._typedMemberErrors };
|
|
8425
|
+
delete updated[key];
|
|
8426
|
+
this._typedMemberErrors = updated;
|
|
8427
|
+
}
|
|
8428
|
+
} else {
|
|
8429
|
+
this._typedMemberErrors = { ...this._typedMemberErrors, [key]: result.errors };
|
|
8430
|
+
}
|
|
8431
|
+
}
|
|
8432
|
+
_handleTypedMemberHeaderBlur(event, columnId, typedMember) {
|
|
8433
|
+
const target = event.target;
|
|
8434
|
+
const value = target instanceof HTMLInputElement && target.type === "checkbox" ? String(target.checked) : target.value;
|
|
8435
|
+
this._validateTypedMemberValue(columnId, typedMember, value);
|
|
8436
|
+
}
|
|
8437
|
+
/** Renders the `field-errors`-style message list for a typed-member header input, if any. */
|
|
8438
|
+
_renderTypedMemberHeaderErrors(columnId, axisId) {
|
|
8439
|
+
const errors = this._typedMemberErrors[this._typedMemberErrorKey(columnId, axisId)];
|
|
8440
|
+
if (!errors || errors.length === 0)
|
|
8441
|
+
return "";
|
|
8442
|
+
return html`
|
|
8443
|
+
<div class="typed-member-header-errors">
|
|
8444
|
+
${errors.map((error2) => html`<span class="typed-member-header-error">${error2.message}</span>`)}
|
|
8445
|
+
</div>
|
|
8446
|
+
`;
|
|
8447
|
+
}
|
|
7891
8448
|
_handleTypedMemberHeaderChange(event, columnId, axisId) {
|
|
7892
8449
|
const target = event.target;
|
|
7893
|
-
const value = target.value;
|
|
8450
|
+
const value = target instanceof HTMLInputElement && target.type === "checkbox" ? String(target.checked) : target.value;
|
|
7894
8451
|
console.log(`đ [FormSection] Typed member header change: columnId=${columnId}, axisId=${axisId}, value=${value}`);
|
|
7895
8452
|
this.dispatchEvent(new CustomEvent("typed-member-change", {
|
|
7896
8453
|
detail: {
|
|
@@ -7901,6 +8458,69 @@ let JupiterFormSection = class extends LitElement {
|
|
|
7901
8458
|
bubbles: true
|
|
7902
8459
|
}));
|
|
7903
8460
|
}
|
|
8461
|
+
/**
|
|
8462
|
+
* Render the typed-member column-header input element, choosing select/checkbox/date/generic
|
|
8463
|
+
* text-like input based on the typed member's XBRL datatype â mirrors the branching in
|
|
8464
|
+
* `form-field.ts`'s `_renderInput`, adapted for the compact header cell.
|
|
8465
|
+
*/
|
|
8466
|
+
_renderTypedMemberHeaderInput(columnId, typedMember, isRequired) {
|
|
8467
|
+
var _a;
|
|
8468
|
+
const typeConfig = this._getTypedMemberInputConfig(typedMember);
|
|
8469
|
+
const currentValue = this._getTypedMemberHeaderValue(columnId, typedMember.axisId);
|
|
8470
|
+
const hasValidationError = !!((_a = this._typedMemberErrors[this._typedMemberErrorKey(columnId, typedMember.axisId)]) == null ? void 0 : _a.length);
|
|
8471
|
+
const fieldClass = `typed-member-header-field ${isRequired ? "required-field" : ""} ${hasValidationError ? "has-error" : ""}`;
|
|
8472
|
+
const requiredTitle = isRequired ? I18n.t("validation.typedMemberRequired") : "";
|
|
8473
|
+
const onChange = (e2) => this._handleTypedMemberHeaderChange(e2, columnId, typedMember.axisId);
|
|
8474
|
+
const onBlur = (e2) => this._handleTypedMemberHeaderBlur(e2, columnId, typedMember);
|
|
8475
|
+
if (typeConfig.fieldType === "select" || isSelectType(typeConfig.fieldType)) {
|
|
8476
|
+
const options = typeConfig.enumerations || [];
|
|
8477
|
+
return html`
|
|
8478
|
+
<select
|
|
8479
|
+
class="${fieldClass}"
|
|
8480
|
+
.value="${currentValue}"
|
|
8481
|
+
title="${requiredTitle}"
|
|
8482
|
+
@change="${onChange}"
|
|
8483
|
+
@blur="${onBlur}"
|
|
8484
|
+
>
|
|
8485
|
+
<option value="">${typeConfig.placeholder || I18n.t("field.select")}</option>
|
|
8486
|
+
${options.map((option) => html`
|
|
8487
|
+
<option value="${option.value}" ?selected="${currentValue === option.value}">
|
|
8488
|
+
${option.label || option.value}
|
|
8489
|
+
</option>
|
|
8490
|
+
`)}
|
|
8491
|
+
</select>
|
|
8492
|
+
`;
|
|
8493
|
+
}
|
|
8494
|
+
if (typeConfig.fieldType === "boolean" || isCheckboxType(typeConfig.fieldType)) {
|
|
8495
|
+
return html`
|
|
8496
|
+
<input
|
|
8497
|
+
type="checkbox"
|
|
8498
|
+
class="${fieldClass}"
|
|
8499
|
+
.checked="${currentValue === "true"}"
|
|
8500
|
+
title="${requiredTitle}"
|
|
8501
|
+
@change="${onChange}"
|
|
8502
|
+
@blur="${onBlur}"
|
|
8503
|
+
/>
|
|
8504
|
+
`;
|
|
8505
|
+
}
|
|
8506
|
+
const inputType = typeConfig.htmlInputType || "text";
|
|
8507
|
+
const placeholder = `${typeConfig.placeholder || I18n.t("section.enterPlaceholder")} ${typedMember.axisLabel}`;
|
|
8508
|
+
return html`
|
|
8509
|
+
<input
|
|
8510
|
+
type="${inputType}"
|
|
8511
|
+
inputmode="${typeConfig.inputMode || ""}"
|
|
8512
|
+
class="${fieldClass}"
|
|
8513
|
+
placeholder="${placeholder}"
|
|
8514
|
+
.value="${currentValue}"
|
|
8515
|
+
min="${typeConfig.min !== void 0 ? typeConfig.min : ""}"
|
|
8516
|
+
max="${typeConfig.max !== void 0 ? typeConfig.max : ""}"
|
|
8517
|
+
step="${typeConfig.step !== void 0 ? typeConfig.step : ""}"
|
|
8518
|
+
title="${requiredTitle}"
|
|
8519
|
+
@input="${onChange}"
|
|
8520
|
+
@blur="${onBlur}"
|
|
8521
|
+
/>
|
|
8522
|
+
`;
|
|
8523
|
+
}
|
|
7904
8524
|
_handleAddRepeat(event) {
|
|
7905
8525
|
event.stopPropagation();
|
|
7906
8526
|
this.dispatchEvent(new CustomEvent("add-concept-repeat", {
|
|
@@ -8044,23 +8664,24 @@ let JupiterFormSection = class extends LitElement {
|
|
|
8044
8664
|
<!-- Typed member input fields in column header -->
|
|
8045
8665
|
${((_a = column2.dimensionData) == null ? void 0 : _a.hasTypedMembers) ? html`
|
|
8046
8666
|
<div class="typed-members-header">
|
|
8047
|
-
${(_c = (_b = column2.dimensionData) == null ? void 0 : _b.typedMembers) == null ? void 0 : _c.map((typedMember) =>
|
|
8667
|
+
${(_c = (_b = column2.dimensionData) == null ? void 0 : _b.typedMembers) == null ? void 0 : _c.map((typedMember) => {
|
|
8668
|
+
const isRequired = !this._getTypedMemberHeaderValue(column2.id, typedMember.axisId) && this._columnHasAnyValue(column2.id);
|
|
8669
|
+
return html`
|
|
8048
8670
|
<div class="typed-member-header-input">
|
|
8049
8671
|
${this.mode === "readonly" ? html`
|
|
8050
8672
|
<span class="typed-member-header-field" style="display:block;padding:4px 0;">
|
|
8051
8673
|
${this._getTypedMemberHeaderValue(column2.id, typedMember.axisId) || "â"}
|
|
8052
8674
|
</span>
|
|
8053
8675
|
` : html`
|
|
8054
|
-
<input
|
|
8055
|
-
|
|
8056
|
-
class="typed-member-
|
|
8057
|
-
|
|
8058
|
-
|
|
8059
|
-
@input="${(e2) => this._handleTypedMemberHeaderChange(e2, column2.id, typedMember.axisId)}"
|
|
8060
|
-
/>
|
|
8676
|
+
<div class="typed-member-input-wrapper">
|
|
8677
|
+
${this._renderTypedMemberHeaderInput(column2.id, typedMember, isRequired)}
|
|
8678
|
+
${isRequired ? html`<span class="typed-member-required-asterisk" title="${I18n.t("validation.typedMemberRequired")}">*</span>` : ""}
|
|
8679
|
+
</div>
|
|
8680
|
+
${this._renderTypedMemberHeaderErrors(column2.id, typedMember.axisId)}
|
|
8061
8681
|
`}
|
|
8062
8682
|
</div>
|
|
8063
|
-
|
|
8683
|
+
`;
|
|
8684
|
+
})}
|
|
8064
8685
|
</div>
|
|
8065
8686
|
` : ""}
|
|
8066
8687
|
</div>
|
|
@@ -8133,6 +8754,7 @@ let JupiterFormSection = class extends LitElement {
|
|
|
8133
8754
|
.availableDimensions="${this.availableDimensions}"
|
|
8134
8755
|
.periodStartDate="${this.periodStartDate}"
|
|
8135
8756
|
.periodEndDate="${this.periodEndDate}"
|
|
8757
|
+
.datatypes="${this.datatypes}"
|
|
8136
8758
|
@dialog-cancel="${this._handleDialogCancel}"
|
|
8137
8759
|
@column-add="${this._handleColumnAdd}"
|
|
8138
8760
|
></jupiter-add-column-dialog>
|
|
@@ -8386,6 +9008,34 @@ JupiterFormSection.styles = css`
|
|
|
8386
9008
|
gap: 2px;
|
|
8387
9009
|
}
|
|
8388
9010
|
|
|
9011
|
+
.typed-member-input-wrapper {
|
|
9012
|
+
display: flex;
|
|
9013
|
+
align-items: center;
|
|
9014
|
+
gap: 4px;
|
|
9015
|
+
}
|
|
9016
|
+
|
|
9017
|
+
.typed-member-header-field.required-field,
|
|
9018
|
+
.typed-member-header-field.has-error {
|
|
9019
|
+
border-color: var(--jupiter-error-color, #d32f2f);
|
|
9020
|
+
}
|
|
9021
|
+
|
|
9022
|
+
.typed-member-header-errors {
|
|
9023
|
+
margin-top: 2px;
|
|
9024
|
+
}
|
|
9025
|
+
|
|
9026
|
+
.typed-member-header-error {
|
|
9027
|
+
display: block;
|
|
9028
|
+
font-size: 11px;
|
|
9029
|
+
color: var(--jupiter-error-color, #d32f2f);
|
|
9030
|
+
}
|
|
9031
|
+
|
|
9032
|
+
.typed-member-required-asterisk {
|
|
9033
|
+
color: var(--jupiter-error-color, #d32f2f);
|
|
9034
|
+
font-weight: bold;
|
|
9035
|
+
font-size: 13px;
|
|
9036
|
+
flex-shrink: 0;
|
|
9037
|
+
}
|
|
9038
|
+
|
|
8389
9039
|
.typed-member-header-label {
|
|
8390
9040
|
font-size: 11px;
|
|
8391
9041
|
font-weight: 500;
|
|
@@ -8514,6 +9164,9 @@ __decorateClass$4([
|
|
|
8514
9164
|
__decorateClass$4([
|
|
8515
9165
|
r()
|
|
8516
9166
|
], JupiterFormSection.prototype, "_insertAfterColumnId", 2);
|
|
9167
|
+
__decorateClass$4([
|
|
9168
|
+
r()
|
|
9169
|
+
], JupiterFormSection.prototype, "_typedMemberErrors", 2);
|
|
8517
9170
|
__decorateClass$4([
|
|
8518
9171
|
r()
|
|
8519
9172
|
], JupiterFormSection.prototype, "_expandedConcepts", 2);
|
|
@@ -10919,6 +11572,7 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
10919
11572
|
this.readonly = false;
|
|
10920
11573
|
this.calculationEnabled = false;
|
|
10921
11574
|
this.formulaEnabled = false;
|
|
11575
|
+
this.tableLayoutEnabled = false;
|
|
10922
11576
|
this.periodStartDate = "2025-01-01";
|
|
10923
11577
|
this.periodEndDate = "2025-12-31";
|
|
10924
11578
|
this.language = "en";
|
|
@@ -10928,6 +11582,7 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
10928
11582
|
this.roleFilterAxes = [];
|
|
10929
11583
|
this.showLastValidationResultBtn = false;
|
|
10930
11584
|
this.isDraftSaving = false;
|
|
11585
|
+
this.hasUnsavedChanges = false;
|
|
10931
11586
|
this.defaultUnits = [];
|
|
10932
11587
|
this.decimals = "INF";
|
|
10933
11588
|
this._formData = {};
|
|
@@ -11016,6 +11671,7 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
11016
11671
|
const oldValue = this._writeFormDataValue(conceptId, columnId, value);
|
|
11017
11672
|
this._touched.add(`${conceptId}-${columnId}`);
|
|
11018
11673
|
this._dirty = true;
|
|
11674
|
+
this.hasUnsavedChanges = true;
|
|
11019
11675
|
this._validateForm();
|
|
11020
11676
|
this.requestUpdate();
|
|
11021
11677
|
this.dispatchEvent(new CustomEvent("field-change", {
|
|
@@ -11053,6 +11709,12 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
11053
11709
|
}
|
|
11054
11710
|
}
|
|
11055
11711
|
updated(changedProperties) {
|
|
11712
|
+
if (changedProperties.has("hasUnsavedChanges")) {
|
|
11713
|
+
this.dispatchEvent(new CustomEvent("has-unsaved-changes-change", {
|
|
11714
|
+
detail: { hasUnsavedChanges: this.hasUnsavedChanges },
|
|
11715
|
+
bubbles: true
|
|
11716
|
+
}));
|
|
11717
|
+
}
|
|
11056
11718
|
if (changedProperties.has("decimals") || changedProperties.has("masterData")) {
|
|
11057
11719
|
this._computeEffectiveMasterData();
|
|
11058
11720
|
}
|
|
@@ -11457,22 +12119,34 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
11457
12119
|
*
|
|
11458
12120
|
* Skipped when form state is being restored from external props or localStorage so that
|
|
11459
12121
|
* the user's previously saved preferences are not overridden.
|
|
12122
|
+
*
|
|
12123
|
+
* Roles that explicitly set showPreviousYear: false in presentation.json opt out of this
|
|
12124
|
+
* auto-enable â that's a deliberate taxonomy-author decision and must win over the
|
|
12125
|
+
* dimension-member heuristic below.
|
|
11460
12126
|
*/
|
|
11461
12127
|
_applyDimensionShowPreviousYear() {
|
|
11462
|
-
var _a, _b;
|
|
12128
|
+
var _a, _b, _c, _d, _e, _f;
|
|
11463
12129
|
if (this.mode !== "admin")
|
|
11464
12130
|
return;
|
|
11465
12131
|
const hasExternalState = !!(this.dynaformsFacts && this.dynaformsMetadata);
|
|
11466
12132
|
const hasLocalDraft = ((_a = this._draftStorageService) == null ? void 0 : _a.hasDraft()) ?? false;
|
|
11467
12133
|
if (hasExternalState || hasLocalDraft)
|
|
11468
12134
|
return;
|
|
12135
|
+
const explicitlyDisabled = /* @__PURE__ */ new Set();
|
|
12136
|
+
(_e = (_d = (_c = (_b = this.xbrlInput) == null ? void 0 : _b.presentation) == null ? void 0 : _c[0]) == null ? void 0 : _d.roles) == null ? void 0 : _e.forEach((role) => {
|
|
12137
|
+
if (role.showPreviousYear === false) {
|
|
12138
|
+
explicitlyDisabled.add(role.id);
|
|
12139
|
+
}
|
|
12140
|
+
});
|
|
11469
12141
|
const updated = { ...this._periodPreferences };
|
|
11470
12142
|
let changed = false;
|
|
11471
12143
|
for (const roleId of Object.keys(updated)) {
|
|
12144
|
+
if (explicitlyDisabled.has(roleId))
|
|
12145
|
+
continue;
|
|
11472
12146
|
const prefs = updated[roleId];
|
|
11473
12147
|
if (prefs.showPreviousYear)
|
|
11474
12148
|
continue;
|
|
11475
|
-
const hasDimensionMembers = ((
|
|
12149
|
+
const hasDimensionMembers = ((_f = prefs.dimensionSelections) == null ? void 0 : _f.some(
|
|
11476
12150
|
(ds) => ds.selectedMemberIds && ds.selectedMemberIds.length > 0
|
|
11477
12151
|
)) ?? false;
|
|
11478
12152
|
if (hasDimensionMembers) {
|
|
@@ -11871,6 +12545,7 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
11871
12545
|
console.log(`đž Updated formData[${conceptId}]:`, this._formData[conceptId]);
|
|
11872
12546
|
this._touched.add(`${conceptId}-${columnId}`);
|
|
11873
12547
|
this._dirty = true;
|
|
12548
|
+
this.hasUnsavedChanges = true;
|
|
11874
12549
|
this._validateForm();
|
|
11875
12550
|
this.requestUpdate();
|
|
11876
12551
|
this.dispatchEvent(new CustomEvent("field-change", {
|
|
@@ -11902,6 +12577,7 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
11902
12577
|
this._storeUnit(conceptId, columnId, unit);
|
|
11903
12578
|
}
|
|
11904
12579
|
this._dirty = true;
|
|
12580
|
+
this.hasUnsavedChanges = true;
|
|
11905
12581
|
this.requestUpdate();
|
|
11906
12582
|
}
|
|
11907
12583
|
_handleUnitChange(event) {
|
|
@@ -11911,6 +12587,7 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
11911
12587
|
console.log(`đˇī¸ Unit change received: conceptId=${conceptId}, columnId=${columnId}, unit=${unit}`);
|
|
11912
12588
|
this._storeUnit(conceptId, columnId, unit);
|
|
11913
12589
|
this._dirty = true;
|
|
12590
|
+
this.hasUnsavedChanges = true;
|
|
11914
12591
|
this.requestUpdate();
|
|
11915
12592
|
}
|
|
11916
12593
|
_storeUnit(conceptId, columnId, unit) {
|
|
@@ -11935,6 +12612,7 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
11935
12612
|
return;
|
|
11936
12613
|
this._storeDecimals(conceptId, columnId, decimals);
|
|
11937
12614
|
this._dirty = true;
|
|
12615
|
+
this.hasUnsavedChanges = true;
|
|
11938
12616
|
this.requestUpdate();
|
|
11939
12617
|
}
|
|
11940
12618
|
_computeEffectiveMasterData() {
|
|
@@ -12076,6 +12754,7 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
12076
12754
|
const { conceptId } = event.detail;
|
|
12077
12755
|
this._repeatCounts = { ...this._repeatCounts, [conceptId]: (this._repeatCounts[conceptId] || 0) + 1 };
|
|
12078
12756
|
this._dirty = true;
|
|
12757
|
+
this.hasUnsavedChanges = true;
|
|
12079
12758
|
}
|
|
12080
12759
|
_handleRemoveConceptRepeat(event) {
|
|
12081
12760
|
const { conceptId: rawConceptId } = event.detail;
|
|
@@ -12128,6 +12807,7 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
12128
12807
|
this._periodData = newPeriodData;
|
|
12129
12808
|
this._repeatCounts = { ...this._repeatCounts, [baseConceptId]: current - 1 };
|
|
12130
12809
|
this._dirty = true;
|
|
12810
|
+
this.hasUnsavedChanges = true;
|
|
12131
12811
|
}
|
|
12132
12812
|
_handleTypedMemberChange(event) {
|
|
12133
12813
|
const { columnId, axisId, value, conceptId } = event.detail;
|
|
@@ -12140,6 +12820,7 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
12140
12820
|
updatedTypedMemberData[storageKey] = { ...updatedTypedMemberData[storageKey], [axisId]: value };
|
|
12141
12821
|
this._typedMemberData = updatedTypedMemberData;
|
|
12142
12822
|
this._dirty = true;
|
|
12823
|
+
this.hasUnsavedChanges = true;
|
|
12143
12824
|
this.requestUpdate();
|
|
12144
12825
|
this.dispatchEvent(new CustomEvent("typed-member-change", {
|
|
12145
12826
|
detail: { columnId, axisId, value },
|
|
@@ -12338,6 +13019,7 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
12338
13019
|
}
|
|
12339
13020
|
this._removeColumnData(columnId, sectionId);
|
|
12340
13021
|
this._dirty = true;
|
|
13022
|
+
this.hasUnsavedChanges = true;
|
|
12341
13023
|
this._validateForm();
|
|
12342
13024
|
this.requestUpdate();
|
|
12343
13025
|
this.dispatchEvent(new CustomEvent("column-remove", {
|
|
@@ -12354,6 +13036,7 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
12354
13036
|
}
|
|
12355
13037
|
this._hiddenColumnIds.get(sectionId).add(columnId);
|
|
12356
13038
|
this._dirty = true;
|
|
13039
|
+
this.hasUnsavedChanges = true;
|
|
12357
13040
|
this.requestUpdate();
|
|
12358
13041
|
this.dispatchEvent(new CustomEvent("column-hide", {
|
|
12359
13042
|
detail: { columnId, sectionId },
|
|
@@ -12367,6 +13050,7 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
12367
13050
|
return;
|
|
12368
13051
|
(_a = this._hiddenColumnIds.get(sectionId)) == null ? void 0 : _a.delete(columnId);
|
|
12369
13052
|
this._dirty = true;
|
|
13053
|
+
this.hasUnsavedChanges = true;
|
|
12370
13054
|
this.requestUpdate();
|
|
12371
13055
|
this.dispatchEvent(new CustomEvent("column-show", {
|
|
12372
13056
|
detail: { columnId, sectionId },
|
|
@@ -12430,8 +13114,8 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
12430
13114
|
if (dimension.typedMember) {
|
|
12431
13115
|
availableDimension.typedMember = {
|
|
12432
13116
|
id: dimension.typedMember.id,
|
|
12433
|
-
dataType: "string"
|
|
12434
|
-
|
|
13117
|
+
dataType: dimension.typedMember.type || "string",
|
|
13118
|
+
validation: dimension.typedMember.validation
|
|
12435
13119
|
};
|
|
12436
13120
|
}
|
|
12437
13121
|
availableDimensions.push(availableDimension);
|
|
@@ -12652,8 +13336,10 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
12652
13336
|
axisLabel: dim.axisLabel,
|
|
12653
13337
|
typedMemberId: dim.axisId,
|
|
12654
13338
|
// Use axisId as typedMemberId for typed dimensions
|
|
12655
|
-
memberLabel: dim.axisLabel
|
|
13339
|
+
memberLabel: dim.axisLabel,
|
|
12656
13340
|
// Use axisLabel as memberLabel
|
|
13341
|
+
type: dim.dataType,
|
|
13342
|
+
validation: dim.validation
|
|
12657
13343
|
}));
|
|
12658
13344
|
}
|
|
12659
13345
|
}
|
|
@@ -12682,6 +13368,7 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
12682
13368
|
}
|
|
12683
13369
|
this._replicateFieldsForNewColumn(newColumnId, request, sectionId);
|
|
12684
13370
|
this._dirty = true;
|
|
13371
|
+
this.hasUnsavedChanges = true;
|
|
12685
13372
|
this.requestUpdate();
|
|
12686
13373
|
this.dispatchEvent(new CustomEvent("column-add", {
|
|
12687
13374
|
detail: { column: newColumn },
|
|
@@ -13001,9 +13688,11 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
13001
13688
|
});
|
|
13002
13689
|
if (draftPayloadSnapshot === this._lastSavedDraftSnapshot) {
|
|
13003
13690
|
console.log(`âšī¸ [Save Draft] No draft content change detected (${source}); skipping event emit.`);
|
|
13691
|
+
this.hasUnsavedChanges = false;
|
|
13004
13692
|
return;
|
|
13005
13693
|
}
|
|
13006
13694
|
this._lastSavedDraftSnapshot = draftPayloadSnapshot;
|
|
13695
|
+
this.hasUnsavedChanges = false;
|
|
13007
13696
|
const saved = this._draftStorageService.saveDraft(draftData, metadata);
|
|
13008
13697
|
console.log("đž [Save Draft] Metadata saved with enhanced selectedRoleIds:", metadata.selectedRoleIds);
|
|
13009
13698
|
this.dynaformsFacts = draftData;
|
|
@@ -14087,6 +14776,7 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
14087
14776
|
this._formData = { ...this.initialData };
|
|
14088
14777
|
this._touched.clear();
|
|
14089
14778
|
this._dirty = false;
|
|
14779
|
+
this.hasUnsavedChanges = false;
|
|
14090
14780
|
this._submitted = false;
|
|
14091
14781
|
this._preservedFormData = {};
|
|
14092
14782
|
this._preservedTypedMemberData = {};
|
|
@@ -15030,6 +15720,7 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
15030
15720
|
setData(data) {
|
|
15031
15721
|
this._formData = { ...data };
|
|
15032
15722
|
this._dirty = true;
|
|
15723
|
+
this.hasUnsavedChanges = true;
|
|
15033
15724
|
this._validateForm();
|
|
15034
15725
|
this.requestUpdate();
|
|
15035
15726
|
}
|
|
@@ -15458,7 +16149,7 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
15458
16149
|
<button
|
|
15459
16150
|
class="btn-secondary"
|
|
15460
16151
|
@click="${this._handleSaveDraft}"
|
|
15461
|
-
?disabled="${this.disabled || this.readonly || this.isDraftSaving}"
|
|
16152
|
+
?disabled="${this.disabled || this.readonly || this.isDraftSaving || !this.hasUnsavedChanges}"
|
|
15462
16153
|
>
|
|
15463
16154
|
${this.isDraftSaving ? I18n.t("form.savingDraft") : I18n.t("form.saveDraft")}
|
|
15464
16155
|
</button>
|
|
@@ -16456,6 +17147,9 @@ __decorateClass([
|
|
|
16456
17147
|
__decorateClass([
|
|
16457
17148
|
n2({ type: Boolean, attribute: "formula-enabled" })
|
|
16458
17149
|
], JupiterDynamicForm.prototype, "formulaEnabled", 2);
|
|
17150
|
+
__decorateClass([
|
|
17151
|
+
n2({ type: Boolean, attribute: "table-layout-enabled" })
|
|
17152
|
+
], JupiterDynamicForm.prototype, "tableLayoutEnabled", 2);
|
|
16459
17153
|
__decorateClass([
|
|
16460
17154
|
n2({ type: String })
|
|
16461
17155
|
], JupiterDynamicForm.prototype, "periodStartDate", 2);
|
|
@@ -16486,6 +17180,9 @@ __decorateClass([
|
|
|
16486
17180
|
__decorateClass([
|
|
16487
17181
|
n2({ type: Boolean, attribute: "is-draft-saving" })
|
|
16488
17182
|
], JupiterDynamicForm.prototype, "isDraftSaving", 2);
|
|
17183
|
+
__decorateClass([
|
|
17184
|
+
n2({ type: Boolean, attribute: "has-unsaved-changes", reflect: true })
|
|
17185
|
+
], JupiterDynamicForm.prototype, "hasUnsavedChanges", 2);
|
|
16489
17186
|
__decorateClass([
|
|
16490
17187
|
n2({ type: Array })
|
|
16491
17188
|
], JupiterDynamicForm.prototype, "defaultUnits", 2);
|