@robosystems/report-components 0.2.0 → 0.2.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/adapters/index.d.ts +1 -1
- package/dist/adapters/index.js +1 -1
- package/dist/{chunk-POSLHOTN.js → chunk-DGP4KRWF.js} +186 -29
- package/dist/chunk-DGP4KRWF.js.map +1 -0
- package/dist/{index-D6FzPTm5.d.ts → index-WkjE6dZu.d.ts} +156 -1
- package/dist/index.d.ts +92 -29
- package/dist/index.js +687 -182
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-POSLHOTN.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,34 +1,8 @@
|
|
|
1
|
-
import { isExternalFactUrl, BLOCK_ORDER,
|
|
2
|
-
export { BLOCK_ORDER, BLOCK_TITLES, NS, cypherAdapter, fetchSecReportShell, fetchSecSection, humanize, isExternalFactUrl, jsonldFileAdapter, mergeSecSections, parseJsonld, parseStore, parseStructureDefinition, qname } from './chunk-
|
|
1
|
+
import { formatDate, BLOCK_TITLES, numericKindOf, humanizeDuration, formatValue, formatPeriod, isExternalFactUrl, BLOCK_ORDER, humanize, qname, currencySymbolFor } from './chunk-DGP4KRWF.js';
|
|
2
|
+
export { BLOCK_ORDER, BLOCK_TITLES, NS, currencySymbolFor, cypherAdapter, fetchSecReportShell, fetchSecSection, formatDate, formatMoney, formatPeriod, formatValue, humanize, humanizeDuration, isExternalFactUrl, jsonldFileAdapter, mergeSecSections, numericKindOf, parseJsonld, parseStore, parseStructureDefinition, qname } from './chunk-DGP4KRWF.js';
|
|
3
3
|
import { useState, useEffect, useMemo, useRef } from 'react';
|
|
4
4
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
5
5
|
|
|
6
|
-
// src/format.ts
|
|
7
|
-
function formatMoney(value, opts = {}) {
|
|
8
|
-
if (value === null || value === void 0 || Number.isNaN(value)) return "\u2014";
|
|
9
|
-
const symbol = opts.currencySymbol ?? "$";
|
|
10
|
-
const abs = Math.abs(value).toLocaleString("en-US", {
|
|
11
|
-
minimumFractionDigits: 2,
|
|
12
|
-
maximumFractionDigits: 2
|
|
13
|
-
});
|
|
14
|
-
return value < 0 ? `${symbol}(${abs})` : `${symbol}${abs}`;
|
|
15
|
-
}
|
|
16
|
-
var MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
|
|
17
|
-
function formatDate(iso) {
|
|
18
|
-
if (!iso) return "";
|
|
19
|
-
const m = /^(\d{4})-(\d{2})-(\d{2})/.exec(iso);
|
|
20
|
-
if (!m) return iso;
|
|
21
|
-
const [, year, month, day] = m;
|
|
22
|
-
const idx = Number(month) - 1;
|
|
23
|
-
return `${MONTHS[idx] ?? month} ${Number(day)}, ${year}`;
|
|
24
|
-
}
|
|
25
|
-
function formatPeriod(period) {
|
|
26
|
-
if (period.type === "instant") {
|
|
27
|
-
return `As of ${formatDate(period.instant ?? period.end)}`;
|
|
28
|
-
}
|
|
29
|
-
return `${formatDate(period.startDate)} \u2013 ${formatDate(period.endDate ?? period.end)}`;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
6
|
// src/project.ts
|
|
33
7
|
function fallbackElement(id) {
|
|
34
8
|
return {
|
|
@@ -81,6 +55,121 @@ function calcSubtotals(model) {
|
|
|
81
55
|
for (const assoc of model.calcAssociations) out.add(assoc.parent);
|
|
82
56
|
return out;
|
|
83
57
|
}
|
|
58
|
+
function sliceReportSection(model, informationBlockId) {
|
|
59
|
+
const ib = model.informationBlocks.find((b) => b.id === informationBlockId);
|
|
60
|
+
return { ...model, informationBlocks: ib ? [ib] : [] };
|
|
61
|
+
}
|
|
62
|
+
function footCheck(model, table, elementId, columnIndex) {
|
|
63
|
+
const kids = model.calcAssociations.filter((a) => a.parent === elementId);
|
|
64
|
+
if (!kids.length) return null;
|
|
65
|
+
const valueOf = (id) => {
|
|
66
|
+
const row = table.rows.find((r) => r.element.id === id && r.members.length === 0);
|
|
67
|
+
return row ? row.cells[columnIndex]?.value ?? null : null;
|
|
68
|
+
};
|
|
69
|
+
const terms = kids.slice().sort((a, b) => a.order - b.order).map((a) => ({
|
|
70
|
+
element: model.elements[a.child] ?? fallbackElement(a.child),
|
|
71
|
+
weight: a.weight,
|
|
72
|
+
value: valueOf(a.child)
|
|
73
|
+
}));
|
|
74
|
+
const present = terms.filter((t) => t.value !== null);
|
|
75
|
+
const expected = present.length ? present.reduce((sum, t) => sum + t.value * t.weight, 0) : null;
|
|
76
|
+
const actual = valueOf(elementId);
|
|
77
|
+
const ok = expected !== null && actual !== null && Math.abs(expected - actual) < 5e-3;
|
|
78
|
+
return {
|
|
79
|
+
element: model.elements[elementId] ?? fallbackElement(elementId),
|
|
80
|
+
columnIndex,
|
|
81
|
+
terms,
|
|
82
|
+
expected,
|
|
83
|
+
actual,
|
|
84
|
+
ok
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// src/pivot.ts
|
|
89
|
+
var DOMAIN = "\u2205";
|
|
90
|
+
function dimKey(axis) {
|
|
91
|
+
return `dim:${axis}`;
|
|
92
|
+
}
|
|
93
|
+
function axisOf(key) {
|
|
94
|
+
return key.startsWith("dim:") ? key.slice(4) : null;
|
|
95
|
+
}
|
|
96
|
+
function dimAxes(keys) {
|
|
97
|
+
return keys.map(axisOf).filter((a) => a !== null);
|
|
98
|
+
}
|
|
99
|
+
function memberByAxis(fact) {
|
|
100
|
+
const m = /* @__PURE__ */ new Map();
|
|
101
|
+
for (const d of fact.dimensions ?? []) m.set(d.axis, d);
|
|
102
|
+
return m;
|
|
103
|
+
}
|
|
104
|
+
function coordinate(byAxis, axes) {
|
|
105
|
+
return axes.map((a) => `${a}=${byAxis.get(a)?.member ?? byAxis.get(a)?.typedValue ?? DOMAIN}`).join("|");
|
|
106
|
+
}
|
|
107
|
+
function sectionFacts(model, ib) {
|
|
108
|
+
return model.facts.filter((f) => f.factSet !== null && f.factSet === ib.factSet);
|
|
109
|
+
}
|
|
110
|
+
function localName(qname2) {
|
|
111
|
+
return qname2.includes(":") ? qname2.split(":").pop() : qname2;
|
|
112
|
+
}
|
|
113
|
+
function presentationNodeLocals(model, structureId) {
|
|
114
|
+
const out = /* @__PURE__ */ new Set();
|
|
115
|
+
for (const a of model.presAssociations) {
|
|
116
|
+
if (a.structure !== structureId) continue;
|
|
117
|
+
out.add(localName(a.parent));
|
|
118
|
+
out.add(localName(a.child));
|
|
119
|
+
}
|
|
120
|
+
return out;
|
|
121
|
+
}
|
|
122
|
+
function presentationScope(model, structureId) {
|
|
123
|
+
const declared = presentationNodeLocals(model, structureId);
|
|
124
|
+
let listsMembers = false;
|
|
125
|
+
for (const l of declared) {
|
|
126
|
+
if (l.endsWith("Member")) {
|
|
127
|
+
listsMembers = true;
|
|
128
|
+
break;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return { declared, listsMembers };
|
|
132
|
+
}
|
|
133
|
+
function dimensionInScope(scope, d) {
|
|
134
|
+
if (!scope.declared.has(localName(d.axis))) return false;
|
|
135
|
+
return !scope.listsMembers || d.member === null || scope.declared.has(localName(d.member));
|
|
136
|
+
}
|
|
137
|
+
function presentationTree(model, structureId) {
|
|
138
|
+
const children = /* @__PURE__ */ new Map();
|
|
139
|
+
const froms = /* @__PURE__ */ new Set();
|
|
140
|
+
const tos = /* @__PURE__ */ new Set();
|
|
141
|
+
for (const assoc of model.presAssociations) {
|
|
142
|
+
if (assoc.structure !== structureId) continue;
|
|
143
|
+
const kids = children.get(assoc.parent) ?? [];
|
|
144
|
+
kids.push([assoc.order, assoc.child]);
|
|
145
|
+
children.set(assoc.parent, kids);
|
|
146
|
+
froms.add(assoc.parent);
|
|
147
|
+
tos.add(assoc.child);
|
|
148
|
+
}
|
|
149
|
+
const childrenOf = /* @__PURE__ */ new Map();
|
|
150
|
+
for (const [parent, kids] of children) {
|
|
151
|
+
childrenOf.set(
|
|
152
|
+
parent,
|
|
153
|
+
kids.sort((a, b) => a[0] - b[0]).map(([, child]) => child)
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
const rootKey = (root) => {
|
|
157
|
+
const kids = children.get(root);
|
|
158
|
+
return kids && kids.length ? kids[0][0] : 0;
|
|
159
|
+
};
|
|
160
|
+
const roots = [...froms].filter((f) => !tos.has(f)).sort((a, b) => rootKey(a) - rootKey(b));
|
|
161
|
+
const indexOf = /* @__PURE__ */ new Map();
|
|
162
|
+
const seen = /* @__PURE__ */ new Set();
|
|
163
|
+
const pre = (node) => {
|
|
164
|
+
if (seen.has(node)) return;
|
|
165
|
+
seen.add(node);
|
|
166
|
+
const ln = localName(node);
|
|
167
|
+
if (!indexOf.has(ln)) indexOf.set(ln, indexOf.size);
|
|
168
|
+
for (const child of childrenOf.get(node) ?? []) pre(child);
|
|
169
|
+
};
|
|
170
|
+
for (const root of roots) pre(root);
|
|
171
|
+
return { childrenOf, roots, indexOf };
|
|
172
|
+
}
|
|
84
173
|
function structureForBlock(model, ib) {
|
|
85
174
|
if (ib.structureId) {
|
|
86
175
|
const byId = model.structures.find((s) => s.id === ib.structureId);
|
|
@@ -88,17 +177,68 @@ function structureForBlock(model, ib) {
|
|
|
88
177
|
}
|
|
89
178
|
return model.structures.find((s) => s.blockType === ib.blockType) ?? null;
|
|
90
179
|
}
|
|
91
|
-
function
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
180
|
+
function fallbackElement2(id) {
|
|
181
|
+
return {
|
|
182
|
+
id,
|
|
183
|
+
qname: qname(id),
|
|
184
|
+
label: humanize(id),
|
|
185
|
+
balance: null,
|
|
186
|
+
periodType: null,
|
|
187
|
+
abstract: false,
|
|
188
|
+
monetary: false
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
var elementOf = (model, id) => model.elements[id] ?? fallbackElement2(id);
|
|
192
|
+
function defaultPivotConfig(model, ib) {
|
|
193
|
+
const facts = sectionFacts(model, ib);
|
|
194
|
+
const structure = structureForBlock(model, ib);
|
|
195
|
+
const scope = structure ? presentationScope(model, structure.id) : { declared: /* @__PURE__ */ new Set(), listsMembers: false };
|
|
196
|
+
const membersByAxis = /* @__PURE__ */ new Map();
|
|
197
|
+
let hasEntity = false;
|
|
198
|
+
for (const f of facts) {
|
|
199
|
+
if (f.entity) hasEntity = true;
|
|
200
|
+
for (const d of f.dimensions ?? []) {
|
|
201
|
+
if (!dimensionInScope(scope, d)) continue;
|
|
202
|
+
const set = membersByAxis.get(d.axis) ?? /* @__PURE__ */ new Set();
|
|
203
|
+
set.add(d.member ?? d.typedValue ?? DOMAIN);
|
|
204
|
+
membersByAxis.set(d.axis, set);
|
|
205
|
+
}
|
|
95
206
|
}
|
|
96
|
-
|
|
207
|
+
const axes = [...membersByAxis.keys()].sort();
|
|
208
|
+
const memberAxes = axes.filter((a) => (membersByAxis.get(a)?.size ?? 0) > 1);
|
|
209
|
+
const slicerAxes = axes.filter((a) => (membersByAxis.get(a)?.size ?? 0) <= 1);
|
|
210
|
+
return {
|
|
211
|
+
rows: ["concept", ...memberAxes.map(dimKey)],
|
|
212
|
+
columns: ["period"],
|
|
213
|
+
slicers: [...hasEntity ? ["entity"] : [], ...slicerAxes.map(dimKey)],
|
|
214
|
+
scale: "auto",
|
|
215
|
+
showAbstracts: true,
|
|
216
|
+
dropSparsePeriods: true
|
|
217
|
+
};
|
|
97
218
|
}
|
|
98
|
-
function
|
|
99
|
-
|
|
219
|
+
function pivotDimensionsOn(config, placement) {
|
|
220
|
+
const isDim = (k) => k.startsWith("dim:");
|
|
221
|
+
const dims = [...config.rows, ...config.columns].filter(isDim);
|
|
222
|
+
const rows = config.rows.filter((k) => !isDim(k));
|
|
223
|
+
const columns = config.columns.filter((k) => !isDim(k));
|
|
224
|
+
if (placement === "rows") rows.push(...dims);
|
|
225
|
+
else columns.push(...dims);
|
|
226
|
+
return { ...config, rows, columns };
|
|
227
|
+
}
|
|
228
|
+
var PERIOD_DENSITY_FRACTION = 0.2;
|
|
229
|
+
function pruneSparsePeriods(model, facts, periods) {
|
|
230
|
+
const count = /* @__PURE__ */ new Map();
|
|
231
|
+
for (const f of facts) {
|
|
232
|
+
const populated = f.value !== null || f.textValue != null && f.textValue !== "";
|
|
233
|
+
const end = model.periods[f.period]?.end;
|
|
234
|
+
if (end && populated) count.set(end, (count.get(end) ?? 0) + 1);
|
|
235
|
+
}
|
|
236
|
+
const max = Math.max(0, ...count.values());
|
|
237
|
+
if (max < 5) return periods;
|
|
238
|
+
const threshold = Math.max(2, max * PERIOD_DENSITY_FRACTION);
|
|
239
|
+
return periods.filter((p) => (count.get(p.end) ?? 0) >= threshold);
|
|
100
240
|
}
|
|
101
|
-
function
|
|
241
|
+
function derivePeriods(model, facts) {
|
|
102
242
|
const byEnd = /* @__PURE__ */ new Map();
|
|
103
243
|
for (const fact of facts) {
|
|
104
244
|
const period = model.periods[fact.period];
|
|
@@ -112,87 +252,307 @@ function deriveColumns(model, facts) {
|
|
|
112
252
|
const aStart = a.startDate ?? a.end;
|
|
113
253
|
const bStart = b.startDate ?? b.end;
|
|
114
254
|
return aStart.localeCompare(bStart) || a.end.localeCompare(b.end);
|
|
115
|
-
})
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
function memberCombos(facts, axes, order, domainLast) {
|
|
258
|
+
if (axes.length === 0) return [{ sig: "", members: [], labels: [] }];
|
|
259
|
+
const perAxis = axes.map((axis) => {
|
|
260
|
+
const byMember = /* @__PURE__ */ new Map();
|
|
261
|
+
let hasDomain = false;
|
|
262
|
+
for (const f of facts) {
|
|
263
|
+
const d = (f.dimensions ?? []).find((x) => x.axis === axis);
|
|
264
|
+
if (d) byMember.set(d.member ?? d.typedValue ?? DOMAIN, d);
|
|
265
|
+
else hasDomain = true;
|
|
266
|
+
}
|
|
267
|
+
const members = [...byMember.entries()].map(([key, qual]) => ({ key, qual })).sort((a, b) => {
|
|
268
|
+
const ao = a.qual?.member ? order.get(localName(a.qual.member)) ?? Infinity : Infinity;
|
|
269
|
+
const bo = b.qual?.member ? order.get(localName(b.qual.member)) ?? Infinity : Infinity;
|
|
270
|
+
return ao - bo || (a.qual?.memberLabel ?? a.key).localeCompare(b.qual?.memberLabel ?? b.key);
|
|
271
|
+
});
|
|
272
|
+
if (hasDomain) {
|
|
273
|
+
const domain = { key: DOMAIN, qual: null };
|
|
274
|
+
members.push(domain);
|
|
275
|
+
}
|
|
276
|
+
return { axis, members };
|
|
277
|
+
});
|
|
278
|
+
let combos = [{ sig: "", members: [], labels: [] }];
|
|
279
|
+
for (const { axis, members } of perAxis) {
|
|
280
|
+
const next = [];
|
|
281
|
+
for (const combo of combos) {
|
|
282
|
+
for (const { key, qual } of members) {
|
|
283
|
+
next.push({
|
|
284
|
+
sig: combo.sig ? `${combo.sig}|${axis}=${key}` : `${axis}=${key}`,
|
|
285
|
+
members: [...combo.members, ...qual ? [qual] : []],
|
|
286
|
+
labels: [...combo.labels, qual ? qual.memberLabel : "Total"]
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
combos = next;
|
|
291
|
+
}
|
|
292
|
+
return combos;
|
|
293
|
+
}
|
|
294
|
+
function headerLevels(segsPerColumn) {
|
|
295
|
+
const levels = segsPerColumn[0]?.length ?? 0;
|
|
296
|
+
const out = [];
|
|
297
|
+
for (let level = 0; level < levels; level++) {
|
|
298
|
+
const row = [];
|
|
299
|
+
let i = 0;
|
|
300
|
+
while (i < segsPerColumn.length) {
|
|
301
|
+
let span = 1;
|
|
302
|
+
while (i + span < segsPerColumn.length && segsPerColumn[i + span][level] === segsPerColumn[i][level] && Array.from({ length: level + 1 }, (_, l) => l).every(
|
|
303
|
+
(l) => segsPerColumn[i + span][l] === segsPerColumn[i][l]
|
|
304
|
+
)) {
|
|
305
|
+
span++;
|
|
306
|
+
}
|
|
307
|
+
row.push({ label: segsPerColumn[i][level], span });
|
|
308
|
+
i += span;
|
|
309
|
+
}
|
|
310
|
+
out.push(row);
|
|
311
|
+
}
|
|
312
|
+
return out;
|
|
313
|
+
}
|
|
314
|
+
var SCALE_FACTOR = {
|
|
315
|
+
ones: { factor: 1, label: "" },
|
|
316
|
+
thousands: { factor: 1e3, label: "thousands" },
|
|
317
|
+
millions: { factor: 1e6, label: "millions" },
|
|
318
|
+
billions: { factor: 1e9, label: "billions" }
|
|
319
|
+
};
|
|
320
|
+
function resolveScale(facts, model, requested) {
|
|
321
|
+
const kindOf = (id) => numericKindOf(elementOf(model, id));
|
|
322
|
+
let factor = 1;
|
|
323
|
+
let label = "";
|
|
324
|
+
if (requested !== "auto") {
|
|
325
|
+
({ factor, label } = SCALE_FACTOR[requested]);
|
|
326
|
+
} else {
|
|
327
|
+
const decimalsCount = /* @__PURE__ */ new Map();
|
|
328
|
+
for (const f of facts) {
|
|
329
|
+
if (f.value === null || kindOf(f.element) !== "monetary") continue;
|
|
330
|
+
const d = f.decimals === null || f.decimals === "INF" ? NaN : Number(f.decimals);
|
|
331
|
+
if (Number.isNaN(d) || d >= 0) continue;
|
|
332
|
+
decimalsCount.set(d, (decimalsCount.get(d) ?? 0) + 1);
|
|
333
|
+
}
|
|
334
|
+
let dominant = 0;
|
|
335
|
+
let best = 0;
|
|
336
|
+
for (const [d, count] of decimalsCount) {
|
|
337
|
+
if (count > best) {
|
|
338
|
+
best = count;
|
|
339
|
+
dominant = d;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
if (dominant <= -9) ({ factor, label } = SCALE_FACTOR.billions);
|
|
343
|
+
else if (dominant <= -6) ({ factor, label } = SCALE_FACTOR.millions);
|
|
344
|
+
else if (dominant <= -3) ({ factor, label } = SCALE_FACTOR.thousands);
|
|
345
|
+
}
|
|
346
|
+
let caption = null;
|
|
347
|
+
if (factor > 1) {
|
|
348
|
+
const hasPerShare = facts.some((f) => {
|
|
349
|
+
const k = kindOf(f.element);
|
|
350
|
+
return f.value !== null && (k === "perShare" || k === "shares");
|
|
351
|
+
});
|
|
352
|
+
caption = hasPerShare ? `In ${label}, except per-share amounts` : `In ${label}`;
|
|
353
|
+
}
|
|
354
|
+
return { factor, label, caption };
|
|
355
|
+
}
|
|
356
|
+
function buildSlicers(model, facts, config) {
|
|
357
|
+
const slicers = [];
|
|
358
|
+
for (const key of config.slicers) {
|
|
359
|
+
if (key === "entity") {
|
|
360
|
+
if (model.entity) {
|
|
361
|
+
slicers.push({
|
|
362
|
+
aspect: "entity",
|
|
363
|
+
label: "Reporting Entity",
|
|
364
|
+
valueLabel: model.entity.name
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
continue;
|
|
368
|
+
}
|
|
369
|
+
const axis = axisOf(key);
|
|
370
|
+
if (!axis) continue;
|
|
371
|
+
let qual = null;
|
|
372
|
+
for (const f of facts) {
|
|
373
|
+
const d = (f.dimensions ?? []).find((x) => x.axis === axis);
|
|
374
|
+
if (d) {
|
|
375
|
+
qual = d;
|
|
376
|
+
break;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
const axisLabel = qual?.axisLabel ?? humanize(axis);
|
|
380
|
+
slicers.push({
|
|
381
|
+
aspect: key,
|
|
382
|
+
label: `${axisLabel} [Axis]`,
|
|
383
|
+
valueLabel: qual ? qual.memberLabel : "Consolidated"
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
return slicers;
|
|
116
387
|
}
|
|
117
|
-
function
|
|
388
|
+
function buildPivot(model, ib, config = defaultPivotConfig(model, ib)) {
|
|
118
389
|
const structure = structureForBlock(model, ib);
|
|
119
|
-
const
|
|
120
|
-
const
|
|
121
|
-
const
|
|
122
|
-
const
|
|
123
|
-
const
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
390
|
+
const tree = structure ? presentationTree(model, structure.id) : { childrenOf: /* @__PURE__ */ new Map(), roots: [], indexOf: /* @__PURE__ */ new Map() };
|
|
391
|
+
const rowDimAxes = dimAxes(config.rows);
|
|
392
|
+
const colDimAxes = dimAxes(config.columns);
|
|
393
|
+
const allowedAxes = /* @__PURE__ */ new Set([...rowDimAxes, ...colDimAxes, ...dimAxes(config.slicers)]);
|
|
394
|
+
const scope = structure ? presentationScope(model, structure.id) : { declared: /* @__PURE__ */ new Set(), listsMembers: false };
|
|
395
|
+
const facts = sectionFacts(model, ib).filter(
|
|
396
|
+
(f) => (f.dimensions ?? []).every((d) => allowedAxes.has(d.axis) && dimensionInScope(scope, d))
|
|
397
|
+
);
|
|
398
|
+
const periodOnColumns = config.columns.includes("period");
|
|
399
|
+
let periods = periodOnColumns ? derivePeriods(model, facts) : [];
|
|
400
|
+
if (periodOnColumns && config.dropSparsePeriods !== false) {
|
|
401
|
+
periods = pruneSparsePeriods(model, facts, periods);
|
|
402
|
+
}
|
|
403
|
+
const colCombos = memberCombos(facts, colDimAxes, tree.indexOf);
|
|
404
|
+
const occupiedCols = /* @__PURE__ */ new Set();
|
|
405
|
+
for (const f of facts) {
|
|
406
|
+
const end = model.periods[f.period]?.end ?? "";
|
|
407
|
+
occupiedCols.add(`${end}#${coordinate(memberByAxis(f), colDimAxes)}`);
|
|
408
|
+
}
|
|
409
|
+
const columns = [];
|
|
410
|
+
const columnSegs = [];
|
|
411
|
+
const periodList = periodOnColumns ? periods : [null];
|
|
412
|
+
for (const period of periodList) {
|
|
413
|
+
for (const combo of colCombos) {
|
|
414
|
+
const end = period?.end ?? "";
|
|
415
|
+
if (!occupiedCols.has(`${end}#${combo.sig}`)) continue;
|
|
416
|
+
const periodLabel = period ? formatDate(period.end) : "";
|
|
417
|
+
columns.push({
|
|
418
|
+
key: `${end}#${combo.sig}`,
|
|
419
|
+
period,
|
|
420
|
+
members: combo.members,
|
|
421
|
+
label: combo.labels.length ? combo.labels[combo.labels.length - 1] : periodLabel
|
|
422
|
+
});
|
|
423
|
+
columnSegs.push([...periodOnColumns ? [periodLabel] : [], ...combo.labels]);
|
|
133
424
|
}
|
|
134
|
-
cells[idx] = { value: fact.value, fact, textValue: fact.textValue ?? null };
|
|
135
425
|
}
|
|
136
|
-
const
|
|
137
|
-
|
|
426
|
+
const columnHeaders = headerLevels(columnSegs);
|
|
427
|
+
const factIndex = /* @__PURE__ */ new Map();
|
|
428
|
+
for (const f of facts) {
|
|
429
|
+
const byAxis = memberByAxis(f);
|
|
430
|
+
const end = model.periods[f.period]?.end ?? "";
|
|
431
|
+
const rowSig = coordinate(byAxis, rowDimAxes);
|
|
432
|
+
const colSig = coordinate(byAxis, colDimAxes);
|
|
433
|
+
factIndex.set(`${f.element}\u241F${rowSig}\u241F${end}\u241F${colSig}`, f);
|
|
434
|
+
}
|
|
435
|
+
const factfulConcepts = new Set(facts.map((f) => f.element));
|
|
436
|
+
const rowCombos = memberCombos(facts, rowDimAxes, tree.indexOf);
|
|
437
|
+
const combosForConcept = (element) => {
|
|
438
|
+
if (rowDimAxes.length === 0) return rowCombos;
|
|
439
|
+
return rowCombos.filter(
|
|
440
|
+
(combo) => columns.some(
|
|
441
|
+
(col) => factIndex.has(
|
|
442
|
+
`${element}\u241F${combo.sig}\u241F${col.period?.end ?? ""}\u241F${coordinate(
|
|
443
|
+
new Map(col.members.map((m) => [m.axis, m])),
|
|
444
|
+
colDimAxes
|
|
445
|
+
)}`
|
|
446
|
+
)
|
|
447
|
+
)
|
|
448
|
+
);
|
|
449
|
+
};
|
|
450
|
+
const cellsFor = (element, rowSig) => columns.map((col) => {
|
|
451
|
+
const colSig = coordinate(new Map(col.members.map((m) => [m.axis, m])), colDimAxes);
|
|
452
|
+
const fact = factIndex.get(`${element}\u241F${rowSig}\u241F${col.period?.end ?? ""}\u241F${colSig}`);
|
|
138
453
|
return {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
454
|
+
value: fact?.value ?? null,
|
|
455
|
+
fact: fact ?? null,
|
|
456
|
+
textValue: fact && fact.value === null ? fact.textValue ?? null : null
|
|
142
457
|
};
|
|
143
|
-
})
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
458
|
+
});
|
|
459
|
+
const subtotals = calcSubtotals(model);
|
|
460
|
+
const hasFactMemo = /* @__PURE__ */ new Map();
|
|
461
|
+
const subtreeHasFact = (id) => {
|
|
462
|
+
const cached = hasFactMemo.get(id);
|
|
463
|
+
if (cached !== void 0) return cached;
|
|
464
|
+
hasFactMemo.set(id, false);
|
|
465
|
+
let has = factfulConcepts.has(id);
|
|
466
|
+
for (const child of tree.childrenOf.get(id) ?? []) {
|
|
467
|
+
if (subtreeHasFact(child)) has = true;
|
|
468
|
+
}
|
|
469
|
+
hasFactMemo.set(id, has);
|
|
470
|
+
return has;
|
|
471
|
+
};
|
|
472
|
+
const rows = [];
|
|
473
|
+
const emitConcept = (id, depth) => {
|
|
474
|
+
const el = elementOf(model, id);
|
|
475
|
+
const combos = combosForConcept(id);
|
|
476
|
+
if (!combos.length) return;
|
|
477
|
+
if (combos.every((c) => c.members.length > 0)) {
|
|
478
|
+
rows.push({
|
|
479
|
+
key: id,
|
|
480
|
+
element: el,
|
|
481
|
+
depth,
|
|
482
|
+
header: false,
|
|
483
|
+
isSubtotal: subtotals.has(id),
|
|
484
|
+
members: [],
|
|
485
|
+
cells: columns.map(() => ({ value: null, fact: null, textValue: null }))
|
|
486
|
+
});
|
|
487
|
+
}
|
|
488
|
+
for (const combo of combos) {
|
|
489
|
+
rows.push({
|
|
490
|
+
key: combo.members.length ? `${id}\u241F${combo.sig}` : id,
|
|
491
|
+
element: el,
|
|
492
|
+
depth: depth + (combo.members.length ? 1 : 0),
|
|
493
|
+
header: false,
|
|
494
|
+
isSubtotal: subtotals.has(id),
|
|
495
|
+
members: combo.members,
|
|
496
|
+
cells: cellsFor(id, combo.sig)
|
|
497
|
+
});
|
|
498
|
+
}
|
|
499
|
+
};
|
|
500
|
+
const seenInTree = /* @__PURE__ */ new Set();
|
|
501
|
+
const walk = (id, depth) => {
|
|
502
|
+
if (seenInTree.has(id) || !subtreeHasFact(id)) return;
|
|
503
|
+
seenInTree.add(id);
|
|
504
|
+
const el = elementOf(model, id);
|
|
505
|
+
if (el.abstract) {
|
|
506
|
+
if (config.showAbstracts) {
|
|
507
|
+
rows.push({
|
|
508
|
+
key: id,
|
|
509
|
+
element: el,
|
|
510
|
+
depth,
|
|
511
|
+
header: true,
|
|
512
|
+
isSubtotal: false,
|
|
513
|
+
members: [],
|
|
514
|
+
cells: []
|
|
515
|
+
});
|
|
516
|
+
}
|
|
517
|
+
for (const child of tree.childrenOf.get(id) ?? []) walk(child, depth + 1);
|
|
518
|
+
} else {
|
|
519
|
+
for (const child of tree.childrenOf.get(id) ?? []) walk(child, depth + 1);
|
|
520
|
+
emitConcept(id, depth);
|
|
521
|
+
}
|
|
522
|
+
};
|
|
523
|
+
for (const root of tree.roots) walk(root, 0);
|
|
524
|
+
for (const id of factfulConcepts) {
|
|
525
|
+
if (seenInTree.has(id) || elementOf(model, id).abstract) continue;
|
|
526
|
+
emitConcept(id, 0);
|
|
527
|
+
}
|
|
149
528
|
const title = ib.structureId ? structure?.structureName ?? ib.label ?? BLOCK_TITLES[ib.blockType] ?? ib.blockType : BLOCK_TITLES[ib.blockType] ?? ib.label ?? ib.blockType;
|
|
150
529
|
return {
|
|
151
530
|
ib,
|
|
152
531
|
blockType: ib.blockType,
|
|
153
532
|
title,
|
|
154
533
|
structureName: ib.structureId ? null : structure?.structureName ?? ib.label ?? null,
|
|
534
|
+
slicers: buildSlicers(model, facts, config),
|
|
535
|
+
columnHeaders,
|
|
155
536
|
columns,
|
|
156
|
-
rows
|
|
537
|
+
rows,
|
|
538
|
+
scale: resolveScale(facts, model, config.scale),
|
|
539
|
+
config
|
|
157
540
|
};
|
|
158
541
|
}
|
|
159
542
|
function reportSections(model) {
|
|
160
|
-
return
|
|
543
|
+
return buildPivots(model).map((p) => ({ id: p.ib.id, title: p.title }));
|
|
161
544
|
}
|
|
162
|
-
function
|
|
163
|
-
const
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
(a, b) => blockOrder(model, a) - blockOrder(model, b) || a.blockType.localeCompare(b.blockType)
|
|
170
|
-
).map((ib) => buildStatement(model, ib, subtotals));
|
|
171
|
-
}
|
|
172
|
-
function footCheck(model, statement, elementId, columnIndex) {
|
|
173
|
-
const kids = model.calcAssociations.filter((a) => a.parent === elementId);
|
|
174
|
-
if (!kids.length) return null;
|
|
175
|
-
const valueOf = (id) => {
|
|
176
|
-
const row = statement.rows.find((r) => r.element.id === id);
|
|
177
|
-
return row ? row.cells[columnIndex]?.value ?? null : null;
|
|
178
|
-
};
|
|
179
|
-
const terms = kids.slice().sort((a, b) => a.order - b.order).map((a) => ({
|
|
180
|
-
element: model.elements[a.child] ?? fallbackElement(a.child),
|
|
181
|
-
weight: a.weight,
|
|
182
|
-
value: valueOf(a.child)
|
|
183
|
-
}));
|
|
184
|
-
const present = terms.filter((t) => t.value !== null);
|
|
185
|
-
const expected = present.length ? present.reduce((sum, t) => sum + t.value * t.weight, 0) : null;
|
|
186
|
-
const actual = valueOf(elementId);
|
|
187
|
-
const ok = expected !== null && actual !== null && Math.abs(expected - actual) < 5e-3;
|
|
188
|
-
return {
|
|
189
|
-
element: model.elements[elementId] ?? fallbackElement(elementId),
|
|
190
|
-
columnIndex,
|
|
191
|
-
terms,
|
|
192
|
-
expected,
|
|
193
|
-
actual,
|
|
194
|
-
ok
|
|
545
|
+
function buildPivots(model, configFor) {
|
|
546
|
+
const order = (ib) => {
|
|
547
|
+
if (ib.structureId) {
|
|
548
|
+
const s = model.structures.find((x) => x.id === ib.structureId);
|
|
549
|
+
if (s?.order !== void 0) return s.order;
|
|
550
|
+
}
|
|
551
|
+
return BLOCK_ORDER[ib.blockType] ?? 99;
|
|
195
552
|
};
|
|
553
|
+
return [...model.informationBlocks].sort((a, b) => order(a) - order(b) || a.blockType.localeCompare(b.blockType)).map(
|
|
554
|
+
(ib) => configFor ? buildPivot(model, ib, configFor(ib, defaultPivotConfig(model, ib))) : buildPivot(model, ib)
|
|
555
|
+
);
|
|
196
556
|
}
|
|
197
557
|
var styles = {
|
|
198
558
|
status: {
|
|
@@ -298,8 +658,6 @@ var styles2 = {
|
|
|
298
658
|
gap: "1rem",
|
|
299
659
|
marginBottom: "0.75rem"
|
|
300
660
|
},
|
|
301
|
-
// `minWidth: 0` lets this flex child shrink below its content so the long
|
|
302
|
-
// label / qname wrap inside the panel instead of overflowing it.
|
|
303
661
|
headerText: { minWidth: 0 },
|
|
304
662
|
label: { fontSize: "1rem", fontWeight: 700, margin: 0, overflowWrap: "anywhere" },
|
|
305
663
|
qname: {
|
|
@@ -307,7 +665,6 @@ var styles2 = {
|
|
|
307
665
|
fontFamily: "var(--rs-font-mono, ui-monospace, monospace)",
|
|
308
666
|
fontSize: "0.8rem",
|
|
309
667
|
color: "var(--rs-primary-700, #1d4ed8)",
|
|
310
|
-
// qnames are single unbroken tokens — break anywhere so they don't overflow.
|
|
311
668
|
overflowWrap: "anywhere"
|
|
312
669
|
},
|
|
313
670
|
close: {
|
|
@@ -327,6 +684,7 @@ var styles2 = {
|
|
|
327
684
|
},
|
|
328
685
|
key: { color: "var(--rs-muted, #6b7280)" },
|
|
329
686
|
mono: { fontFamily: "var(--rs-font-mono, ui-monospace, monospace)" },
|
|
687
|
+
dims: { display: "grid", gap: "0.15rem" },
|
|
330
688
|
footing: {
|
|
331
689
|
marginTop: "0.85rem",
|
|
332
690
|
paddingTop: "0.75rem",
|
|
@@ -351,20 +709,19 @@ function Field({ k, children }) {
|
|
|
351
709
|
/* @__PURE__ */ jsx("span", { children })
|
|
352
710
|
] });
|
|
353
711
|
}
|
|
354
|
-
function
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
columnIndex,
|
|
359
|
-
onClose,
|
|
360
|
-
currencySymbol = "$"
|
|
361
|
-
}) {
|
|
712
|
+
function symbolFor(unit) {
|
|
713
|
+
return unit?.symbol ?? currencySymbolFor(unit?.measure) ?? "$";
|
|
714
|
+
}
|
|
715
|
+
function FactInspector({ report, table, row, columnIndex, onClose }) {
|
|
362
716
|
const { element } = row;
|
|
363
717
|
const cell = row.cells[columnIndex];
|
|
364
|
-
const
|
|
365
|
-
const
|
|
366
|
-
const
|
|
367
|
-
const
|
|
718
|
+
const fact = cell?.fact ?? null;
|
|
719
|
+
const period = fact ? report.periods[fact.period] : null;
|
|
720
|
+
const unit = fact?.unit ? report.units[fact.unit] : void 0;
|
|
721
|
+
const kind = numericKindOf(element);
|
|
722
|
+
const dims = fact?.dimensions ?? row.members;
|
|
723
|
+
const foot = footCheck(report, table, element.id, columnIndex);
|
|
724
|
+
const sym = symbolFor(unit);
|
|
368
725
|
return /* @__PURE__ */ jsxs("div", { style: styles2.panel, children: [
|
|
369
726
|
/* @__PURE__ */ jsxs("div", { style: styles2.header, children: [
|
|
370
727
|
/* @__PURE__ */ jsxs("div", { style: styles2.headerText, children: [
|
|
@@ -374,19 +731,28 @@ function FactInspector({
|
|
|
374
731
|
onClose ? /* @__PURE__ */ jsx("button", { style: styles2.close, onClick: onClose, "aria-label": "Close", children: "\xD7" }) : null
|
|
375
732
|
] }),
|
|
376
733
|
/* @__PURE__ */ jsxs("div", { style: styles2.grid, children: [
|
|
377
|
-
/* @__PURE__ */ jsx(Field, { k: "Value", children: cell
|
|
378
|
-
/* @__PURE__ */ jsxs(Field, { k: "Period", children: [
|
|
379
|
-
formatPeriod(
|
|
734
|
+
/* @__PURE__ */ jsx(Field, { k: "Value", children: cell?.value === null && cell?.textValue ? /* @__PURE__ */ jsx("span", { children: humanizeDuration(cell.textValue) ?? cell.textValue }) : /* @__PURE__ */ jsx("span", { style: styles2.mono, children: formatValue(cell?.value ?? null, { numericKind: kind, symbol: sym }) }) }),
|
|
735
|
+
period ? /* @__PURE__ */ jsxs(Field, { k: "Period", children: [
|
|
736
|
+
formatPeriod(period),
|
|
380
737
|
" ",
|
|
381
738
|
/* @__PURE__ */ jsxs("span", { style: styles2.key, children: [
|
|
382
739
|
"(",
|
|
383
|
-
|
|
740
|
+
period.type,
|
|
384
741
|
")"
|
|
385
742
|
] })
|
|
386
|
-
] }),
|
|
743
|
+
] }) : null,
|
|
744
|
+
report.entity ? /* @__PURE__ */ jsx(Field, { k: "Entity", children: report.entity.name }) : null,
|
|
745
|
+
dims.length ? /* @__PURE__ */ jsx(Field, { k: "Dimensions", children: /* @__PURE__ */ jsx("span", { style: styles2.dims, children: dims.map((d) => /* @__PURE__ */ jsxs("span", { children: [
|
|
746
|
+
/* @__PURE__ */ jsxs("span", { style: styles2.key, children: [
|
|
747
|
+
d.axisLabel,
|
|
748
|
+
":"
|
|
749
|
+
] }),
|
|
750
|
+
" ",
|
|
751
|
+
d.explicit ? d.memberLabel : d.typedValue ?? d.memberLabel
|
|
752
|
+
] }, d.axis)) }) }) : null,
|
|
387
753
|
/* @__PURE__ */ jsx(Field, { k: "Unit", children: unit ? unit.label : "\u2014" }),
|
|
388
754
|
element.balance ? /* @__PURE__ */ jsx(Field, { k: "Balance", children: element.balance }) : null,
|
|
389
|
-
|
|
755
|
+
fact?.decimals ? /* @__PURE__ */ jsx(Field, { k: "Decimals", children: fact.decimals }) : null
|
|
390
756
|
] }),
|
|
391
757
|
foot ? /* @__PURE__ */ jsxs("div", { style: styles2.footing, children: [
|
|
392
758
|
/* @__PURE__ */ jsxs("div", { style: styles2.footTitle, children: [
|
|
@@ -398,17 +764,18 @@ function FactInspector({
|
|
|
398
764
|
const op = i === 0 ? term.weight < 0 ? "\u2212" : "" : term.weight < 0 ? " \u2212 " : " + ";
|
|
399
765
|
return /* @__PURE__ */ jsxs("span", { title: term.element.qname, children: [
|
|
400
766
|
op,
|
|
401
|
-
|
|
402
|
-
|
|
767
|
+
formatValue(term.value === null ? 0 : Math.abs(term.value), {
|
|
768
|
+
numericKind: "monetary",
|
|
769
|
+
symbol: sym
|
|
403
770
|
})
|
|
404
771
|
] }, term.element.id);
|
|
405
772
|
}),
|
|
406
773
|
" = ",
|
|
407
|
-
/* @__PURE__ */ jsx("strong", { children:
|
|
774
|
+
/* @__PURE__ */ jsx("strong", { children: formatValue(foot.expected, { numericKind: "monetary", symbol: sym }) }),
|
|
408
775
|
" ",
|
|
409
776
|
foot.ok ? /* @__PURE__ */ jsx("span", { style: { color: "var(--rs-success, #00875a)" }, children: "\u2713" }) : /* @__PURE__ */ jsxs("span", { style: { color: "var(--rs-error, #dc2626)" }, children: [
|
|
410
777
|
"\u2717 (reported ",
|
|
411
|
-
|
|
778
|
+
formatValue(foot.actual, { numericKind: "monetary", symbol: sym }),
|
|
412
779
|
")"
|
|
413
780
|
] })
|
|
414
781
|
] })
|
|
@@ -421,10 +788,7 @@ var styles3 = {
|
|
|
421
788
|
color: "var(--rs-text, #111827)",
|
|
422
789
|
marginBottom: "2.5rem"
|
|
423
790
|
},
|
|
424
|
-
heading: {
|
|
425
|
-
textAlign: "center",
|
|
426
|
-
marginBottom: "1rem"
|
|
427
|
-
},
|
|
791
|
+
heading: { textAlign: "center", marginBottom: "0.75rem" },
|
|
428
792
|
title: {
|
|
429
793
|
fontFamily: "var(--rs-font-heading, var(--rs-font-sans, sans-serif))",
|
|
430
794
|
fontSize: "1.15rem",
|
|
@@ -432,28 +796,92 @@ var styles3 = {
|
|
|
432
796
|
letterSpacing: "0.02em",
|
|
433
797
|
margin: 0
|
|
434
798
|
},
|
|
435
|
-
subtitle: {
|
|
436
|
-
|
|
799
|
+
subtitle: { fontSize: "0.85rem", color: "var(--rs-muted, #6b7280)", marginTop: "0.25rem" },
|
|
800
|
+
scaleCaption: {
|
|
801
|
+
fontSize: "0.8rem",
|
|
802
|
+
fontStyle: "italic",
|
|
437
803
|
color: "var(--rs-muted, #6b7280)",
|
|
438
804
|
marginTop: "0.25rem"
|
|
439
805
|
},
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
806
|
+
slicers: {
|
|
807
|
+
display: "flex",
|
|
808
|
+
flexWrap: "wrap",
|
|
809
|
+
gap: "0.5rem",
|
|
810
|
+
margin: "0 0 0.75rem"
|
|
811
|
+
},
|
|
812
|
+
slicer: {
|
|
813
|
+
display: "inline-flex",
|
|
814
|
+
gap: "0.4rem",
|
|
815
|
+
alignItems: "baseline",
|
|
816
|
+
border: "1px solid var(--rs-border, #e5e7eb)",
|
|
817
|
+
borderRadius: "6px",
|
|
818
|
+
padding: "0.2rem 0.55rem",
|
|
819
|
+
fontSize: "0.78rem",
|
|
820
|
+
background: "var(--rs-slicer-bg, #f9fafb)"
|
|
821
|
+
},
|
|
822
|
+
slicerLabel: {
|
|
823
|
+
color: "var(--rs-muted, #6b7280)",
|
|
824
|
+
textTransform: "uppercase",
|
|
825
|
+
letterSpacing: "0.03em",
|
|
826
|
+
fontSize: "0.68rem"
|
|
444
827
|
},
|
|
828
|
+
slicerValue: { fontWeight: 600 },
|
|
829
|
+
controls: {
|
|
830
|
+
display: "flex",
|
|
831
|
+
alignItems: "center",
|
|
832
|
+
gap: "0.5rem",
|
|
833
|
+
margin: "0 0 0.9rem",
|
|
834
|
+
fontSize: "0.8rem"
|
|
835
|
+
},
|
|
836
|
+
controlLabel: { color: "var(--rs-muted, #6b7280)" },
|
|
837
|
+
segmented: {
|
|
838
|
+
display: "inline-flex",
|
|
839
|
+
border: "1px solid var(--rs-border, #e5e7eb)",
|
|
840
|
+
borderRadius: "7px",
|
|
841
|
+
overflow: "hidden"
|
|
842
|
+
},
|
|
843
|
+
segment: {
|
|
844
|
+
border: "none",
|
|
845
|
+
padding: "0.25rem 0.7rem",
|
|
846
|
+
fontSize: "0.78rem",
|
|
847
|
+
cursor: "pointer",
|
|
848
|
+
background: "transparent",
|
|
849
|
+
color: "var(--rs-text, #111827)"
|
|
850
|
+
},
|
|
851
|
+
segmentActive: {
|
|
852
|
+
background: "var(--rs-primary-600, #2563eb)",
|
|
853
|
+
color: "#ffffff",
|
|
854
|
+
fontWeight: 600
|
|
855
|
+
},
|
|
856
|
+
scroll: { overflowX: "auto" },
|
|
857
|
+
table: { width: "100%", borderCollapse: "collapse", fontSize: "0.875rem" },
|
|
445
858
|
th: {
|
|
446
859
|
borderBottom: "2px solid var(--rs-border-strong, #9ca3af)",
|
|
447
|
-
padding: "0.
|
|
860
|
+
padding: "0.4rem 0.75rem",
|
|
448
861
|
fontWeight: 600,
|
|
449
862
|
color: "var(--rs-muted, #6b7280)",
|
|
450
863
|
fontSize: "0.8rem"
|
|
451
864
|
},
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
labelCell: {
|
|
865
|
+
thGroup: {
|
|
866
|
+
borderBottom: "1px solid var(--rs-border, #e5e7eb)",
|
|
455
867
|
padding: "0.3rem 0.75rem",
|
|
456
|
-
|
|
868
|
+
fontWeight: 600,
|
|
869
|
+
color: "var(--rs-muted, #6b7280)",
|
|
870
|
+
fontSize: "0.8rem",
|
|
871
|
+
textAlign: "center"
|
|
872
|
+
},
|
|
873
|
+
// A floor on the concept column so a wide table (many member/period columns)
|
|
874
|
+
// scrolls horizontally rather than squeezing labels down to vertical text.
|
|
875
|
+
thConcept: { textAlign: "left", verticalAlign: "bottom", minWidth: "18rem" },
|
|
876
|
+
thNum: { textAlign: "right", whiteSpace: "nowrap" },
|
|
877
|
+
labelCell: { padding: "0.3rem 0.75rem", whiteSpace: "normal", minWidth: "18rem" },
|
|
878
|
+
headerRowLabel: {
|
|
879
|
+
padding: "0.4rem 0.75rem",
|
|
880
|
+
fontWeight: 700,
|
|
881
|
+
fontSize: "0.8rem",
|
|
882
|
+
textTransform: "uppercase",
|
|
883
|
+
letterSpacing: "0.02em",
|
|
884
|
+
color: "var(--rs-muted, #6b7280)"
|
|
457
885
|
},
|
|
458
886
|
numCell: {
|
|
459
887
|
padding: "0.3rem 0.75rem",
|
|
@@ -462,10 +890,7 @@ var styles3 = {
|
|
|
462
890
|
fontFamily: "var(--rs-font-mono, ui-monospace, SFMono-Regular, monospace)",
|
|
463
891
|
fontVariantNumeric: "tabular-nums"
|
|
464
892
|
},
|
|
465
|
-
textBlockCell: {
|
|
466
|
-
padding: "0.75rem",
|
|
467
|
-
borderTop: "1px solid var(--rs-border, #e5e7eb)"
|
|
468
|
-
},
|
|
893
|
+
textBlockCell: { padding: "0.75rem", borderTop: "1px solid var(--rs-border, #e5e7eb)" },
|
|
469
894
|
textBlockLabel: {
|
|
470
895
|
fontSize: "0.7rem",
|
|
471
896
|
textTransform: "uppercase",
|
|
@@ -475,35 +900,101 @@ var styles3 = {
|
|
|
475
900
|
marginBottom: "0.5rem"
|
|
476
901
|
}
|
|
477
902
|
};
|
|
903
|
+
function cellSymbol(unit) {
|
|
904
|
+
return unit?.symbol ?? currencySymbolFor(unit?.measure) ?? "$";
|
|
905
|
+
}
|
|
478
906
|
function StatementTable({
|
|
479
|
-
|
|
907
|
+
table,
|
|
908
|
+
units,
|
|
480
909
|
onCellClick,
|
|
481
|
-
|
|
482
|
-
|
|
910
|
+
selected = null,
|
|
911
|
+
dimensionPlacement,
|
|
912
|
+
onDimensionPlacementChange
|
|
483
913
|
}) {
|
|
484
|
-
const { columns, rows } =
|
|
485
|
-
const
|
|
914
|
+
const { columns, rows, columnHeaders, scale } = table;
|
|
915
|
+
const totalCols = columns.length;
|
|
916
|
+
const dimensional = table.config.rows.some((k) => k.startsWith("dim:")) || table.config.columns.some((k) => k.startsWith("dim:"));
|
|
917
|
+
const showToggle = onDimensionPlacementChange != null && dimensional;
|
|
486
918
|
return /* @__PURE__ */ jsxs("section", { style: styles3.wrap, children: [
|
|
487
919
|
/* @__PURE__ */ jsxs("header", { style: styles3.heading, children: [
|
|
488
|
-
/* @__PURE__ */ jsx("h3", { style: styles3.title, children:
|
|
489
|
-
|
|
490
|
-
|
|
920
|
+
/* @__PURE__ */ jsx("h3", { style: styles3.title, children: table.title }),
|
|
921
|
+
table.structureName ? /* @__PURE__ */ jsx("div", { style: styles3.subtitle, children: table.structureName }) : null,
|
|
922
|
+
scale.caption ? /* @__PURE__ */ jsx("div", { style: styles3.scaleCaption, children: scale.caption }) : null
|
|
491
923
|
] }),
|
|
492
|
-
/* @__PURE__ */
|
|
493
|
-
/* @__PURE__ */ jsx("
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
924
|
+
table.slicers.length ? /* @__PURE__ */ jsx("div", { style: styles3.slicers, children: table.slicers.map((s) => /* @__PURE__ */ jsxs("div", { style: styles3.slicer, children: [
|
|
925
|
+
/* @__PURE__ */ jsx("span", { style: styles3.slicerLabel, children: s.label }),
|
|
926
|
+
/* @__PURE__ */ jsx("span", { style: styles3.slicerValue, children: s.valueLabel })
|
|
927
|
+
] }, s.aspect)) }) : null,
|
|
928
|
+
showToggle ? /* @__PURE__ */ jsxs("div", { style: styles3.controls, children: [
|
|
929
|
+
/* @__PURE__ */ jsx("span", { style: styles3.controlLabel, children: "Dimensions" }),
|
|
930
|
+
/* @__PURE__ */ jsx("div", { style: styles3.segmented, role: "group", "aria-label": "Dimension placement", children: ["rows", "columns"].map((placement) => /* @__PURE__ */ jsx(
|
|
931
|
+
"button",
|
|
932
|
+
{
|
|
933
|
+
type: "button",
|
|
934
|
+
onClick: () => onDimensionPlacementChange?.(placement),
|
|
935
|
+
"aria-pressed": dimensionPlacement === placement,
|
|
936
|
+
style: {
|
|
937
|
+
...styles3.segment,
|
|
938
|
+
...dimensionPlacement === placement ? styles3.segmentActive : {}
|
|
939
|
+
},
|
|
940
|
+
children: placement === "rows" ? "As rows" : "As columns"
|
|
941
|
+
},
|
|
942
|
+
placement
|
|
943
|
+
)) })
|
|
944
|
+
] }) : null,
|
|
945
|
+
/* @__PURE__ */ jsx("div", { style: styles3.scroll, children: /* @__PURE__ */ jsxs("table", { style: styles3.table, children: [
|
|
946
|
+
/* @__PURE__ */ jsx("thead", { children: columnHeaders.map((level, levelIndex) => {
|
|
947
|
+
const isLast = levelIndex === columnHeaders.length - 1;
|
|
948
|
+
let leaf = 0;
|
|
949
|
+
return /* @__PURE__ */ jsxs("tr", { children: [
|
|
950
|
+
levelIndex === 0 ? /* @__PURE__ */ jsx(
|
|
951
|
+
"th",
|
|
952
|
+
{
|
|
953
|
+
rowSpan: columnHeaders.length,
|
|
954
|
+
style: { ...styles3.th, ...styles3.thConcept },
|
|
955
|
+
"aria-label": "Concept"
|
|
956
|
+
}
|
|
957
|
+
) : null,
|
|
958
|
+
level.map((h, i) => {
|
|
959
|
+
const key = `${levelIndex}-${i}-${leaf}`;
|
|
960
|
+
leaf += h.span;
|
|
961
|
+
return /* @__PURE__ */ jsx(
|
|
962
|
+
"th",
|
|
963
|
+
{
|
|
964
|
+
colSpan: h.span,
|
|
965
|
+
style: isLast ? { ...styles3.th, ...styles3.thNum } : styles3.thGroup,
|
|
966
|
+
children: h.label
|
|
967
|
+
},
|
|
968
|
+
key
|
|
969
|
+
);
|
|
970
|
+
})
|
|
971
|
+
] }, levelIndex);
|
|
972
|
+
}) }),
|
|
497
973
|
/* @__PURE__ */ jsx("tbody", { children: rows.map((row) => {
|
|
974
|
+
if (row.header) {
|
|
975
|
+
return /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx(
|
|
976
|
+
"td",
|
|
977
|
+
{
|
|
978
|
+
colSpan: totalCols + 1,
|
|
979
|
+
style: {
|
|
980
|
+
...styles3.headerRowLabel,
|
|
981
|
+
paddingLeft: `${row.depth * 20 + 12}px`
|
|
982
|
+
},
|
|
983
|
+
title: row.element.qname,
|
|
984
|
+
children: row.element.label
|
|
985
|
+
}
|
|
986
|
+
) }, row.key);
|
|
987
|
+
}
|
|
498
988
|
const textBlock = row.cells.find(
|
|
499
989
|
(c) => c.value === null && isExternalFactUrl(c.textValue)
|
|
500
990
|
);
|
|
501
991
|
if (textBlock?.textValue) {
|
|
502
|
-
return /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsxs("td", { colSpan:
|
|
503
|
-
/* @__PURE__ */ jsx("div", { style: styles3.textBlockLabel, title: row.element.qname, children: row
|
|
992
|
+
return /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsxs("td", { colSpan: totalCols + 1, style: styles3.textBlockCell, children: [
|
|
993
|
+
/* @__PURE__ */ jsx("div", { style: styles3.textBlockLabel, title: row.element.qname, children: rowLabel(row) }),
|
|
504
994
|
/* @__PURE__ */ jsx(ExternalTextBlock, { url: textBlock.textValue })
|
|
505
|
-
] }) }, row.
|
|
995
|
+
] }) }, row.key);
|
|
506
996
|
}
|
|
997
|
+
const kind = numericKindOf(row.element);
|
|
507
998
|
const isGrandTotal = row.isSubtotal && row.depth === 0;
|
|
508
999
|
const allZeroOrNull = row.cells.every((c) => c.value === null || c.value === 0);
|
|
509
1000
|
const rowStyle = {
|
|
@@ -517,11 +1008,14 @@ function StatementTable({
|
|
|
517
1008
|
borderBottom: isGrandTotal ? "3px double var(--rs-text, #111827)" : void 0
|
|
518
1009
|
};
|
|
519
1010
|
return /* @__PURE__ */ jsxs("tr", { style: rowStyle, children: [
|
|
520
|
-
/* @__PURE__ */ jsx("td", { style: labelStyle, title: row.element.qname, children: row
|
|
1011
|
+
/* @__PURE__ */ jsx("td", { style: labelStyle, title: row.element.qname, children: rowLabel(row) }),
|
|
521
1012
|
row.cells.map((cell, i) => {
|
|
522
|
-
const isSelected = selected !== null && selected.
|
|
1013
|
+
const isSelected = selected !== null && selected.rowKey === row.key && selected.columnIndex === i;
|
|
523
1014
|
const clickable = onCellClick && cell.fact !== null;
|
|
524
1015
|
const isText = cell.value === null && cell.textValue != null && cell.textValue !== "";
|
|
1016
|
+
const duration = isText ? humanizeDuration(cell.textValue) : null;
|
|
1017
|
+
const isProse = isText && !duration;
|
|
1018
|
+
const unit = cell.fact?.unit && units ? units[cell.fact.unit] : void 0;
|
|
525
1019
|
const numStyle = {
|
|
526
1020
|
...styles3.numCell,
|
|
527
1021
|
fontWeight: row.isSubtotal ? 600 : 400,
|
|
@@ -529,27 +1023,36 @@ function StatementTable({
|
|
|
529
1023
|
cursor: clickable ? "pointer" : "default",
|
|
530
1024
|
backgroundColor: isSelected ? "var(--rs-selected-bg, #dbeafe)" : void 0,
|
|
531
1025
|
color: !isText && !row.isSubtotal && (cell.value === null || cell.value === 0) ? "var(--rs-muted, #9ca3af)" : void 0,
|
|
532
|
-
...
|
|
1026
|
+
...isProse ? {
|
|
533
1027
|
textAlign: "left",
|
|
534
1028
|
whiteSpace: "normal",
|
|
535
1029
|
fontFamily: "var(--rs-font-sans, ui-sans-serif, system-ui, sans-serif)"
|
|
536
1030
|
} : {}
|
|
537
1031
|
};
|
|
1032
|
+
const display = isText ? duration ?? cell.textValue : cell.fact === null ? "" : cell.value === 0 ? "\u2014" : formatValue(cell.value, {
|
|
1033
|
+
numericKind: kind,
|
|
1034
|
+
symbol: cellSymbol(unit),
|
|
1035
|
+
scaleFactor: scale.factor
|
|
1036
|
+
});
|
|
538
1037
|
return /* @__PURE__ */ jsx(
|
|
539
1038
|
"td",
|
|
540
1039
|
{
|
|
541
1040
|
style: numStyle,
|
|
542
1041
|
onClick: clickable ? () => onCellClick?.(row, i) : void 0,
|
|
543
|
-
children:
|
|
1042
|
+
children: display
|
|
544
1043
|
},
|
|
545
1044
|
columns[i].key
|
|
546
1045
|
);
|
|
547
1046
|
})
|
|
548
|
-
] }, row.
|
|
1047
|
+
] }, row.key);
|
|
549
1048
|
}) })
|
|
550
|
-
] })
|
|
1049
|
+
] }) })
|
|
551
1050
|
] });
|
|
552
1051
|
}
|
|
1052
|
+
function rowLabel(row) {
|
|
1053
|
+
if (!row.members.length) return row.element.label;
|
|
1054
|
+
return row.members.map((m) => m.memberLabel).join(" \xB7 ");
|
|
1055
|
+
}
|
|
553
1056
|
var styles4 = {
|
|
554
1057
|
layout: {
|
|
555
1058
|
display: "grid",
|
|
@@ -573,11 +1076,19 @@ var styles4 = {
|
|
|
573
1076
|
},
|
|
574
1077
|
panel: { position: "sticky", top: "1rem" }
|
|
575
1078
|
};
|
|
576
|
-
function ReportView({ report,
|
|
577
|
-
const
|
|
1079
|
+
function ReportView({ report, inspect = true, dimensionAxis = "rows" }) {
|
|
1080
|
+
const [dimAxis, setDimAxis] = useState(dimensionAxis);
|
|
578
1081
|
const [selection, setSelection] = useState(null);
|
|
1082
|
+
const statements = useMemo(
|
|
1083
|
+
() => buildPivots(report, (_ib, cfg) => pivotDimensionsOn(cfg, dimAxis)),
|
|
1084
|
+
[report, dimAxis]
|
|
1085
|
+
);
|
|
579
1086
|
const selected = selection !== null ? statements[selection.statementIndex] : null;
|
|
580
|
-
const selectedRow = selected?.rows.find((r) => r.
|
|
1087
|
+
const selectedRow = selected?.rows.find((r) => r.key === selection?.rowKey) ?? null;
|
|
1088
|
+
const setPlacement = (placement) => {
|
|
1089
|
+
setSelection(null);
|
|
1090
|
+
setDimAxis(placement);
|
|
1091
|
+
};
|
|
581
1092
|
return /* @__PURE__ */ jsxs("div", { children: [
|
|
582
1093
|
report.entity ? /* @__PURE__ */ jsx("header", { style: styles4.entity, children: /* @__PURE__ */ jsx("h2", { style: styles4.entityName, children: report.entity.name }) }) : null,
|
|
583
1094
|
/* @__PURE__ */ jsxs(
|
|
@@ -591,17 +1102,12 @@ function ReportView({ report, currencySymbol = "$", inspect = true }) {
|
|
|
591
1102
|
/* @__PURE__ */ jsx("div", { children: statements.map((statement, statementIndex) => /* @__PURE__ */ jsx(
|
|
592
1103
|
StatementTable,
|
|
593
1104
|
{
|
|
594
|
-
statement,
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
} :
|
|
600
|
-
onCellClick: inspect ? (row, columnIndex) => setSelection({
|
|
601
|
-
statementIndex,
|
|
602
|
-
elementId: row.element.id,
|
|
603
|
-
columnIndex
|
|
604
|
-
}) : void 0
|
|
1105
|
+
table: statement,
|
|
1106
|
+
units: report.units,
|
|
1107
|
+
dimensionPlacement: dimAxis,
|
|
1108
|
+
onDimensionPlacementChange: setPlacement,
|
|
1109
|
+
selected: selection && selection.statementIndex === statementIndex ? { rowKey: selection.rowKey, columnIndex: selection.columnIndex } : null,
|
|
1110
|
+
onCellClick: inspect ? (row, columnIndex) => setSelection({ statementIndex, rowKey: row.key, columnIndex }) : void 0
|
|
605
1111
|
},
|
|
606
1112
|
statement.ib.id
|
|
607
1113
|
)) }),
|
|
@@ -609,10 +1115,9 @@ function ReportView({ report, currencySymbol = "$", inspect = true }) {
|
|
|
609
1115
|
FactInspector,
|
|
610
1116
|
{
|
|
611
1117
|
report,
|
|
612
|
-
|
|
1118
|
+
table: selected,
|
|
613
1119
|
row: selectedRow,
|
|
614
1120
|
columnIndex: selection.columnIndex,
|
|
615
|
-
currencySymbol,
|
|
616
1121
|
onClose: () => setSelection(null)
|
|
617
1122
|
}
|
|
618
1123
|
) }) : null
|
|
@@ -622,6 +1127,6 @@ function ReportView({ report, currencySymbol = "$", inspect = true }) {
|
|
|
622
1127
|
] });
|
|
623
1128
|
}
|
|
624
1129
|
|
|
625
|
-
export { ExternalTextBlock, FactInspector, ReportView, StatementTable,
|
|
1130
|
+
export { ExternalTextBlock, FactInspector, ReportView, StatementTable, buildPivot, buildPivots, calcSubtotals, defaultPivotConfig, footCheck, pivotDimensionsOn, presentationOrder, reportSections, sliceReportSection };
|
|
626
1131
|
//# sourceMappingURL=index.js.map
|
|
627
1132
|
//# sourceMappingURL=index.js.map
|