jupiter-dynamic-forms 1.20.5 â 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 +1013 -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
|
}
|
|
@@ -3087,15 +3175,37 @@ function narrowByPreferredOccurrence(candidates, ctx) {
|
|
|
3087
3175
|
});
|
|
3088
3176
|
return result;
|
|
3089
3177
|
}
|
|
3090
|
-
function
|
|
3091
|
-
|
|
3178
|
+
function narrowToDirectChildOfDescendantAnchor(candidates, variable, ctx) {
|
|
3179
|
+
var _a, _b;
|
|
3180
|
+
if (variable.bindAsSequence)
|
|
3181
|
+
return candidates;
|
|
3182
|
+
if (new Set(candidates.map((candidate) => candidate.conceptId)).size <= 1)
|
|
3183
|
+
return candidates;
|
|
3184
|
+
const relationFilter = variable.filters.find(
|
|
3185
|
+
(filter2) => filter2.type === "CONCEPT_RELATION" && filter2.attributes["axis"] === "descendant"
|
|
3186
|
+
);
|
|
3187
|
+
const anchorQName = relationFilter == null ? void 0 : relationFilter.attributes["qname"];
|
|
3188
|
+
const linkrole = relationFilter == null ? void 0 : relationFilter.attributes["linkrole"];
|
|
3189
|
+
const anchorConceptId = anchorQName && linkrole ? (_a = ctx.conceptIdByQNamePerRole.get(linkrole)) == null ? void 0 : _a.get(anchorQName) : void 0;
|
|
3190
|
+
const directChildren = new Set(anchorConceptId && linkrole ? ((_b = ctx.childrenByRole.get(linkrole)) == null ? void 0 : _b.get(anchorConceptId)) || [] : []);
|
|
3191
|
+
if (directChildren.size === 0)
|
|
3192
|
+
return candidates;
|
|
3193
|
+
return candidates.filter((candidate) => directChildren.has(candidate.conceptId));
|
|
3194
|
+
}
|
|
3195
|
+
function resolveNarrowedCandidates(variable, ctx) {
|
|
3196
|
+
return narrowByPreferredOccurrence(
|
|
3197
|
+
narrowToDirectChildOfDescendantAnchor(narrowToCurrentPeriod(resolveMatchedCandidates(variable, ctx), variable, ctx), variable, ctx),
|
|
3198
|
+
ctx
|
|
3199
|
+
);
|
|
3200
|
+
}
|
|
3201
|
+
function valuesFromCandidates(variable, matched, ctx) {
|
|
3092
3202
|
const values = matched.map((candidate) => {
|
|
3093
3203
|
var _a;
|
|
3094
3204
|
return resolveRawValue((_a = ctx.formData[candidate.conceptId]) == null ? void 0 : _a[candidate.columnId]);
|
|
3095
3205
|
}).filter((value) => value !== void 0);
|
|
3206
|
+
if (values.length === 0 && variable.fallbackValue === "()")
|
|
3207
|
+
return [];
|
|
3096
3208
|
if (variable.bindAsSequence) {
|
|
3097
|
-
if (values.length === 0 && variable.fallbackValue === "()")
|
|
3098
|
-
return [];
|
|
3099
3209
|
if (values.length === 0)
|
|
3100
3210
|
return void 0;
|
|
3101
3211
|
return values;
|
|
@@ -3109,6 +3219,186 @@ function resolveFormulaVariable(variable, ctx) {
|
|
|
3109
3219
|
}
|
|
3110
3220
|
return values[0];
|
|
3111
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
|
+
}
|
|
3112
3402
|
function resolveFormulaVariableMatchCount(variable, ctx) {
|
|
3113
3403
|
return resolveMatchedCandidates(variable, ctx).length;
|
|
3114
3404
|
}
|
|
@@ -3151,11 +3441,7 @@ function resolveVariableColumnsForConcept(variable, targetConceptId, ctx) {
|
|
|
3151
3441
|
return resolved.map((candidate) => candidate.columnId);
|
|
3152
3442
|
}
|
|
3153
3443
|
function buildAssertionBindings(assertion, ctx) {
|
|
3154
|
-
|
|
3155
|
-
for (const variable of assertion.variables) {
|
|
3156
|
-
bindings.set(variable.name, resolveFormulaVariable(variable, ctx));
|
|
3157
|
-
}
|
|
3158
|
-
return bindings;
|
|
3444
|
+
return resolveAssertionVariableBindings(assertion.variables, ctx, assertion.implicitFiltering);
|
|
3159
3445
|
}
|
|
3160
3446
|
function buildAssertionFallbackUsage(assertion, ctx) {
|
|
3161
3447
|
var _a;
|
|
@@ -3248,7 +3534,10 @@ class FormulaValidationService {
|
|
|
3248
3534
|
_evaluateAssertion(assertion, role, ctx, language) {
|
|
3249
3535
|
try {
|
|
3250
3536
|
const bindings = buildAssertionBindings(assertion, ctx);
|
|
3251
|
-
|
|
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) {
|
|
3252
3541
|
const bindingCheck = checkAllVariablesBound(assertion, bindings);
|
|
3253
3542
|
if (!bindingCheck.satisfied) {
|
|
3254
3543
|
return this._skippedResult(assertion, role, bindingCheck.skipReason ?? "unresolvable");
|
|
@@ -3264,6 +3553,12 @@ class FormulaValidationService {
|
|
|
3264
3553
|
const satisfied2 = evaluateExistenceTest(assertion.test, matchedFactCount);
|
|
3265
3554
|
return this._evaluatedResult(assertion, role, ctx, satisfied2, language, matchedFactCount);
|
|
3266
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
|
+
}
|
|
3267
3562
|
const satisfied = evaluateFormulaTest(assertion.test, bindings, fallbackUsage);
|
|
3268
3563
|
return this._evaluatedResult(assertion, role, ctx, satisfied, language);
|
|
3269
3564
|
} catch (error2) {
|
|
@@ -3592,204 +3887,6 @@ class FactMatcher {
|
|
|
3592
3887
|
console.log(" Match Result:", match ? match.value : "No match");
|
|
3593
3888
|
}
|
|
3594
3889
|
}
|
|
3595
|
-
class XBRLValidator {
|
|
3596
|
-
/**
|
|
3597
|
-
* Validates a value against XBRL datatype validation rules
|
|
3598
|
-
* Only validates when value is not blank
|
|
3599
|
-
*
|
|
3600
|
-
* @param conceptType - The XBRL concept type (e.g., "nl-types:houseNumberNLItemType")
|
|
3601
|
-
* @param value - The input value to validate
|
|
3602
|
-
* @param datatypes - Array of XBRL datatypes from taxonomy
|
|
3603
|
-
* @returns Validation result with errors if any
|
|
3604
|
-
*/
|
|
3605
|
-
static validateConceptValue(conceptType, value, datatypes) {
|
|
3606
|
-
const errors = [];
|
|
3607
|
-
if (value === null || value === void 0 || value === "") {
|
|
3608
|
-
return { valid: true, errors: [] };
|
|
3609
|
-
}
|
|
3610
|
-
if (!conceptType || !datatypes || datatypes.length === 0) {
|
|
3611
|
-
return { valid: true, errors: [] };
|
|
3612
|
-
}
|
|
3613
|
-
const datatype = this.findDatatype(conceptType, datatypes);
|
|
3614
|
-
if (!datatype || !datatype.validation) {
|
|
3615
|
-
return { valid: true, errors: [] };
|
|
3616
|
-
}
|
|
3617
|
-
const validation2 = datatype.validation;
|
|
3618
|
-
const stringValue = String(value);
|
|
3619
|
-
if (validation2.pattern) {
|
|
3620
|
-
const patternError = this.validatePattern(stringValue, validation2.pattern);
|
|
3621
|
-
if (patternError) {
|
|
3622
|
-
errors.push(patternError);
|
|
3623
|
-
}
|
|
3624
|
-
}
|
|
3625
|
-
if (validation2.length !== void 0) {
|
|
3626
|
-
const lengthError = this.validateExactLength(stringValue, validation2.length);
|
|
3627
|
-
if (lengthError) {
|
|
3628
|
-
errors.push(lengthError);
|
|
3629
|
-
}
|
|
3630
|
-
}
|
|
3631
|
-
if (validation2.minLength !== void 0) {
|
|
3632
|
-
const minLengthError = this.validateMinLength(stringValue, validation2.minLength);
|
|
3633
|
-
if (minLengthError) {
|
|
3634
|
-
errors.push(minLengthError);
|
|
3635
|
-
}
|
|
3636
|
-
}
|
|
3637
|
-
if (validation2.maxLength !== void 0) {
|
|
3638
|
-
const maxLengthError = this.validateMaxLength(stringValue, validation2.maxLength);
|
|
3639
|
-
if (maxLengthError) {
|
|
3640
|
-
errors.push(maxLengthError);
|
|
3641
|
-
}
|
|
3642
|
-
}
|
|
3643
|
-
if (validation2.totalDigits !== void 0) {
|
|
3644
|
-
const totalDigitsError = this.validateTotalDigits(stringValue, validation2.totalDigits);
|
|
3645
|
-
if (totalDigitsError) {
|
|
3646
|
-
errors.push(totalDigitsError);
|
|
3647
|
-
}
|
|
3648
|
-
}
|
|
3649
|
-
return {
|
|
3650
|
-
valid: errors.length === 0,
|
|
3651
|
-
errors
|
|
3652
|
-
};
|
|
3653
|
-
}
|
|
3654
|
-
/**
|
|
3655
|
-
* Finds the datatype definition for a given concept type
|
|
3656
|
-
* Handles both exact matches and base type chain resolution
|
|
3657
|
-
*/
|
|
3658
|
-
static findDatatype(conceptType, datatypes) {
|
|
3659
|
-
let datatype = datatypes.find((dt) => dt.type === conceptType);
|
|
3660
|
-
if (datatype) {
|
|
3661
|
-
return datatype;
|
|
3662
|
-
}
|
|
3663
|
-
const visited = /* @__PURE__ */ new Set();
|
|
3664
|
-
let currentType = conceptType;
|
|
3665
|
-
while (currentType && !visited.has(currentType)) {
|
|
3666
|
-
visited.add(currentType);
|
|
3667
|
-
datatype = datatypes.find((dt) => dt.type === currentType);
|
|
3668
|
-
if (datatype) {
|
|
3669
|
-
if (datatype.validation) {
|
|
3670
|
-
return datatype;
|
|
3671
|
-
}
|
|
3672
|
-
currentType = datatype.basetype;
|
|
3673
|
-
} else {
|
|
3674
|
-
break;
|
|
3675
|
-
}
|
|
3676
|
-
}
|
|
3677
|
-
return void 0;
|
|
3678
|
-
}
|
|
3679
|
-
/**
|
|
3680
|
-
* Validates that value matches the regex pattern
|
|
3681
|
-
*/
|
|
3682
|
-
static validatePattern(value, pattern) {
|
|
3683
|
-
try {
|
|
3684
|
-
const regex = new RegExp(`^${pattern}$`);
|
|
3685
|
-
if (!regex.test(value)) {
|
|
3686
|
-
return {
|
|
3687
|
-
type: "pattern",
|
|
3688
|
-
message: I18n.t("xbrlValidation.patternMismatch", { pattern }),
|
|
3689
|
-
expectedValue: pattern,
|
|
3690
|
-
actualValue: value
|
|
3691
|
-
};
|
|
3692
|
-
}
|
|
3693
|
-
} catch (error2) {
|
|
3694
|
-
console.error(`Invalid regex pattern: ${pattern}`, error2);
|
|
3695
|
-
return {
|
|
3696
|
-
type: "pattern",
|
|
3697
|
-
message: I18n.t("xbrlValidation.invalidPattern", { pattern }),
|
|
3698
|
-
expectedValue: pattern,
|
|
3699
|
-
actualValue: value
|
|
3700
|
-
};
|
|
3701
|
-
}
|
|
3702
|
-
return null;
|
|
3703
|
-
}
|
|
3704
|
-
/**
|
|
3705
|
-
* Validates that value has exact length
|
|
3706
|
-
*/
|
|
3707
|
-
static validateExactLength(value, requiredLength) {
|
|
3708
|
-
if (value.length !== requiredLength) {
|
|
3709
|
-
return {
|
|
3710
|
-
type: "length",
|
|
3711
|
-
message: I18n.t("xbrlValidation.exactLength", { required: requiredLength, current: value.length }),
|
|
3712
|
-
expectedValue: requiredLength,
|
|
3713
|
-
actualValue: value.length
|
|
3714
|
-
};
|
|
3715
|
-
}
|
|
3716
|
-
return null;
|
|
3717
|
-
}
|
|
3718
|
-
/**
|
|
3719
|
-
* Validates minimum length
|
|
3720
|
-
*/
|
|
3721
|
-
static validateMinLength(value, minLength) {
|
|
3722
|
-
if (value.length < minLength) {
|
|
3723
|
-
return {
|
|
3724
|
-
type: "minLength",
|
|
3725
|
-
message: I18n.t("xbrlValidation.minLength", { required: minLength, current: value.length }),
|
|
3726
|
-
expectedValue: minLength,
|
|
3727
|
-
actualValue: value.length
|
|
3728
|
-
};
|
|
3729
|
-
}
|
|
3730
|
-
return null;
|
|
3731
|
-
}
|
|
3732
|
-
/**
|
|
3733
|
-
* Validates maximum length
|
|
3734
|
-
*/
|
|
3735
|
-
static validateMaxLength(value, maxLength) {
|
|
3736
|
-
if (value.length > maxLength) {
|
|
3737
|
-
return {
|
|
3738
|
-
type: "maxLength",
|
|
3739
|
-
message: I18n.t("xbrlValidation.maxLength", { required: maxLength, current: value.length }),
|
|
3740
|
-
expectedValue: maxLength,
|
|
3741
|
-
actualValue: value.length
|
|
3742
|
-
};
|
|
3743
|
-
}
|
|
3744
|
-
return null;
|
|
3745
|
-
}
|
|
3746
|
-
/**
|
|
3747
|
-
* Validates total digits (for numeric values)
|
|
3748
|
-
*/
|
|
3749
|
-
static validateTotalDigits(value, totalDigits) {
|
|
3750
|
-
const digitsOnly = value.replace(/[^\d]/g, "");
|
|
3751
|
-
if (digitsOnly.length > totalDigits) {
|
|
3752
|
-
return {
|
|
3753
|
-
type: "totalDigits",
|
|
3754
|
-
message: I18n.t("xbrlValidation.totalDigits", { required: totalDigits, current: digitsOnly.length }),
|
|
3755
|
-
expectedValue: totalDigits,
|
|
3756
|
-
actualValue: digitsOnly.length
|
|
3757
|
-
};
|
|
3758
|
-
}
|
|
3759
|
-
return null;
|
|
3760
|
-
}
|
|
3761
|
-
/**
|
|
3762
|
-
* Gets a human-readable summary of validation rules for a concept type
|
|
3763
|
-
* Useful for displaying validation hints to users
|
|
3764
|
-
*/
|
|
3765
|
-
static getValidationRulesSummary(conceptType, datatypes) {
|
|
3766
|
-
if (!conceptType || !datatypes) {
|
|
3767
|
-
return [];
|
|
3768
|
-
}
|
|
3769
|
-
const datatype = this.findDatatype(conceptType, datatypes);
|
|
3770
|
-
if (!datatype || !datatype.validation) {
|
|
3771
|
-
return [];
|
|
3772
|
-
}
|
|
3773
|
-
const validation2 = datatype.validation;
|
|
3774
|
-
const rules = [];
|
|
3775
|
-
if (validation2.pattern) {
|
|
3776
|
-
rules.push(I18n.t("xbrlValidation.ruleSummaryPattern", { pattern: validation2.pattern }));
|
|
3777
|
-
}
|
|
3778
|
-
if (validation2.length !== void 0) {
|
|
3779
|
-
rules.push(I18n.t("xbrlValidation.ruleSummaryExactLength", { required: validation2.length }));
|
|
3780
|
-
}
|
|
3781
|
-
if (validation2.minLength !== void 0) {
|
|
3782
|
-
rules.push(I18n.t("xbrlValidation.ruleSummaryMinLength", { required: validation2.minLength }));
|
|
3783
|
-
}
|
|
3784
|
-
if (validation2.maxLength !== void 0) {
|
|
3785
|
-
rules.push(I18n.t("xbrlValidation.ruleSummaryMaxLength", { required: validation2.maxLength }));
|
|
3786
|
-
}
|
|
3787
|
-
if (validation2.totalDigits !== void 0) {
|
|
3788
|
-
rules.push(I18n.t("xbrlValidation.ruleSummaryTotalDigits", { required: validation2.totalDigits }));
|
|
3789
|
-
}
|
|
3790
|
-
return rules;
|
|
3791
|
-
}
|
|
3792
|
-
}
|
|
3793
3890
|
const TYPE_INPUT_MAP = {
|
|
3794
3891
|
// ==========================================
|
|
3795
3892
|
// Dutch XBRL Types (nl-types namespace)
|
|
@@ -3826,14 +3923,21 @@ const TYPE_INPUT_MAP = {
|
|
|
3826
3923
|
htmlInputType: "text",
|
|
3827
3924
|
placeholder: I18n.t("field.enterText")
|
|
3828
3925
|
},
|
|
3829
|
-
// 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.
|
|
3830
3930
|
"xbrli:dateItemType": {
|
|
3831
3931
|
fieldType: "date",
|
|
3832
|
-
htmlInputType: "date"
|
|
3932
|
+
htmlInputType: "date",
|
|
3933
|
+
min: "1900-01-01",
|
|
3934
|
+
max: "2100-12-31"
|
|
3833
3935
|
},
|
|
3834
3936
|
"xbrli:dateTimeItemType": {
|
|
3835
3937
|
fieldType: "datetime",
|
|
3836
|
-
htmlInputType: "datetime-local"
|
|
3938
|
+
htmlInputType: "datetime-local",
|
|
3939
|
+
min: "1900-01-01T00:00",
|
|
3940
|
+
max: "2100-12-31T23:59"
|
|
3837
3941
|
},
|
|
3838
3942
|
// Numeric types - integers
|
|
3839
3943
|
"xbrli:integerItemType": {
|
|
@@ -3993,12 +4097,16 @@ function getInputTypeForConceptType(conceptType, datatypes) {
|
|
|
3993
4097
|
if (lowerType.includes("time")) {
|
|
3994
4098
|
return {
|
|
3995
4099
|
fieldType: "datetime",
|
|
3996
|
-
htmlInputType: "datetime-local"
|
|
4100
|
+
htmlInputType: "datetime-local",
|
|
4101
|
+
min: "1900-01-01T00:00",
|
|
4102
|
+
max: "2100-12-31T23:59"
|
|
3997
4103
|
};
|
|
3998
4104
|
}
|
|
3999
4105
|
return {
|
|
4000
4106
|
fieldType: "date",
|
|
4001
|
-
htmlInputType: "date"
|
|
4107
|
+
htmlInputType: "date",
|
|
4108
|
+
min: "1900-01-01",
|
|
4109
|
+
max: "2100-12-31"
|
|
4002
4110
|
};
|
|
4003
4111
|
}
|
|
4004
4112
|
if (lowerType.includes("boolean") || lowerType.includes("bool")) {
|
|
@@ -4131,12 +4239,16 @@ function determineInputTypeFromBaseChain(baseTypeChain, datatypes) {
|
|
|
4131
4239
|
if (chainString.includes("datetime") || chainString.includes("time")) {
|
|
4132
4240
|
return {
|
|
4133
4241
|
fieldType: "datetime",
|
|
4134
|
-
htmlInputType: "datetime-local"
|
|
4242
|
+
htmlInputType: "datetime-local",
|
|
4243
|
+
min: "1900-01-01T00:00",
|
|
4244
|
+
max: "2100-12-31T23:59"
|
|
4135
4245
|
};
|
|
4136
4246
|
}
|
|
4137
4247
|
return {
|
|
4138
4248
|
fieldType: "date",
|
|
4139
|
-
htmlInputType: "date"
|
|
4249
|
+
htmlInputType: "date",
|
|
4250
|
+
min: "1900-01-01",
|
|
4251
|
+
max: "2100-12-31"
|
|
4140
4252
|
};
|
|
4141
4253
|
}
|
|
4142
4254
|
if (chainLower.some((type) => type.includes("boolean") || type.includes("bool"))) {
|
|
@@ -4167,48 +4279,262 @@ function determineInputTypeFromBaseChain(baseTypeChain, datatypes) {
|
|
|
4167
4279
|
placeholder: I18n.t("field.enterPercentage")
|
|
4168
4280
|
};
|
|
4169
4281
|
}
|
|
4170
|
-
if (chainLower.some((type) => type.includes("decimal") || type === "decimal")) {
|
|
4171
|
-
if (chainString.includes("monetary") || chainString.includes("currency")) {
|
|
4172
|
-
if (chainString.includes("nodecimals") || chainString.includes("no-decimals") || chainString.includes("nodecimals20")) {
|
|
4173
|
-
return {
|
|
4174
|
-
fieldType: "number",
|
|
4175
|
-
htmlInputType: "text",
|
|
4176
|
-
inputMode: "decimal",
|
|
4177
|
-
allowDecimals: false,
|
|
4178
|
-
step: 1,
|
|
4179
|
-
placeholder: I18n.t("field.enterAmountNoDecimals")
|
|
4180
|
-
};
|
|
4181
|
-
}
|
|
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) {
|
|
4182
4483
|
return {
|
|
4183
|
-
|
|
4184
|
-
|
|
4185
|
-
|
|
4186
|
-
|
|
4187
|
-
step: 0.01,
|
|
4188
|
-
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
|
|
4189
4488
|
};
|
|
4190
4489
|
}
|
|
4191
|
-
return
|
|
4192
|
-
fieldType: "decimal",
|
|
4193
|
-
htmlInputType: "text",
|
|
4194
|
-
inputMode: "decimal",
|
|
4195
|
-
allowDecimals: true,
|
|
4196
|
-
step: "any",
|
|
4197
|
-
placeholder: I18n.t("field.enterDecimalValue")
|
|
4198
|
-
};
|
|
4490
|
+
return null;
|
|
4199
4491
|
}
|
|
4200
|
-
|
|
4201
|
-
|
|
4202
|
-
|
|
4203
|
-
|
|
4204
|
-
|
|
4205
|
-
|
|
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;
|
|
4206
4537
|
}
|
|
4207
|
-
return {
|
|
4208
|
-
fieldType: "text",
|
|
4209
|
-
htmlInputType: "text",
|
|
4210
|
-
placeholder: I18n.t("field.enterValue")
|
|
4211
|
-
};
|
|
4212
4538
|
}
|
|
4213
4539
|
var __defProp$7 = Object.defineProperty;
|
|
4214
4540
|
var __getOwnPropDesc$7 = Object.getOwnPropertyDescriptor;
|
|
@@ -4290,6 +4616,14 @@ let JupiterFormField = class extends LitElement {
|
|
|
4290
4616
|
}
|
|
4291
4617
|
_handleInput(event) {
|
|
4292
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
|
+
}
|
|
4293
4627
|
const oldValue = this.value;
|
|
4294
4628
|
let value = target.value;
|
|
4295
4629
|
if (this._isNumericField()) {
|
|
@@ -4427,7 +4761,7 @@ let JupiterFormField = class extends LitElement {
|
|
|
4427
4761
|
return;
|
|
4428
4762
|
}
|
|
4429
4763
|
const typeConfig = getInputTypeForConceptType(this.conceptType, this.datatypes);
|
|
4430
|
-
const allowNegative = typeConfig.min === void 0 || typeConfig.min < 0;
|
|
4764
|
+
const allowNegative = typeConfig.min === void 0 || typeof typeConfig.min === "number" && typeConfig.min < 0;
|
|
4431
4765
|
if (event.key === "-" && allowNegative && !target.value.includes("-") && (target.selectionStart === 0 || target.value.length === 0)) {
|
|
4432
4766
|
return;
|
|
4433
4767
|
}
|
|
@@ -4921,6 +5255,33 @@ let JupiterFormField = class extends LitElement {
|
|
|
4921
5255
|
} else {
|
|
4922
5256
|
renderedInputValue = effectiveValue ?? "";
|
|
4923
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
|
+
}
|
|
4924
5285
|
return html`
|
|
4925
5286
|
<input
|
|
4926
5287
|
id="${fieldId}"
|
|
@@ -4935,6 +5296,7 @@ let JupiterFormField = class extends LitElement {
|
|
|
4935
5296
|
min="${min !== void 0 ? min : ""}"
|
|
4936
5297
|
max="${max !== void 0 ? max : ""}"
|
|
4937
5298
|
@input="${this._handleInput}"
|
|
5299
|
+
@change="${this._handleInput}"
|
|
4938
5300
|
@focus="${this._handleFocus}"
|
|
4939
5301
|
@blur="${this._handleBlur}"
|
|
4940
5302
|
@keydown="${this._handleKeyDown}"
|
|
@@ -4987,6 +5349,8 @@ let JupiterFormField = class extends LitElement {
|
|
|
4987
5349
|
<div class="date-wrapper">
|
|
4988
5350
|
<input
|
|
4989
5351
|
type="date"
|
|
5352
|
+
min="1900-01-01"
|
|
5353
|
+
max="2100-12-31"
|
|
4990
5354
|
.value="${this.periodInstantDate || this.field.periodInstantDate || ""}"
|
|
4991
5355
|
@change="${(e2) => this._handlePeriodChange(e2, "instant")}"
|
|
4992
5356
|
?disabled="${this.disabled}"
|
|
@@ -5006,6 +5370,8 @@ let JupiterFormField = class extends LitElement {
|
|
|
5006
5370
|
<div class="date-wrapper">
|
|
5007
5371
|
<input
|
|
5008
5372
|
type="date"
|
|
5373
|
+
min="1900-01-01"
|
|
5374
|
+
max="2100-12-31"
|
|
5009
5375
|
.value="${this.periodStartDate || this.field.periodStartDate || ""}"
|
|
5010
5376
|
@change="${(e2) => this._handlePeriodChange(e2, "start")}"
|
|
5011
5377
|
?disabled="${this.disabled}"
|
|
@@ -5020,6 +5386,8 @@ let JupiterFormField = class extends LitElement {
|
|
|
5020
5386
|
<div class="date-wrapper">
|
|
5021
5387
|
<input
|
|
5022
5388
|
type="date"
|
|
5389
|
+
min="1900-01-01"
|
|
5390
|
+
max="2100-12-31"
|
|
5023
5391
|
.value="${this.periodEndDate || this.field.periodEndDate || ""}"
|
|
5024
5392
|
@change="${(e2) => this._handlePeriodChange(e2, "end")}"
|
|
5025
5393
|
?disabled="${this.disabled}"
|
|
@@ -5053,6 +5421,8 @@ let JupiterFormField = class extends LitElement {
|
|
|
5053
5421
|
<div class="date-wrapper">
|
|
5054
5422
|
<input
|
|
5055
5423
|
type="date"
|
|
5424
|
+
min="1900-01-01"
|
|
5425
|
+
max="2100-12-31"
|
|
5056
5426
|
.value="${this.periodInstantDate || this.field.periodInstantDate || ""}"
|
|
5057
5427
|
@change="${(e2) => this._handlePeriodChange(e2, "instant")}"
|
|
5058
5428
|
?disabled="${this.disabled}"
|
|
@@ -5070,6 +5440,8 @@ let JupiterFormField = class extends LitElement {
|
|
|
5070
5440
|
<div class="date-wrapper">
|
|
5071
5441
|
<input
|
|
5072
5442
|
type="date"
|
|
5443
|
+
min="1900-01-01"
|
|
5444
|
+
max="2100-12-31"
|
|
5073
5445
|
.value="${this.periodStartDate || this.field.periodStartDate || ""}"
|
|
5074
5446
|
@change="${(e2) => this._handlePeriodChange(e2, "start")}"
|
|
5075
5447
|
?disabled="${this.disabled}"
|
|
@@ -5088,6 +5460,8 @@ let JupiterFormField = class extends LitElement {
|
|
|
5088
5460
|
<div class="date-wrapper">
|
|
5089
5461
|
<input
|
|
5090
5462
|
type="date"
|
|
5463
|
+
min="1900-01-01"
|
|
5464
|
+
max="2100-12-31"
|
|
5091
5465
|
.value="${this.periodEndDate || this.field.periodEndDate || ""}"
|
|
5092
5466
|
@change="${(e2) => this._handlePeriodChange(e2, "end")}"
|
|
5093
5467
|
?disabled="${this.disabled}"
|
|
@@ -5318,6 +5692,11 @@ JupiterFormField.styles = css`
|
|
|
5318
5692
|
border-radius: 4px;
|
|
5319
5693
|
transition: background-color 0.2s;
|
|
5320
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;
|
|
5321
5700
|
}
|
|
5322
5701
|
|
|
5323
5702
|
.period-icon-btn:hover {
|
|
@@ -6580,6 +6959,7 @@ let JupiterAddColumnDialog = class extends LitElement {
|
|
|
6580
6959
|
this._instantDate = "";
|
|
6581
6960
|
this._selectedType = "duration";
|
|
6582
6961
|
this._selectedDimensions = /* @__PURE__ */ new Map();
|
|
6962
|
+
this._typedValueErrors = {};
|
|
6583
6963
|
}
|
|
6584
6964
|
updated(changedProperties) {
|
|
6585
6965
|
if (changedProperties.has("open") && this.open) {
|
|
@@ -6643,6 +7023,7 @@ let JupiterAddColumnDialog = class extends LitElement {
|
|
|
6643
7023
|
this._instantDate = e2.target.value;
|
|
6644
7024
|
}
|
|
6645
7025
|
_handleDimensionToggle(dimensionId, event) {
|
|
7026
|
+
var _a, _b;
|
|
6646
7027
|
const checkbox = event.target;
|
|
6647
7028
|
if (checkbox.checked) {
|
|
6648
7029
|
const dimension = this.availableDimensions.find((d2) => d2.id === dimensionId);
|
|
@@ -6650,7 +7031,9 @@ let JupiterAddColumnDialog = class extends LitElement {
|
|
|
6650
7031
|
const selection = {
|
|
6651
7032
|
axisId: dimension.id,
|
|
6652
7033
|
axisLabel: dimension.axisLabel,
|
|
6653
|
-
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
|
|
6654
7037
|
};
|
|
6655
7038
|
this._selectedDimensions.set(dimensionId, selection);
|
|
6656
7039
|
}
|
|
@@ -6669,24 +7052,130 @@ let JupiterAddColumnDialog = class extends LitElement {
|
|
|
6669
7052
|
}
|
|
6670
7053
|
}
|
|
6671
7054
|
_handleTypedValueChange(dimensionId, event) {
|
|
6672
|
-
const
|
|
7055
|
+
const target = event.target;
|
|
7056
|
+
const value = target instanceof HTMLInputElement && target.type === "checkbox" ? String(target.checked) : target.value;
|
|
6673
7057
|
const existing = this._selectedDimensions.get(dimensionId);
|
|
6674
7058
|
if (existing) {
|
|
6675
|
-
existing.typedValue =
|
|
7059
|
+
existing.typedValue = value;
|
|
6676
7060
|
this._selectedDimensions.set(dimensionId, existing);
|
|
6677
7061
|
}
|
|
6678
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
|
+
}
|
|
6679
7166
|
_resetForm() {
|
|
6680
7167
|
this._startDate = this.periodStartDate || "";
|
|
6681
7168
|
this._endDate = this.periodEndDate || "";
|
|
6682
7169
|
this._instantDate = this.periodEndDate || this.periodStartDate || "";
|
|
6683
7170
|
this._selectedType = this.periodType === "instant" ? "instant" : "duration";
|
|
6684
7171
|
this._selectedDimensions.clear();
|
|
7172
|
+
this._typedValueErrors = {};
|
|
6685
7173
|
this._autoSelectSingleMemberDimensions();
|
|
6686
7174
|
this.requestUpdate();
|
|
6687
7175
|
}
|
|
6688
7176
|
_autoSelectSingleMemberDimensions() {
|
|
6689
7177
|
this.availableDimensions.forEach((dimension) => {
|
|
7178
|
+
var _a, _b;
|
|
6690
7179
|
if (dimension.members && dimension.members.length > 0) {
|
|
6691
7180
|
const member = dimension.members[0];
|
|
6692
7181
|
const selection = {
|
|
@@ -6702,7 +7191,9 @@ let JupiterAddColumnDialog = class extends LitElement {
|
|
|
6702
7191
|
const selection = {
|
|
6703
7192
|
axisId: dimension.id,
|
|
6704
7193
|
axisLabel: dimension.axisLabel,
|
|
6705
|
-
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
|
|
6706
7197
|
// Note: typedValue will be entered in column header, not here
|
|
6707
7198
|
};
|
|
6708
7199
|
this._selectedDimensions.set(dimension.id, selection);
|
|
@@ -6728,6 +7219,8 @@ let JupiterAddColumnDialog = class extends LitElement {
|
|
|
6728
7219
|
<div class="date-wrapper">
|
|
6729
7220
|
<input
|
|
6730
7221
|
type="date"
|
|
7222
|
+
min="1900-01-01"
|
|
7223
|
+
max="2100-12-31"
|
|
6731
7224
|
class="form-input"
|
|
6732
7225
|
.value="${this._instantDate}"
|
|
6733
7226
|
@change="${this._handleInstantDateChange}"
|
|
@@ -6747,6 +7240,8 @@ let JupiterAddColumnDialog = class extends LitElement {
|
|
|
6747
7240
|
<div class="date-wrapper">
|
|
6748
7241
|
<input
|
|
6749
7242
|
type="date"
|
|
7243
|
+
min="1900-01-01"
|
|
7244
|
+
max="2100-12-31"
|
|
6750
7245
|
class="form-input"
|
|
6751
7246
|
.value="${this._startDate}"
|
|
6752
7247
|
@change="${this._handleStartDateChange}"
|
|
@@ -6764,6 +7259,8 @@ let JupiterAddColumnDialog = class extends LitElement {
|
|
|
6764
7259
|
<div class="date-wrapper">
|
|
6765
7260
|
<input
|
|
6766
7261
|
type="date"
|
|
7262
|
+
min="1900-01-01"
|
|
7263
|
+
max="2100-12-31"
|
|
6767
7264
|
class="form-input"
|
|
6768
7265
|
.value="${this._endDate}"
|
|
6769
7266
|
@change="${this._handleEndDateChange}"
|
|
@@ -6786,6 +7283,7 @@ let JupiterAddColumnDialog = class extends LitElement {
|
|
|
6786
7283
|
<p class="form-description">${I18n.t("column.dimensionsDescription")}</p>
|
|
6787
7284
|
|
|
6788
7285
|
${this.availableDimensions.map((dimension) => {
|
|
7286
|
+
var _a, _b;
|
|
6789
7287
|
const isSelected = this._selectedDimensions.has(dimension.id);
|
|
6790
7288
|
const selection = this._selectedDimensions.get(dimension.id);
|
|
6791
7289
|
const isTypedDimension = !!(dimension.typedMember && (!dimension.members || dimension.members.length === 0));
|
|
@@ -6810,13 +7308,8 @@ let JupiterAddColumnDialog = class extends LitElement {
|
|
|
6810
7308
|
<!-- Typed Dimension Input -->
|
|
6811
7309
|
<div class="member-selection">
|
|
6812
7310
|
<label>${I18n.t("column.enterValue")} ${dimension.conceptName}:</label>
|
|
6813
|
-
|
|
6814
|
-
|
|
6815
|
-
class="typed-input"
|
|
6816
|
-
.value="${(selection == null ? void 0 : selection.typedValue) || ""}"
|
|
6817
|
-
@input="${(e2) => this._handleTypedValueChange(dimension.id, e2)}"
|
|
6818
|
-
placeholder="${I18n.t("column.enterValuePlaceholder")}"
|
|
6819
|
-
/>
|
|
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)}
|
|
6820
7313
|
</div>
|
|
6821
7314
|
` : html`
|
|
6822
7315
|
<!-- Domain Members Selection -->
|
|
@@ -7133,6 +7626,20 @@ JupiterAddColumnDialog.styles = css`
|
|
|
7133
7626
|
font-size: 14px;
|
|
7134
7627
|
}
|
|
7135
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
|
+
|
|
7136
7643
|
.member-list {
|
|
7137
7644
|
max-height: 120px;
|
|
7138
7645
|
overflow-y: auto;
|
|
@@ -7188,6 +7695,9 @@ __decorateClass$5([
|
|
|
7188
7695
|
__decorateClass$5([
|
|
7189
7696
|
n2({ type: String })
|
|
7190
7697
|
], JupiterAddColumnDialog.prototype, "periodEndDate", 2);
|
|
7698
|
+
__decorateClass$5([
|
|
7699
|
+
n2({ type: Array })
|
|
7700
|
+
], JupiterAddColumnDialog.prototype, "datatypes", 2);
|
|
7191
7701
|
__decorateClass$5([
|
|
7192
7702
|
r()
|
|
7193
7703
|
], JupiterAddColumnDialog.prototype, "_startDate", 2);
|
|
@@ -7203,6 +7713,9 @@ __decorateClass$5([
|
|
|
7203
7713
|
__decorateClass$5([
|
|
7204
7714
|
r()
|
|
7205
7715
|
], JupiterAddColumnDialog.prototype, "_selectedDimensions", 2);
|
|
7716
|
+
__decorateClass$5([
|
|
7717
|
+
r()
|
|
7718
|
+
], JupiterAddColumnDialog.prototype, "_typedValueErrors", 2);
|
|
7206
7719
|
JupiterAddColumnDialog = __decorateClass$5([
|
|
7207
7720
|
t$1("jupiter-add-column-dialog")
|
|
7208
7721
|
], JupiterAddColumnDialog);
|
|
@@ -7247,6 +7760,7 @@ let JupiterFormSection = class extends LitElement {
|
|
|
7247
7760
|
this._sectionPeriodType = "duration";
|
|
7248
7761
|
this._openMenuColumnId = null;
|
|
7249
7762
|
this._insertAfterColumnId = null;
|
|
7763
|
+
this._typedMemberErrors = {};
|
|
7250
7764
|
this._boundFieldBlur = (e2) => {
|
|
7251
7765
|
this._clearHighlights();
|
|
7252
7766
|
this._focusedConceptId = null;
|
|
@@ -7868,9 +8382,72 @@ let JupiterFormSection = class extends LitElement {
|
|
|
7868
8382
|
var _a;
|
|
7869
8383
|
return ((_a = this.typedMemberData[columnId]) == null ? void 0 : _a[axisId]) || "";
|
|
7870
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
|
+
}
|
|
7871
8448
|
_handleTypedMemberHeaderChange(event, columnId, axisId) {
|
|
7872
8449
|
const target = event.target;
|
|
7873
|
-
const value = target.value;
|
|
8450
|
+
const value = target instanceof HTMLInputElement && target.type === "checkbox" ? String(target.checked) : target.value;
|
|
7874
8451
|
console.log(`đ [FormSection] Typed member header change: columnId=${columnId}, axisId=${axisId}, value=${value}`);
|
|
7875
8452
|
this.dispatchEvent(new CustomEvent("typed-member-change", {
|
|
7876
8453
|
detail: {
|
|
@@ -7881,6 +8458,69 @@ let JupiterFormSection = class extends LitElement {
|
|
|
7881
8458
|
bubbles: true
|
|
7882
8459
|
}));
|
|
7883
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
|
+
}
|
|
7884
8524
|
_handleAddRepeat(event) {
|
|
7885
8525
|
event.stopPropagation();
|
|
7886
8526
|
this.dispatchEvent(new CustomEvent("add-concept-repeat", {
|
|
@@ -8024,23 +8664,24 @@ let JupiterFormSection = class extends LitElement {
|
|
|
8024
8664
|
<!-- Typed member input fields in column header -->
|
|
8025
8665
|
${((_a = column2.dimensionData) == null ? void 0 : _a.hasTypedMembers) ? html`
|
|
8026
8666
|
<div class="typed-members-header">
|
|
8027
|
-
${(_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`
|
|
8028
8670
|
<div class="typed-member-header-input">
|
|
8029
8671
|
${this.mode === "readonly" ? html`
|
|
8030
8672
|
<span class="typed-member-header-field" style="display:block;padding:4px 0;">
|
|
8031
8673
|
${this._getTypedMemberHeaderValue(column2.id, typedMember.axisId) || "â"}
|
|
8032
8674
|
</span>
|
|
8033
8675
|
` : html`
|
|
8034
|
-
<input
|
|
8035
|
-
|
|
8036
|
-
class="typed-member-
|
|
8037
|
-
|
|
8038
|
-
|
|
8039
|
-
@input="${(e2) => this._handleTypedMemberHeaderChange(e2, column2.id, typedMember.axisId)}"
|
|
8040
|
-
/>
|
|
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)}
|
|
8041
8681
|
`}
|
|
8042
8682
|
</div>
|
|
8043
|
-
|
|
8683
|
+
`;
|
|
8684
|
+
})}
|
|
8044
8685
|
</div>
|
|
8045
8686
|
` : ""}
|
|
8046
8687
|
</div>
|
|
@@ -8113,6 +8754,7 @@ let JupiterFormSection = class extends LitElement {
|
|
|
8113
8754
|
.availableDimensions="${this.availableDimensions}"
|
|
8114
8755
|
.periodStartDate="${this.periodStartDate}"
|
|
8115
8756
|
.periodEndDate="${this.periodEndDate}"
|
|
8757
|
+
.datatypes="${this.datatypes}"
|
|
8116
8758
|
@dialog-cancel="${this._handleDialogCancel}"
|
|
8117
8759
|
@column-add="${this._handleColumnAdd}"
|
|
8118
8760
|
></jupiter-add-column-dialog>
|
|
@@ -8366,6 +9008,34 @@ JupiterFormSection.styles = css`
|
|
|
8366
9008
|
gap: 2px;
|
|
8367
9009
|
}
|
|
8368
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
|
+
|
|
8369
9039
|
.typed-member-header-label {
|
|
8370
9040
|
font-size: 11px;
|
|
8371
9041
|
font-weight: 500;
|
|
@@ -8494,6 +9164,9 @@ __decorateClass$4([
|
|
|
8494
9164
|
__decorateClass$4([
|
|
8495
9165
|
r()
|
|
8496
9166
|
], JupiterFormSection.prototype, "_insertAfterColumnId", 2);
|
|
9167
|
+
__decorateClass$4([
|
|
9168
|
+
r()
|
|
9169
|
+
], JupiterFormSection.prototype, "_typedMemberErrors", 2);
|
|
8497
9170
|
__decorateClass$4([
|
|
8498
9171
|
r()
|
|
8499
9172
|
], JupiterFormSection.prototype, "_expandedConcepts", 2);
|
|
@@ -10899,6 +11572,7 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
10899
11572
|
this.readonly = false;
|
|
10900
11573
|
this.calculationEnabled = false;
|
|
10901
11574
|
this.formulaEnabled = false;
|
|
11575
|
+
this.tableLayoutEnabled = false;
|
|
10902
11576
|
this.periodStartDate = "2025-01-01";
|
|
10903
11577
|
this.periodEndDate = "2025-12-31";
|
|
10904
11578
|
this.language = "en";
|
|
@@ -10908,6 +11582,7 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
10908
11582
|
this.roleFilterAxes = [];
|
|
10909
11583
|
this.showLastValidationResultBtn = false;
|
|
10910
11584
|
this.isDraftSaving = false;
|
|
11585
|
+
this.hasUnsavedChanges = false;
|
|
10911
11586
|
this.defaultUnits = [];
|
|
10912
11587
|
this.decimals = "INF";
|
|
10913
11588
|
this._formData = {};
|
|
@@ -10996,6 +11671,7 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
10996
11671
|
const oldValue = this._writeFormDataValue(conceptId, columnId, value);
|
|
10997
11672
|
this._touched.add(`${conceptId}-${columnId}`);
|
|
10998
11673
|
this._dirty = true;
|
|
11674
|
+
this.hasUnsavedChanges = true;
|
|
10999
11675
|
this._validateForm();
|
|
11000
11676
|
this.requestUpdate();
|
|
11001
11677
|
this.dispatchEvent(new CustomEvent("field-change", {
|
|
@@ -11033,6 +11709,12 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
11033
11709
|
}
|
|
11034
11710
|
}
|
|
11035
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
|
+
}
|
|
11036
11718
|
if (changedProperties.has("decimals") || changedProperties.has("masterData")) {
|
|
11037
11719
|
this._computeEffectiveMasterData();
|
|
11038
11720
|
}
|
|
@@ -11437,22 +12119,34 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
11437
12119
|
*
|
|
11438
12120
|
* Skipped when form state is being restored from external props or localStorage so that
|
|
11439
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.
|
|
11440
12126
|
*/
|
|
11441
12127
|
_applyDimensionShowPreviousYear() {
|
|
11442
|
-
var _a, _b;
|
|
12128
|
+
var _a, _b, _c, _d, _e, _f;
|
|
11443
12129
|
if (this.mode !== "admin")
|
|
11444
12130
|
return;
|
|
11445
12131
|
const hasExternalState = !!(this.dynaformsFacts && this.dynaformsMetadata);
|
|
11446
12132
|
const hasLocalDraft = ((_a = this._draftStorageService) == null ? void 0 : _a.hasDraft()) ?? false;
|
|
11447
12133
|
if (hasExternalState || hasLocalDraft)
|
|
11448
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
|
+
});
|
|
11449
12141
|
const updated = { ...this._periodPreferences };
|
|
11450
12142
|
let changed = false;
|
|
11451
12143
|
for (const roleId of Object.keys(updated)) {
|
|
12144
|
+
if (explicitlyDisabled.has(roleId))
|
|
12145
|
+
continue;
|
|
11452
12146
|
const prefs = updated[roleId];
|
|
11453
12147
|
if (prefs.showPreviousYear)
|
|
11454
12148
|
continue;
|
|
11455
|
-
const hasDimensionMembers = ((
|
|
12149
|
+
const hasDimensionMembers = ((_f = prefs.dimensionSelections) == null ? void 0 : _f.some(
|
|
11456
12150
|
(ds) => ds.selectedMemberIds && ds.selectedMemberIds.length > 0
|
|
11457
12151
|
)) ?? false;
|
|
11458
12152
|
if (hasDimensionMembers) {
|
|
@@ -11851,6 +12545,7 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
11851
12545
|
console.log(`đž Updated formData[${conceptId}]:`, this._formData[conceptId]);
|
|
11852
12546
|
this._touched.add(`${conceptId}-${columnId}`);
|
|
11853
12547
|
this._dirty = true;
|
|
12548
|
+
this.hasUnsavedChanges = true;
|
|
11854
12549
|
this._validateForm();
|
|
11855
12550
|
this.requestUpdate();
|
|
11856
12551
|
this.dispatchEvent(new CustomEvent("field-change", {
|
|
@@ -11882,6 +12577,7 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
11882
12577
|
this._storeUnit(conceptId, columnId, unit);
|
|
11883
12578
|
}
|
|
11884
12579
|
this._dirty = true;
|
|
12580
|
+
this.hasUnsavedChanges = true;
|
|
11885
12581
|
this.requestUpdate();
|
|
11886
12582
|
}
|
|
11887
12583
|
_handleUnitChange(event) {
|
|
@@ -11891,6 +12587,7 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
11891
12587
|
console.log(`đˇī¸ Unit change received: conceptId=${conceptId}, columnId=${columnId}, unit=${unit}`);
|
|
11892
12588
|
this._storeUnit(conceptId, columnId, unit);
|
|
11893
12589
|
this._dirty = true;
|
|
12590
|
+
this.hasUnsavedChanges = true;
|
|
11894
12591
|
this.requestUpdate();
|
|
11895
12592
|
}
|
|
11896
12593
|
_storeUnit(conceptId, columnId, unit) {
|
|
@@ -11915,6 +12612,7 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
11915
12612
|
return;
|
|
11916
12613
|
this._storeDecimals(conceptId, columnId, decimals);
|
|
11917
12614
|
this._dirty = true;
|
|
12615
|
+
this.hasUnsavedChanges = true;
|
|
11918
12616
|
this.requestUpdate();
|
|
11919
12617
|
}
|
|
11920
12618
|
_computeEffectiveMasterData() {
|
|
@@ -12056,6 +12754,7 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
12056
12754
|
const { conceptId } = event.detail;
|
|
12057
12755
|
this._repeatCounts = { ...this._repeatCounts, [conceptId]: (this._repeatCounts[conceptId] || 0) + 1 };
|
|
12058
12756
|
this._dirty = true;
|
|
12757
|
+
this.hasUnsavedChanges = true;
|
|
12059
12758
|
}
|
|
12060
12759
|
_handleRemoveConceptRepeat(event) {
|
|
12061
12760
|
const { conceptId: rawConceptId } = event.detail;
|
|
@@ -12108,6 +12807,7 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
12108
12807
|
this._periodData = newPeriodData;
|
|
12109
12808
|
this._repeatCounts = { ...this._repeatCounts, [baseConceptId]: current - 1 };
|
|
12110
12809
|
this._dirty = true;
|
|
12810
|
+
this.hasUnsavedChanges = true;
|
|
12111
12811
|
}
|
|
12112
12812
|
_handleTypedMemberChange(event) {
|
|
12113
12813
|
const { columnId, axisId, value, conceptId } = event.detail;
|
|
@@ -12120,6 +12820,7 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
12120
12820
|
updatedTypedMemberData[storageKey] = { ...updatedTypedMemberData[storageKey], [axisId]: value };
|
|
12121
12821
|
this._typedMemberData = updatedTypedMemberData;
|
|
12122
12822
|
this._dirty = true;
|
|
12823
|
+
this.hasUnsavedChanges = true;
|
|
12123
12824
|
this.requestUpdate();
|
|
12124
12825
|
this.dispatchEvent(new CustomEvent("typed-member-change", {
|
|
12125
12826
|
detail: { columnId, axisId, value },
|
|
@@ -12318,6 +13019,7 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
12318
13019
|
}
|
|
12319
13020
|
this._removeColumnData(columnId, sectionId);
|
|
12320
13021
|
this._dirty = true;
|
|
13022
|
+
this.hasUnsavedChanges = true;
|
|
12321
13023
|
this._validateForm();
|
|
12322
13024
|
this.requestUpdate();
|
|
12323
13025
|
this.dispatchEvent(new CustomEvent("column-remove", {
|
|
@@ -12334,6 +13036,7 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
12334
13036
|
}
|
|
12335
13037
|
this._hiddenColumnIds.get(sectionId).add(columnId);
|
|
12336
13038
|
this._dirty = true;
|
|
13039
|
+
this.hasUnsavedChanges = true;
|
|
12337
13040
|
this.requestUpdate();
|
|
12338
13041
|
this.dispatchEvent(new CustomEvent("column-hide", {
|
|
12339
13042
|
detail: { columnId, sectionId },
|
|
@@ -12347,6 +13050,7 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
12347
13050
|
return;
|
|
12348
13051
|
(_a = this._hiddenColumnIds.get(sectionId)) == null ? void 0 : _a.delete(columnId);
|
|
12349
13052
|
this._dirty = true;
|
|
13053
|
+
this.hasUnsavedChanges = true;
|
|
12350
13054
|
this.requestUpdate();
|
|
12351
13055
|
this.dispatchEvent(new CustomEvent("column-show", {
|
|
12352
13056
|
detail: { columnId, sectionId },
|
|
@@ -12410,8 +13114,8 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
12410
13114
|
if (dimension.typedMember) {
|
|
12411
13115
|
availableDimension.typedMember = {
|
|
12412
13116
|
id: dimension.typedMember.id,
|
|
12413
|
-
dataType: "string"
|
|
12414
|
-
|
|
13117
|
+
dataType: dimension.typedMember.type || "string",
|
|
13118
|
+
validation: dimension.typedMember.validation
|
|
12415
13119
|
};
|
|
12416
13120
|
}
|
|
12417
13121
|
availableDimensions.push(availableDimension);
|
|
@@ -12632,8 +13336,10 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
12632
13336
|
axisLabel: dim.axisLabel,
|
|
12633
13337
|
typedMemberId: dim.axisId,
|
|
12634
13338
|
// Use axisId as typedMemberId for typed dimensions
|
|
12635
|
-
memberLabel: dim.axisLabel
|
|
13339
|
+
memberLabel: dim.axisLabel,
|
|
12636
13340
|
// Use axisLabel as memberLabel
|
|
13341
|
+
type: dim.dataType,
|
|
13342
|
+
validation: dim.validation
|
|
12637
13343
|
}));
|
|
12638
13344
|
}
|
|
12639
13345
|
}
|
|
@@ -12662,6 +13368,7 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
12662
13368
|
}
|
|
12663
13369
|
this._replicateFieldsForNewColumn(newColumnId, request, sectionId);
|
|
12664
13370
|
this._dirty = true;
|
|
13371
|
+
this.hasUnsavedChanges = true;
|
|
12665
13372
|
this.requestUpdate();
|
|
12666
13373
|
this.dispatchEvent(new CustomEvent("column-add", {
|
|
12667
13374
|
detail: { column: newColumn },
|
|
@@ -12981,9 +13688,11 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
12981
13688
|
});
|
|
12982
13689
|
if (draftPayloadSnapshot === this._lastSavedDraftSnapshot) {
|
|
12983
13690
|
console.log(`âšī¸ [Save Draft] No draft content change detected (${source}); skipping event emit.`);
|
|
13691
|
+
this.hasUnsavedChanges = false;
|
|
12984
13692
|
return;
|
|
12985
13693
|
}
|
|
12986
13694
|
this._lastSavedDraftSnapshot = draftPayloadSnapshot;
|
|
13695
|
+
this.hasUnsavedChanges = false;
|
|
12987
13696
|
const saved = this._draftStorageService.saveDraft(draftData, metadata);
|
|
12988
13697
|
console.log("đž [Save Draft] Metadata saved with enhanced selectedRoleIds:", metadata.selectedRoleIds);
|
|
12989
13698
|
this.dynaformsFacts = draftData;
|
|
@@ -14067,6 +14776,7 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
14067
14776
|
this._formData = { ...this.initialData };
|
|
14068
14777
|
this._touched.clear();
|
|
14069
14778
|
this._dirty = false;
|
|
14779
|
+
this.hasUnsavedChanges = false;
|
|
14070
14780
|
this._submitted = false;
|
|
14071
14781
|
this._preservedFormData = {};
|
|
14072
14782
|
this._preservedTypedMemberData = {};
|
|
@@ -15010,6 +15720,7 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
15010
15720
|
setData(data) {
|
|
15011
15721
|
this._formData = { ...data };
|
|
15012
15722
|
this._dirty = true;
|
|
15723
|
+
this.hasUnsavedChanges = true;
|
|
15013
15724
|
this._validateForm();
|
|
15014
15725
|
this.requestUpdate();
|
|
15015
15726
|
}
|
|
@@ -15438,7 +16149,7 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
15438
16149
|
<button
|
|
15439
16150
|
class="btn-secondary"
|
|
15440
16151
|
@click="${this._handleSaveDraft}"
|
|
15441
|
-
?disabled="${this.disabled || this.readonly || this.isDraftSaving}"
|
|
16152
|
+
?disabled="${this.disabled || this.readonly || this.isDraftSaving || !this.hasUnsavedChanges}"
|
|
15442
16153
|
>
|
|
15443
16154
|
${this.isDraftSaving ? I18n.t("form.savingDraft") : I18n.t("form.saveDraft")}
|
|
15444
16155
|
</button>
|
|
@@ -16436,6 +17147,9 @@ __decorateClass([
|
|
|
16436
17147
|
__decorateClass([
|
|
16437
17148
|
n2({ type: Boolean, attribute: "formula-enabled" })
|
|
16438
17149
|
], JupiterDynamicForm.prototype, "formulaEnabled", 2);
|
|
17150
|
+
__decorateClass([
|
|
17151
|
+
n2({ type: Boolean, attribute: "table-layout-enabled" })
|
|
17152
|
+
], JupiterDynamicForm.prototype, "tableLayoutEnabled", 2);
|
|
16439
17153
|
__decorateClass([
|
|
16440
17154
|
n2({ type: String })
|
|
16441
17155
|
], JupiterDynamicForm.prototype, "periodStartDate", 2);
|
|
@@ -16466,6 +17180,9 @@ __decorateClass([
|
|
|
16466
17180
|
__decorateClass([
|
|
16467
17181
|
n2({ type: Boolean, attribute: "is-draft-saving" })
|
|
16468
17182
|
], JupiterDynamicForm.prototype, "isDraftSaving", 2);
|
|
17183
|
+
__decorateClass([
|
|
17184
|
+
n2({ type: Boolean, attribute: "has-unsaved-changes", reflect: true })
|
|
17185
|
+
], JupiterDynamicForm.prototype, "hasUnsavedChanges", 2);
|
|
16469
17186
|
__decorateClass([
|
|
16470
17187
|
n2({ type: Array })
|
|
16471
17188
|
], JupiterDynamicForm.prototype, "defaultUnits", 2);
|