jupiter-dynamic-forms 1.21.0 → 1.22.1
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/advanced-filter.d.ts +13 -0
- package/dist/core/advanced-filter.d.ts.map +1 -1
- package/dist/core/concept-tree.d.ts +2 -0
- package/dist/core/concept-tree.d.ts.map +1 -1
- package/dist/core/dynamic-form.d.ts +67 -3
- package/dist/core/dynamic-form.d.ts.map +1 -1
- package/dist/core/filter-roles-dialog.d.ts.map +1 -1
- package/dist/core/form-field.d.ts +14 -2
- package/dist/core/form-field.d.ts.map +1 -1
- package/dist/core/form-section.d.ts +22 -0
- package/dist/core/form-section.d.ts.map +1 -1
- package/dist/index.js +712 -274
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1635 -160
- package/dist/index.mjs.map +1 -1
- package/dist/schema/types.d.ts +9 -0
- package/dist/schema/types.d.ts.map +1 -1
- package/dist/utils/balance-check.d.ts +33 -0
- package/dist/utils/balance-check.d.ts.map +1 -0
- package/dist/utils/column-header-labels.d.ts +10 -0
- package/dist/utils/column-header-labels.d.ts.map +1 -0
- package/dist/utils/fuzzy-match.d.ts +10 -0
- package/dist/utils/fuzzy-match.d.ts.map +1 -0
- package/dist/utils/number-parser.d.ts +49 -0
- package/dist/utils/number-parser.d.ts.map +1 -0
- package/dist/utils/xbrl-validation.d.ts +1 -1
- package/dist/utils/xbrl-validation.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { LitElement, html, css } from "lit";
|
|
1
|
+
import { LitElement, html, nothing, css } from "lit";
|
|
2
2
|
/**
|
|
3
3
|
* @license
|
|
4
4
|
* Copyright 2017 Google LLC
|
|
@@ -1740,7 +1740,10 @@ const filter$1 = {
|
|
|
1740
1740
|
searchByConceptDescription: "Type concept name or label to filter roles and rows. Press Apply Filter to apply.",
|
|
1741
1741
|
searchByConceptPlaceholder: "Type concept name or label and press Apply Filter...",
|
|
1742
1742
|
clearFilters: "Clear Filters",
|
|
1743
|
-
resetFilter: "Reset"
|
|
1743
|
+
resetFilter: "Reset",
|
|
1744
|
+
bannerConcept: 'Filtered by "{{concept}}" — {{count}} row(s) visible',
|
|
1745
|
+
bannerFactsOnly: "Only filled-in fields are shown",
|
|
1746
|
+
showAll: "Show all"
|
|
1744
1747
|
};
|
|
1745
1748
|
const column$1 = {
|
|
1746
1749
|
addColumn: "Add Column",
|
|
@@ -1764,26 +1767,33 @@ const column$1 = {
|
|
|
1764
1767
|
required: "required"
|
|
1765
1768
|
};
|
|
1766
1769
|
const section$1 = {
|
|
1767
|
-
enterPlaceholder: "Enter"
|
|
1770
|
+
enterPlaceholder: "Enter",
|
|
1771
|
+
position: "Section {{current}} of {{total}}",
|
|
1772
|
+
previous: "← Previous",
|
|
1773
|
+
next: "Next: {{title}} →",
|
|
1774
|
+
locateInList: "Show this section in the list on the left",
|
|
1775
|
+
amountScaleBase: "Amounts in €",
|
|
1776
|
+
amountScaleFactor: "Amounts in € × {{factor}}"
|
|
1768
1777
|
};
|
|
1769
1778
|
const field$1 = {
|
|
1770
1779
|
selectOption: "Select an option...",
|
|
1771
1780
|
select: "Select...",
|
|
1772
|
-
|
|
1773
|
-
|
|
1781
|
+
emptyOption: "—",
|
|
1782
|
+
enterDetailedExplanation: "Detailed explanation",
|
|
1783
|
+
enterAmountNoDecimals: "Amount, no decimals",
|
|
1774
1784
|
enterCurrency: "0.00",
|
|
1775
|
-
enterText: "
|
|
1776
|
-
enterWholeNumber: "
|
|
1777
|
-
enterPositiveNumber: "
|
|
1778
|
-
enterDecimalValue: "
|
|
1779
|
-
enterNumberOfShares: "
|
|
1785
|
+
enterText: "Text",
|
|
1786
|
+
enterWholeNumber: "Whole number",
|
|
1787
|
+
enterPositiveNumber: "Positive number",
|
|
1788
|
+
enterDecimalValue: "Decimal value",
|
|
1789
|
+
enterNumberOfShares: "Number of shares",
|
|
1780
1790
|
yearFormat: "YYYY",
|
|
1781
1791
|
enterPercentage: "e.g. 0.25 for 25% or 0.2550 for 25.50%",
|
|
1782
1792
|
emailPlaceholder: "email@example.com",
|
|
1783
1793
|
urlPlaceholder: "https://example.com",
|
|
1784
1794
|
phonePlaceholder: "+1 (555) 000-0000",
|
|
1785
|
-
enterValue: "
|
|
1786
|
-
enterDetailedInformation: "
|
|
1795
|
+
enterValue: "Value",
|
|
1796
|
+
enterDetailedInformation: "Detailed information",
|
|
1787
1797
|
scale: "Scale",
|
|
1788
1798
|
addText: "Add text",
|
|
1789
1799
|
editText: "Edit",
|
|
@@ -1796,7 +1806,12 @@ const field$1 = {
|
|
|
1796
1806
|
date: "Date",
|
|
1797
1807
|
unit: "Unit",
|
|
1798
1808
|
selectUnit: "Select Unit",
|
|
1799
|
-
leaveBlankForInf: "Leave blank for INF"
|
|
1809
|
+
leaveBlankForInf: "Leave blank for INF",
|
|
1810
|
+
prefilledLocked: "Pre-filled — cannot be changed here",
|
|
1811
|
+
invalidNumber: `Can't read "{{value}}" as a number`,
|
|
1812
|
+
noDecimalsAllowed: "This field doesn't accept decimals",
|
|
1813
|
+
negativeNotAllowed: "A negative value isn't allowed here",
|
|
1814
|
+
ambiguousNumber: '"{{value}}" is ambiguous — retype it without the thousands separator'
|
|
1800
1815
|
};
|
|
1801
1816
|
const admin$1 = {
|
|
1802
1817
|
title: "Configure Roles",
|
|
@@ -1832,6 +1847,16 @@ const error$1 = {
|
|
|
1832
1847
|
ok: "OK"
|
|
1833
1848
|
}
|
|
1834
1849
|
};
|
|
1850
|
+
const typedDimensionWarning$1 = {
|
|
1851
|
+
title: "Typed dimension values missing",
|
|
1852
|
+
message: "The form contains errors for the following roles where a typed dimension value is missing. Do you still want to continue?",
|
|
1853
|
+
role: "Role:",
|
|
1854
|
+
field: "Field:",
|
|
1855
|
+
value: "Value:",
|
|
1856
|
+
clickToFocus: "Click to go to this fact",
|
|
1857
|
+
"continue": "Continue to validate",
|
|
1858
|
+
cancel: "Cancel"
|
|
1859
|
+
};
|
|
1835
1860
|
const calculationWarning$1 = {
|
|
1836
1861
|
title: "Calculation warning",
|
|
1837
1862
|
summaryLine: 'Total "{{label}}" should equal {{calculated}}, but you entered {{entered}}.',
|
|
@@ -1841,6 +1866,10 @@ const calculationWarning$1 = {
|
|
|
1841
1866
|
added: "Added",
|
|
1842
1867
|
subtracted: "Subtracted (opposite balance)"
|
|
1843
1868
|
};
|
|
1869
|
+
const balanceCheck$1 = {
|
|
1870
|
+
balanced: "{{column}}: In balance",
|
|
1871
|
+
difference: "Difference {{column}}: {{amount}}"
|
|
1872
|
+
};
|
|
1844
1873
|
const formulaValidation$1 = {
|
|
1845
1874
|
dialogTitle: "Formula check results",
|
|
1846
1875
|
allPassed: "All formula checks passed",
|
|
@@ -1869,7 +1898,9 @@ const enTranslations = {
|
|
|
1869
1898
|
validation: validation$1,
|
|
1870
1899
|
xbrlValidation: xbrlValidation$1,
|
|
1871
1900
|
error: error$1,
|
|
1901
|
+
typedDimensionWarning: typedDimensionWarning$1,
|
|
1872
1902
|
calculationWarning: calculationWarning$1,
|
|
1903
|
+
balanceCheck: balanceCheck$1,
|
|
1873
1904
|
formulaValidation: formulaValidation$1,
|
|
1874
1905
|
scrollToConcept: scrollToConcept$1
|
|
1875
1906
|
};
|
|
@@ -1956,7 +1987,10 @@ const filter = {
|
|
|
1956
1987
|
searchByConceptDescription: "Typ een conceptnaam of label om rollen en rijen te filteren. Druk op Filter toepassen om te activeren.",
|
|
1957
1988
|
searchByConceptPlaceholder: "Typ conceptnaam of label en druk op Filter toepassen...",
|
|
1958
1989
|
clearFilters: "Filters wissen",
|
|
1959
|
-
resetFilter: "Resetten"
|
|
1990
|
+
resetFilter: "Resetten",
|
|
1991
|
+
bannerConcept: 'Gefilterd op "{{concept}}" — {{count}} rij(en) zichtbaar',
|
|
1992
|
+
bannerFactsOnly: "Alleen ingevulde velden worden getoond",
|
|
1993
|
+
showAll: "Alles tonen"
|
|
1960
1994
|
};
|
|
1961
1995
|
const column = {
|
|
1962
1996
|
addColumn: "Kolom toevoegen",
|
|
@@ -1980,26 +2014,33 @@ const column = {
|
|
|
1980
2014
|
required: "verplicht"
|
|
1981
2015
|
};
|
|
1982
2016
|
const section = {
|
|
1983
|
-
enterPlaceholder: "Invoeren"
|
|
2017
|
+
enterPlaceholder: "Invoeren",
|
|
2018
|
+
position: "Onderdeel {{current}} van {{total}}",
|
|
2019
|
+
previous: "← Vorige",
|
|
2020
|
+
next: "Volgende: {{title}} →",
|
|
2021
|
+
locateInList: "Toon dit onderdeel in de lijst aan de linkerkant",
|
|
2022
|
+
amountScaleBase: "Bedragen in €",
|
|
2023
|
+
amountScaleFactor: "Bedragen in € × {{factor}}"
|
|
1984
2024
|
};
|
|
1985
2025
|
const field = {
|
|
1986
2026
|
selectOption: "Selecteer een optie...",
|
|
1987
2027
|
select: "Selecteer...",
|
|
1988
|
-
|
|
1989
|
-
|
|
2028
|
+
emptyOption: "—",
|
|
2029
|
+
enterDetailedExplanation: "Gedetailleerde uitleg",
|
|
2030
|
+
enterAmountNoDecimals: "Bedrag, zonder decimalen",
|
|
1990
2031
|
enterCurrency: "0,00",
|
|
1991
|
-
enterText: "
|
|
1992
|
-
enterWholeNumber: "
|
|
1993
|
-
enterPositiveNumber: "
|
|
1994
|
-
enterDecimalValue: "
|
|
1995
|
-
enterNumberOfShares: "
|
|
2032
|
+
enterText: "Tekst",
|
|
2033
|
+
enterWholeNumber: "Geheel getal",
|
|
2034
|
+
enterPositiveNumber: "Positief getal",
|
|
2035
|
+
enterDecimalValue: "Decimale waarde",
|
|
2036
|
+
enterNumberOfShares: "Aantal aandelen",
|
|
1996
2037
|
yearFormat: "JJJJ",
|
|
1997
2038
|
enterPercentage: "bijv. 0,25 voor 25% of 0,2550 voor 25,50%",
|
|
1998
2039
|
emailPlaceholder: "email@voorbeeld.nl",
|
|
1999
2040
|
urlPlaceholder: "https://voorbeeld.nl",
|
|
2000
2041
|
phonePlaceholder: "+31 (0)20 123 4567",
|
|
2001
|
-
enterValue: "
|
|
2002
|
-
enterDetailedInformation: "
|
|
2042
|
+
enterValue: "Waarde",
|
|
2043
|
+
enterDetailedInformation: "Gedetailleerde informatie",
|
|
2003
2044
|
scale: "Schaal",
|
|
2004
2045
|
addText: "Tekst toevoegen",
|
|
2005
2046
|
editText: "Bewerken",
|
|
@@ -2012,7 +2053,12 @@ const field = {
|
|
|
2012
2053
|
date: "Datum",
|
|
2013
2054
|
unit: "Eenheid",
|
|
2014
2055
|
selectUnit: "Selecteer eenheid",
|
|
2015
|
-
leaveBlankForInf: "Leeg laten voor INF"
|
|
2056
|
+
leaveBlankForInf: "Leeg laten voor INF",
|
|
2057
|
+
prefilledLocked: "Vooraf ingevuld — kan hier niet worden gewijzigd",
|
|
2058
|
+
invalidNumber: "Kan '{{value}}' niet lezen als getal",
|
|
2059
|
+
noDecimalsAllowed: "Dit veld accepteert geen decimalen",
|
|
2060
|
+
negativeNotAllowed: "Een negatieve waarde is hier niet toegestaan",
|
|
2061
|
+
ambiguousNumber: "'{{value}}' is dubbelzinnig — typ het opnieuw zonder duizendtalscheidingsteken"
|
|
2016
2062
|
};
|
|
2017
2063
|
const admin = {
|
|
2018
2064
|
title: "Rollen configureren",
|
|
@@ -2048,6 +2094,16 @@ const error = {
|
|
|
2048
2094
|
ok: "OK"
|
|
2049
2095
|
}
|
|
2050
2096
|
};
|
|
2097
|
+
const typedDimensionWarning = {
|
|
2098
|
+
title: "Waarden voor typed dimensie ontbreken",
|
|
2099
|
+
message: "Het formulier bevat fouten voor de volgende rollen waar een waarde voor de typed dimensie ontbreekt. Wilt u toch doorgaan?",
|
|
2100
|
+
role: "Rol:",
|
|
2101
|
+
field: "Veld:",
|
|
2102
|
+
value: "Waarde:",
|
|
2103
|
+
clickToFocus: "Klik om naar dit feit te gaan",
|
|
2104
|
+
"continue": "Doorgaan met valideren",
|
|
2105
|
+
cancel: "Annuleren"
|
|
2106
|
+
};
|
|
2051
2107
|
const calculationWarning = {
|
|
2052
2108
|
title: "Berekeningswaarschuwing",
|
|
2053
2109
|
summaryLine: 'Totaal "{{label}}" zou {{calculated}} moeten zijn, maar u heeft {{entered}} ingevoerd.',
|
|
@@ -2057,6 +2113,10 @@ const calculationWarning = {
|
|
|
2057
2113
|
added: "Opgeteld",
|
|
2058
2114
|
subtracted: "Afgetrokken (tegengestelde balans)"
|
|
2059
2115
|
};
|
|
2116
|
+
const balanceCheck = {
|
|
2117
|
+
balanced: "{{column}}: In balans",
|
|
2118
|
+
difference: "Verschil {{column}}: {{amount}}"
|
|
2119
|
+
};
|
|
2060
2120
|
const formulaValidation = {
|
|
2061
2121
|
dialogTitle: "Resultaten formulecontrole",
|
|
2062
2122
|
allPassed: "Alle formulecontroles geslaagd",
|
|
@@ -2085,7 +2145,9 @@ const nlTranslations = {
|
|
|
2085
2145
|
validation,
|
|
2086
2146
|
xbrlValidation,
|
|
2087
2147
|
error,
|
|
2148
|
+
typedDimensionWarning,
|
|
2088
2149
|
calculationWarning,
|
|
2150
|
+
balanceCheck,
|
|
2089
2151
|
formulaValidation,
|
|
2090
2152
|
scrollToConcept
|
|
2091
2153
|
};
|
|
@@ -3969,6 +4031,113 @@ function rankMatchingSections(roleQuery, sections) {
|
|
|
3969
4031
|
return [];
|
|
3970
4032
|
return sections.map((section2, index) => ({ section: section2, index, score: scoreSection(query, section2) })).filter((entry) => entry.score !== null).sort((a2, b2) => a2.score.tier - b2.score.tier || a2.score.looseness - b2.score.looseness || a2.index - b2.index).map(({ section: section2, score }) => ({ section: section2, tier: score.tier }));
|
|
3971
4033
|
}
|
|
4034
|
+
const ASSETS_CONCEPT_NAME = "jenv-bw2-i:Assets";
|
|
4035
|
+
const EQUITY_AND_LIABILITIES_CONCEPT_NAME = "jenv-bw2-i:EquityAndLiabilities";
|
|
4036
|
+
function findBalanceSheetTotals(concepts) {
|
|
4037
|
+
let assetsConceptId;
|
|
4038
|
+
let equityConceptId;
|
|
4039
|
+
const visit = (nodes) => {
|
|
4040
|
+
var _a, _b;
|
|
4041
|
+
for (const node of nodes) {
|
|
4042
|
+
if (isTotalLabel$1(node.preferredLabel)) {
|
|
4043
|
+
const name = ((_a = node.conceptMetadata) == null ? void 0 : _a.conceptName) ?? node.name;
|
|
4044
|
+
if (name === ASSETS_CONCEPT_NAME)
|
|
4045
|
+
assetsConceptId = node.id;
|
|
4046
|
+
else if (name === EQUITY_AND_LIABILITIES_CONCEPT_NAME)
|
|
4047
|
+
equityConceptId = node.id;
|
|
4048
|
+
}
|
|
4049
|
+
if ((_b = node.children) == null ? void 0 : _b.length)
|
|
4050
|
+
visit(node.children);
|
|
4051
|
+
}
|
|
4052
|
+
};
|
|
4053
|
+
visit(concepts);
|
|
4054
|
+
return assetsConceptId && equityConceptId ? { assetsConceptId, equityConceptId } : null;
|
|
4055
|
+
}
|
|
4056
|
+
function computeBalanceDifference(formData, columns, totals) {
|
|
4057
|
+
var _a, _b;
|
|
4058
|
+
const results = [];
|
|
4059
|
+
for (const column2 of columns) {
|
|
4060
|
+
if (column2.hidden)
|
|
4061
|
+
continue;
|
|
4062
|
+
const assetsValue = parseFloat((_a = formData[totals.assetsConceptId]) == null ? void 0 : _a[column2.id]);
|
|
4063
|
+
const equityValue = parseFloat((_b = formData[totals.equityConceptId]) == null ? void 0 : _b[column2.id]);
|
|
4064
|
+
if (isNaN(assetsValue) || isNaN(equityValue))
|
|
4065
|
+
continue;
|
|
4066
|
+
const difference = assetsValue - equityValue;
|
|
4067
|
+
const columnLabel = column2.description && column2.description !== column2.title ? `${column2.title} ${column2.description}` : column2.title;
|
|
4068
|
+
results.push({
|
|
4069
|
+
columnId: column2.id,
|
|
4070
|
+
columnLabel,
|
|
4071
|
+
assetsValue,
|
|
4072
|
+
equityValue,
|
|
4073
|
+
difference,
|
|
4074
|
+
balanced: Math.abs(difference) < CALCULATION_TOLERANCE
|
|
4075
|
+
});
|
|
4076
|
+
}
|
|
4077
|
+
return results;
|
|
4078
|
+
}
|
|
4079
|
+
function getColumnParts(dimensionData) {
|
|
4080
|
+
var _a, _b;
|
|
4081
|
+
if (!dimensionData)
|
|
4082
|
+
return [];
|
|
4083
|
+
const parts = [];
|
|
4084
|
+
if ((_a = dimensionData.combinations) == null ? void 0 : _a.length) {
|
|
4085
|
+
for (const c2 of dimensionData.combinations) {
|
|
4086
|
+
parts.push({ key: c2.axisId || c2.memberLabel, label: c2.memberLabel });
|
|
4087
|
+
}
|
|
4088
|
+
} else {
|
|
4089
|
+
const selectedDimensions = dimensionData.selectedDimensions;
|
|
4090
|
+
for (const d2 of selectedDimensions ?? []) {
|
|
4091
|
+
if (!d2.isTyped && d2.memberLabel)
|
|
4092
|
+
parts.push({ key: d2.axisId, label: d2.memberLabel });
|
|
4093
|
+
}
|
|
4094
|
+
}
|
|
4095
|
+
if ((_b = dimensionData.typedMembers) == null ? void 0 : _b.length) {
|
|
4096
|
+
for (const t2 of dimensionData.typedMembers) {
|
|
4097
|
+
parts.push({ key: t2.axisId, label: t2.axisLabel });
|
|
4098
|
+
}
|
|
4099
|
+
}
|
|
4100
|
+
if (parts.length === 0 && dimensionData.memberLabel && (dimensionData.axisId || dimensionData.axisLabel)) {
|
|
4101
|
+
parts.push({ key: dimensionData.axisId || dimensionData.axisLabel, label: dimensionData.memberLabel });
|
|
4102
|
+
}
|
|
4103
|
+
return parts;
|
|
4104
|
+
}
|
|
4105
|
+
function splitCommonMembers(columns) {
|
|
4106
|
+
const perColumnLabel = {};
|
|
4107
|
+
const partsByColumnId = /* @__PURE__ */ new Map();
|
|
4108
|
+
for (const column2 of columns) {
|
|
4109
|
+
if (column2.type !== "dimension")
|
|
4110
|
+
continue;
|
|
4111
|
+
const parts = getColumnParts(column2.dimensionData);
|
|
4112
|
+
if (parts.length > 0)
|
|
4113
|
+
partsByColumnId.set(column2.id, parts);
|
|
4114
|
+
}
|
|
4115
|
+
const eligible = columns.filter((c2) => partsByColumnId.has(c2.id));
|
|
4116
|
+
if (eligible.length < 2) {
|
|
4117
|
+
for (const column2 of columns)
|
|
4118
|
+
perColumnLabel[column2.id] = column2.title;
|
|
4119
|
+
return { caption: "", perColumnLabel };
|
|
4120
|
+
}
|
|
4121
|
+
const referenceParts = partsByColumnId.get(eligible[0].id);
|
|
4122
|
+
const commonKeys = new Set(
|
|
4123
|
+
referenceParts.filter(
|
|
4124
|
+
(part) => eligible.every((column2) => {
|
|
4125
|
+
const match = partsByColumnId.get(column2.id).find((p2) => p2.key === part.key);
|
|
4126
|
+
return (match == null ? void 0 : match.label) === part.label;
|
|
4127
|
+
})
|
|
4128
|
+
).map((part) => part.key)
|
|
4129
|
+
);
|
|
4130
|
+
const caption = referenceParts.filter((part) => commonKeys.has(part.key)).map((part) => part.label).join(" · ");
|
|
4131
|
+
for (const column2 of columns) {
|
|
4132
|
+
const parts = partsByColumnId.get(column2.id);
|
|
4133
|
+
if (!parts) {
|
|
4134
|
+
perColumnLabel[column2.id] = column2.title;
|
|
4135
|
+
continue;
|
|
4136
|
+
}
|
|
4137
|
+
perColumnLabel[column2.id] = parts.filter((part) => !commonKeys.has(part.key)).map((part) => part.label).join(" · ");
|
|
4138
|
+
}
|
|
4139
|
+
return { caption, perColumnLabel };
|
|
4140
|
+
}
|
|
3972
4141
|
const TYPE_INPUT_MAP = {
|
|
3973
4142
|
// ==========================================
|
|
3974
4143
|
// Dutch XBRL Types (nl-types namespace)
|
|
@@ -4618,6 +4787,125 @@ class XBRLValidator {
|
|
|
4618
4787
|
return rules;
|
|
4619
4788
|
}
|
|
4620
4789
|
}
|
|
4790
|
+
function isValidThousandsGrouping(groups) {
|
|
4791
|
+
return groups.length >= 1 && groups[0].length >= 1 && groups[0].length <= 3 && groups.slice(1).every((g) => g.length === 3);
|
|
4792
|
+
}
|
|
4793
|
+
function parseLocaleNumber(raw, options = {}) {
|
|
4794
|
+
const allowDecimals = options.allowDecimals !== false;
|
|
4795
|
+
const allowNegative = options.allowNegative !== false;
|
|
4796
|
+
const isDutch = (options.locale ?? "nl-NL").toLowerCase().startsWith("nl");
|
|
4797
|
+
let s2 = raw.trim();
|
|
4798
|
+
if (s2 === "") {
|
|
4799
|
+
return { ok: false, reason: "unreadable" };
|
|
4800
|
+
}
|
|
4801
|
+
s2 = s2.replace(/EUR/gi, "").replace(/[€'\s]/g, "");
|
|
4802
|
+
if (s2 === "") {
|
|
4803
|
+
return { ok: false, reason: "unreadable" };
|
|
4804
|
+
}
|
|
4805
|
+
let negative = false;
|
|
4806
|
+
const parenMatch = /^\((.*)\)$/.exec(s2);
|
|
4807
|
+
if (parenMatch) {
|
|
4808
|
+
negative = true;
|
|
4809
|
+
s2 = parenMatch[1];
|
|
4810
|
+
}
|
|
4811
|
+
if (s2.startsWith("+")) {
|
|
4812
|
+
s2 = s2.slice(1);
|
|
4813
|
+
} else if (s2.startsWith("-")) {
|
|
4814
|
+
negative = true;
|
|
4815
|
+
s2 = s2.slice(1);
|
|
4816
|
+
} else if (s2.endsWith("-")) {
|
|
4817
|
+
negative = true;
|
|
4818
|
+
s2 = s2.slice(0, -1);
|
|
4819
|
+
}
|
|
4820
|
+
if (s2 === "" || !/^[0-9.,]+$/.test(s2)) {
|
|
4821
|
+
return { ok: false, reason: "unreadable" };
|
|
4822
|
+
}
|
|
4823
|
+
const dotCount = (s2.match(/\./g) || []).length;
|
|
4824
|
+
const commaCount = (s2.match(/,/g) || []).length;
|
|
4825
|
+
let numericString;
|
|
4826
|
+
let isThousandsOnly = false;
|
|
4827
|
+
if (dotCount === 0 && commaCount === 0) {
|
|
4828
|
+
numericString = s2;
|
|
4829
|
+
isThousandsOnly = true;
|
|
4830
|
+
} else if (dotCount > 0 && commaCount > 0) {
|
|
4831
|
+
const lastDot = s2.lastIndexOf(".");
|
|
4832
|
+
const lastComma = s2.lastIndexOf(",");
|
|
4833
|
+
const decimalSep = lastDot > lastComma ? "." : ",";
|
|
4834
|
+
const thousandsSep = decimalSep === "." ? "," : ".";
|
|
4835
|
+
const decimalCount = decimalSep === "." ? dotCount : commaCount;
|
|
4836
|
+
const decimalIndex = decimalSep === "." ? lastDot : lastComma;
|
|
4837
|
+
const thousandsBeforeDecimal = [...s2].every((ch, i2) => ch !== thousandsSep || i2 < decimalIndex);
|
|
4838
|
+
if (decimalCount !== 1 || !thousandsBeforeDecimal) {
|
|
4839
|
+
return { ok: false, reason: "unreadable" };
|
|
4840
|
+
}
|
|
4841
|
+
const intPart = s2.slice(0, decimalIndex);
|
|
4842
|
+
const fracPart = s2.slice(decimalIndex + 1);
|
|
4843
|
+
const groups = intPart.split(thousandsSep);
|
|
4844
|
+
if (!isValidThousandsGrouping(groups) || fracPart.length === 0) {
|
|
4845
|
+
return { ok: false, reason: "unreadable" };
|
|
4846
|
+
}
|
|
4847
|
+
numericString = `${groups.join("")}.${fracPart}`;
|
|
4848
|
+
} else if (dotCount >= 2 || commaCount >= 2) {
|
|
4849
|
+
const sep = dotCount >= 2 ? "." : ",";
|
|
4850
|
+
const groups = s2.split(sep);
|
|
4851
|
+
if (!isValidThousandsGrouping(groups)) {
|
|
4852
|
+
return { ok: false, reason: "unreadable" };
|
|
4853
|
+
}
|
|
4854
|
+
numericString = groups.join("");
|
|
4855
|
+
isThousandsOnly = true;
|
|
4856
|
+
} else {
|
|
4857
|
+
const sep = dotCount === 1 ? "." : ",";
|
|
4858
|
+
const idx = s2.indexOf(sep);
|
|
4859
|
+
const intPart = s2.slice(0, idx);
|
|
4860
|
+
const fracPart = s2.slice(idx + 1);
|
|
4861
|
+
if (intPart === "" || fracPart === "") {
|
|
4862
|
+
return { ok: false, reason: "unreadable" };
|
|
4863
|
+
}
|
|
4864
|
+
const isPlausibleThousandsGroup = fracPart.length === 3 && intPart !== "0";
|
|
4865
|
+
if (isDutch) {
|
|
4866
|
+
if (sep === ".") {
|
|
4867
|
+
if (allowDecimals) {
|
|
4868
|
+
numericString = `${intPart}.${fracPart}`;
|
|
4869
|
+
} else if (isPlausibleThousandsGroup) {
|
|
4870
|
+
numericString = intPart + fracPart;
|
|
4871
|
+
isThousandsOnly = true;
|
|
4872
|
+
} else {
|
|
4873
|
+
numericString = `${intPart}.${fracPart}`;
|
|
4874
|
+
}
|
|
4875
|
+
} else if (isPlausibleThousandsGroup) {
|
|
4876
|
+
if (allowDecimals) {
|
|
4877
|
+
return { ok: false, reason: "ambiguous" };
|
|
4878
|
+
}
|
|
4879
|
+
numericString = intPart + fracPart;
|
|
4880
|
+
isThousandsOnly = true;
|
|
4881
|
+
} else {
|
|
4882
|
+
numericString = `${intPart}.${fracPart}`;
|
|
4883
|
+
}
|
|
4884
|
+
} else if (sep === ".") {
|
|
4885
|
+
numericString = `${intPart}.${fracPart}`;
|
|
4886
|
+
} else if (isPlausibleThousandsGroup) {
|
|
4887
|
+
numericString = intPart + fracPart;
|
|
4888
|
+
isThousandsOnly = true;
|
|
4889
|
+
} else {
|
|
4890
|
+
return { ok: false, reason: "unreadable" };
|
|
4891
|
+
}
|
|
4892
|
+
}
|
|
4893
|
+
if (numericString === null) {
|
|
4894
|
+
return { ok: false, reason: "unreadable" };
|
|
4895
|
+
}
|
|
4896
|
+
const value = Number(numericString);
|
|
4897
|
+
if (!Number.isFinite(value)) {
|
|
4898
|
+
return { ok: false, reason: "unreadable" };
|
|
4899
|
+
}
|
|
4900
|
+
if (negative && !allowNegative) {
|
|
4901
|
+
return { ok: false, reason: "negativeNotAllowed" };
|
|
4902
|
+
}
|
|
4903
|
+
const signedValue = negative ? -value : value;
|
|
4904
|
+
if (!allowDecimals && !isThousandsOnly && signedValue !== Math.trunc(signedValue)) {
|
|
4905
|
+
return { ok: false, reason: "decimalsNotAllowed" };
|
|
4906
|
+
}
|
|
4907
|
+
return { ok: true, value: signedValue };
|
|
4908
|
+
}
|
|
4621
4909
|
var __defProp$8 = Object.defineProperty;
|
|
4622
4910
|
var __getOwnPropDesc$8 = Object.getOwnPropertyDescriptor;
|
|
4623
4911
|
var __decorateClass$8 = (decorators, target, key, kind) => {
|
|
@@ -4636,6 +4924,7 @@ let JupiterFormField = class extends LitElement {
|
|
|
4636
4924
|
this.value = null;
|
|
4637
4925
|
this.disabled = false;
|
|
4638
4926
|
this.locale = "en-US";
|
|
4927
|
+
this.numberLocale = "nl-NL";
|
|
4639
4928
|
this.hideLabel = false;
|
|
4640
4929
|
this.mode = "inputForm";
|
|
4641
4930
|
this.globalDecimals = "INF";
|
|
@@ -4646,6 +4935,7 @@ let JupiterFormField = class extends LitElement {
|
|
|
4646
4935
|
this._showPeriodPopup = false;
|
|
4647
4936
|
this._availableUnits = [];
|
|
4648
4937
|
this._numericDraftValue = null;
|
|
4938
|
+
this._focused = false;
|
|
4649
4939
|
}
|
|
4650
4940
|
willUpdate(changedProperties) {
|
|
4651
4941
|
var _a;
|
|
@@ -4687,6 +4977,20 @@ let JupiterFormField = class extends LitElement {
|
|
|
4687
4977
|
_isRoundingLevelConcept() {
|
|
4688
4978
|
return !!this.conceptId && this.conceptId.includes("DocumentIntendedRoundingLevel");
|
|
4689
4979
|
}
|
|
4980
|
+
/**
|
|
4981
|
+
* JDF-110: lock icon (SVG, not emoji) shown on a masterData-prefilled or rounding-level-locked
|
|
4982
|
+
* cell, so a locked value reads as "filled and protected" rather than empty placeholder text.
|
|
4983
|
+
* The host's `prefilledTooltip` overrides the default hint text.
|
|
4984
|
+
*/
|
|
4985
|
+
_renderLockIcon() {
|
|
4986
|
+
const tooltip = this.prefilledTooltip || I18n.t("field.prefilledLocked");
|
|
4987
|
+
return html`
|
|
4988
|
+
<svg class="lock-icon" viewBox="0 0 24 24" role="img" aria-label="${tooltip}">
|
|
4989
|
+
<title>${tooltip}</title>
|
|
4990
|
+
<path fill="currentColor" d="M12 1a5 5 0 0 0-5 5v3H6a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V11a2 2 0 0 0-2-2h-1V6a5 5 0 0 0-5-5zm-3 8V6a3 3 0 0 1 6 0v3z"/>
|
|
4991
|
+
</svg>
|
|
4992
|
+
`;
|
|
4993
|
+
}
|
|
4690
4994
|
_isNumericField() {
|
|
4691
4995
|
var _a;
|
|
4692
4996
|
const typeConfig = getInputTypeForConceptType(this.conceptType, this.datatypes);
|
|
@@ -4694,7 +4998,32 @@ let JupiterFormField = class extends LitElement {
|
|
|
4694
4998
|
return isNumericType(effectiveFieldType) || this._getInputType() === "number";
|
|
4695
4999
|
}
|
|
4696
5000
|
_isIntermediateNumericValue(raw) {
|
|
4697
|
-
return raw === "-" || raw === "." || raw === "-.";
|
|
5001
|
+
return raw === "-" || raw === "." || raw === "-." || raw === "," || raw === "-,";
|
|
5002
|
+
}
|
|
5003
|
+
// JDF-091: the same allowDecimals/allowNegative resolution _handleKeyDown already computes,
|
|
5004
|
+
// shared with the parser so every code path agrees on what's allowed for this field.
|
|
5005
|
+
_getNumberParseOptions() {
|
|
5006
|
+
const typeConfig = getInputTypeForConceptType(this.conceptType, this.datatypes);
|
|
5007
|
+
const allowNegative = typeConfig.min === void 0 || typeof typeConfig.min === "number" && typeConfig.min < 0;
|
|
5008
|
+
return {
|
|
5009
|
+
locale: this.numberLocale,
|
|
5010
|
+
allowDecimals: typeConfig.allowDecimals !== false,
|
|
5011
|
+
allowNegative
|
|
5012
|
+
};
|
|
5013
|
+
}
|
|
5014
|
+
// JDF-091: maps a parse failure reason to the field's inline error message.
|
|
5015
|
+
_numberParseErrorMessage(reason, raw) {
|
|
5016
|
+
switch (reason) {
|
|
5017
|
+
case "decimalsNotAllowed":
|
|
5018
|
+
return I18n.t("field.noDecimalsAllowed");
|
|
5019
|
+
case "negativeNotAllowed":
|
|
5020
|
+
return I18n.t("field.negativeNotAllowed");
|
|
5021
|
+
case "ambiguous":
|
|
5022
|
+
return I18n.t("field.ambiguousNumber", { value: raw });
|
|
5023
|
+
case "unreadable":
|
|
5024
|
+
default:
|
|
5025
|
+
return I18n.t("field.invalidNumber", { value: raw });
|
|
5026
|
+
}
|
|
4698
5027
|
}
|
|
4699
5028
|
_handleInput(event) {
|
|
4700
5029
|
const target = event.target;
|
|
@@ -4714,8 +5043,8 @@ let JupiterFormField = class extends LitElement {
|
|
|
4714
5043
|
if (rawValue === "" || this._isIntermediateNumericValue(rawValue)) {
|
|
4715
5044
|
value = null;
|
|
4716
5045
|
} else {
|
|
4717
|
-
const parsed =
|
|
4718
|
-
value =
|
|
5046
|
+
const parsed = parseLocaleNumber(rawValue, this._getNumberParseOptions());
|
|
5047
|
+
value = parsed.ok ? parsed.value : null;
|
|
4719
5048
|
}
|
|
4720
5049
|
} else if (this.field.type === "boolean") {
|
|
4721
5050
|
value = target.checked;
|
|
@@ -4737,6 +5066,7 @@ let JupiterFormField = class extends LitElement {
|
|
|
4737
5066
|
}));
|
|
4738
5067
|
}
|
|
4739
5068
|
_handleFocus() {
|
|
5069
|
+
this._focused = true;
|
|
4740
5070
|
this.dispatchEvent(new CustomEvent("field-focus", {
|
|
4741
5071
|
detail: {
|
|
4742
5072
|
fieldId: this.field.id,
|
|
@@ -4747,17 +5077,25 @@ let JupiterFormField = class extends LitElement {
|
|
|
4747
5077
|
}));
|
|
4748
5078
|
}
|
|
4749
5079
|
_handleBlur() {
|
|
5080
|
+
this._focused = false;
|
|
4750
5081
|
let userTypedNewValue = false;
|
|
5082
|
+
let numberParseError = null;
|
|
4751
5083
|
if (this._isNumericField() && this._numericDraftValue !== null) {
|
|
4752
5084
|
userTypedNewValue = true;
|
|
4753
5085
|
const raw = this._numericDraftValue;
|
|
4754
5086
|
if (raw === "" || this._isIntermediateNumericValue(raw)) {
|
|
4755
5087
|
this.value = null;
|
|
5088
|
+
this._numericDraftValue = null;
|
|
4756
5089
|
} else {
|
|
4757
|
-
const parsed =
|
|
4758
|
-
|
|
5090
|
+
const parsed = parseLocaleNumber(raw, this._getNumberParseOptions());
|
|
5091
|
+
if (parsed.ok) {
|
|
5092
|
+
this.value = parsed.value;
|
|
5093
|
+
this._numericDraftValue = null;
|
|
5094
|
+
} else {
|
|
5095
|
+
this.value = null;
|
|
5096
|
+
numberParseError = { type: "invalidNumber", message: this._numberParseErrorMessage(parsed.reason, raw) };
|
|
5097
|
+
}
|
|
4759
5098
|
}
|
|
4760
|
-
this._numericDraftValue = null;
|
|
4761
5099
|
}
|
|
4762
5100
|
if (this._isPercentItemType() && userTypedNewValue && this.value !== null && this.value !== void 0) {
|
|
4763
5101
|
const numVal = Number(this.value);
|
|
@@ -4773,6 +5111,9 @@ let JupiterFormField = class extends LitElement {
|
|
|
4773
5111
|
this._touched = true;
|
|
4774
5112
|
this._validateXBRLDatatype();
|
|
4775
5113
|
this._validateUnitSelection();
|
|
5114
|
+
if (numberParseError) {
|
|
5115
|
+
this._xbrlErrors = [numberParseError, ...this._xbrlErrors];
|
|
5116
|
+
}
|
|
4776
5117
|
console.log(`🟦 [FormField] Blur event - fieldId: ${this.field.id}, conceptId: ${this.conceptId}, columnId: ${this.columnId}`);
|
|
4777
5118
|
console.log(`🟦 [FormField] Current _xbrlErrors:`, this._xbrlErrors);
|
|
4778
5119
|
console.log(`🟦 [FormField] Current value:`, this.value);
|
|
@@ -4850,6 +5191,9 @@ let JupiterFormField = class extends LitElement {
|
|
|
4850
5191
|
if (event.key === "." && typeConfig.allowDecimals !== false && !target.value.includes(".")) {
|
|
4851
5192
|
return;
|
|
4852
5193
|
}
|
|
5194
|
+
if (event.key === "," || event.key === "€" || event.key === "(" || event.key === ")" || event.key === " ") {
|
|
5195
|
+
return;
|
|
5196
|
+
}
|
|
4853
5197
|
if (event.key >= "0" && event.key <= "9") {
|
|
4854
5198
|
return;
|
|
4855
5199
|
}
|
|
@@ -5037,6 +5381,10 @@ let JupiterFormField = class extends LitElement {
|
|
|
5037
5381
|
max-width: 600px; width: 90vw;
|
|
5038
5382
|
box-shadow: 0 8px 32px rgba(0,0,0,0.2); overflow: hidden;
|
|
5039
5383
|
font-family: inherit;
|
|
5384
|
+
/* JDF-118: don't rely on the UA's default margin:auto centering — a host page's
|
|
5385
|
+
* { margin: 0 } reset wins over it and pins the dialog to the top-left corner.
|
|
5386
|
+
Position explicitly instead, independent of any margin. */
|
|
5387
|
+
position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); margin: 0;
|
|
5040
5388
|
}
|
|
5041
5389
|
dialog.jdf-text-entry-dialog::backdrop { background: rgba(0,0,0,0.35); }
|
|
5042
5390
|
.jdf-text-dialog-header {
|
|
@@ -5057,7 +5405,9 @@ let JupiterFormField = class extends LitElement {
|
|
|
5057
5405
|
.jdf-text-dialog-close:hover { background: #ddd; color: #333; }
|
|
5058
5406
|
.jdf-text-dialog-body { padding: 20px 20px 12px; }
|
|
5059
5407
|
.jdf-text-dialog-textarea {
|
|
5060
|
-
|
|
5408
|
+
/* JDF-118: at least 60vh tall by default, growing further with content (up to 75vh,
|
|
5409
|
+
see the input listener below), instead of the old cramped 200px/50vh cap. */
|
|
5410
|
+
width: 100%; min-height: 60vh; max-height: 75vh;
|
|
5061
5411
|
padding: 10px 12px; font-size: 14px; font-family: inherit;
|
|
5062
5412
|
border: 1px solid #ddd; border-radius: 4px;
|
|
5063
5413
|
box-sizing: border-box; resize: vertical; line-height: 1.5;
|
|
@@ -5115,16 +5465,25 @@ let JupiterFormField = class extends LitElement {
|
|
|
5115
5465
|
</div>
|
|
5116
5466
|
`;
|
|
5117
5467
|
const cleanTitle = fieldLabel;
|
|
5118
|
-
dialog.querySelector(".jdf-text-dialog-title")
|
|
5468
|
+
const titleEl = dialog.querySelector(".jdf-text-dialog-title");
|
|
5469
|
+
titleEl.textContent = cleanTitle || fieldLabel;
|
|
5470
|
+
titleEl.title = cleanTitle || fieldLabel;
|
|
5119
5471
|
dialog.querySelector(".jdf-text-dialog-cancel").textContent = I18n.t("field.cancel");
|
|
5120
5472
|
dialog.querySelector(".jdf-text-dialog-save").textContent = I18n.t("field.save");
|
|
5121
5473
|
const textarea = dialog.querySelector(".jdf-text-dialog-textarea");
|
|
5122
5474
|
const charCount = dialog.querySelector(".jdf-text-dialog-char-count");
|
|
5123
5475
|
textarea.value = currentValue;
|
|
5124
5476
|
charCount.textContent = `${currentValue.length} ${I18n.t("field.characters")}`;
|
|
5477
|
+
const growTextarea = () => {
|
|
5478
|
+
textarea.style.height = "auto";
|
|
5479
|
+
const maxPx = window.innerHeight * 0.75;
|
|
5480
|
+
textarea.style.height = `${Math.min(textarea.scrollHeight, maxPx)}px`;
|
|
5481
|
+
};
|
|
5125
5482
|
textarea.addEventListener("input", () => {
|
|
5126
5483
|
charCount.textContent = `${textarea.value.length} ${I18n.t("field.characters")}`;
|
|
5484
|
+
growTextarea();
|
|
5127
5485
|
});
|
|
5486
|
+
growTextarea();
|
|
5128
5487
|
dialog.querySelector(".jdf-text-dialog-close").addEventListener("click", () => dialog.close());
|
|
5129
5488
|
dialog.querySelector(".jdf-text-dialog-cancel").addEventListener("click", () => dialog.close());
|
|
5130
5489
|
dialog.querySelector(".jdf-text-dialog-save").addEventListener("click", () => {
|
|
@@ -5149,13 +5508,14 @@ let JupiterFormField = class extends LitElement {
|
|
|
5149
5508
|
dialog.showModal();
|
|
5150
5509
|
textarea.focus();
|
|
5151
5510
|
}
|
|
5152
|
-
_renderTextDialogTrigger(effectiveValue, effectiveDisabled) {
|
|
5511
|
+
_renderTextDialogTrigger(effectiveValue, effectiveDisabled, isLocked = false) {
|
|
5153
5512
|
const hasValue = effectiveValue !== null && effectiveValue !== void 0 && String(effectiveValue).trim() !== "";
|
|
5154
5513
|
const displayText = hasValue ? String(effectiveValue) : "";
|
|
5155
5514
|
if (effectiveDisabled) {
|
|
5156
5515
|
return html`
|
|
5157
|
-
<span class="text-readonly-label ${hasValue ? "" : "empty"}"
|
|
5516
|
+
<span class="text-readonly-label ${hasValue ? "" : "empty"} ${isLocked ? "locked" : ""}"
|
|
5158
5517
|
title="${displayText}">${hasValue ? displayText : "—"}</span>
|
|
5518
|
+
${isLocked ? this._renderLockIcon() : ""}
|
|
5159
5519
|
`;
|
|
5160
5520
|
}
|
|
5161
5521
|
if (!hasValue) {
|
|
@@ -5166,7 +5526,8 @@ let JupiterFormField = class extends LitElement {
|
|
|
5166
5526
|
var _a;
|
|
5167
5527
|
return this._openTextDialog("", ((_a = this.field) == null ? void 0 : _a.label) || "");
|
|
5168
5528
|
}}">
|
|
5169
|
-
|
|
5529
|
+
<span class="text-trigger-plus" aria-hidden="true">+</span>
|
|
5530
|
+
<span class="text-trigger-label">${I18n.t("field.addText")}</span>
|
|
5170
5531
|
</button>
|
|
5171
5532
|
`;
|
|
5172
5533
|
}
|
|
@@ -5259,14 +5620,14 @@ let JupiterFormField = class extends LitElement {
|
|
|
5259
5620
|
const isMonetary = this._isMonetaryType();
|
|
5260
5621
|
return html`<span class="readonly-value ${isEmpty ? "empty" : ""} ${isMonetary ? "monetary" : ""}">${isEmpty ? "—" : displayValue}</span>`;
|
|
5261
5622
|
}
|
|
5262
|
-
_renderInput(effectiveValue = this.value, effectiveDisabled = this.disabled) {
|
|
5623
|
+
_renderInput(effectiveValue = this.value, effectiveDisabled = this.disabled, isLocked = false) {
|
|
5263
5624
|
if (this.mode === "readonly") {
|
|
5264
5625
|
return this._renderReadonlyValue(effectiveValue);
|
|
5265
5626
|
}
|
|
5266
5627
|
const hasErrors = this._errors.some((e2) => e2.severity === "error");
|
|
5267
5628
|
const hasWarnings = this._errors.some((e2) => e2.severity === "warning");
|
|
5268
5629
|
const isMonetary = this._isMonetaryType();
|
|
5269
|
-
const cssClass = `field-input ${hasErrors ? "error" : hasWarnings ? "warning" : ""} ${isMonetary ? "monetary" : ""}`;
|
|
5630
|
+
const cssClass = `field-input ${hasErrors ? "error" : hasWarnings ? "warning" : ""} ${isMonetary ? "monetary" : ""} ${isLocked ? "locked" : ""}`;
|
|
5270
5631
|
const fieldId = `${this.conceptId}__${this.columnId}`;
|
|
5271
5632
|
const fieldName = `data[${this.conceptId}][${this.columnId}]`;
|
|
5272
5633
|
const typeConfig = getInputTypeForConceptType(this.conceptType, this.datatypes);
|
|
@@ -5274,7 +5635,7 @@ let JupiterFormField = class extends LitElement {
|
|
|
5274
5635
|
;
|
|
5275
5636
|
const effectiveFieldType = typeConfig.fieldType || this.field.type || "text";
|
|
5276
5637
|
if (effectiveFieldType === "textarea" || isTextareaType(effectiveFieldType)) {
|
|
5277
|
-
return this._renderTextDialogTrigger(effectiveValue, effectiveDisabled || this.field.disabled || false);
|
|
5638
|
+
return this._renderTextDialogTrigger(effectiveValue, effectiveDisabled || this.field.disabled || false, isLocked);
|
|
5278
5639
|
}
|
|
5279
5640
|
if (effectiveFieldType === "select" || this.field.type === "select") {
|
|
5280
5641
|
const selectOptions = typeConfig.enumerations || this.field.options || [];
|
|
@@ -5289,7 +5650,7 @@ let JupiterFormField = class extends LitElement {
|
|
|
5289
5650
|
@focus="${this._handleFocus}"
|
|
5290
5651
|
@blur="${this._handleBlur}"
|
|
5291
5652
|
>
|
|
5292
|
-
<option value="">${
|
|
5653
|
+
<option value="">${I18n.t("field.emptyOption")}</option>
|
|
5293
5654
|
${selectOptions.map((option) => {
|
|
5294
5655
|
const optionValue = option.value;
|
|
5295
5656
|
const optionLabel = option.label || option.value;
|
|
@@ -5337,6 +5698,9 @@ let JupiterFormField = class extends LitElement {
|
|
|
5337
5698
|
} else {
|
|
5338
5699
|
renderedInputValue = effectiveValue ?? "";
|
|
5339
5700
|
}
|
|
5701
|
+
const hintText = typeConfig.placeholder || this.field.placeholder || "";
|
|
5702
|
+
const hintId = `${fieldId}-hint`;
|
|
5703
|
+
const hintSpan = hintText ? html`<span id="${hintId}" class="field-hint-sr-only">${hintText}</span>` : "";
|
|
5340
5704
|
if (inputType === "date" || inputType === "datetime-local") {
|
|
5341
5705
|
return html`
|
|
5342
5706
|
<div class="date-wrapper">
|
|
@@ -5348,7 +5712,8 @@ let JupiterFormField = class extends LitElement {
|
|
|
5348
5712
|
class="${cssClass}"
|
|
5349
5713
|
.value="${renderedInputValue}"
|
|
5350
5714
|
?disabled="${effectiveDisabled || this.field.disabled}"
|
|
5351
|
-
placeholder="${
|
|
5715
|
+
placeholder="${this._focused ? hintText : ""}"
|
|
5716
|
+
aria-describedby="${hintText ? hintId : nothing}"
|
|
5352
5717
|
min="${min !== void 0 ? min : ""}"
|
|
5353
5718
|
max="${max !== void 0 ? max : ""}"
|
|
5354
5719
|
@input="${this._handleInput}"
|
|
@@ -5361,6 +5726,7 @@ let JupiterFormField = class extends LitElement {
|
|
|
5361
5726
|
a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14
|
|
5362
5727
|
a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2zm0 16H5V9h14z"/>
|
|
5363
5728
|
</svg>
|
|
5729
|
+
${hintSpan}
|
|
5364
5730
|
</div>
|
|
5365
5731
|
`;
|
|
5366
5732
|
}
|
|
@@ -5373,7 +5739,8 @@ let JupiterFormField = class extends LitElement {
|
|
|
5373
5739
|
class="${cssClass}"
|
|
5374
5740
|
.value="${renderedInputValue}"
|
|
5375
5741
|
?disabled="${effectiveDisabled || this.field.disabled}"
|
|
5376
|
-
placeholder="${
|
|
5742
|
+
placeholder="${this._focused ? hintText : ""}"
|
|
5743
|
+
aria-describedby="${hintText ? hintId : nothing}"
|
|
5377
5744
|
step="${step !== void 0 ? step : ""}"
|
|
5378
5745
|
min="${min !== void 0 ? min : ""}"
|
|
5379
5746
|
max="${max !== void 0 ? max : ""}"
|
|
@@ -5382,7 +5749,7 @@ let JupiterFormField = class extends LitElement {
|
|
|
5382
5749
|
@focus="${this._handleFocus}"
|
|
5383
5750
|
@blur="${this._handleBlur}"
|
|
5384
5751
|
@keydown="${this._handleKeyDown}"
|
|
5385
|
-
|
|
5752
|
+
/>${hintSpan}
|
|
5386
5753
|
`;
|
|
5387
5754
|
}
|
|
5388
5755
|
_getInputType() {
|
|
@@ -5485,9 +5852,18 @@ let JupiterFormField = class extends LitElement {
|
|
|
5485
5852
|
}
|
|
5486
5853
|
return html``;
|
|
5487
5854
|
}
|
|
5855
|
+
// JDF-118: the period popup is a plain Lit overlay, not a native <dialog>, so it doesn't get
|
|
5856
|
+
// Escape-to-close for free the way the info/text dialogs do. Keydown on the overlay catches it
|
|
5857
|
+
// as it bubbles up from whichever control inside the popup has focus.
|
|
5858
|
+
_handlePeriodPopupKeydown(e2) {
|
|
5859
|
+
if (e2.key === "Escape") {
|
|
5860
|
+
e2.stopPropagation();
|
|
5861
|
+
this._closePeriodPopup();
|
|
5862
|
+
}
|
|
5863
|
+
}
|
|
5488
5864
|
_renderPeriodPopup() {
|
|
5489
5865
|
return html`
|
|
5490
|
-
<div class="period-popup-overlay" @click="${this._handlePopupOverlayClick}">
|
|
5866
|
+
<div class="period-popup-overlay" @click="${this._handlePopupOverlayClick}" @keydown="${this._handlePeriodPopupKeydown}">
|
|
5491
5867
|
<div class="period-popup" @click="${(e2) => e2.stopPropagation()}">
|
|
5492
5868
|
<div class="period-popup-header">
|
|
5493
5869
|
<div class="period-popup-title">${I18n.t("field.editPeriod")}</div>
|
|
@@ -5676,6 +6052,10 @@ let JupiterFormField = class extends LitElement {
|
|
|
5676
6052
|
const hasUserValue = this.value !== null && this.value !== void 0;
|
|
5677
6053
|
const effectiveValue = isPredefinedValue ? this.masterData[baseConceptId] : hasUserValue ? this.value : factValue;
|
|
5678
6054
|
const effectiveDisabled = isPredefinedValue || isRoundingLevelLocked || this.disabled || this.mode === "readonly";
|
|
6055
|
+
const isLocked = (isPredefinedValue || isRoundingLevelLocked) && this.mode !== "readonly";
|
|
6056
|
+
const typeConfigForLock = getInputTypeForConceptType(this.conceptType, this.datatypes);
|
|
6057
|
+
const effectiveFieldTypeForLock = typeConfigForLock.fieldType || this.field.type || "text";
|
|
6058
|
+
const isTextareaField = effectiveFieldTypeForLock === "textarea" || isTextareaType(effectiveFieldTypeForLock);
|
|
5679
6059
|
return html`
|
|
5680
6060
|
<div class="field-container">
|
|
5681
6061
|
${showLabel ? html`
|
|
@@ -5683,23 +6063,28 @@ let JupiterFormField = class extends LitElement {
|
|
|
5683
6063
|
${this.field.label}
|
|
5684
6064
|
</label>
|
|
5685
6065
|
` : ""}
|
|
5686
|
-
|
|
6066
|
+
|
|
5687
6067
|
<div class="field-wrapper">
|
|
5688
|
-
${this._renderInput(effectiveValue, effectiveDisabled)}
|
|
5689
|
-
|
|
5690
|
-
${hasPeriodControl && this.mode !== "readonly" && !isRoundingLevelLocked ? html`
|
|
5691
|
-
<
|
|
5692
|
-
|
|
5693
|
-
|
|
5694
|
-
|
|
5695
|
-
|
|
5696
|
-
|
|
5697
|
-
|
|
5698
|
-
|
|
5699
|
-
|
|
6068
|
+
${this._renderInput(effectiveValue, effectiveDisabled, isLocked)}
|
|
6069
|
+
|
|
6070
|
+
${isLocked && !isTextareaField || hasPeriodControl && this.mode !== "readonly" && !isRoundingLevelLocked ? html`
|
|
6071
|
+
<div class="field-trailing-icons">
|
|
6072
|
+
${isLocked && !isTextareaField ? this._renderLockIcon() : ""}
|
|
6073
|
+
${hasPeriodControl && this.mode !== "readonly" && !isRoundingLevelLocked ? html`
|
|
6074
|
+
<button
|
|
6075
|
+
class="period-icon-btn"
|
|
6076
|
+
type="button"
|
|
6077
|
+
tabindex="-1"
|
|
6078
|
+
@click="${this._handlePeriodIconClick}"
|
|
6079
|
+
title="Edit period"
|
|
6080
|
+
>
|
|
6081
|
+
📅
|
|
6082
|
+
</button>
|
|
6083
|
+
` : ""}
|
|
6084
|
+
</div>
|
|
5700
6085
|
` : ""}
|
|
5701
6086
|
</div>
|
|
5702
|
-
|
|
6087
|
+
|
|
5703
6088
|
${this._errors.length > 0 && this._touched ? html`
|
|
5704
6089
|
<div class="field-errors">
|
|
5705
6090
|
${this._errors.map((error2) => html`
|
|
@@ -5762,6 +6147,25 @@ JupiterFormField.styles = css`
|
|
|
5762
6147
|
overflow: hidden;
|
|
5763
6148
|
}
|
|
5764
6149
|
|
|
6150
|
+
/* JDF-110: holds the lock icon and/or the period icon together on the wrapper's right edge
|
|
6151
|
+
(same rail regardless of the sibling input's own width — the date-wrapper next to it sizes
|
|
6152
|
+
to its dd/mm/yyyy content, which is narrower than a full-width text/select input). Only one
|
|
6153
|
+
auto-margin is needed to push the whole group right, however many icons it contains. */
|
|
6154
|
+
.field-trailing-icons {
|
|
6155
|
+
display: flex;
|
|
6156
|
+
align-items: center;
|
|
6157
|
+
gap: 2px;
|
|
6158
|
+
margin-left: auto;
|
|
6159
|
+
flex-shrink: 0;
|
|
6160
|
+
}
|
|
6161
|
+
|
|
6162
|
+
.lock-icon {
|
|
6163
|
+
width: 15px;
|
|
6164
|
+
height: 15px;
|
|
6165
|
+
color: var(--jupiter-locked-text, #6b6b6b);
|
|
6166
|
+
flex-shrink: 0;
|
|
6167
|
+
}
|
|
6168
|
+
|
|
5765
6169
|
.period-icon-btn {
|
|
5766
6170
|
background: transparent;
|
|
5767
6171
|
border: none;
|
|
@@ -5774,10 +6178,6 @@ JupiterFormField.styles = css`
|
|
|
5774
6178
|
border-radius: 4px;
|
|
5775
6179
|
transition: background-color 0.2s;
|
|
5776
6180
|
opacity: 0.7;
|
|
5777
|
-
/* Keep this icon on the wrapper's right edge (same rail as every other field type)
|
|
5778
|
-
regardless of the sibling input's own width — the date-wrapper next to it sizes to
|
|
5779
|
-
its dd/mm/yyyy content, which is narrower than a full-width text/select input. */
|
|
5780
|
-
margin-left: auto;
|
|
5781
6181
|
flex-shrink: 0;
|
|
5782
6182
|
}
|
|
5783
6183
|
|
|
@@ -5965,6 +6365,21 @@ JupiterFormField.styles = css`
|
|
|
5965
6365
|
cursor: not-allowed;
|
|
5966
6366
|
}
|
|
5967
6367
|
|
|
6368
|
+
/* JDF-109: screen-reader-only hint — the visible placeholder only shows on focus, but a
|
|
6369
|
+
screen-reader user isn't shown "this cell looks empty" the way sighted users are, so the
|
|
6370
|
+
hint stays available via aria-describedby regardless of focus. */
|
|
6371
|
+
.field-hint-sr-only {
|
|
6372
|
+
position: absolute;
|
|
6373
|
+
width: 1px;
|
|
6374
|
+
height: 1px;
|
|
6375
|
+
padding: 0;
|
|
6376
|
+
margin: -1px;
|
|
6377
|
+
overflow: hidden;
|
|
6378
|
+
clip: rect(0, 0, 0, 0);
|
|
6379
|
+
white-space: nowrap;
|
|
6380
|
+
border: 0;
|
|
6381
|
+
}
|
|
6382
|
+
|
|
5968
6383
|
.field-input {
|
|
5969
6384
|
width: 100%;
|
|
5970
6385
|
padding: 6px 8px; /* Reduced padding for table cells */
|
|
@@ -5989,6 +6404,18 @@ JupiterFormField.styles = css`
|
|
|
5989
6404
|
cursor: not-allowed;
|
|
5990
6405
|
}
|
|
5991
6406
|
|
|
6407
|
+
/* JDF-110: a locked (masterData-prefilled / rounding-level) value must not look like the
|
|
6408
|
+
greyed-out placeholder text a generic :disabled input gets — normal dark text on a subtly
|
|
6409
|
+
tinted background instead, so it clearly reads as "filled" rather than "empty". Two classes
|
|
6410
|
+
+ :disabled beats the plain :disabled rule above on specificity. */
|
|
6411
|
+
.field-input.locked:disabled {
|
|
6412
|
+
/* !important: .field-input itself forces background: transparent !important, which would
|
|
6413
|
+
otherwise always win over this tint regardless of selector specificity. */
|
|
6414
|
+
background: var(--jupiter-locked-background, #eef2f7) !important;
|
|
6415
|
+
color: var(--jupiter-locked-text, #333);
|
|
6416
|
+
cursor: default;
|
|
6417
|
+
}
|
|
6418
|
+
|
|
5992
6419
|
.field-input.error {
|
|
5993
6420
|
border-color: var(--jupiter-error-color, #d32f2f);
|
|
5994
6421
|
}
|
|
@@ -6101,26 +6528,52 @@ JupiterFormField.styles = css`
|
|
|
6101
6528
|
border-color: var(--jupiter-primary-color, #1976d2);
|
|
6102
6529
|
}
|
|
6103
6530
|
|
|
6104
|
-
/* Text dialog trigger — empty state
|
|
6531
|
+
/* JDF-109: Text dialog trigger — empty state. A subtle "+" at rest (no dashed box, since an
|
|
6532
|
+
empty cell should look empty) that expands to the full "Add text" label on hover/focus,
|
|
6533
|
+
so the affordance is discoverable without every empty text cell shouting for attention. */
|
|
6105
6534
|
.text-trigger-btn {
|
|
6535
|
+
display: inline-flex;
|
|
6536
|
+
align-items: center;
|
|
6106
6537
|
flex: 1;
|
|
6107
6538
|
min-width: 0;
|
|
6108
6539
|
padding: 5px 8px;
|
|
6109
|
-
border: 1px
|
|
6540
|
+
border: 1px solid transparent;
|
|
6110
6541
|
border-radius: 4px;
|
|
6111
6542
|
background: transparent;
|
|
6112
|
-
color: var(--jupiter-text-secondary, #
|
|
6113
|
-
font-size:
|
|
6543
|
+
color: var(--jupiter-text-secondary, #999);
|
|
6544
|
+
font-size: 13px;
|
|
6114
6545
|
cursor: pointer;
|
|
6115
|
-
text-align: center;
|
|
6116
6546
|
font-family: inherit;
|
|
6117
6547
|
box-sizing: border-box;
|
|
6118
6548
|
}
|
|
6119
6549
|
|
|
6120
|
-
.text-trigger-
|
|
6550
|
+
.text-trigger-plus {
|
|
6551
|
+
font-size: 14px;
|
|
6552
|
+
line-height: 1;
|
|
6553
|
+
}
|
|
6554
|
+
|
|
6555
|
+
.text-trigger-label {
|
|
6556
|
+
display: inline-block;
|
|
6557
|
+
max-width: 0;
|
|
6558
|
+
overflow: hidden;
|
|
6559
|
+
white-space: nowrap;
|
|
6560
|
+
opacity: 0;
|
|
6561
|
+
transition: max-width 0.15s ease, opacity 0.15s ease, margin-left 0.15s ease;
|
|
6562
|
+
}
|
|
6563
|
+
|
|
6564
|
+
.text-trigger-btn:hover,
|
|
6565
|
+
.text-trigger-btn:focus-visible {
|
|
6121
6566
|
border-color: var(--jupiter-primary-color, #1976d2);
|
|
6122
6567
|
color: var(--jupiter-primary-color, #1976d2);
|
|
6123
6568
|
background: rgba(25, 118, 210, 0.04);
|
|
6569
|
+
outline: none;
|
|
6570
|
+
}
|
|
6571
|
+
|
|
6572
|
+
.text-trigger-btn:hover .text-trigger-label,
|
|
6573
|
+
.text-trigger-btn:focus-visible .text-trigger-label {
|
|
6574
|
+
max-width: 160px;
|
|
6575
|
+
opacity: 1;
|
|
6576
|
+
margin-left: 6px;
|
|
6124
6577
|
}
|
|
6125
6578
|
|
|
6126
6579
|
/* Text dialog trigger — filled state */
|
|
@@ -6179,6 +6632,15 @@ JupiterFormField.styles = css`
|
|
|
6179
6632
|
min-width: 0;
|
|
6180
6633
|
}
|
|
6181
6634
|
|
|
6635
|
+
/* JDF-110: same treatment as .field-input.locked, for masterData-prefilled/rounding-level
|
|
6636
|
+
text-block cells (rendered as a plain label rather than an <input>). */
|
|
6637
|
+
.text-readonly-label.locked {
|
|
6638
|
+
background: var(--jupiter-locked-background, #eef2f7);
|
|
6639
|
+
color: var(--jupiter-locked-text, #333);
|
|
6640
|
+
padding: 5px 8px;
|
|
6641
|
+
border-radius: 4px;
|
|
6642
|
+
}
|
|
6643
|
+
|
|
6182
6644
|
.text-readonly-label.empty {
|
|
6183
6645
|
color: var(--jupiter-text-secondary, #999);
|
|
6184
6646
|
font-style: italic;
|
|
@@ -6220,6 +6682,9 @@ __decorateClass$8([
|
|
|
6220
6682
|
__decorateClass$8([
|
|
6221
6683
|
n2({ type: String })
|
|
6222
6684
|
], JupiterFormField.prototype, "locale", 2);
|
|
6685
|
+
__decorateClass$8([
|
|
6686
|
+
n2({ type: String })
|
|
6687
|
+
], JupiterFormField.prototype, "numberLocale", 2);
|
|
6223
6688
|
__decorateClass$8([
|
|
6224
6689
|
n2({ type: Boolean })
|
|
6225
6690
|
], JupiterFormField.prototype, "hideLabel", 2);
|
|
@@ -6229,6 +6694,9 @@ __decorateClass$8([
|
|
|
6229
6694
|
__decorateClass$8([
|
|
6230
6695
|
n2({ type: Object })
|
|
6231
6696
|
], JupiterFormField.prototype, "masterData", 2);
|
|
6697
|
+
__decorateClass$8([
|
|
6698
|
+
n2({ type: String })
|
|
6699
|
+
], JupiterFormField.prototype, "prefilledTooltip", 2);
|
|
6232
6700
|
__decorateClass$8([
|
|
6233
6701
|
n2({ type: Array })
|
|
6234
6702
|
], JupiterFormField.prototype, "facts", 2);
|
|
@@ -6274,6 +6742,9 @@ __decorateClass$8([
|
|
|
6274
6742
|
__decorateClass$8([
|
|
6275
6743
|
r()
|
|
6276
6744
|
], JupiterFormField.prototype, "_numericDraftValue", 2);
|
|
6745
|
+
__decorateClass$8([
|
|
6746
|
+
r()
|
|
6747
|
+
], JupiterFormField.prototype, "_focused", 2);
|
|
6277
6748
|
JupiterFormField = __decorateClass$8([
|
|
6278
6749
|
t$1("jupiter-form-field")
|
|
6279
6750
|
], JupiterFormField);
|
|
@@ -6300,6 +6771,7 @@ let JupiterConceptTree = class extends LitElement {
|
|
|
6300
6771
|
this.defaultUnits = [];
|
|
6301
6772
|
this.disabled = false;
|
|
6302
6773
|
this.locale = "en-US";
|
|
6774
|
+
this.numberLocale = "nl-NL";
|
|
6303
6775
|
this.expandedConcepts = /* @__PURE__ */ new Set();
|
|
6304
6776
|
this.mode = "inputForm";
|
|
6305
6777
|
this.typedMemberData = {};
|
|
@@ -6396,6 +6868,10 @@ let JupiterConceptTree = class extends LitElement {
|
|
|
6396
6868
|
border: none; border-radius: 8px; padding: 0;
|
|
6397
6869
|
max-width: 480px; width: 90vw;
|
|
6398
6870
|
box-shadow: 0 8px 32px rgba(0,0,0,0.2); overflow: hidden;
|
|
6871
|
+
/* JDF-118: don't rely on the UA's default margin:auto centering — a host page's
|
|
6872
|
+
* { margin: 0 } reset (index.html has one) wins over it and pins the dialog to the
|
|
6873
|
+
top-left corner. Position explicitly instead, independent of any margin. */
|
|
6874
|
+
position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); margin: 0;
|
|
6399
6875
|
}
|
|
6400
6876
|
dialog.jdf-concept-info-dialog::backdrop { background: rgba(0,0,0,0.35); }
|
|
6401
6877
|
.jdf-dialog-header {
|
|
@@ -6488,6 +6964,8 @@ let JupiterConceptTree = class extends LitElement {
|
|
|
6488
6964
|
border: none; border-radius: 8px; padding: 0;
|
|
6489
6965
|
max-width: 480px; width: 90vw;
|
|
6490
6966
|
box-shadow: 0 8px 32px rgba(0,0,0,0.2); overflow: hidden;
|
|
6967
|
+
/* JDF-118: see jdf-concept-info-dialog above — explicit centering, immune to host margin resets. */
|
|
6968
|
+
position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); margin: 0;
|
|
6491
6969
|
}
|
|
6492
6970
|
dialog.jdf-calc-warning-dialog::backdrop { background: rgba(0,0,0,0.35); }
|
|
6493
6971
|
.jdf-calc-warning-header {
|
|
@@ -6625,9 +7103,11 @@ let JupiterConceptTree = class extends LitElement {
|
|
|
6625
7103
|
.periodInstantDate="${field2.periodInstantDate || (field2.periodType === "instant" ? field2.periodEndDate || field2.periodStartDate : void 0)}"
|
|
6626
7104
|
.disabled="${this.disabled}"
|
|
6627
7105
|
.locale="${this.locale}"
|
|
7106
|
+
.numberLocale="${this.numberLocale}"
|
|
6628
7107
|
.hideLabel="${true}"
|
|
6629
7108
|
.mode="${this.mode}"
|
|
6630
7109
|
.masterData="${this.masterData}"
|
|
7110
|
+
.prefilledTooltip="${this.prefilledTooltip}"
|
|
6631
7111
|
.facts="${this.facts}"
|
|
6632
7112
|
.column="${column2}"
|
|
6633
7113
|
.typedMemberValues="${this.typedMemberData[`${this.concept.id}__${column2.id}`] || {}}"
|
|
@@ -6972,6 +7452,9 @@ __decorateClass$7([
|
|
|
6972
7452
|
__decorateClass$7([
|
|
6973
7453
|
n2({ type: String })
|
|
6974
7454
|
], JupiterConceptTree.prototype, "locale", 2);
|
|
7455
|
+
__decorateClass$7([
|
|
7456
|
+
n2({ type: String })
|
|
7457
|
+
], JupiterConceptTree.prototype, "numberLocale", 2);
|
|
6975
7458
|
__decorateClass$7([
|
|
6976
7459
|
n2({ type: Set })
|
|
6977
7460
|
], JupiterConceptTree.prototype, "expandedConcepts", 2);
|
|
@@ -6984,6 +7467,9 @@ __decorateClass$7([
|
|
|
6984
7467
|
__decorateClass$7([
|
|
6985
7468
|
n2({ type: Object })
|
|
6986
7469
|
], JupiterConceptTree.prototype, "masterData", 2);
|
|
7470
|
+
__decorateClass$7([
|
|
7471
|
+
n2({ type: String })
|
|
7472
|
+
], JupiterConceptTree.prototype, "prefilledTooltip", 2);
|
|
6987
7473
|
__decorateClass$7([
|
|
6988
7474
|
n2({ type: Array })
|
|
6989
7475
|
], JupiterConceptTree.prototype, "facts", 2);
|
|
@@ -7827,6 +8313,7 @@ let JupiterFormSection = class extends LitElement {
|
|
|
7827
8313
|
this.disabled = false;
|
|
7828
8314
|
this.collapsible = true;
|
|
7829
8315
|
this.locale = "en-US";
|
|
8316
|
+
this.numberLocale = "nl-NL";
|
|
7830
8317
|
this.isFirstSection = false;
|
|
7831
8318
|
this.availableDimensions = [];
|
|
7832
8319
|
this.hideHeader = false;
|
|
@@ -7858,6 +8345,7 @@ let JupiterFormSection = class extends LitElement {
|
|
|
7858
8345
|
this._highlightMap = /* @__PURE__ */ new Map();
|
|
7859
8346
|
this._focusedConceptId = null;
|
|
7860
8347
|
this._calculationMismatches = /* @__PURE__ */ new Map();
|
|
8348
|
+
this._balanceCheckTotals = null;
|
|
7861
8349
|
this._totalChildrenMap = /* @__PURE__ */ new Map();
|
|
7862
8350
|
this._memberParentMap = /* @__PURE__ */ new Map();
|
|
7863
8351
|
this._totalConceptMap = /* @__PURE__ */ new Map();
|
|
@@ -7877,6 +8365,13 @@ let JupiterFormSection = class extends LitElement {
|
|
|
7877
8365
|
document.removeEventListener("click", () => this._closeColumnMenu());
|
|
7878
8366
|
this.removeEventListener("field-blur", this._boundFieldBlur);
|
|
7879
8367
|
}
|
|
8368
|
+
willUpdate(changedProperties) {
|
|
8369
|
+
var _a;
|
|
8370
|
+
super.willUpdate(changedProperties);
|
|
8371
|
+
if (changedProperties.has("section") || changedProperties.has("calculationEnabled")) {
|
|
8372
|
+
this._balanceCheckTotals = this.calculationEnabled && ((_a = this.section) == null ? void 0 : _a.concepts) ? findBalanceSheetTotals(this.section.concepts) : null;
|
|
8373
|
+
}
|
|
8374
|
+
}
|
|
7880
8375
|
updated(changedProperties) {
|
|
7881
8376
|
var _a;
|
|
7882
8377
|
super.updated(changedProperties);
|
|
@@ -8501,9 +8996,95 @@ let JupiterFormSection = class extends LitElement {
|
|
|
8501
8996
|
this._highlightMap = /* @__PURE__ */ new Map();
|
|
8502
8997
|
}
|
|
8503
8998
|
}
|
|
8999
|
+
/**
|
|
9000
|
+
* JDF-095: clicking the amber "Verschil" chip highlights and scrolls to the two total rows for
|
|
9001
|
+
* that column, reusing the same `.highlight-total` styling the calculation-warning feature
|
|
9002
|
+
* already applies on field focus. Read-only — no formData/state other than the highlight map.
|
|
9003
|
+
*/
|
|
9004
|
+
_handleBalanceCheckClick(columnId) {
|
|
9005
|
+
var _a, _b, _c;
|
|
9006
|
+
if (!this._balanceCheckTotals)
|
|
9007
|
+
return;
|
|
9008
|
+
const { assetsConceptId, equityConceptId } = this._balanceCheckTotals;
|
|
9009
|
+
const newMap = new Map(this._highlightMap);
|
|
9010
|
+
newMap.set(assetsConceptId, { type: "total", columnId });
|
|
9011
|
+
newMap.set(equityConceptId, { type: "total", columnId });
|
|
9012
|
+
this._highlightMap = newMap;
|
|
9013
|
+
const trees = Array.from(((_a = this.shadowRoot) == null ? void 0 : _a.querySelectorAll("jupiter-concept-tree")) ?? []);
|
|
9014
|
+
let targetField = null;
|
|
9015
|
+
for (const tree of trees) {
|
|
9016
|
+
const fields = Array.from(((_b = tree.shadowRoot) == null ? void 0 : _b.querySelectorAll("jupiter-form-field")) ?? []);
|
|
9017
|
+
targetField = fields.find((f2) => f2.conceptId === assetsConceptId && f2.columnId === columnId);
|
|
9018
|
+
if (targetField)
|
|
9019
|
+
break;
|
|
9020
|
+
}
|
|
9021
|
+
if (!targetField)
|
|
9022
|
+
return;
|
|
9023
|
+
targetField.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
9024
|
+
const tableContainer = (_c = this.shadowRoot) == null ? void 0 : _c.querySelector(".table-container");
|
|
9025
|
+
if (tableContainer && tableContainer.scrollWidth > tableContainer.clientWidth) {
|
|
9026
|
+
const fieldRect = targetField.getBoundingClientRect();
|
|
9027
|
+
const containerRect = tableContainer.getBoundingClientRect();
|
|
9028
|
+
const targetScrollLeft = Math.max(
|
|
9029
|
+
0,
|
|
9030
|
+
fieldRect.left - containerRect.left + tableContainer.scrollLeft - tableContainer.clientWidth / 2 + fieldRect.width / 2
|
|
9031
|
+
);
|
|
9032
|
+
tableContainer.scrollTo({ left: targetScrollLeft, behavior: "smooth" });
|
|
9033
|
+
}
|
|
9034
|
+
}
|
|
9035
|
+
/** JDF-095: formats the balance difference for display only — never affects stored/displayed cell values. */
|
|
9036
|
+
_formatBalanceDifferenceAmount(n3) {
|
|
9037
|
+
try {
|
|
9038
|
+
return new Intl.NumberFormat(this.locale, { maximumFractionDigits: 2 }).format(Math.abs(n3));
|
|
9039
|
+
} catch {
|
|
9040
|
+
return JupiterFormSection._formatNumber(Math.abs(n3));
|
|
9041
|
+
}
|
|
9042
|
+
}
|
|
9043
|
+
_renderBalanceCheckStrip() {
|
|
9044
|
+
if (!this.calculationEnabled || !this._balanceCheckTotals)
|
|
9045
|
+
return "";
|
|
9046
|
+
const results = computeBalanceDifference(this.formData, this.columns, this._balanceCheckTotals);
|
|
9047
|
+
if (results.length === 0)
|
|
9048
|
+
return "";
|
|
9049
|
+
return html`
|
|
9050
|
+
<div class="balance-check-strip">
|
|
9051
|
+
${results.map((result) => result.balanced ? html`
|
|
9052
|
+
<span class="balance-check-chip balanced" title="${result.columnLabel}">
|
|
9053
|
+
✓ ${I18n.t("balanceCheck.balanced", { column: result.columnLabel })}
|
|
9054
|
+
</span>
|
|
9055
|
+
` : html`
|
|
9056
|
+
<button
|
|
9057
|
+
type="button"
|
|
9058
|
+
class="balance-check-chip unbalanced"
|
|
9059
|
+
title="${result.columnLabel}"
|
|
9060
|
+
@click="${() => this._handleBalanceCheckClick(result.columnId)}"
|
|
9061
|
+
>
|
|
9062
|
+
${I18n.t("balanceCheck.difference", {
|
|
9063
|
+
column: result.columnLabel,
|
|
9064
|
+
amount: this._formatBalanceDifferenceAmount(result.difference)
|
|
9065
|
+
})}
|
|
9066
|
+
</button>
|
|
9067
|
+
`)}
|
|
9068
|
+
</div>
|
|
9069
|
+
`;
|
|
9070
|
+
}
|
|
9071
|
+
/**
|
|
9072
|
+
* Column-header typed-member storage is scoped per role/section (not just columnId): the
|
|
9073
|
+
* form builder assigns generic ids like "typed-duration" to any role with a single typed
|
|
9074
|
+
* dimension and no explicit members, so the same axis+columnId pair can occur in more than
|
|
9075
|
+
* one role (e.g. a "Separate" and "Consolidated" variant of the same note). Without the
|
|
9076
|
+
* section id, a value typed in one role's column header would leak into every other role
|
|
9077
|
+
* whose column happens to share that generic id (JDF-090).
|
|
9078
|
+
*/
|
|
9079
|
+
_scopedColumnKey(columnId) {
|
|
9080
|
+
return `${this.section.id}__${columnId}`;
|
|
9081
|
+
}
|
|
8504
9082
|
_getTypedMemberHeaderValue(columnId, axisId) {
|
|
8505
|
-
var _a;
|
|
8506
|
-
|
|
9083
|
+
var _a, _b;
|
|
9084
|
+
const scopedValue = (_a = this.typedMemberData[this._scopedColumnKey(columnId)]) == null ? void 0 : _a[axisId];
|
|
9085
|
+
if (scopedValue !== void 0)
|
|
9086
|
+
return scopedValue;
|
|
9087
|
+
return ((_b = this.typedMemberData[columnId]) == null ? void 0 : _b[axisId]) || "";
|
|
8507
9088
|
}
|
|
8508
9089
|
/** True once any concept in this section has a non-empty value entered for the given column. */
|
|
8509
9090
|
_columnHasAnyValue(columnId) {
|
|
@@ -8571,9 +9152,10 @@ let JupiterFormSection = class extends LitElement {
|
|
|
8571
9152
|
_handleTypedMemberHeaderChange(event, columnId, axisId) {
|
|
8572
9153
|
const target = event.target;
|
|
8573
9154
|
const value = target instanceof HTMLInputElement && target.type === "checkbox" ? String(target.checked) : target.value;
|
|
8574
|
-
console.log(`🔍 [FormSection] Typed member header change: columnId=${columnId}, axisId=${axisId}, value=${value}`);
|
|
9155
|
+
console.log(`🔍 [FormSection] Typed member header change: sectionId=${this.section.id}, columnId=${columnId}, axisId=${axisId}, value=${value}`);
|
|
8575
9156
|
this.dispatchEvent(new CustomEvent("typed-member-change", {
|
|
8576
9157
|
detail: {
|
|
9158
|
+
sectionId: this.section.id,
|
|
8577
9159
|
columnId,
|
|
8578
9160
|
axisId,
|
|
8579
9161
|
value
|
|
@@ -8593,6 +9175,7 @@ let JupiterFormSection = class extends LitElement {
|
|
|
8593
9175
|
const hasValidationError = !!((_a = this._typedMemberErrors[this._typedMemberErrorKey(columnId, typedMember.axisId)]) == null ? void 0 : _a.length);
|
|
8594
9176
|
const fieldClass = `typed-member-header-field ${isRequired ? "required-field" : ""} ${hasValidationError ? "has-error" : ""}`;
|
|
8595
9177
|
const requiredTitle = isRequired ? I18n.t("validation.typedMemberRequired") : "";
|
|
9178
|
+
const fieldTitle = [typedMember.axisLabel, requiredTitle].filter(Boolean).join(" — ");
|
|
8596
9179
|
const onChange = (e2) => this._handleTypedMemberHeaderChange(e2, columnId, typedMember.axisId);
|
|
8597
9180
|
const onBlur = (e2) => this._handleTypedMemberHeaderBlur(e2, columnId, typedMember);
|
|
8598
9181
|
if (typeConfig.fieldType === "select" || isSelectType(typeConfig.fieldType)) {
|
|
@@ -8601,7 +9184,7 @@ let JupiterFormSection = class extends LitElement {
|
|
|
8601
9184
|
<select
|
|
8602
9185
|
class="${fieldClass}"
|
|
8603
9186
|
.value="${currentValue}"
|
|
8604
|
-
title="${
|
|
9187
|
+
title="${fieldTitle}"
|
|
8605
9188
|
@change="${onChange}"
|
|
8606
9189
|
@blur="${onBlur}"
|
|
8607
9190
|
>
|
|
@@ -8620,7 +9203,7 @@ let JupiterFormSection = class extends LitElement {
|
|
|
8620
9203
|
type="checkbox"
|
|
8621
9204
|
class="${fieldClass}"
|
|
8622
9205
|
.checked="${currentValue === "true"}"
|
|
8623
|
-
title="${
|
|
9206
|
+
title="${fieldTitle}"
|
|
8624
9207
|
@change="${onChange}"
|
|
8625
9208
|
@blur="${onBlur}"
|
|
8626
9209
|
/>
|
|
@@ -8638,7 +9221,7 @@ let JupiterFormSection = class extends LitElement {
|
|
|
8638
9221
|
min="${typeConfig.min !== void 0 ? typeConfig.min : ""}"
|
|
8639
9222
|
max="${typeConfig.max !== void 0 ? typeConfig.max : ""}"
|
|
8640
9223
|
step="${typeConfig.step !== void 0 ? typeConfig.step : ""}"
|
|
8641
|
-
title="${
|
|
9224
|
+
title="${fieldTitle}"
|
|
8642
9225
|
@input="${onChange}"
|
|
8643
9226
|
@blur="${onBlur}"
|
|
8644
9227
|
/>
|
|
@@ -8681,10 +9264,12 @@ let JupiterFormSection = class extends LitElement {
|
|
|
8681
9264
|
.defaultUnits="${this.defaultUnits}"
|
|
8682
9265
|
.disabled="${this.disabled}"
|
|
8683
9266
|
.locale="${this.locale}"
|
|
9267
|
+
.numberLocale="${this.numberLocale}"
|
|
8684
9268
|
.language="${this.language}"
|
|
8685
9269
|
.expandedConcepts="${this._expandedConcepts}"
|
|
8686
9270
|
.mode="${this.mode}"
|
|
8687
9271
|
.masterData="${this.masterData}"
|
|
9272
|
+
.prefilledTooltip="${this.prefilledTooltip}"
|
|
8688
9273
|
.facts="${instanceConcept.facts}"
|
|
8689
9274
|
.typedMemberData="${this.typedMemberData}"
|
|
8690
9275
|
.showAddButton="${opts.showAdd || false}"
|
|
@@ -8744,9 +9329,10 @@ let JupiterFormSection = class extends LitElement {
|
|
|
8744
9329
|
</div>
|
|
8745
9330
|
`;
|
|
8746
9331
|
}
|
|
9332
|
+
const { caption: columnCaption, perColumnLabel } = splitCommonMembers(this.columns);
|
|
8747
9333
|
return html`
|
|
8748
9334
|
${!this.hideHeader ? html`
|
|
8749
|
-
<div class="section-header ${!this.collapsible ? "not-collapsible" : ""}"
|
|
9335
|
+
<div class="section-header ${!this.collapsible ? "not-collapsible" : ""}"
|
|
8750
9336
|
@click="${this._toggleExpanded}">
|
|
8751
9337
|
<div>
|
|
8752
9338
|
<h3 class="section-title">${this.section.title}</h3>
|
|
@@ -8763,6 +9349,8 @@ let JupiterFormSection = class extends LitElement {
|
|
|
8763
9349
|
` : ""}
|
|
8764
9350
|
|
|
8765
9351
|
<div class="section-content ${!this._expanded ? "collapsed" : ""}">
|
|
9352
|
+
${columnCaption ? html`<div class="column-caption">${columnCaption}</div>` : ""}
|
|
9353
|
+
${this._renderBalanceCheckStrip()}
|
|
8766
9354
|
<div class="table-container">
|
|
8767
9355
|
<table class="form-table">
|
|
8768
9356
|
<thead class="table-header">
|
|
@@ -8777,10 +9365,10 @@ let JupiterFormSection = class extends LitElement {
|
|
|
8777
9365
|
@dblclick="${() => this._handleShowColumn(column2.id)}"
|
|
8778
9366
|
></th>
|
|
8779
9367
|
` : html`
|
|
8780
|
-
<th class="header-cell ${column2.removable ? "removable" : ""}">
|
|
9368
|
+
<th class="header-cell ${column2.removable ? "removable" : ""}" title="${column2.title}">
|
|
8781
9369
|
<div class="column-header-content">
|
|
8782
9370
|
<div class="column-title">
|
|
8783
|
-
${column2.
|
|
9371
|
+
${perColumnLabel[column2.id]}
|
|
8784
9372
|
${column2.description ? html`<div style="font-weight: normal; font-size: 12px; color: var(--jupiter-text-secondary, #666);">${column2.description}</div>` : ""}
|
|
8785
9373
|
</div>
|
|
8786
9374
|
|
|
@@ -8968,6 +9556,53 @@ JupiterFormSection.styles = css`
|
|
|
8968
9556
|
overflow-x: auto;
|
|
8969
9557
|
}
|
|
8970
9558
|
|
|
9559
|
+
/* JDF-113: caption for dimension members shared by every column in the role (e.g.
|
|
9560
|
+
"Geconsolideerd · Commercieel"), so they don't also repeat inside every column header. */
|
|
9561
|
+
.column-caption {
|
|
9562
|
+
font-size: 13px;
|
|
9563
|
+
font-weight: 600;
|
|
9564
|
+
color: var(--jupiter-text-secondary, #666);
|
|
9565
|
+
padding: 4px 0 8px 0;
|
|
9566
|
+
}
|
|
9567
|
+
|
|
9568
|
+
/* JDF-095: live, read-only balance-difference strip (Activa = Passiva). */
|
|
9569
|
+
.balance-check-strip {
|
|
9570
|
+
display: flex;
|
|
9571
|
+
flex-wrap: wrap;
|
|
9572
|
+
gap: 8px;
|
|
9573
|
+
padding: 8px 0 12px 0;
|
|
9574
|
+
position: sticky;
|
|
9575
|
+
left: 0;
|
|
9576
|
+
}
|
|
9577
|
+
|
|
9578
|
+
.balance-check-chip {
|
|
9579
|
+
display: inline-flex;
|
|
9580
|
+
align-items: center;
|
|
9581
|
+
gap: 6px;
|
|
9582
|
+
padding: 6px 12px;
|
|
9583
|
+
border-radius: 16px;
|
|
9584
|
+
font-size: 13px;
|
|
9585
|
+
font-weight: 600;
|
|
9586
|
+
border: none;
|
|
9587
|
+
font-family: inherit;
|
|
9588
|
+
}
|
|
9589
|
+
|
|
9590
|
+
.balance-check-chip.balanced {
|
|
9591
|
+
background: var(--jupiter-success-light, #e8f5e9);
|
|
9592
|
+
color: var(--jupiter-success-color, #4caf50);
|
|
9593
|
+
}
|
|
9594
|
+
|
|
9595
|
+
.balance-check-chip.unbalanced {
|
|
9596
|
+
background: var(--jupiter-warning-light, #fff3e0);
|
|
9597
|
+
color: var(--jupiter-warning-color, #ff9800);
|
|
9598
|
+
cursor: pointer;
|
|
9599
|
+
}
|
|
9600
|
+
|
|
9601
|
+
.balance-check-chip.unbalanced:hover {
|
|
9602
|
+
background: var(--jupiter-warning-color, #ff9800);
|
|
9603
|
+
color: #fff;
|
|
9604
|
+
}
|
|
9605
|
+
|
|
8971
9606
|
.form-table {
|
|
8972
9607
|
width: 100%;
|
|
8973
9608
|
table-layout: fixed;
|
|
@@ -9236,6 +9871,9 @@ __decorateClass$5([
|
|
|
9236
9871
|
__decorateClass$5([
|
|
9237
9872
|
n2({ type: String })
|
|
9238
9873
|
], JupiterFormSection.prototype, "locale", 2);
|
|
9874
|
+
__decorateClass$5([
|
|
9875
|
+
n2({ type: String })
|
|
9876
|
+
], JupiterFormSection.prototype, "numberLocale", 2);
|
|
9239
9877
|
__decorateClass$5([
|
|
9240
9878
|
n2({ type: Boolean })
|
|
9241
9879
|
], JupiterFormSection.prototype, "isFirstSection", 2);
|
|
@@ -9257,6 +9895,9 @@ __decorateClass$5([
|
|
|
9257
9895
|
__decorateClass$5([
|
|
9258
9896
|
n2({ type: Object })
|
|
9259
9897
|
], JupiterFormSection.prototype, "masterData", 2);
|
|
9898
|
+
__decorateClass$5([
|
|
9899
|
+
n2({ type: String })
|
|
9900
|
+
], JupiterFormSection.prototype, "prefilledTooltip", 2);
|
|
9260
9901
|
__decorateClass$5([
|
|
9261
9902
|
n2({ type: String })
|
|
9262
9903
|
], JupiterFormSection.prototype, "periodStartDate", 2);
|
|
@@ -9308,6 +9949,31 @@ __decorateClass$5([
|
|
|
9308
9949
|
JupiterFormSection = __decorateClass$5([
|
|
9309
9950
|
t$1("jupiter-form-section")
|
|
9310
9951
|
], JupiterFormSection);
|
|
9952
|
+
function fuzzyScore(query, text) {
|
|
9953
|
+
if (!query)
|
|
9954
|
+
return 0;
|
|
9955
|
+
const q = query.toLowerCase();
|
|
9956
|
+
const t2 = text.toLowerCase();
|
|
9957
|
+
const substringIndex = t2.indexOf(q);
|
|
9958
|
+
if (substringIndex !== -1) {
|
|
9959
|
+
const charBefore = t2[substringIndex - 1];
|
|
9960
|
+
const atWordStart = substringIndex === 0 || !charBefore || /[^a-z0-9]/.test(charBefore);
|
|
9961
|
+
return 1e4 - substringIndex + (atWordStart ? 500 : 0);
|
|
9962
|
+
}
|
|
9963
|
+
let qi = 0;
|
|
9964
|
+
let streak = 0;
|
|
9965
|
+
let score = 0;
|
|
9966
|
+
for (let ti = 0; ti < t2.length && qi < q.length; ti++) {
|
|
9967
|
+
if (t2[ti] === q[qi]) {
|
|
9968
|
+
qi++;
|
|
9969
|
+
streak++;
|
|
9970
|
+
score += streak;
|
|
9971
|
+
} else {
|
|
9972
|
+
streak = 0;
|
|
9973
|
+
}
|
|
9974
|
+
}
|
|
9975
|
+
return qi === q.length ? score : -1;
|
|
9976
|
+
}
|
|
9311
9977
|
var __defProp$4 = Object.defineProperty;
|
|
9312
9978
|
var __getOwnPropDesc$4 = Object.getOwnPropertyDescriptor;
|
|
9313
9979
|
var __decorateClass$4 = (decorators, target, key, kind) => {
|
|
@@ -9319,17 +9985,101 @@ var __decorateClass$4 = (decorators, target, key, kind) => {
|
|
|
9319
9985
|
__defProp$4(target, key, result);
|
|
9320
9986
|
return result;
|
|
9321
9987
|
};
|
|
9988
|
+
const MAX_SUGGESTIONS = 8;
|
|
9322
9989
|
let JupiterAdvancedFilter = class extends LitElement {
|
|
9323
9990
|
constructor() {
|
|
9324
9991
|
super(...arguments);
|
|
9325
9992
|
this.showFactsOnly = false;
|
|
9326
9993
|
this.conceptSearchText = "";
|
|
9994
|
+
this.availableRoles = [];
|
|
9327
9995
|
this._localConceptSearchText = "";
|
|
9996
|
+
this._conceptOptions = [];
|
|
9997
|
+
this._suggestions = [];
|
|
9998
|
+
this._showSuggestions = false;
|
|
9999
|
+
this._activeSuggestionIndex = -1;
|
|
9328
10000
|
}
|
|
9329
10001
|
updated(changedProperties) {
|
|
9330
10002
|
if (changedProperties.has("conceptSearchText") && this.conceptSearchText !== this._localConceptSearchText) {
|
|
9331
10003
|
this._localConceptSearchText = this.conceptSearchText;
|
|
9332
10004
|
}
|
|
10005
|
+
if (changedProperties.has("availableRoles")) {
|
|
10006
|
+
this._conceptOptions = this._buildConceptOptions();
|
|
10007
|
+
}
|
|
10008
|
+
}
|
|
10009
|
+
// Unique concepts (by originalConceptId) built from presentation-derived role trees only —
|
|
10010
|
+
// no calculation/hypercube/datatype data is consulted, per the autosuggest requirement.
|
|
10011
|
+
_buildConceptOptions() {
|
|
10012
|
+
const seen = /* @__PURE__ */ new Set();
|
|
10013
|
+
const options = [];
|
|
10014
|
+
const walk = (concepts) => {
|
|
10015
|
+
var _a;
|
|
10016
|
+
if (!concepts)
|
|
10017
|
+
return;
|
|
10018
|
+
for (const concept of concepts) {
|
|
10019
|
+
const id = concept.originalConceptId || concept.id;
|
|
10020
|
+
if (id && !concept.abstract && !seen.has(id)) {
|
|
10021
|
+
seen.add(id);
|
|
10022
|
+
options.push({ id, name: concept.label || concept.name || id });
|
|
10023
|
+
}
|
|
10024
|
+
if ((_a = concept.children) == null ? void 0 : _a.length) {
|
|
10025
|
+
walk(concept.children);
|
|
10026
|
+
}
|
|
10027
|
+
}
|
|
10028
|
+
};
|
|
10029
|
+
for (const role of this.availableRoles) {
|
|
10030
|
+
walk(role.concepts);
|
|
10031
|
+
}
|
|
10032
|
+
return options;
|
|
10033
|
+
}
|
|
10034
|
+
_computeSuggestions(query) {
|
|
10035
|
+
const trimmed = query.trim();
|
|
10036
|
+
if (!trimmed || this._conceptOptions.length === 0) {
|
|
10037
|
+
return [];
|
|
10038
|
+
}
|
|
10039
|
+
const scored = [];
|
|
10040
|
+
for (const option of this._conceptOptions) {
|
|
10041
|
+
const score = Math.max(
|
|
10042
|
+
fuzzyScore(trimmed, option.name),
|
|
10043
|
+
fuzzyScore(trimmed, option.id)
|
|
10044
|
+
);
|
|
10045
|
+
if (score >= 0) {
|
|
10046
|
+
scored.push({ option, score });
|
|
10047
|
+
}
|
|
10048
|
+
}
|
|
10049
|
+
scored.sort((a2, b2) => b2.score - a2.score);
|
|
10050
|
+
return scored.slice(0, MAX_SUGGESTIONS).map((s2) => s2.option);
|
|
10051
|
+
}
|
|
10052
|
+
_selectSuggestion(option) {
|
|
10053
|
+
var _a;
|
|
10054
|
+
this._localConceptSearchText = option.name;
|
|
10055
|
+
this._showSuggestions = false;
|
|
10056
|
+
this._activeSuggestionIndex = -1;
|
|
10057
|
+
this.dispatchEvent(new CustomEvent("filter-change", {
|
|
10058
|
+
detail: { showFactsOnly: this.showFactsOnly, conceptSearchText: this._localConceptSearchText },
|
|
10059
|
+
bubbles: true,
|
|
10060
|
+
composed: true
|
|
10061
|
+
}));
|
|
10062
|
+
const input = (_a = this.shadowRoot) == null ? void 0 : _a.querySelector(".concept-search-input");
|
|
10063
|
+
input == null ? void 0 : input.focus();
|
|
10064
|
+
}
|
|
10065
|
+
_handleConceptSearchFocus() {
|
|
10066
|
+
if (this._localConceptSearchText.trim()) {
|
|
10067
|
+
this._suggestions = this._computeSuggestions(this._localConceptSearchText);
|
|
10068
|
+
this._showSuggestions = this._suggestions.length > 0;
|
|
10069
|
+
}
|
|
10070
|
+
}
|
|
10071
|
+
_handleConceptSearchBlur() {
|
|
10072
|
+
this._showSuggestions = false;
|
|
10073
|
+
this._activeSuggestionIndex = -1;
|
|
10074
|
+
}
|
|
10075
|
+
_renderHighlighted(text, query) {
|
|
10076
|
+
const q = query.trim().toLowerCase();
|
|
10077
|
+
if (!q)
|
|
10078
|
+
return text;
|
|
10079
|
+
const idx = text.toLowerCase().indexOf(q);
|
|
10080
|
+
if (idx === -1)
|
|
10081
|
+
return text;
|
|
10082
|
+
return html`${text.slice(0, idx)}<mark>${text.slice(idx, idx + q.length)}</mark>${text.slice(idx + q.length)}`;
|
|
9333
10083
|
}
|
|
9334
10084
|
_handleShowFactsOnlyChange(e2) {
|
|
9335
10085
|
const checked = e2.target.checked;
|
|
@@ -9344,15 +10094,55 @@ let JupiterAdvancedFilter = class extends LitElement {
|
|
|
9344
10094
|
}
|
|
9345
10095
|
_handleConceptSearchInput(e2) {
|
|
9346
10096
|
this._localConceptSearchText = this._sanitizeSearchText(e2.target.value);
|
|
10097
|
+
this._suggestions = this._computeSuggestions(this._localConceptSearchText);
|
|
10098
|
+
this._showSuggestions = this._suggestions.length > 0;
|
|
10099
|
+
this._activeSuggestionIndex = -1;
|
|
9347
10100
|
this.dispatchEvent(new CustomEvent("filter-change", {
|
|
9348
10101
|
detail: { showFactsOnly: this.showFactsOnly, conceptSearchText: this._localConceptSearchText },
|
|
9349
10102
|
bubbles: true,
|
|
9350
10103
|
composed: true
|
|
9351
10104
|
}));
|
|
9352
10105
|
}
|
|
10106
|
+
// The "Apply Filter" button is otherwise the only way to apply this search — pressing Enter
|
|
10107
|
+
// in the input did nothing, forcing a mouse trip for what looks like a normal search box.
|
|
10108
|
+
_handleConceptSearchKeydown(e2) {
|
|
10109
|
+
if (this._showSuggestions && this._suggestions.length > 0) {
|
|
10110
|
+
if (e2.key === "ArrowDown") {
|
|
10111
|
+
e2.preventDefault();
|
|
10112
|
+
this._activeSuggestionIndex = (this._activeSuggestionIndex + 1) % this._suggestions.length;
|
|
10113
|
+
return;
|
|
10114
|
+
}
|
|
10115
|
+
if (e2.key === "ArrowUp") {
|
|
10116
|
+
e2.preventDefault();
|
|
10117
|
+
this._activeSuggestionIndex = this._activeSuggestionIndex <= 0 ? this._suggestions.length - 1 : this._activeSuggestionIndex - 1;
|
|
10118
|
+
return;
|
|
10119
|
+
}
|
|
10120
|
+
if (e2.key === "Escape") {
|
|
10121
|
+
this._showSuggestions = false;
|
|
10122
|
+
this._activeSuggestionIndex = -1;
|
|
10123
|
+
return;
|
|
10124
|
+
}
|
|
10125
|
+
if (e2.key === "Enter" && this._activeSuggestionIndex >= 0) {
|
|
10126
|
+
e2.preventDefault();
|
|
10127
|
+
this._selectSuggestion(this._suggestions[this._activeSuggestionIndex]);
|
|
10128
|
+
return;
|
|
10129
|
+
}
|
|
10130
|
+
}
|
|
10131
|
+
if (e2.key !== "Enter")
|
|
10132
|
+
return;
|
|
10133
|
+
e2.preventDefault();
|
|
10134
|
+
this._showSuggestions = false;
|
|
10135
|
+
this.dispatchEvent(new CustomEvent("filter-apply", {
|
|
10136
|
+
bubbles: true,
|
|
10137
|
+
composed: true
|
|
10138
|
+
}));
|
|
10139
|
+
}
|
|
9353
10140
|
_clearConceptSearch() {
|
|
9354
10141
|
var _a;
|
|
9355
10142
|
this._localConceptSearchText = "";
|
|
10143
|
+
this._suggestions = [];
|
|
10144
|
+
this._showSuggestions = false;
|
|
10145
|
+
this._activeSuggestionIndex = -1;
|
|
9356
10146
|
const input = (_a = this.shadowRoot) == null ? void 0 : _a.querySelector(".concept-search-input");
|
|
9357
10147
|
if (input) {
|
|
9358
10148
|
input.value = "";
|
|
@@ -9389,7 +10179,14 @@ let JupiterAdvancedFilter = class extends LitElement {
|
|
|
9389
10179
|
class="concept-search-input"
|
|
9390
10180
|
placeholder="${I18n.t("filter.searchByConceptPlaceholder")}"
|
|
9391
10181
|
.value="${this._localConceptSearchText}"
|
|
10182
|
+
autocomplete="off"
|
|
10183
|
+
role="combobox"
|
|
10184
|
+
aria-expanded="${this._showSuggestions}"
|
|
10185
|
+
aria-autocomplete="list"
|
|
9392
10186
|
@input="${this._handleConceptSearchInput}"
|
|
10187
|
+
@keydown="${this._handleConceptSearchKeydown}"
|
|
10188
|
+
@focus="${this._handleConceptSearchFocus}"
|
|
10189
|
+
@blur="${this._handleConceptSearchBlur}"
|
|
9393
10190
|
/>
|
|
9394
10191
|
${this._localConceptSearchText ? html`
|
|
9395
10192
|
<button
|
|
@@ -9398,6 +10195,22 @@ let JupiterAdvancedFilter = class extends LitElement {
|
|
|
9398
10195
|
title="${I18n.t("filter.clearSearch")}"
|
|
9399
10196
|
>×</button>
|
|
9400
10197
|
` : ""}
|
|
10198
|
+
${this._showSuggestions ? html`
|
|
10199
|
+
<ul class="concept-suggestions" role="listbox">
|
|
10200
|
+
${this._suggestions.map((option, index) => html`
|
|
10201
|
+
<li
|
|
10202
|
+
class="concept-suggestion-item ${index === this._activeSuggestionIndex ? "active" : ""}"
|
|
10203
|
+
role="option"
|
|
10204
|
+
aria-selected="${index === this._activeSuggestionIndex}"
|
|
10205
|
+
@mousedown="${(e2) => e2.preventDefault()}"
|
|
10206
|
+
@click="${() => this._selectSuggestion(option)}"
|
|
10207
|
+
>
|
|
10208
|
+
<span class="concept-suggestion-name">${this._renderHighlighted(option.name, this._localConceptSearchText)}</span>
|
|
10209
|
+
<span class="concept-suggestion-id">${option.id}</span>
|
|
10210
|
+
</li>
|
|
10211
|
+
`)}
|
|
10212
|
+
</ul>
|
|
10213
|
+
` : ""}
|
|
9401
10214
|
</div>
|
|
9402
10215
|
</div>
|
|
9403
10216
|
</div>
|
|
@@ -9504,6 +10317,56 @@ JupiterAdvancedFilter.styles = css`
|
|
|
9504
10317
|
.concept-search-clear:hover {
|
|
9505
10318
|
color: var(--primaryTextColor, var(--jupiter-text-primary, #333));
|
|
9506
10319
|
}
|
|
10320
|
+
|
|
10321
|
+
.concept-suggestions {
|
|
10322
|
+
position: absolute;
|
|
10323
|
+
top: calc(100% + 4px);
|
|
10324
|
+
left: 0;
|
|
10325
|
+
right: 0;
|
|
10326
|
+
z-index: 20;
|
|
10327
|
+
margin: 0;
|
|
10328
|
+
padding: 4px;
|
|
10329
|
+
list-style: none;
|
|
10330
|
+
max-height: 260px;
|
|
10331
|
+
overflow-y: auto;
|
|
10332
|
+
background: var(--jupiter-background, #fff);
|
|
10333
|
+
border: 1px solid var(--primaryTextColor, var(--jupiter-border-color, #ddd));
|
|
10334
|
+
border-radius: 4px;
|
|
10335
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
10336
|
+
}
|
|
10337
|
+
|
|
10338
|
+
.concept-suggestion-item {
|
|
10339
|
+
display: flex;
|
|
10340
|
+
flex-direction: column;
|
|
10341
|
+
gap: 2px;
|
|
10342
|
+
padding: 6px 8px;
|
|
10343
|
+
border-radius: 3px;
|
|
10344
|
+
cursor: pointer;
|
|
10345
|
+
}
|
|
10346
|
+
|
|
10347
|
+
.concept-suggestion-item:hover,
|
|
10348
|
+
.concept-suggestion-item.active {
|
|
10349
|
+
background: var(--menuBgColorLighter, var(--jupiter-hover-background, #f5f5f5));
|
|
10350
|
+
}
|
|
10351
|
+
|
|
10352
|
+
.concept-suggestion-name {
|
|
10353
|
+
font-size: 13px;
|
|
10354
|
+
color: var(--primaryTextColor, var(--jupiter-text-primary, #333));
|
|
10355
|
+
}
|
|
10356
|
+
|
|
10357
|
+
.concept-suggestion-name mark {
|
|
10358
|
+
background: var(--jupiter-primary-color, #1976d2);
|
|
10359
|
+
color: white;
|
|
10360
|
+
padding: 0 1px;
|
|
10361
|
+
border-radius: 2px;
|
|
10362
|
+
font-weight: 500;
|
|
10363
|
+
}
|
|
10364
|
+
|
|
10365
|
+
.concept-suggestion-id {
|
|
10366
|
+
font-size: 11px;
|
|
10367
|
+
color: var(--primaryTextColor, var(--jupiter-text-secondary, #999));
|
|
10368
|
+
word-break: break-all;
|
|
10369
|
+
}
|
|
9507
10370
|
`;
|
|
9508
10371
|
__decorateClass$4([
|
|
9509
10372
|
n2({ type: Boolean })
|
|
@@ -9511,9 +10374,24 @@ __decorateClass$4([
|
|
|
9511
10374
|
__decorateClass$4([
|
|
9512
10375
|
n2({ type: String })
|
|
9513
10376
|
], JupiterAdvancedFilter.prototype, "conceptSearchText", 2);
|
|
10377
|
+
__decorateClass$4([
|
|
10378
|
+
n2({ type: Array })
|
|
10379
|
+
], JupiterAdvancedFilter.prototype, "availableRoles", 2);
|
|
10380
|
+
__decorateClass$4([
|
|
10381
|
+
r()
|
|
10382
|
+
], JupiterAdvancedFilter.prototype, "_localConceptSearchText", 2);
|
|
10383
|
+
__decorateClass$4([
|
|
10384
|
+
r()
|
|
10385
|
+
], JupiterAdvancedFilter.prototype, "_conceptOptions", 2);
|
|
10386
|
+
__decorateClass$4([
|
|
10387
|
+
r()
|
|
10388
|
+
], JupiterAdvancedFilter.prototype, "_suggestions", 2);
|
|
10389
|
+
__decorateClass$4([
|
|
10390
|
+
r()
|
|
10391
|
+
], JupiterAdvancedFilter.prototype, "_showSuggestions", 2);
|
|
9514
10392
|
__decorateClass$4([
|
|
9515
10393
|
r()
|
|
9516
|
-
], JupiterAdvancedFilter.prototype, "
|
|
10394
|
+
], JupiterAdvancedFilter.prototype, "_activeSuggestionIndex", 2);
|
|
9517
10395
|
JupiterAdvancedFilter = __decorateClass$4([
|
|
9518
10396
|
t$1("jupiter-advanced-filter")
|
|
9519
10397
|
], JupiterAdvancedFilter);
|
|
@@ -10408,9 +11286,11 @@ let JupiterFilterRolesDialog = class extends LitElement {
|
|
|
10408
11286
|
</div>
|
|
10409
11287
|
` : this._showAdvancedFilter ? html`
|
|
10410
11288
|
<jupiter-advanced-filter
|
|
11289
|
+
.availableRoles="${this.availableRoles}"
|
|
10411
11290
|
.showFactsOnly="${this._showFactsOnly}"
|
|
10412
11291
|
.conceptSearchText="${this._conceptSearchText}"
|
|
10413
11292
|
@filter-change="${this._handleFilterChange}"
|
|
11293
|
+
@filter-apply="${this._handleAdvancedFilterApply}"
|
|
10414
11294
|
></jupiter-advanced-filter>
|
|
10415
11295
|
` : html`
|
|
10416
11296
|
<p class="description">
|
|
@@ -11873,6 +12753,7 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
11873
12753
|
this.periodEndDate = "2025-12-31";
|
|
11874
12754
|
this.language = "en";
|
|
11875
12755
|
this.reportingLanguage = "en";
|
|
12756
|
+
this.numberLocale = "";
|
|
11876
12757
|
this.display = "accordion";
|
|
11877
12758
|
this.mode = "inputForm";
|
|
11878
12759
|
this.roleFilterAxes = [];
|
|
@@ -11904,6 +12785,8 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
11904
12785
|
this._xbrlFormErrors = [];
|
|
11905
12786
|
this._calculationWarnings = /* @__PURE__ */ new Map();
|
|
11906
12787
|
this._showErrorPopup = false;
|
|
12788
|
+
this._showTypedDimensionWarning = false;
|
|
12789
|
+
this._typedDimensionWarningFacts = [];
|
|
11907
12790
|
this._submitDisabled = false;
|
|
11908
12791
|
this._allSections = [];
|
|
11909
12792
|
this._selectedRoleIds = [];
|
|
@@ -11911,6 +12794,7 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
11911
12794
|
this._revealRolePrompt = null;
|
|
11912
12795
|
this._showFactsOnly = false;
|
|
11913
12796
|
this._conceptSearchText = "";
|
|
12797
|
+
this._lastFocusedFieldIdentity = null;
|
|
11914
12798
|
this._periodPreferences = {};
|
|
11915
12799
|
this._activeSidePanelRoleId = null;
|
|
11916
12800
|
this._sidePanelSearchQuery = "";
|
|
@@ -11923,6 +12807,8 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
11923
12807
|
this._contextMenuY = 0;
|
|
11924
12808
|
this._contextMenuRoleId = null;
|
|
11925
12809
|
this._validationStatus = "idle";
|
|
12810
|
+
this._validationDotCount = 1;
|
|
12811
|
+
this._validationDotIntervalId = null;
|
|
11926
12812
|
this._formulaValidationResults = [];
|
|
11927
12813
|
this._showFormulaValidationDialog = false;
|
|
11928
12814
|
this._formulaValidationService = new FormulaValidationService();
|
|
@@ -11977,6 +12863,23 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
11977
12863
|
bubbles: true
|
|
11978
12864
|
}));
|
|
11979
12865
|
});
|
|
12866
|
+
this.addEventListener("focusin", (e2) => {
|
|
12867
|
+
var _a;
|
|
12868
|
+
const input = e2.composedPath().find(
|
|
12869
|
+
(el) => el instanceof HTMLInputElement || el instanceof HTMLSelectElement || el instanceof HTMLTextAreaElement
|
|
12870
|
+
);
|
|
12871
|
+
if (!input)
|
|
12872
|
+
return;
|
|
12873
|
+
let node = input;
|
|
12874
|
+
while (node && ((_a = node.tagName) == null ? void 0 : _a.toLowerCase()) !== "jupiter-form-field") {
|
|
12875
|
+
const root = node.getRootNode();
|
|
12876
|
+
node = root instanceof ShadowRoot ? root.host : null;
|
|
12877
|
+
}
|
|
12878
|
+
const fieldHost = node;
|
|
12879
|
+
if ((fieldHost == null ? void 0 : fieldHost.conceptId) && (fieldHost == null ? void 0 : fieldHost.columnId)) {
|
|
12880
|
+
this._lastFocusedFieldIdentity = { conceptId: fieldHost.conceptId, columnId: fieldHost.columnId };
|
|
12881
|
+
}
|
|
12882
|
+
});
|
|
11980
12883
|
document.addEventListener("click", () => {
|
|
11981
12884
|
if (this._showRoleContextMenu) {
|
|
11982
12885
|
this._showRoleContextMenu = false;
|
|
@@ -12006,6 +12909,10 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
12006
12909
|
clearInterval(this._autoSaveTimer);
|
|
12007
12910
|
this._autoSaveTimer = null;
|
|
12008
12911
|
}
|
|
12912
|
+
if (this._validationDotIntervalId !== null) {
|
|
12913
|
+
clearInterval(this._validationDotIntervalId);
|
|
12914
|
+
this._validationDotIntervalId = null;
|
|
12915
|
+
}
|
|
12009
12916
|
this._settleRevealRolePrompt(false);
|
|
12010
12917
|
}
|
|
12011
12918
|
/**
|
|
@@ -12209,11 +13116,12 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
12209
13116
|
(tm) => tm.axisId === normalizedAxisId || this._normalizeAxisId(tm.axisId) === normalizedAxisId
|
|
12210
13117
|
);
|
|
12211
13118
|
if (typedMember) {
|
|
12212
|
-
|
|
12213
|
-
|
|
13119
|
+
const columnKey = this._scopedColumnKey(section2.id, matchingColumn.id);
|
|
13120
|
+
if (!this._typedMemberData[columnKey]) {
|
|
13121
|
+
this._typedMemberData[columnKey] = {};
|
|
12214
13122
|
}
|
|
12215
13123
|
const axisKey = typedMember.axisId;
|
|
12216
|
-
this._typedMemberData[
|
|
13124
|
+
this._typedMemberData[columnKey][axisKey] = dim.typedMemberValue;
|
|
12217
13125
|
console.log(`✅ [DynamicForm] Set typed member value:`, {
|
|
12218
13126
|
sectionId: section2.id,
|
|
12219
13127
|
conceptId: concept.id,
|
|
@@ -12619,16 +13527,17 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
12619
13527
|
}
|
|
12620
13528
|
_preserveSectionData(section2) {
|
|
12621
13529
|
section2.concepts.forEach((concept) => {
|
|
12622
|
-
this._preserveConceptData(concept);
|
|
13530
|
+
this._preserveConceptData(concept, section2.id);
|
|
12623
13531
|
});
|
|
12624
13532
|
}
|
|
12625
|
-
_preserveConceptData(concept) {
|
|
13533
|
+
_preserveConceptData(concept, sectionId) {
|
|
12626
13534
|
if (this._formData[concept.id]) {
|
|
12627
13535
|
this._preservedFormData[concept.id] = { ...this._formData[concept.id] };
|
|
12628
13536
|
}
|
|
12629
13537
|
concept.fields.forEach((field2) => {
|
|
12630
|
-
|
|
12631
|
-
|
|
13538
|
+
const columnKey = this._scopedColumnKey(sectionId, field2.columnId);
|
|
13539
|
+
if (this._typedMemberData[columnKey]) {
|
|
13540
|
+
this._preservedTypedMemberData[columnKey] = { ...this._typedMemberData[columnKey] };
|
|
12632
13541
|
}
|
|
12633
13542
|
const crossRoleKey = `${concept.id}__${field2.columnId}`;
|
|
12634
13543
|
if (this._typedMemberData[crossRoleKey]) {
|
|
@@ -12637,22 +13546,23 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
12637
13546
|
});
|
|
12638
13547
|
if (concept.children) {
|
|
12639
13548
|
concept.children.forEach((child) => {
|
|
12640
|
-
this._preserveConceptData(child);
|
|
13549
|
+
this._preserveConceptData(child, sectionId);
|
|
12641
13550
|
});
|
|
12642
13551
|
}
|
|
12643
13552
|
}
|
|
12644
13553
|
_restoreSectionData(section2) {
|
|
12645
13554
|
section2.concepts.forEach((concept) => {
|
|
12646
|
-
this._restoreConceptData(concept);
|
|
13555
|
+
this._restoreConceptData(concept, section2.id);
|
|
12647
13556
|
});
|
|
12648
13557
|
}
|
|
12649
|
-
_restoreConceptData(concept) {
|
|
13558
|
+
_restoreConceptData(concept, sectionId) {
|
|
12650
13559
|
if (this._preservedFormData[concept.id]) {
|
|
12651
13560
|
this._formData[concept.id] = { ...this._preservedFormData[concept.id] };
|
|
12652
13561
|
}
|
|
12653
13562
|
concept.fields.forEach((field2) => {
|
|
12654
|
-
|
|
12655
|
-
|
|
13563
|
+
const columnKey = this._scopedColumnKey(sectionId, field2.columnId);
|
|
13564
|
+
if (this._preservedTypedMemberData[columnKey]) {
|
|
13565
|
+
this._typedMemberData[columnKey] = { ...this._preservedTypedMemberData[columnKey] };
|
|
12656
13566
|
}
|
|
12657
13567
|
const crossRoleKey = `${concept.id}__${field2.columnId}`;
|
|
12658
13568
|
if (this._preservedTypedMemberData[crossRoleKey]) {
|
|
@@ -12661,7 +13571,7 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
12661
13571
|
});
|
|
12662
13572
|
if (concept.children) {
|
|
12663
13573
|
concept.children.forEach((child) => {
|
|
12664
|
-
this._restoreConceptData(child);
|
|
13574
|
+
this._restoreConceptData(child, sectionId);
|
|
12665
13575
|
});
|
|
12666
13576
|
}
|
|
12667
13577
|
}
|
|
@@ -12671,9 +13581,183 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
12671
13581
|
get _hasActiveAdvancedFilters() {
|
|
12672
13582
|
return this._showFactsOnly || this._conceptSearchText.trim().length > 0;
|
|
12673
13583
|
}
|
|
13584
|
+
// JDF-101 item 3: the footer's role-count badge normally shows the Filter Roles selection
|
|
13585
|
+
// (selected/total), which the concept-search/facts-only filter never touches — exactly the
|
|
13586
|
+
// stale-looking "549/549" the ticket's Summary complains about. While an advanced filter is
|
|
13587
|
+
// active, count the roles actually rendering instead — the same visibility rule the active-filter
|
|
13588
|
+
// banner (`_renderActiveFilterBanner`) and the content area already use — so the badge and the
|
|
13589
|
+
// banner always agree on what's on screen.
|
|
13590
|
+
get _footerVisibleRoleCount() {
|
|
13591
|
+
if (!this._hasActiveAdvancedFilters || !this._currentSchema) {
|
|
13592
|
+
return this._selectedRoleIds.length;
|
|
13593
|
+
}
|
|
13594
|
+
return this._getSidePanelVisibleSections(this._currentSchema).length;
|
|
13595
|
+
}
|
|
13596
|
+
// JDF-101: counts the rows the concept filter leaves visible within `sectionsInView` (the
|
|
13597
|
+
// sections actually rendered for the current display mode — the active role alone in
|
|
13598
|
+
// sidePanel, every filtered role in accordion) and picks a human label for the banner.
|
|
13599
|
+
// Deliberately scoped to `sectionsInView` rather than the whole taxonomy: the same search text
|
|
13600
|
+
// can substring-match unrelated concepts in other roles (e.g. a taxonomy's technical
|
|
13601
|
+
// period-marker rows, JDF-106), so resolving the label globally can name a concept the user
|
|
13602
|
+
// never clicked. Scoping to what's on screen narrows that a lot, but the underlying search is
|
|
13603
|
+
// itself a broad substring match (`_getConceptSearchMatchIds`) that can still pull in dozens of
|
|
13604
|
+
// unrelated concepts whose name merely contains the search text as a substring (e.g. searching
|
|
13605
|
+
// "IntangibleAssets" also matches "IntangibleAssetsPresentationOrder", a JDF-106 technical
|
|
13606
|
+
// helper row) — especially in accordion mode, where every matching role renders at once. So an
|
|
13607
|
+
// exact local-name match (what scrollToConcept always provides, having stripped the prefix
|
|
13608
|
+
// itself) is preferred outright over any merely-substring match, and a non-abstract row's label
|
|
13609
|
+
// is preferred over an abstract group header's within each tier.
|
|
13610
|
+
_analyzeConceptFilterMatches(sectionsInView, matchIds) {
|
|
13611
|
+
const localName = (name) => (name.includes(":") ? name.split(":").pop() : name).toLowerCase();
|
|
13612
|
+
const searchLocal = this._conceptSearchText.trim().toLowerCase();
|
|
13613
|
+
let count = 0;
|
|
13614
|
+
let exactConcreteLabel = "";
|
|
13615
|
+
let exactAbstractLabel = "";
|
|
13616
|
+
let concreteLabel = "";
|
|
13617
|
+
let abstractLabel = "";
|
|
13618
|
+
const walk = (nodes) => {
|
|
13619
|
+
var _a;
|
|
13620
|
+
for (const node of nodes) {
|
|
13621
|
+
if (matchIds.has(node.originalConceptId)) {
|
|
13622
|
+
count++;
|
|
13623
|
+
const isExact = localName(node.name) === searchLocal;
|
|
13624
|
+
if (isExact && node.abstract && !exactAbstractLabel)
|
|
13625
|
+
exactAbstractLabel = node.label;
|
|
13626
|
+
else if (isExact && !node.abstract && !exactConcreteLabel)
|
|
13627
|
+
exactConcreteLabel = node.label;
|
|
13628
|
+
else if (node.abstract && !abstractLabel)
|
|
13629
|
+
abstractLabel = node.label;
|
|
13630
|
+
else if (!node.abstract && !concreteLabel)
|
|
13631
|
+
concreteLabel = node.label;
|
|
13632
|
+
}
|
|
13633
|
+
if ((_a = node.children) == null ? void 0 : _a.length)
|
|
13634
|
+
walk(node.children);
|
|
13635
|
+
}
|
|
13636
|
+
};
|
|
13637
|
+
for (const section2 of sectionsInView)
|
|
13638
|
+
walk(section2.concepts);
|
|
13639
|
+
return {
|
|
13640
|
+
count,
|
|
13641
|
+
label: exactConcreteLabel || exactAbstractLabel || concreteLabel || abstractLabel || this._conceptSearchText
|
|
13642
|
+
};
|
|
13643
|
+
}
|
|
13644
|
+
/**
|
|
13645
|
+
* JDF-101: banner shown at the top of the content area whenever a concept search or
|
|
13646
|
+
* "facts only" filter is narrowing what's visible, so customers don't mistake a filtered view
|
|
13647
|
+
* for missing data. `sectionsInView` should be whatever sections are actually rendered for the
|
|
13648
|
+
* current display mode (the active role alone in sidePanel; every filtered role in accordion),
|
|
13649
|
+
* so the row count and named concept match what the user is actually looking at. Purely a
|
|
13650
|
+
* read-only display — clearing it goes through the existing `_handleClearAdvancedFilters` (same
|
|
13651
|
+
* focus-preserving rebuild as the footer's "Clear Filters" link).
|
|
13652
|
+
*/
|
|
13653
|
+
_renderActiveFilterBanner(sectionsInView) {
|
|
13654
|
+
if (!this._hasActiveAdvancedFilters)
|
|
13655
|
+
return "";
|
|
13656
|
+
const hasConceptSearch = this._conceptSearchText.trim().length > 0;
|
|
13657
|
+
const message = hasConceptSearch ? (() => {
|
|
13658
|
+
const matchIds = this._getConceptSearchMatchIds(this._conceptSearchText);
|
|
13659
|
+
const { count, label } = this._analyzeConceptFilterMatches(sectionsInView, matchIds);
|
|
13660
|
+
return I18n.t("filter.bannerConcept", { concept: label, count });
|
|
13661
|
+
})() : I18n.t("filter.bannerFactsOnly");
|
|
13662
|
+
return html`
|
|
13663
|
+
<div class="active-filter-banner">
|
|
13664
|
+
<span class="active-filter-banner-text">${message}</span>
|
|
13665
|
+
<button
|
|
13666
|
+
type="button"
|
|
13667
|
+
class="active-filter-banner-clear"
|
|
13668
|
+
@click="${this._handleClearAdvancedFilters}"
|
|
13669
|
+
>${I18n.t("filter.showAll")}</button>
|
|
13670
|
+
</div>
|
|
13671
|
+
`;
|
|
13672
|
+
}
|
|
12674
13673
|
_handleClearAdvancedFilters() {
|
|
13674
|
+
const refocusTarget = this._lastFocusedFieldIdentity;
|
|
12675
13675
|
this._showFactsOnly = false;
|
|
12676
13676
|
this._conceptSearchText = "";
|
|
13677
|
+
this.updateComplete.then(async () => {
|
|
13678
|
+
var _a;
|
|
13679
|
+
const sections = Array.from(((_a = this.shadowRoot) == null ? void 0 : _a.querySelectorAll("jupiter-form-section")) ?? []);
|
|
13680
|
+
await Promise.all(sections.map((s2) => s2.updateComplete));
|
|
13681
|
+
const trees = sections.flatMap((s2) => {
|
|
13682
|
+
var _a2;
|
|
13683
|
+
return Array.from(((_a2 = s2.shadowRoot) == null ? void 0 : _a2.querySelectorAll("jupiter-concept-tree")) ?? []);
|
|
13684
|
+
});
|
|
13685
|
+
await Promise.all(trees.map((t2) => t2.updateComplete));
|
|
13686
|
+
this._scrollActiveRoleIntoView();
|
|
13687
|
+
if (refocusTarget) {
|
|
13688
|
+
this._restoreFieldFocus(refocusTarget);
|
|
13689
|
+
}
|
|
13690
|
+
});
|
|
13691
|
+
}
|
|
13692
|
+
// JDF-091: the locale actually used for number parsing — the explicit `numberLocale` prop when
|
|
13693
|
+
// a host sets one, otherwise derived from `language` so a user seeing the form in English gets
|
|
13694
|
+
// English number formatting rather than always Dutch (English speakers paste "100,000", not
|
|
13695
|
+
// "100.000"). Mirrors the existing `.locale="${config.locale || 'en-US'}"` fallback pattern.
|
|
13696
|
+
_effectiveNumberLocale() {
|
|
13697
|
+
return this.numberLocale || (this.language === "nl" ? "nl-NL" : "en-US");
|
|
13698
|
+
}
|
|
13699
|
+
// JDF-093 Part B (scoped down, per product decision 2026-09-12): a read-only "amounts are in
|
|
13700
|
+
// €, or € × scale" caption shown once, in the sidePanel section header — display only, derived
|
|
13701
|
+
// from the same `decimals` prop that already drives `DocumentIntendedRoundingLevel`
|
|
13702
|
+
// (_computeEffectiveMasterData), so there's no new source-of-truth question to answer and no
|
|
13703
|
+
// change to submission semantics. `decimals` is a positive "how many places to round off" —
|
|
13704
|
+
// e.g. '3' means the document rounds to the nearest 1000, i.e. amounts are being reported in
|
|
13705
|
+
// thousands — so the scale factor customers should read this as is 10^decimals.
|
|
13706
|
+
_amountScaleFactor() {
|
|
13707
|
+
if (this.decimals === "INF" || this.decimals === void 0 || this.decimals === null) {
|
|
13708
|
+
return null;
|
|
13709
|
+
}
|
|
13710
|
+
const parsed = parseFloat(this.decimals);
|
|
13711
|
+
if (isNaN(parsed) || parsed <= 0) {
|
|
13712
|
+
return null;
|
|
13713
|
+
}
|
|
13714
|
+
return Math.pow(10, parsed);
|
|
13715
|
+
}
|
|
13716
|
+
_amountScaleCaption() {
|
|
13717
|
+
const factor = this._amountScaleFactor();
|
|
13718
|
+
if (factor === null) {
|
|
13719
|
+
return I18n.t("section.amountScaleBase");
|
|
13720
|
+
}
|
|
13721
|
+
const formattedFactor = new Intl.NumberFormat(this._effectiveNumberLocale()).format(factor);
|
|
13722
|
+
return I18n.t("section.amountScaleFactor", { factor: formattedFactor });
|
|
13723
|
+
}
|
|
13724
|
+
// Only worth stating for a role that actually has a monetary amount to enter — a caption
|
|
13725
|
+
// reading "Amounts in €" on e.g. "Address of the legal entity" would just be noise.
|
|
13726
|
+
_sectionHasMonetaryConcepts(concepts) {
|
|
13727
|
+
return concepts.some(
|
|
13728
|
+
(concept) => {
|
|
13729
|
+
var _a;
|
|
13730
|
+
return ((_a = concept.type) == null ? void 0 : _a.toLowerCase().includes("monetary")) || concept.children && this._sectionHasMonetaryConcepts(concept.children);
|
|
13731
|
+
}
|
|
13732
|
+
);
|
|
13733
|
+
}
|
|
13734
|
+
// Brings the currently selected role's entry in the sidePanel left-panel list back into view
|
|
13735
|
+
// (it stays selected across a filter change but can scroll out of sight when the list regrows).
|
|
13736
|
+
// Centered rather than 'nearest': 'nearest' places the item flush against the scroll boundary,
|
|
13737
|
+
// which reads as "still cut off" when its row height isn't an exact multiple of the container.
|
|
13738
|
+
_scrollActiveRoleIntoView() {
|
|
13739
|
+
var _a;
|
|
13740
|
+
const activeItem = (_a = this.shadowRoot) == null ? void 0 : _a.querySelector(".side-panel-roles-list .side-panel-role-item.active");
|
|
13741
|
+
activeItem == null ? void 0 : activeItem.scrollIntoView({ block: "center" });
|
|
13742
|
+
}
|
|
13743
|
+
// Re-locates the fact input identified by _lastFocusedFieldIdentity after a rebuild and restores
|
|
13744
|
+
// focus to it, mirroring the shadow-DOM traversal scrollToConcept/_handleErrorFieldClick use.
|
|
13745
|
+
_restoreFieldFocus(target) {
|
|
13746
|
+
var _a, _b, _c, _d;
|
|
13747
|
+
const sections = ((_a = this.shadowRoot) == null ? void 0 : _a.querySelectorAll("jupiter-form-section")) ?? [];
|
|
13748
|
+
for (const section2 of Array.from(sections)) {
|
|
13749
|
+
const trees = ((_b = section2.shadowRoot) == null ? void 0 : _b.querySelectorAll("jupiter-concept-tree")) ?? [];
|
|
13750
|
+
for (const tree of Array.from(trees)) {
|
|
13751
|
+
const fields = ((_c = tree.shadowRoot) == null ? void 0 : _c.querySelectorAll("jupiter-form-field")) ?? [];
|
|
13752
|
+
for (const fieldEl of Array.from(fields)) {
|
|
13753
|
+
if (fieldEl.conceptId === target.conceptId && fieldEl.columnId === target.columnId) {
|
|
13754
|
+
const input = (_d = fieldEl.shadowRoot) == null ? void 0 : _d.querySelector('input:not([type="hidden"]), select, textarea');
|
|
13755
|
+
input == null ? void 0 : input.focus({ preventScroll: true });
|
|
13756
|
+
return;
|
|
13757
|
+
}
|
|
13758
|
+
}
|
|
13759
|
+
}
|
|
13760
|
+
}
|
|
12677
13761
|
}
|
|
12678
13762
|
_handleFilterRolesClick() {
|
|
12679
13763
|
this._showFilterDialog = true;
|
|
@@ -13166,9 +14250,21 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
13166
14250
|
this._dirty = true;
|
|
13167
14251
|
this.hasUnsavedChanges = true;
|
|
13168
14252
|
}
|
|
14253
|
+
/**
|
|
14254
|
+
* Column-header typed-member storage is scoped per role/section, not just columnId: the
|
|
14255
|
+
* form builder assigns generic ids (e.g. "typed-duration") to any role with a single typed
|
|
14256
|
+
* dimension and no explicit members, so the same columnId+axisId pair can recur across
|
|
14257
|
+
* unrelated roles (e.g. a "Separate" and "Consolidated" variant of the same note). Without
|
|
14258
|
+
* the section id, a value typed into one role's column header leaks into every other role
|
|
14259
|
+
* whose column happens to share that generic id (JDF-090). Mirrors `_scopedColumnKey` in
|
|
14260
|
+
* form-section.ts.
|
|
14261
|
+
*/
|
|
14262
|
+
_scopedColumnKey(sectionId, columnId) {
|
|
14263
|
+
return `${sectionId}__${columnId}`;
|
|
14264
|
+
}
|
|
13169
14265
|
_handleTypedMemberChange(event) {
|
|
13170
|
-
const { columnId, axisId, value, conceptId } = event.detail;
|
|
13171
|
-
const storageKey = conceptId ? `${conceptId}__${columnId}` : columnId;
|
|
14266
|
+
const { columnId, axisId, value, conceptId, sectionId } = event.detail;
|
|
14267
|
+
const storageKey = conceptId ? `${conceptId}__${columnId}` : sectionId ? this._scopedColumnKey(sectionId, columnId) : columnId;
|
|
13172
14268
|
console.log(`🔍 [DynamicForm] Typed member change: storageKey=${storageKey}, axisId=${axisId}, value=${value}`);
|
|
13173
14269
|
const updatedTypedMemberData = { ...this._typedMemberData };
|
|
13174
14270
|
if (!updatedTypedMemberData[storageKey]) {
|
|
@@ -13253,10 +14349,23 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
13253
14349
|
return this._xbrlFormErrors.some((error2) => error2.sectionId === roleId);
|
|
13254
14350
|
}
|
|
13255
14351
|
async _handleErrorFieldClick(conceptId, columnId, sectionId) {
|
|
13256
|
-
var _a, _b, _c, _d, _e, _f, _g;
|
|
13257
|
-
console.log(`🎯 [Error Click] Attempting to focus field: ${conceptId}__${columnId} in section: ${sectionId}`);
|
|
13258
14352
|
this._showErrorPopup = false;
|
|
13259
14353
|
this.requestUpdate();
|
|
14354
|
+
await this._focusFormField(conceptId, columnId, sectionId);
|
|
14355
|
+
}
|
|
14356
|
+
async _handleTypedDimensionFactClick(conceptId, columnId, sectionId) {
|
|
14357
|
+
this._showTypedDimensionWarning = false;
|
|
14358
|
+
this.requestUpdate();
|
|
14359
|
+
await this._focusFormField(conceptId, columnId, sectionId);
|
|
14360
|
+
}
|
|
14361
|
+
/**
|
|
14362
|
+
* Reveals (hidden role/column) and focuses a specific fact's input field, with a red highlight.
|
|
14363
|
+
* Shared by every click-to-field affordance (validation error popup, typed-dimension warning
|
|
14364
|
+
* popup) so their behavior stays identical — same role/column reveal prompts, same highlight.
|
|
14365
|
+
*/
|
|
14366
|
+
async _focusFormField(conceptId, columnId, sectionId) {
|
|
14367
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
14368
|
+
console.log(`🎯 [Focus Field] Attempting to focus field: ${conceptId}__${columnId} in section: ${sectionId}`);
|
|
13260
14369
|
await this.updateComplete;
|
|
13261
14370
|
if (!((_a = this._currentSchema) == null ? void 0 : _a.sections.some((s2) => s2.id === sectionId))) {
|
|
13262
14371
|
const section2 = this._allSections.find((s2) => s2.id === sectionId);
|
|
@@ -13273,6 +14382,7 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
13273
14382
|
this._activeSidePanelRoleId = sectionId;
|
|
13274
14383
|
this.requestUpdate();
|
|
13275
14384
|
await this.updateComplete;
|
|
14385
|
+
this._scrollActiveRoleIntoView();
|
|
13276
14386
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
13277
14387
|
}
|
|
13278
14388
|
const sectionElements = (_c = this.shadowRoot) == null ? void 0 : _c.querySelectorAll("jupiter-form-section");
|
|
@@ -13622,9 +14732,9 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
13622
14732
|
const dimensionParts = [];
|
|
13623
14733
|
const dimensionKeys = [];
|
|
13624
14734
|
for (const dimSelection of request.selectedDimensions) {
|
|
13625
|
-
if (dimSelection.isTyped
|
|
13626
|
-
dimensionParts.push(
|
|
13627
|
-
dimensionKeys.push(`${dimSelection.axisId}|${dimSelection.typedValue}`);
|
|
14735
|
+
if (dimSelection.isTyped) {
|
|
14736
|
+
dimensionParts.push(dimSelection.axisLabel);
|
|
14737
|
+
dimensionKeys.push(`${dimSelection.axisId}|${dimSelection.typedValue || ""}`);
|
|
13628
14738
|
} else if (dimSelection.memberId && dimSelection.memberLabel) {
|
|
13629
14739
|
dimensionParts.push(dimSelection.memberLabel);
|
|
13630
14740
|
dimensionKeys.push(`${dimSelection.axisId}|${dimSelection.memberId}`);
|
|
@@ -13873,6 +14983,69 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
13873
14983
|
bubbles: true
|
|
13874
14984
|
}));
|
|
13875
14985
|
}
|
|
14986
|
+
/**
|
|
14987
|
+
* Validate pre-flight: for every currently visible column with at least one typed dimension,
|
|
14988
|
+
* find columns where a typed-member value hasn't been entered yet but the user has already
|
|
14989
|
+
* entered fact data into that column — i.e. the column is in use but incomplete. Returns one
|
|
14990
|
+
* entry per such fact (a column can produce several, one per concept row with a value).
|
|
14991
|
+
*/
|
|
14992
|
+
_findMissingTypedDimensionFacts() {
|
|
14993
|
+
var _a;
|
|
14994
|
+
const missingFacts = [];
|
|
14995
|
+
if (!this._currentSchema)
|
|
14996
|
+
return missingFacts;
|
|
14997
|
+
for (const section2 of this._currentSchema.sections) {
|
|
14998
|
+
for (const column2 of section2.columns || []) {
|
|
14999
|
+
const typedMembers = (_a = column2.dimensionData) == null ? void 0 : _a.typedMembers;
|
|
15000
|
+
if (!typedMembers || typedMembers.length === 0)
|
|
15001
|
+
continue;
|
|
15002
|
+
const enteredTypedValues = this._typedMemberData[this._scopedColumnKey(section2.id, column2.id)] || {};
|
|
15003
|
+
const hasMissingTypedValue = typedMembers.some((tm) => {
|
|
15004
|
+
var _a2;
|
|
15005
|
+
return !((_a2 = enteredTypedValues[tm.axisId]) == null ? void 0 : _a2.trim());
|
|
15006
|
+
});
|
|
15007
|
+
if (!hasMissingTypedValue)
|
|
15008
|
+
continue;
|
|
15009
|
+
this._collectEnteredFactsForColumn(section2.concepts, column2, section2, missingFacts);
|
|
15010
|
+
}
|
|
15011
|
+
}
|
|
15012
|
+
return missingFacts;
|
|
15013
|
+
}
|
|
15014
|
+
_collectEnteredFactsForColumn(concepts, column2, section2, out) {
|
|
15015
|
+
var _a, _b;
|
|
15016
|
+
for (const concept of concepts) {
|
|
15017
|
+
const value = (_a = this._formData[concept.id]) == null ? void 0 : _a[column2.id];
|
|
15018
|
+
if (value !== void 0 && value !== null && String(value).trim() !== "") {
|
|
15019
|
+
out.push({
|
|
15020
|
+
conceptId: concept.id,
|
|
15021
|
+
conceptLabel: concept.label,
|
|
15022
|
+
columnId: column2.id,
|
|
15023
|
+
columnTitle: column2.title,
|
|
15024
|
+
sectionId: section2.id,
|
|
15025
|
+
sectionTitle: section2.title,
|
|
15026
|
+
value
|
|
15027
|
+
});
|
|
15028
|
+
}
|
|
15029
|
+
if ((_b = concept.children) == null ? void 0 : _b.length) {
|
|
15030
|
+
this._collectEnteredFactsForColumn(concept.children, column2, section2, out);
|
|
15031
|
+
}
|
|
15032
|
+
}
|
|
15033
|
+
}
|
|
15034
|
+
/** Groups the typed-dimension warning's flat fact list by role, in first-seen order, for display. */
|
|
15035
|
+
_groupTypedDimensionWarningFactsByRole() {
|
|
15036
|
+
const groups = [];
|
|
15037
|
+
const groupBySectionId = /* @__PURE__ */ new Map();
|
|
15038
|
+
for (const fact of this._typedDimensionWarningFacts) {
|
|
15039
|
+
let group = groupBySectionId.get(fact.sectionId);
|
|
15040
|
+
if (!group) {
|
|
15041
|
+
group = { sectionId: fact.sectionId, sectionTitle: fact.sectionTitle, facts: [] };
|
|
15042
|
+
groupBySectionId.set(fact.sectionId, group);
|
|
15043
|
+
groups.push(group);
|
|
15044
|
+
}
|
|
15045
|
+
group.facts.push(fact);
|
|
15046
|
+
}
|
|
15047
|
+
return groups;
|
|
15048
|
+
}
|
|
13876
15049
|
_handleSubmit() {
|
|
13877
15050
|
console.log("📝 Form submission started...");
|
|
13878
15051
|
this._submitted = true;
|
|
@@ -13883,6 +15056,25 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
13883
15056
|
this._submitDisabled = false;
|
|
13884
15057
|
return;
|
|
13885
15058
|
}
|
|
15059
|
+
const missingTypedDimensionFacts = this._findMissingTypedDimensionFacts();
|
|
15060
|
+
if (missingTypedDimensionFacts.length > 0) {
|
|
15061
|
+
console.log("⚠️ [Submit] Typed dimension values missing for entered facts:", missingTypedDimensionFacts);
|
|
15062
|
+
this._typedDimensionWarningFacts = missingTypedDimensionFacts;
|
|
15063
|
+
this._showTypedDimensionWarning = true;
|
|
15064
|
+
this._submitDisabled = false;
|
|
15065
|
+
this.requestUpdate();
|
|
15066
|
+
return;
|
|
15067
|
+
}
|
|
15068
|
+
this._continueSubmit();
|
|
15069
|
+
}
|
|
15070
|
+
/**
|
|
15071
|
+
* The original Validate/submit logic (error checks + submission), extracted so "Continue to
|
|
15072
|
+
* validate" on the typed-dimension warning popup can re-enter it directly without re-running
|
|
15073
|
+
* (and re-triggering) the typed-dimension pre-flight in _handleSubmit() above.
|
|
15074
|
+
*/
|
|
15075
|
+
_continueSubmit() {
|
|
15076
|
+
this._submitted = true;
|
|
15077
|
+
this._submitDisabled = true;
|
|
13886
15078
|
console.log(`🔵 [Submit] Checking for errors...`);
|
|
13887
15079
|
console.log(`🔵 [Submit] _xbrlFormErrors.length: ${this._xbrlFormErrors.length}`);
|
|
13888
15080
|
console.log(`🔵 [Submit] _xbrlFormErrors:`, this._xbrlFormErrors);
|
|
@@ -14938,12 +16130,12 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
14938
16130
|
const sectionTitle = (section2 == null ? void 0 : section2.title) || "Unknown";
|
|
14939
16131
|
concepts.forEach((concept) => {
|
|
14940
16132
|
if (sectionTitle === targetRole) {
|
|
14941
|
-
console.
|
|
16133
|
+
console.log(`[DUPLICATE DEBUG] Processing concept ${concept.originalConceptId || concept.id} in target role`);
|
|
14942
16134
|
if (concept.fields) {
|
|
14943
|
-
console.
|
|
16135
|
+
console.log(`[DUPLICATE DEBUG] Concept has ${concept.fields.length} fields`);
|
|
14944
16136
|
concept.fields.forEach((field2, index) => {
|
|
14945
16137
|
var _a;
|
|
14946
|
-
console.
|
|
16138
|
+
console.log(`[DUPLICATE DEBUG] Field ${index}: columnId=${field2.columnId}, current value=${(_a = this._formData[concept.id]) == null ? void 0 : _a[field2.columnId]}`);
|
|
14947
16139
|
});
|
|
14948
16140
|
}
|
|
14949
16141
|
}
|
|
@@ -14967,7 +16159,7 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
14967
16159
|
const masterValue = (_a = this._effectiveMasterData) == null ? void 0 : _a[baseConceptId];
|
|
14968
16160
|
if (masterValue !== void 0 && masterValue !== null && masterValue !== "") {
|
|
14969
16161
|
fieldValue = masterValue;
|
|
14970
|
-
console.
|
|
16162
|
+
console.log(` 📦 [Submission] Using masterData for: ${baseConceptId} [${field2.columnId}] = ${JSON.stringify(fieldValue)}`);
|
|
14971
16163
|
}
|
|
14972
16164
|
}
|
|
14973
16165
|
if (fieldValue !== void 0 && fieldValue !== null && fieldValue !== "") {
|
|
@@ -15014,17 +16206,20 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
15014
16206
|
} else {
|
|
15015
16207
|
console.log(`🔍 [DynamicForm] No dimension data found for field column ${field2.columnId}. Column type: ${column2 == null ? void 0 : column2.type}, has dimensionData: ${!!(column2 == null ? void 0 : column2.dimensionData)}`);
|
|
15016
16208
|
}
|
|
15017
|
-
|
|
16209
|
+
const headerColumnKey = section2 ? this._scopedColumnKey(section2.id, field2.columnId) : field2.columnId;
|
|
16210
|
+
const scopedTypedMemberData = this._typedMemberData[headerColumnKey];
|
|
16211
|
+
const legacyTypedMemberData = this._typedMemberData[field2.columnId];
|
|
16212
|
+
if (scopedTypedMemberData || legacyTypedMemberData) {
|
|
15018
16213
|
const applicableTypedDimensions = this._getApplicableTypedDimensions(concept, section2);
|
|
15019
16214
|
console.log(`🔍 [DynamicForm] Concept ${concept.id} applicable typed dimensions:`, applicableTypedDimensions);
|
|
15020
16215
|
if (applicableTypedDimensions.length > 0) {
|
|
15021
16216
|
const filteredTypedMembers = {};
|
|
15022
|
-
const allTypedMemberData = this._typedMemberData[field2.columnId];
|
|
15023
16217
|
applicableTypedDimensions.forEach((dimensionId) => {
|
|
15024
|
-
|
|
16218
|
+
const value = (scopedTypedMemberData == null ? void 0 : scopedTypedMemberData[dimensionId]) ?? (legacyTypedMemberData == null ? void 0 : legacyTypedMemberData[dimensionId]);
|
|
16219
|
+
if (value) {
|
|
15025
16220
|
const memberName = this._getTypedMemberNameForAxis(dimensionId, section2);
|
|
15026
16221
|
filteredTypedMembers[dimensionId] = {
|
|
15027
|
-
value
|
|
16222
|
+
value,
|
|
15028
16223
|
memberName: memberName || "Unknown"
|
|
15029
16224
|
};
|
|
15030
16225
|
}
|
|
@@ -15070,7 +16265,7 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
15070
16265
|
}
|
|
15071
16266
|
}
|
|
15072
16267
|
if (sectionTitle === targetRole) {
|
|
15073
|
-
console.
|
|
16268
|
+
console.log(`[DUPLICATE DEBUG] Final submission entry:`, submissionEntry);
|
|
15074
16269
|
}
|
|
15075
16270
|
submissionData.push(submissionEntry);
|
|
15076
16271
|
}
|
|
@@ -15261,45 +16456,45 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
15261
16456
|
console.warn(`⚠️ Section not found for roleId: ${roleId}`);
|
|
15262
16457
|
return;
|
|
15263
16458
|
}
|
|
15264
|
-
console.
|
|
16459
|
+
console.log(`
|
|
15265
16460
|
========================================`);
|
|
15266
|
-
console.
|
|
15267
|
-
console.
|
|
16461
|
+
console.log(`🔍 ANALYZING ROLE: "${section2.title}"`);
|
|
16462
|
+
console.log(`========================================`);
|
|
15268
16463
|
if (this.masterData) {
|
|
15269
|
-
console.
|
|
16464
|
+
console.log(`📦 masterData keys:`, Object.keys(this.masterData));
|
|
15270
16465
|
} else {
|
|
15271
|
-
console.
|
|
16466
|
+
console.log(`📦 masterData: NOT PROVIDED`);
|
|
15272
16467
|
}
|
|
15273
16468
|
const allFields = [];
|
|
15274
16469
|
this._collectAllFields(section2.concepts || [], new Set((section2.columns || []).map((col) => col.id)), section2.columns || [], allFields);
|
|
15275
16470
|
const hasErrors = this._roleHasErrors(roleId);
|
|
15276
16471
|
const emptyCount = allFields.filter((f2) => f2.isEmpty).length;
|
|
15277
16472
|
const filledCount = allFields.filter((f2) => !f2.isEmpty).length;
|
|
15278
|
-
console.
|
|
16473
|
+
console.log(`
|
|
15279
16474
|
📈 SUMMARY:`);
|
|
15280
|
-
console.
|
|
15281
|
-
console.
|
|
15282
|
-
console.
|
|
15283
|
-
console.
|
|
16475
|
+
console.log(` Total Fields: ${allFields.length}`);
|
|
16476
|
+
console.log(` Filled: ${filledCount}`);
|
|
16477
|
+
console.log(` Empty: ${emptyCount}`);
|
|
16478
|
+
console.log(` Has Errors: ${hasErrors}`);
|
|
15284
16479
|
let result;
|
|
15285
16480
|
if (hasErrors) {
|
|
15286
16481
|
result = "INVALID";
|
|
15287
|
-
console.
|
|
16482
|
+
console.log(`
|
|
15288
16483
|
🔴 RESULT: INVALID (contains validation errors)`);
|
|
15289
16484
|
} else if (filledCount === 0) {
|
|
15290
16485
|
result = "UNTOUCHED";
|
|
15291
|
-
console.
|
|
16486
|
+
console.log(`
|
|
15292
16487
|
⬜ RESULT: UNTOUCHED (no data entered)`);
|
|
15293
16488
|
} else if (emptyCount > 0) {
|
|
15294
16489
|
result = "INCOMPLETE";
|
|
15295
|
-
console.
|
|
16490
|
+
console.log(`
|
|
15296
16491
|
🟠 RESULT: INCOMPLETE (${emptyCount} field(s) blank)`);
|
|
15297
16492
|
} else {
|
|
15298
16493
|
result = "COMPLETE";
|
|
15299
|
-
console.
|
|
16494
|
+
console.log(`
|
|
15300
16495
|
🟢 RESULT: COMPLETE (all fields filled)`);
|
|
15301
16496
|
}
|
|
15302
|
-
console.
|
|
16497
|
+
console.log(`========================================
|
|
15303
16498
|
`);
|
|
15304
16499
|
if (result === "INVALID")
|
|
15305
16500
|
;
|
|
@@ -15335,7 +16530,7 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
15335
16530
|
const masterValue = (_c = this._effectiveMasterData) == null ? void 0 : _c[fieldConceptId];
|
|
15336
16531
|
if ((value === null || value === void 0 || typeof value === "string" && value.trim() === "") && masterValue !== void 0) {
|
|
15337
16532
|
value = masterValue;
|
|
15338
|
-
console.
|
|
16533
|
+
console.log(` 📦 Using masterData value for: ${fieldConceptId} [${field2.columnId}] = ${JSON.stringify(value)}`);
|
|
15339
16534
|
}
|
|
15340
16535
|
let isEmpty = value === null || value === void 0 || typeof value === "string" && value.trim() === "";
|
|
15341
16536
|
if (isEmpty && concept.facts && concept.facts.length > 0) {
|
|
@@ -15380,6 +16575,7 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
15380
16575
|
searchInput.value = "";
|
|
15381
16576
|
searchInput.focus();
|
|
15382
16577
|
}
|
|
16578
|
+
this.updateComplete.then(() => this._scrollActiveRoleIntoView());
|
|
15383
16579
|
}
|
|
15384
16580
|
_toggleSidePanelCollapse() {
|
|
15385
16581
|
this._sidePanelCollapsed = !this._sidePanelCollapsed;
|
|
@@ -15835,8 +17031,20 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
15835
17031
|
return null;
|
|
15836
17032
|
}
|
|
15837
17033
|
_renderAccordionLayout(schema, config, showValidationSummary, errorCount) {
|
|
17034
|
+
const conceptMatchIds = this._getConceptSearchMatchIds(this._conceptSearchText);
|
|
17035
|
+
const hasConceptSearch = this._conceptSearchText.trim().length > 0;
|
|
17036
|
+
let filteredSections = schema.sections;
|
|
17037
|
+
if (this.mode === "readonly" || this._showFactsOnly) {
|
|
17038
|
+
filteredSections = filteredSections.filter((s2) => !this._isSectionBlank(s2));
|
|
17039
|
+
}
|
|
17040
|
+
if (hasConceptSearch) {
|
|
17041
|
+
filteredSections = filteredSections.filter((s2) => this._isSectionConceptSearchMatch(s2, conceptMatchIds));
|
|
17042
|
+
}
|
|
15838
17043
|
return html`
|
|
15839
17044
|
<div class="form-sections">
|
|
17045
|
+
<!-- JDF-101: names the active concept/facts-only filter so a narrowed view isn't mistaken for missing data -->
|
|
17046
|
+
${this.mode !== "admin" ? this._renderActiveFilterBanner(filteredSections) : ""}
|
|
17047
|
+
|
|
15840
17048
|
<!-- Validation Summary -->
|
|
15841
17049
|
${showValidationSummary ? html`
|
|
15842
17050
|
<div class="validation-summary ${errorCount === 0 ? "warnings-only" : ""}">
|
|
@@ -15861,15 +17069,7 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
15861
17069
|
<p>${I18n.t("filter.roles")}: ${this._allSections.length}</p>
|
|
15862
17070
|
</div>
|
|
15863
17071
|
` : (() => {
|
|
15864
|
-
const
|
|
15865
|
-
const hasConceptSearch = this._conceptSearchText.trim().length > 0;
|
|
15866
|
-
let sections = schema.sections;
|
|
15867
|
-
if (this.mode === "readonly" || this._showFactsOnly) {
|
|
15868
|
-
sections = sections.filter((s2) => !this._isSectionBlank(s2));
|
|
15869
|
-
}
|
|
15870
|
-
if (hasConceptSearch) {
|
|
15871
|
-
sections = sections.filter((s2) => this._isSectionConceptSearchMatch(s2, conceptMatchIds));
|
|
15872
|
-
}
|
|
17072
|
+
const sections = filteredSections;
|
|
15873
17073
|
return sections.map((section2, index) => {
|
|
15874
17074
|
var _a;
|
|
15875
17075
|
return html`
|
|
@@ -15889,6 +17089,7 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
15889
17089
|
.disabled="${this.disabled || this.readonly}"
|
|
15890
17090
|
.collapsible="${config.collapsibleSections !== false}"
|
|
15891
17091
|
.locale="${config.locale || "en-US"}"
|
|
17092
|
+
.numberLocale="${this._effectiveNumberLocale()}"
|
|
15892
17093
|
.language="${this.language}"
|
|
15893
17094
|
.isFirstSection="${index === 0}"
|
|
15894
17095
|
.availableDimensions="${this._getAvailableDimensionsForSection(section2.id)}"
|
|
@@ -15896,6 +17097,7 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
15896
17097
|
.showFactsOnly="${this._showFactsOnly}"
|
|
15897
17098
|
.conceptMatchIds="${hasConceptSearch ? conceptMatchIds : void 0}"
|
|
15898
17099
|
.masterData="${this._effectiveMasterData}"
|
|
17100
|
+
.prefilledTooltip="${this.prefilledTooltip}"
|
|
15899
17101
|
.periodStartDate="${this.periodStartDate}"
|
|
15900
17102
|
.periodEndDate="${this.periodEndDate}"
|
|
15901
17103
|
.calculationEnabled="${this.calculationEnabled}"
|
|
@@ -15918,19 +17120,53 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
15918
17120
|
</div>
|
|
15919
17121
|
`;
|
|
15920
17122
|
}
|
|
15921
|
-
|
|
15922
|
-
|
|
17123
|
+
// Sections visible in the side panel before the search box's own filter is applied — shared
|
|
17124
|
+
// by the render path and the Previous/Next navigation so both agree on "what's in the list".
|
|
17125
|
+
// Also reused by the footer's role-count badge (`_footerVisibleRoleCount`, JDF-101 item 3),
|
|
17126
|
+
// since the readonly/facts-only/concept-search visibility rule computed here is the same one
|
|
17127
|
+
// `_renderAccordionLayout` recomputes inline for its own copy of the active-filter banner.
|
|
17128
|
+
_getSidePanelVisibleSections(schema) {
|
|
15923
17129
|
const conceptMatchIds = this._getConceptSearchMatchIds(this._conceptSearchText);
|
|
15924
17130
|
const hasConceptSearch = this._conceptSearchText.trim().length > 0;
|
|
15925
17131
|
let visibleSections = this.mode === "readonly" || this._showFactsOnly ? schema.sections.filter((s2) => !this._isSectionBlank(s2)) : schema.sections;
|
|
15926
17132
|
if (hasConceptSearch) {
|
|
15927
17133
|
visibleSections = visibleSections.filter((s2) => this._isSectionConceptSearchMatch(s2, conceptMatchIds));
|
|
15928
17134
|
}
|
|
17135
|
+
return visibleSections;
|
|
17136
|
+
}
|
|
17137
|
+
// Moves the active side-panel role to the previous/next entry in the currently visible list
|
|
17138
|
+
// (JDF-099), scrolls the content back to the top, and brings the new role into view on the left.
|
|
17139
|
+
_handleSidePanelPrevNext(direction) {
|
|
17140
|
+
const schema = this._currentSchema;
|
|
17141
|
+
if (!schema)
|
|
17142
|
+
return;
|
|
17143
|
+
const filteredSections = this._filterSidePanelSections(this._getSidePanelVisibleSections(schema));
|
|
17144
|
+
const activeIndex = filteredSections.findIndex((s2) => s2.id === this._activeSidePanelRoleId);
|
|
17145
|
+
if (activeIndex === -1)
|
|
17146
|
+
return;
|
|
17147
|
+
const targetSection = filteredSections[direction === "prev" ? activeIndex - 1 : activeIndex + 1];
|
|
17148
|
+
if (!targetSection)
|
|
17149
|
+
return;
|
|
17150
|
+
this._handleSidePanelRoleClick(targetSection.id);
|
|
17151
|
+
this.updateComplete.then(() => {
|
|
17152
|
+
var _a;
|
|
17153
|
+
const contentEl = (_a = this.shadowRoot) == null ? void 0 : _a.querySelector(".side-panel-content");
|
|
17154
|
+
contentEl == null ? void 0 : contentEl.scrollTo({ top: 0, behavior: "smooth" });
|
|
17155
|
+
this._scrollActiveRoleIntoView();
|
|
17156
|
+
});
|
|
17157
|
+
}
|
|
17158
|
+
_renderSidePanelLayout(schema, config, showValidationSummary, errorCount) {
|
|
17159
|
+
var _a;
|
|
17160
|
+
const conceptMatchIds = this._getConceptSearchMatchIds(this._conceptSearchText);
|
|
17161
|
+
const hasConceptSearch = this._conceptSearchText.trim().length > 0;
|
|
17162
|
+
const visibleSections = this._getSidePanelVisibleSections(schema);
|
|
15929
17163
|
const filteredSections = this._filterSidePanelSections(visibleSections);
|
|
15930
|
-
if (!this._activeSidePanelRoleId || !
|
|
17164
|
+
if (!this._activeSidePanelRoleId || !visibleSections.find((s2) => s2.id === this._activeSidePanelRoleId)) {
|
|
15931
17165
|
this._activeSidePanelRoleId = filteredSections.length > 0 ? filteredSections[0].id : null;
|
|
15932
17166
|
}
|
|
15933
17167
|
const activeSection = visibleSections.find((s2) => s2.id === this._activeSidePanelRoleId);
|
|
17168
|
+
const activeSectionIndex = activeSection ? filteredSections.findIndex((s2) => s2.id === activeSection.id) : -1;
|
|
17169
|
+
const nextSection = activeSectionIndex >= 0 ? filteredSections[activeSectionIndex + 1] : void 0;
|
|
15934
17170
|
return html`
|
|
15935
17171
|
<div class="side-panel-layout">
|
|
15936
17172
|
<!-- Left: Roles List (filtered) -->
|
|
@@ -15966,7 +17202,7 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
15966
17202
|
</svg>
|
|
15967
17203
|
`}
|
|
15968
17204
|
</div>
|
|
15969
|
-
|
|
17205
|
+
|
|
15970
17206
|
<!-- Search Results Info -->
|
|
15971
17207
|
${this._sidePanelSearchQuery.trim() && filteredSections.length !== visibleSections.length ? html`
|
|
15972
17208
|
<div class="side-panel-search-results-info">
|
|
@@ -16003,6 +17239,9 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
16003
17239
|
<!-- Right: Active Section Content -->
|
|
16004
17240
|
<div class="side-panel-content">
|
|
16005
17241
|
<div class="form-sections">
|
|
17242
|
+
<!-- JDF-101: names the active concept/facts-only filter so a narrowed view isn't mistaken for missing data -->
|
|
17243
|
+
${this.mode !== "admin" ? this._renderActiveFilterBanner(activeSection ? [activeSection] : []) : ""}
|
|
17244
|
+
|
|
16006
17245
|
<!-- Validation Summary -->
|
|
16007
17246
|
${showValidationSummary ? html`
|
|
16008
17247
|
<div class="validation-summary ${errorCount === 0 ? "warnings-only" : ""}">
|
|
@@ -16021,6 +17260,25 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
16021
17260
|
${this._renderAdminModeContent(activeSection)}
|
|
16022
17261
|
</div>
|
|
16023
17262
|
` : activeSection ? html`
|
|
17263
|
+
<div
|
|
17264
|
+
class="side-panel-content-header"
|
|
17265
|
+
role="button"
|
|
17266
|
+
tabindex="0"
|
|
17267
|
+
title="${I18n.t("section.locateInList")}"
|
|
17268
|
+
@click="${() => this._scrollActiveRoleIntoView()}"
|
|
17269
|
+
@keydown="${(e2) => {
|
|
17270
|
+
if (e2.key === "Enter" || e2.key === " ") {
|
|
17271
|
+
e2.preventDefault();
|
|
17272
|
+
this._scrollActiveRoleIntoView();
|
|
17273
|
+
}
|
|
17274
|
+
}}"
|
|
17275
|
+
>
|
|
17276
|
+
<div class="side-panel-content-position">${I18n.t("section.position", { current: activeSectionIndex + 1, total: filteredSections.length })}</div>
|
|
17277
|
+
<h2 class="side-panel-content-title">${activeSection.title}</h2>
|
|
17278
|
+
${this._sectionHasMonetaryConcepts(activeSection.concepts) ? html`
|
|
17279
|
+
<div class="side-panel-content-amount-scale">${this._amountScaleCaption()}</div>
|
|
17280
|
+
` : ""}
|
|
17281
|
+
</div>
|
|
16024
17282
|
<jupiter-form-section
|
|
16025
17283
|
section-id="${activeSection.id}"
|
|
16026
17284
|
.section="${activeSection}"
|
|
@@ -16038,6 +17296,7 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
16038
17296
|
.collapsible="${false}"
|
|
16039
17297
|
.hideHeader="${true}"
|
|
16040
17298
|
.locale="${config.locale || "en-US"}"
|
|
17299
|
+
.numberLocale="${this._effectiveNumberLocale()}"
|
|
16041
17300
|
.language="${this.language}"
|
|
16042
17301
|
.isFirstSection="${true}"
|
|
16043
17302
|
.availableDimensions="${this._getAvailableDimensionsForSection(activeSection.id)}"
|
|
@@ -16045,6 +17304,7 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
16045
17304
|
.showFactsOnly="${this._showFactsOnly}"
|
|
16046
17305
|
.conceptMatchIds="${hasConceptSearch ? conceptMatchIds : void 0}"
|
|
16047
17306
|
.masterData="${this._effectiveMasterData}"
|
|
17307
|
+
.prefilledTooltip="${this.prefilledTooltip}"
|
|
16048
17308
|
.periodStartDate="${this.periodStartDate}"
|
|
16049
17309
|
.periodEndDate="${this.periodEndDate}"
|
|
16050
17310
|
.calculationEnabled="${this.calculationEnabled}"
|
|
@@ -16069,6 +17329,22 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
16069
17329
|
>
|
|
16070
17330
|
<label for="role-complete-${activeSection.id}">${I18n.t("form.markAsComplete")}</label>
|
|
16071
17331
|
</div>
|
|
17332
|
+
<div class="side-panel-nav-row">
|
|
17333
|
+
${activeSectionIndex > 0 ? html`
|
|
17334
|
+
<button
|
|
17335
|
+
type="button"
|
|
17336
|
+
class="side-panel-nav-btn side-panel-nav-prev"
|
|
17337
|
+
@click="${() => this._handleSidePanelPrevNext("prev")}"
|
|
17338
|
+
>${I18n.t("section.previous")}</button>
|
|
17339
|
+
` : html`<span></span>`}
|
|
17340
|
+
${nextSection ? html`
|
|
17341
|
+
<button
|
|
17342
|
+
type="button"
|
|
17343
|
+
class="side-panel-nav-btn side-panel-nav-next"
|
|
17344
|
+
@click="${() => this._handleSidePanelPrevNext("next")}"
|
|
17345
|
+
>${I18n.t("section.next", { title: nextSection.title })}</button>
|
|
17346
|
+
` : ""}
|
|
17347
|
+
</div>
|
|
16072
17348
|
` : html`
|
|
16073
17349
|
<div class="no-roles-message">
|
|
16074
17350
|
<h3>${I18n.t("form.noRoleSelected")}</h3>
|
|
@@ -16107,8 +17383,23 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
16107
17383
|
} else {
|
|
16108
17384
|
this._validationStatus = status;
|
|
16109
17385
|
}
|
|
17386
|
+
this._syncValidationDotAnimation();
|
|
16110
17387
|
this.requestUpdate();
|
|
16111
17388
|
}
|
|
17389
|
+
/** Starts/stops the Validate button's dot-cycling animation to match _validationStatus. */
|
|
17390
|
+
_syncValidationDotAnimation() {
|
|
17391
|
+
if (this._validationStatus === "inProgress") {
|
|
17392
|
+
if (this._validationDotIntervalId === null) {
|
|
17393
|
+
this._validationDotCount = 1;
|
|
17394
|
+
this._validationDotIntervalId = setInterval(() => {
|
|
17395
|
+
this._validationDotCount = this._validationDotCount % 3 + 1;
|
|
17396
|
+
}, 500);
|
|
17397
|
+
}
|
|
17398
|
+
} else if (this._validationDotIntervalId !== null) {
|
|
17399
|
+
clearInterval(this._validationDotIntervalId);
|
|
17400
|
+
this._validationDotIntervalId = null;
|
|
17401
|
+
}
|
|
17402
|
+
}
|
|
16112
17403
|
reset() {
|
|
16113
17404
|
this._handleReset();
|
|
16114
17405
|
}
|
|
@@ -16348,7 +17639,7 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
16348
17639
|
console.log(`[scrollToConcept] Section "${section2.id}" has concept "${found.id}" (no dims, taking first)`);
|
|
16349
17640
|
return { concept: found, strong: true, inPeriod: inPeriod(cols.map((c2) => c2.id)) };
|
|
16350
17641
|
}
|
|
16351
|
-
async scrollToConcept(conceptName, dimensions, match, columnId, exactConceptId, role) {
|
|
17642
|
+
async scrollToConcept(conceptName, dimensions, match, columnId, exactConceptId, role, options) {
|
|
16352
17643
|
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o;
|
|
16353
17644
|
console.log(`[scrollToConcept] ▶ START conceptName=${conceptName} value=${match == null ? void 0 : match.value} period=${JSON.stringify(match == null ? void 0 : match.period)} dims=${JSON.stringify(dimensions)} columnId=${columnId} exactConceptId=${exactConceptId} role=${role}`);
|
|
16354
17645
|
const hasValueMatch = (match == null ? void 0 : match.value) !== void 0 && (match == null ? void 0 : match.value) !== null;
|
|
@@ -16424,9 +17715,14 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
16424
17715
|
}
|
|
16425
17716
|
const periodPinned = !!period && !!targetColumnId && this._periodMatches(this._cellPeriod(targetSection, targetConcept, targetColumnId), period);
|
|
16426
17717
|
console.log(`[scrollToConcept] targetColumnId=${targetColumnId} hasValueMatch=${hasValueMatch} targetValue=${targetValue} periodPinned=${periodPinned}`);
|
|
16427
|
-
|
|
17718
|
+
if ((options == null ? void 0 : options.filterToConcept) !== false) {
|
|
17719
|
+
this._conceptSearchText = conceptName.includes(":") ? conceptName.split(":").pop() : conceptName;
|
|
17720
|
+
}
|
|
16428
17721
|
this.requestUpdate();
|
|
16429
17722
|
await this.updateComplete;
|
|
17723
|
+
if (this.display === "sidePanel") {
|
|
17724
|
+
this._scrollActiveRoleIntoView();
|
|
17725
|
+
}
|
|
16430
17726
|
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
16431
17727
|
const sectionElements = (_d = this.shadowRoot) == null ? void 0 : _d.querySelectorAll("jupiter-form-section");
|
|
16432
17728
|
console.log(`[scrollToConcept] DOM: found ${(sectionElements == null ? void 0 : sectionElements.length) ?? 0} jupiter-form-section elements`);
|
|
@@ -16637,7 +17933,14 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
16637
17933
|
<path d="M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z"/>
|
|
16638
17934
|
</svg>
|
|
16639
17935
|
${this._selectedRoleIds.length === 0 ? I18n.t("filter.selectRoles") : I18n.t("filter.filterRoles")}
|
|
16640
|
-
<span class="roles-count"
|
|
17936
|
+
<span class="roles-count">
|
|
17937
|
+
${this._hasActiveAdvancedFilters ? html`
|
|
17938
|
+
<svg class="roles-count-filter-icon" viewBox="0 0 24 24">
|
|
17939
|
+
<path d="M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z"/>
|
|
17940
|
+
</svg>
|
|
17941
|
+
` : ""}
|
|
17942
|
+
${this._footerVisibleRoleCount}/${this._allSections.length}
|
|
17943
|
+
</span>
|
|
16641
17944
|
</button>
|
|
16642
17945
|
${this._hasActiveAdvancedFilters ? html`
|
|
16643
17946
|
<button
|
|
@@ -16662,16 +17965,6 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
16662
17965
|
|
|
16663
17966
|
|
|
16664
17967
|
<!-- Validation state buttons -->
|
|
16665
|
-
${this._validationStatus === "inProgress" ? html`
|
|
16666
|
-
<button
|
|
16667
|
-
class="btn-preview"
|
|
16668
|
-
@click="${() => this.dispatchEvent(new CustomEvent("showPreview", { bubbles: true, composed: true }))}"
|
|
16669
|
-
?disabled="${this.disabled || this.readonly}"
|
|
16670
|
-
>
|
|
16671
|
-
${I18n.t("form.download")}
|
|
16672
|
-
</button>
|
|
16673
|
-
` : ""}
|
|
16674
|
-
|
|
16675
17968
|
${this._validationStatus === "complete" || this.showLastValidationResultBtn ? html`
|
|
16676
17969
|
<button
|
|
16677
17970
|
class="btn-secondary btn-last-results"
|
|
@@ -16693,11 +17986,11 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
16693
17986
|
` : ""}
|
|
16694
17987
|
|
|
16695
17988
|
<button
|
|
16696
|
-
class="btn-primary"
|
|
16697
|
-
@click="${this._handleSubmit}"
|
|
16698
|
-
?disabled="${this.disabled || this.readonly || this._submitDisabled
|
|
17989
|
+
class="btn-primary ${this._validationStatus === "inProgress" ? "btn-primary--validating" : ""}"
|
|
17990
|
+
@click="${this._validationStatus === "inProgress" ? () => this.dispatchEvent(new CustomEvent("resume-validation-progress", { bubbles: true, composed: true })) : this._handleSubmit}"
|
|
17991
|
+
?disabled="${this.disabled || this.readonly || this._submitDisabled}"
|
|
16699
17992
|
>
|
|
16700
|
-
${this.submitButtonLabel || (this.mode === "admin" ? I18n.t("form.save") : I18n.t("form.submit"))}
|
|
17993
|
+
${this._validationStatus === "inProgress" ? `${I18n.t("form.validationInProgress")}${".".repeat(this._validationDotCount)}` : this.submitButtonLabel || (this.mode === "admin" ? I18n.t("form.save") : I18n.t("form.submit"))}
|
|
16701
17994
|
</button>
|
|
16702
17995
|
|
|
16703
17996
|
</div>
|
|
@@ -16799,7 +18092,61 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
16799
18092
|
</div>
|
|
16800
18093
|
</div>
|
|
16801
18094
|
` : ""}
|
|
16802
|
-
|
|
18095
|
+
|
|
18096
|
+
<!-- Typed Dimension Warning Popup -->
|
|
18097
|
+
${this._showTypedDimensionWarning ? html`
|
|
18098
|
+
<div class="error-popup-overlay" @click="${() => {
|
|
18099
|
+
this._showTypedDimensionWarning = false;
|
|
18100
|
+
this.requestUpdate();
|
|
18101
|
+
}}">
|
|
18102
|
+
<div class="error-popup" @click="${(e2) => e2.stopPropagation()}">
|
|
18103
|
+
<div class="error-popup-header">
|
|
18104
|
+
<h3>⚠️ ${I18n.t("typedDimensionWarning.title")}</h3>
|
|
18105
|
+
<button class="error-popup-close" @click="${() => {
|
|
18106
|
+
this._showTypedDimensionWarning = false;
|
|
18107
|
+
this.requestUpdate();
|
|
18108
|
+
}}">×</button>
|
|
18109
|
+
</div>
|
|
18110
|
+
<div class="error-popup-content">
|
|
18111
|
+
<p>${I18n.t("typedDimensionWarning.message")}</p>
|
|
18112
|
+
<ul class="error-list">
|
|
18113
|
+
${this._groupTypedDimensionWarningFactsByRole().map((group) => html`
|
|
18114
|
+
<li>
|
|
18115
|
+
<strong>${I18n.t("typedDimensionWarning.role")}</strong> ${group.sectionTitle}
|
|
18116
|
+
<ul>
|
|
18117
|
+
${group.facts.map((fact) => html`
|
|
18118
|
+
<li>
|
|
18119
|
+
<strong>${I18n.t("typedDimensionWarning.field")}</strong>
|
|
18120
|
+
<a
|
|
18121
|
+
href="javascript:void(0)"
|
|
18122
|
+
class="error-field-link"
|
|
18123
|
+
@click="${() => this._handleTypedDimensionFactClick(fact.conceptId, fact.columnId, fact.sectionId)}"
|
|
18124
|
+
title="${I18n.t("typedDimensionWarning.clickToFocus")}"
|
|
18125
|
+
>
|
|
18126
|
+
${fact.conceptLabel} (${fact.columnTitle})
|
|
18127
|
+
</a>
|
|
18128
|
+
— <strong>${I18n.t("typedDimensionWarning.value")}</strong> ${fact.value}
|
|
18129
|
+
</li>
|
|
18130
|
+
`)}
|
|
18131
|
+
</ul>
|
|
18132
|
+
</li>
|
|
18133
|
+
`)}
|
|
18134
|
+
</ul>
|
|
18135
|
+
</div>
|
|
18136
|
+
<div class="error-popup-footer">
|
|
18137
|
+
<button class="btn-secondary" @click="${() => {
|
|
18138
|
+
this._showTypedDimensionWarning = false;
|
|
18139
|
+
this.requestUpdate();
|
|
18140
|
+
}}">${I18n.t("typedDimensionWarning.cancel")}</button>
|
|
18141
|
+
<button class="btn-primary" @click="${() => {
|
|
18142
|
+
this._showTypedDimensionWarning = false;
|
|
18143
|
+
this._continueSubmit();
|
|
18144
|
+
}}">${I18n.t("typedDimensionWarning.continue")}</button>
|
|
18145
|
+
</div>
|
|
18146
|
+
</div>
|
|
18147
|
+
</div>
|
|
18148
|
+
` : ""}
|
|
18149
|
+
|
|
16803
18150
|
<!-- Role Context Menu -->
|
|
16804
18151
|
${this._showRoleContextMenu ? html`
|
|
16805
18152
|
<div
|
|
@@ -16918,7 +18265,45 @@ JupiterDynamicForm.styles = css`
|
|
|
16918
18265
|
color: var(--jupiter-warning-color, #ff9800);
|
|
16919
18266
|
}
|
|
16920
18267
|
|
|
16921
|
-
|
|
18268
|
+
/* JDF-101: banner naming an active concept/facts-only filter — informational (blue), distinct
|
|
18269
|
+
from the error/warning red-amber palette above, since a filter isn't a problem to fix. */
|
|
18270
|
+
.active-filter-banner {
|
|
18271
|
+
display: flex;
|
|
18272
|
+
align-items: center;
|
|
18273
|
+
justify-content: space-between;
|
|
18274
|
+
gap: 12px;
|
|
18275
|
+
margin-bottom: 16px;
|
|
18276
|
+
padding: 10px 16px;
|
|
18277
|
+
border-radius: 4px;
|
|
18278
|
+
background: var(--jupiter-info-background, #e3f2fd);
|
|
18279
|
+
border: 1px solid var(--jupiter-primary-color, #1976d2);
|
|
18280
|
+
color: var(--jupiter-primary-color, #1976d2);
|
|
18281
|
+
font-size: 14px;
|
|
18282
|
+
}
|
|
18283
|
+
|
|
18284
|
+
.active-filter-banner-text {
|
|
18285
|
+
font-weight: 500;
|
|
18286
|
+
}
|
|
18287
|
+
|
|
18288
|
+
.active-filter-banner-clear {
|
|
18289
|
+
flex-shrink: 0;
|
|
18290
|
+
background: none;
|
|
18291
|
+
border: 1px solid var(--jupiter-primary-color, #1976d2);
|
|
18292
|
+
border-radius: 4px;
|
|
18293
|
+
padding: 6px 12px;
|
|
18294
|
+
font-size: 13px;
|
|
18295
|
+
font-weight: 600;
|
|
18296
|
+
color: var(--jupiter-primary-color, #1976d2);
|
|
18297
|
+
cursor: pointer;
|
|
18298
|
+
}
|
|
18299
|
+
|
|
18300
|
+
.active-filter-banner-clear:hover {
|
|
18301
|
+
background: var(--jupiter-primary-color, #1976d2);
|
|
18302
|
+
color: #fff;
|
|
18303
|
+
}
|
|
18304
|
+
|
|
18305
|
+
.form-actions button,
|
|
18306
|
+
.error-popup-footer button {
|
|
16922
18307
|
padding: 10px 20px;
|
|
16923
18308
|
border: none;
|
|
16924
18309
|
border-radius: 4px;
|
|
@@ -16975,12 +18360,14 @@ JupiterDynamicForm.styles = css`
|
|
|
16975
18360
|
background: var(--jupiter-validate-color-dark, #2e7d32);
|
|
16976
18361
|
}
|
|
16977
18362
|
|
|
16978
|
-
.btn-
|
|
16979
|
-
|
|
16980
|
-
|
|
18363
|
+
.btn-primary--validating {
|
|
18364
|
+
opacity: 0.85;
|
|
18365
|
+
animation: jupiter-validating-pulse 1.5s ease-in-out infinite;
|
|
16981
18366
|
}
|
|
16982
|
-
|
|
16983
|
-
|
|
18367
|
+
|
|
18368
|
+
@keyframes jupiter-validating-pulse {
|
|
18369
|
+
0%, 100% { opacity: 0.85; }
|
|
18370
|
+
50% { opacity: 1; }
|
|
16984
18371
|
}
|
|
16985
18372
|
|
|
16986
18373
|
|
|
@@ -17062,6 +18449,7 @@ JupiterDynamicForm.styles = css`
|
|
|
17062
18449
|
.roles-count {
|
|
17063
18450
|
display: inline-flex;
|
|
17064
18451
|
align-items: center;
|
|
18452
|
+
gap: 3px;
|
|
17065
18453
|
background: var(--primaryTextColor, var(--jupiter-primary-color, #1976d2));
|
|
17066
18454
|
color: var(--headerBgColor, white) !important;
|
|
17067
18455
|
border-radius: 10px;
|
|
@@ -17073,6 +18461,14 @@ JupiterDynamicForm.styles = css`
|
|
|
17073
18461
|
justify-content: center;
|
|
17074
18462
|
}
|
|
17075
18463
|
|
|
18464
|
+
/* JDF-101 item 3: marks the badge as counting the advanced-filter-narrowed roles, not the
|
|
18465
|
+
Filter Roles selection, so it doesn't read as a plain (and here, smaller) role count. */
|
|
18466
|
+
.roles-count-filter-icon {
|
|
18467
|
+
width: 10px;
|
|
18468
|
+
height: 10px;
|
|
18469
|
+
fill: currentColor;
|
|
18470
|
+
}
|
|
18471
|
+
|
|
17076
18472
|
.no-roles-message {
|
|
17077
18473
|
padding: 40px;
|
|
17078
18474
|
text-align: center;
|
|
@@ -17319,6 +18715,70 @@ JupiterDynamicForm.styles = css`
|
|
|
17319
18715
|
background: var(--bg-color-2, var(--jupiter-background, #fff));
|
|
17320
18716
|
}
|
|
17321
18717
|
|
|
18718
|
+
.side-panel-content-header {
|
|
18719
|
+
padding: 20px 24px 12px 24px;
|
|
18720
|
+
cursor: pointer;
|
|
18721
|
+
border-radius: 4px;
|
|
18722
|
+
}
|
|
18723
|
+
|
|
18724
|
+
.side-panel-content-header:hover {
|
|
18725
|
+
background: var(--jupiter-hover-color, rgba(25, 118, 210, 0.08));
|
|
18726
|
+
}
|
|
18727
|
+
|
|
18728
|
+
.side-panel-content-header:focus-visible {
|
|
18729
|
+
outline: 2px solid var(--jupiter-primary-color, #1976d2);
|
|
18730
|
+
outline-offset: 2px;
|
|
18731
|
+
}
|
|
18732
|
+
|
|
18733
|
+
.side-panel-content-position {
|
|
18734
|
+
font-size: 12px;
|
|
18735
|
+
font-weight: 600;
|
|
18736
|
+
letter-spacing: 0.02em;
|
|
18737
|
+
text-transform: uppercase;
|
|
18738
|
+
color: var(--jupiter-text-secondary, #757575);
|
|
18739
|
+
margin-bottom: 4px;
|
|
18740
|
+
}
|
|
18741
|
+
|
|
18742
|
+
.side-panel-content-title {
|
|
18743
|
+
font-size: 18px;
|
|
18744
|
+
font-weight: 600;
|
|
18745
|
+
margin: 0;
|
|
18746
|
+
color: var(--jupiter-text-primary, #333);
|
|
18747
|
+
}
|
|
18748
|
+
|
|
18749
|
+
/* JDF-093 Part B: stated once, here, rather than repeated on every input hint. */
|
|
18750
|
+
.side-panel-content-amount-scale {
|
|
18751
|
+
font-size: 13px;
|
|
18752
|
+
color: var(--jupiter-text-secondary, #757575);
|
|
18753
|
+
margin-top: 4px;
|
|
18754
|
+
}
|
|
18755
|
+
|
|
18756
|
+
.side-panel-nav-row {
|
|
18757
|
+
display: flex;
|
|
18758
|
+
align-items: center;
|
|
18759
|
+
justify-content: space-between;
|
|
18760
|
+
gap: 12px;
|
|
18761
|
+
padding: 4px 16px 16px 16px;
|
|
18762
|
+
}
|
|
18763
|
+
|
|
18764
|
+
.side-panel-nav-btn {
|
|
18765
|
+
background: none;
|
|
18766
|
+
border: 1px solid var(--jupiter-border-color, #e0e0e0);
|
|
18767
|
+
border-radius: 4px;
|
|
18768
|
+
padding: 8px 16px;
|
|
18769
|
+
font-size: 14px;
|
|
18770
|
+
color: var(--jupiter-primary-color, #1976d2);
|
|
18771
|
+
cursor: pointer;
|
|
18772
|
+
}
|
|
18773
|
+
|
|
18774
|
+
.side-panel-nav-btn:hover {
|
|
18775
|
+
background: var(--jupiter-hover-color, rgba(25, 118, 210, 0.08));
|
|
18776
|
+
}
|
|
18777
|
+
|
|
18778
|
+
.side-panel-nav-next {
|
|
18779
|
+
margin-left: auto;
|
|
18780
|
+
}
|
|
18781
|
+
|
|
17322
18782
|
.side-panel-content .form-sections {
|
|
17323
18783
|
padding: 0 24px 24px 24px;
|
|
17324
18784
|
padding-top: 0;
|
|
@@ -17681,6 +19141,9 @@ __decorateClass([
|
|
|
17681
19141
|
__decorateClass([
|
|
17682
19142
|
n2({ type: String })
|
|
17683
19143
|
], JupiterDynamicForm.prototype, "reportingLanguage", 2);
|
|
19144
|
+
__decorateClass([
|
|
19145
|
+
n2({ type: String })
|
|
19146
|
+
], JupiterDynamicForm.prototype, "numberLocale", 2);
|
|
17684
19147
|
__decorateClass([
|
|
17685
19148
|
n2({ type: String })
|
|
17686
19149
|
], JupiterDynamicForm.prototype, "display", 2);
|
|
@@ -17711,6 +19174,9 @@ __decorateClass([
|
|
|
17711
19174
|
__decorateClass([
|
|
17712
19175
|
n2({ type: Object, attribute: "master-data" })
|
|
17713
19176
|
], JupiterDynamicForm.prototype, "masterData", 2);
|
|
19177
|
+
__decorateClass([
|
|
19178
|
+
n2({ type: String, attribute: "prefilled-tooltip" })
|
|
19179
|
+
], JupiterDynamicForm.prototype, "prefilledTooltip", 2);
|
|
17714
19180
|
__decorateClass([
|
|
17715
19181
|
n2({ type: Object, attribute: "dynaforms-metadata" })
|
|
17716
19182
|
], JupiterDynamicForm.prototype, "dynaformsMetadata", 2);
|
|
@@ -17789,6 +19255,12 @@ __decorateClass([
|
|
|
17789
19255
|
__decorateClass([
|
|
17790
19256
|
r()
|
|
17791
19257
|
], JupiterDynamicForm.prototype, "_showErrorPopup", 2);
|
|
19258
|
+
__decorateClass([
|
|
19259
|
+
r()
|
|
19260
|
+
], JupiterDynamicForm.prototype, "_showTypedDimensionWarning", 2);
|
|
19261
|
+
__decorateClass([
|
|
19262
|
+
r()
|
|
19263
|
+
], JupiterDynamicForm.prototype, "_typedDimensionWarningFacts", 2);
|
|
17792
19264
|
__decorateClass([
|
|
17793
19265
|
r()
|
|
17794
19266
|
], JupiterDynamicForm.prototype, "_submitDisabled", 2);
|
|
@@ -17849,6 +19321,9 @@ __decorateClass([
|
|
|
17849
19321
|
__decorateClass([
|
|
17850
19322
|
r()
|
|
17851
19323
|
], JupiterDynamicForm.prototype, "_validationStatus", 2);
|
|
19324
|
+
__decorateClass([
|
|
19325
|
+
r()
|
|
19326
|
+
], JupiterDynamicForm.prototype, "_validationDotCount", 2);
|
|
17852
19327
|
__decorateClass([
|
|
17853
19328
|
r()
|
|
17854
19329
|
], JupiterDynamicForm.prototype, "_formulaValidationResults", 2);
|