@lmfaole/basics 0.2.1 → 0.4.0
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/README.md +306 -22
- package/basic-styling/components/basic-accordion.css +65 -0
- package/basic-styling/components/basic-alert.css +27 -0
- package/basic-styling/components/basic-dialog.css +41 -0
- package/basic-styling/components/basic-popover.css +54 -0
- package/basic-styling/components/basic-summary-table.css +76 -0
- package/basic-styling/components/basic-table.css +48 -0
- package/basic-styling/components/basic-tabs.css +45 -0
- package/basic-styling/components/basic-toast.css +102 -0
- package/basic-styling/components/basic-toc.css +30 -0
- package/basic-styling/components.css +9 -0
- package/basic-styling/global.css +61 -0
- package/basic-styling/index.css +2 -0
- package/basic-styling/tokens/base.css +19 -0
- package/basic-styling/tokens/palette.css +117 -0
- package/basic-styling/tokens/palette.tokens.json +1019 -0
- package/components/basic-accordion/index.d.ts +5 -5
- package/components/basic-accordion/index.js +169 -165
- package/components/basic-alert/index.d.ts +53 -0
- package/components/basic-alert/index.js +189 -0
- package/components/basic-alert/register.d.ts +1 -0
- package/components/basic-alert/register.js +3 -0
- package/components/basic-dialog/index.js +21 -1
- package/components/basic-summary-table/index.d.ts +69 -0
- package/components/basic-summary-table/index.js +536 -0
- package/components/basic-summary-table/register.d.ts +1 -0
- package/components/basic-summary-table/register.js +3 -0
- package/components/basic-table/index.d.ts +75 -0
- package/components/basic-table/index.js +612 -0
- package/components/basic-table/register.d.ts +1 -0
- package/components/basic-table/register.js +3 -0
- package/components/basic-tabs/index.d.ts +2 -3
- package/components/basic-tabs/index.js +4 -30
- package/components/basic-toast/index.d.ts +65 -0
- package/components/basic-toast/index.js +429 -0
- package/components/basic-toast/register.d.ts +1 -0
- package/components/basic-toast/register.js +3 -0
- package/index.d.ts +4 -0
- package/index.js +4 -0
- package/package.json +28 -41
- package/readme.mdx +1 -1
|
@@ -0,0 +1,536 @@
|
|
|
1
|
+
import {
|
|
2
|
+
TableElement,
|
|
3
|
+
normalizeTableCellSpan,
|
|
4
|
+
normalizeTableRowHeaderColumn,
|
|
5
|
+
} from "../basic-table/index.js";
|
|
6
|
+
|
|
7
|
+
const HTMLTableElementBase = globalThis.HTMLTableElement ?? class {};
|
|
8
|
+
|
|
9
|
+
export const SUMMARY_TABLE_TAG_NAME = "basic-summary-table";
|
|
10
|
+
|
|
11
|
+
const DEFAULT_TOTAL_LABEL = "Totalt";
|
|
12
|
+
const GENERATED_SUMMARY_ROW_ATTRIBUTE = "data-basic-summary-table-generated-row";
|
|
13
|
+
const GENERATED_SUMMARY_CELL_ATTRIBUTE = "data-basic-summary-table-generated-cell";
|
|
14
|
+
const GENERATED_SUMMARY_LABEL_ATTRIBUTE = "data-basic-summary-table-generated-label";
|
|
15
|
+
const SUMMARY_OBSERVER_OPTIONS = {
|
|
16
|
+
subtree: true,
|
|
17
|
+
attributes: true,
|
|
18
|
+
attributeFilter: ["data-value"],
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export function normalizeSummaryColumns(value) {
|
|
22
|
+
if (!value?.trim()) {
|
|
23
|
+
return [];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return Array.from(
|
|
27
|
+
new Set(
|
|
28
|
+
value
|
|
29
|
+
.split(",")
|
|
30
|
+
.map((part) => Number.parseInt(part.trim(), 10))
|
|
31
|
+
.filter((column) => Number.isInteger(column) && column > 0),
|
|
32
|
+
),
|
|
33
|
+
).sort((left, right) => left - right);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function normalizeSummaryTotalLabel(value) {
|
|
37
|
+
return value?.trim() || DEFAULT_TOTAL_LABEL;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function normalizeSummaryLocale(value) {
|
|
41
|
+
return value?.trim() || undefined;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function findSummaryNumberMatch(value) {
|
|
45
|
+
const text = String(value ?? "").trim();
|
|
46
|
+
|
|
47
|
+
if (!text) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const match = text.match(/(?:-\s*)?\d(?:[\d\s.,]*\d)?/);
|
|
52
|
+
|
|
53
|
+
if (!match || typeof match.index !== "number") {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
text,
|
|
59
|
+
numberText: match[0],
|
|
60
|
+
start: match.index,
|
|
61
|
+
end: match.index + match[0].length,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function getDecimalSeparator(value) {
|
|
66
|
+
const numberMatch = findSummaryNumberMatch(value);
|
|
67
|
+
const text = numberMatch?.numberText.trim() ?? String(value ?? "").trim();
|
|
68
|
+
const lastComma = text.lastIndexOf(",");
|
|
69
|
+
const lastDot = text.lastIndexOf(".");
|
|
70
|
+
|
|
71
|
+
if (lastComma !== -1 && lastDot !== -1) {
|
|
72
|
+
return lastComma > lastDot ? "," : ".";
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (lastComma !== -1) {
|
|
76
|
+
const digitsAfter = text.length - lastComma - 1;
|
|
77
|
+
return digitsAfter > 0 && digitsAfter <= 2 ? "," : null;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (lastDot !== -1) {
|
|
81
|
+
const digitsAfter = text.length - lastDot - 1;
|
|
82
|
+
return digitsAfter > 0 && digitsAfter <= 2 ? "." : null;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function countFractionDigits(value) {
|
|
89
|
+
const numberMatch = findSummaryNumberMatch(value);
|
|
90
|
+
const text = numberMatch?.numberText.trim() ?? String(value ?? "").trim();
|
|
91
|
+
|
|
92
|
+
if (!text) {
|
|
93
|
+
return 0;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const decimalSeparator = getDecimalSeparator(text);
|
|
97
|
+
|
|
98
|
+
if (!decimalSeparator) {
|
|
99
|
+
return 0;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return text.length - text.lastIndexOf(decimalSeparator) - 1;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function parseSummaryNumber(value) {
|
|
106
|
+
const numberMatch = findSummaryNumberMatch(value);
|
|
107
|
+
const text = numberMatch?.numberText.trim() ?? String(value ?? "").trim();
|
|
108
|
+
|
|
109
|
+
if (!text) {
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const decimalSeparator = getDecimalSeparator(text);
|
|
114
|
+
let normalized = text.replace(/[^\d,.\-]/g, "");
|
|
115
|
+
|
|
116
|
+
if (!normalized || normalized === "-") {
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (decimalSeparator === ",") {
|
|
121
|
+
normalized = normalized.replace(/\./g, "").replace(",", ".");
|
|
122
|
+
} else if (decimalSeparator === ".") {
|
|
123
|
+
normalized = normalized.replace(/,/g, "");
|
|
124
|
+
} else {
|
|
125
|
+
normalized = normalized.replace(/[.,]/g, "");
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const parsed = Number.parseFloat(normalized);
|
|
129
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function formatSummaryNumber(value, { locale, fractionDigits = 0 } = {}) {
|
|
133
|
+
return new Intl.NumberFormat(locale, {
|
|
134
|
+
minimumFractionDigits: fractionDigits,
|
|
135
|
+
maximumFractionDigits: fractionDigits,
|
|
136
|
+
}).format(value);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function getSummaryAffixes(value) {
|
|
140
|
+
const numberMatch = findSummaryNumberMatch(value);
|
|
141
|
+
|
|
142
|
+
if (!numberMatch) {
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return {
|
|
147
|
+
prefix: numberMatch.text.slice(0, numberMatch.start),
|
|
148
|
+
suffix: numberMatch.text.slice(numberMatch.end),
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function formatSummaryValue(value, {
|
|
153
|
+
locale,
|
|
154
|
+
fractionDigits = 0,
|
|
155
|
+
prefix = "",
|
|
156
|
+
suffix = "",
|
|
157
|
+
} = {}) {
|
|
158
|
+
return `${prefix}${formatSummaryNumber(value, { locale, fractionDigits })}${suffix}`;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function findCellAtColumnIndex(row, targetColumnIndex) {
|
|
162
|
+
let columnIndex = 0;
|
|
163
|
+
|
|
164
|
+
for (const cell of Array.from(row.cells)) {
|
|
165
|
+
const colSpan = normalizeTableCellSpan(cell.getAttribute("colspan"));
|
|
166
|
+
|
|
167
|
+
if (targetColumnIndex >= columnIndex && targetColumnIndex < columnIndex + colSpan) {
|
|
168
|
+
return cell;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
columnIndex += colSpan;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function getTable(root) {
|
|
178
|
+
const table = root.querySelector("table");
|
|
179
|
+
return table instanceof HTMLTableElementBase && table.closest(root.tagName.toLowerCase()) === root
|
|
180
|
+
? table
|
|
181
|
+
: null;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function getLogicalColumnCount(table) {
|
|
185
|
+
let maxColumns = 0;
|
|
186
|
+
|
|
187
|
+
for (const row of Array.from(table.rows)) {
|
|
188
|
+
if (row.hasAttribute(GENERATED_SUMMARY_ROW_ATTRIBUTE)) {
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
let columnCount = 0;
|
|
193
|
+
|
|
194
|
+
for (const cell of Array.from(row.cells)) {
|
|
195
|
+
columnCount += normalizeTableCellSpan(cell.getAttribute("colspan"));
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
maxColumns = Math.max(maxColumns, columnCount);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return maxColumns;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function getBodyRows(table) {
|
|
205
|
+
if (table.tBodies.length > 0) {
|
|
206
|
+
return Array.from(table.tBodies).flatMap((section) => Array.from(section.rows));
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
return Array.from(table.rows).filter(
|
|
210
|
+
(row) => row.parentElement?.tagName !== "THEAD" && row.parentElement?.tagName !== "TFOOT",
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function collectSummaryColumns(table, configuredColumns, labelColumnIndex) {
|
|
215
|
+
if (configuredColumns.length > 0) {
|
|
216
|
+
return configuredColumns
|
|
217
|
+
.map((column) => column - 1)
|
|
218
|
+
.filter((column) => column >= 0 && column !== labelColumnIndex);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const inferredColumns = new Set();
|
|
222
|
+
|
|
223
|
+
for (const row of getBodyRows(table)) {
|
|
224
|
+
const logicalColumnCount = getLogicalColumnCount(table);
|
|
225
|
+
|
|
226
|
+
for (let columnIndex = 0; columnIndex < logicalColumnCount; columnIndex += 1) {
|
|
227
|
+
if (columnIndex === labelColumnIndex) {
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const cell = findCellAtColumnIndex(row, columnIndex);
|
|
232
|
+
const rawValue = cell?.getAttribute("data-value") ?? cell?.textContent ?? "";
|
|
233
|
+
|
|
234
|
+
if (parseSummaryNumber(rawValue) !== null) {
|
|
235
|
+
inferredColumns.add(columnIndex);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return Array.from(inferredColumns).sort((left, right) => left - right);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function calculateSummaryTotals(table, summaryColumns) {
|
|
244
|
+
const totals = new Map();
|
|
245
|
+
|
|
246
|
+
for (const columnIndex of summaryColumns) {
|
|
247
|
+
totals.set(columnIndex, {
|
|
248
|
+
total: 0,
|
|
249
|
+
fractionDigits: 0,
|
|
250
|
+
prefix: "",
|
|
251
|
+
suffix: "",
|
|
252
|
+
affixInitialized: false,
|
|
253
|
+
affixConsistent: true,
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
for (const row of getBodyRows(table)) {
|
|
258
|
+
for (const columnIndex of summaryColumns) {
|
|
259
|
+
const cell = findCellAtColumnIndex(row, columnIndex);
|
|
260
|
+
const rawValue = cell?.getAttribute("data-value") ?? cell?.textContent ?? "";
|
|
261
|
+
const parsedValue = parseSummaryNumber(rawValue);
|
|
262
|
+
|
|
263
|
+
if (parsedValue === null) {
|
|
264
|
+
continue;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const current = totals.get(columnIndex);
|
|
268
|
+
|
|
269
|
+
if (!current) {
|
|
270
|
+
continue;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
current.total += parsedValue;
|
|
274
|
+
current.fractionDigits = Math.max(current.fractionDigits, countFractionDigits(rawValue));
|
|
275
|
+
|
|
276
|
+
const affixes = getSummaryAffixes(cell?.textContent ?? "");
|
|
277
|
+
|
|
278
|
+
if (!affixes) {
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
if (!current.affixInitialized) {
|
|
283
|
+
current.prefix = affixes.prefix;
|
|
284
|
+
current.suffix = affixes.suffix;
|
|
285
|
+
current.affixInitialized = true;
|
|
286
|
+
continue;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
if (current.prefix !== affixes.prefix || current.suffix !== affixes.suffix) {
|
|
290
|
+
current.affixConsistent = false;
|
|
291
|
+
current.prefix = "";
|
|
292
|
+
current.suffix = "";
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
return totals;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function ensureGeneratedSummaryRow(table) {
|
|
301
|
+
let tfoot = table.tFoot;
|
|
302
|
+
|
|
303
|
+
if (!tfoot) {
|
|
304
|
+
tfoot = document.createElement("tfoot");
|
|
305
|
+
table.append(tfoot);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
let row = tfoot.querySelector(`tr[${GENERATED_SUMMARY_ROW_ATTRIBUTE}]`);
|
|
309
|
+
|
|
310
|
+
if (!row) {
|
|
311
|
+
row = document.createElement("tr");
|
|
312
|
+
row.setAttribute(GENERATED_SUMMARY_ROW_ATTRIBUTE, "");
|
|
313
|
+
tfoot.append(row);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
return row;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function ensureSummaryRowCell(row, columnIndex, tagName) {
|
|
320
|
+
const expectedTagName = tagName.toUpperCase();
|
|
321
|
+
const existingCell = row.cells[columnIndex] ?? null;
|
|
322
|
+
|
|
323
|
+
if (existingCell?.tagName === expectedTagName) {
|
|
324
|
+
return existingCell;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
const replacement = document.createElement(tagName);
|
|
328
|
+
|
|
329
|
+
if (existingCell) {
|
|
330
|
+
row.replaceChild(replacement, existingCell);
|
|
331
|
+
return replacement;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
row.append(replacement);
|
|
335
|
+
return replacement;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
function syncSummaryCellText(cell, text) {
|
|
339
|
+
if (cell.textContent !== text) {
|
|
340
|
+
cell.textContent = text;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
function removeGeneratedSummaryRow(table) {
|
|
345
|
+
table.querySelector(`tr[${GENERATED_SUMMARY_ROW_ATTRIBUTE}]`)?.remove();
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
function syncSummaryFooter(table, {
|
|
349
|
+
labelColumnIndex,
|
|
350
|
+
locale,
|
|
351
|
+
summaryColumns,
|
|
352
|
+
totalLabel,
|
|
353
|
+
totals,
|
|
354
|
+
}) {
|
|
355
|
+
if (summaryColumns.length === 0) {
|
|
356
|
+
removeGeneratedSummaryRow(table);
|
|
357
|
+
return false;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
const logicalColumnCount = getLogicalColumnCount(table);
|
|
361
|
+
const effectiveLabelColumnIndex = Math.min(labelColumnIndex, Math.max(logicalColumnCount - 1, 0));
|
|
362
|
+
const summaryColumnSet = new Set(summaryColumns.filter((column) => column < logicalColumnCount));
|
|
363
|
+
const row = ensureGeneratedSummaryRow(table);
|
|
364
|
+
|
|
365
|
+
for (let columnIndex = 0; columnIndex < logicalColumnCount; columnIndex += 1) {
|
|
366
|
+
if (columnIndex === effectiveLabelColumnIndex) {
|
|
367
|
+
const labelCell = ensureSummaryRowCell(row, columnIndex, "th");
|
|
368
|
+
|
|
369
|
+
if (labelCell.getAttribute("scope") !== "row") {
|
|
370
|
+
labelCell.setAttribute("scope", "row");
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
labelCell.setAttribute(GENERATED_SUMMARY_LABEL_ATTRIBUTE, "");
|
|
374
|
+
labelCell.removeAttribute(GENERATED_SUMMARY_CELL_ATTRIBUTE);
|
|
375
|
+
labelCell.removeAttribute("data-summary-total");
|
|
376
|
+
labelCell.removeAttribute("data-summary-empty");
|
|
377
|
+
delete labelCell.dataset.value;
|
|
378
|
+
syncSummaryCellText(labelCell, totalLabel);
|
|
379
|
+
continue;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
const valueCell = ensureSummaryRowCell(row, columnIndex, "td");
|
|
383
|
+
valueCell.setAttribute(GENERATED_SUMMARY_CELL_ATTRIBUTE, "");
|
|
384
|
+
valueCell.removeAttribute(GENERATED_SUMMARY_LABEL_ATTRIBUTE);
|
|
385
|
+
valueCell.removeAttribute("scope");
|
|
386
|
+
|
|
387
|
+
if (summaryColumnSet.has(columnIndex)) {
|
|
388
|
+
const summary = totals.get(columnIndex) ?? {
|
|
389
|
+
total: 0,
|
|
390
|
+
fractionDigits: 0,
|
|
391
|
+
prefix: "",
|
|
392
|
+
suffix: "",
|
|
393
|
+
affixInitialized: false,
|
|
394
|
+
affixConsistent: true,
|
|
395
|
+
};
|
|
396
|
+
const formattedValue = formatSummaryValue(summary.total, {
|
|
397
|
+
locale,
|
|
398
|
+
fractionDigits: summary.fractionDigits,
|
|
399
|
+
prefix: summary.affixConsistent ? summary.prefix : "",
|
|
400
|
+
suffix: summary.affixConsistent ? summary.suffix : "",
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
syncSummaryCellText(valueCell, formattedValue);
|
|
404
|
+
valueCell.dataset.value = String(summary.total);
|
|
405
|
+
valueCell.dataset.summaryTotal = "";
|
|
406
|
+
valueCell.removeAttribute("data-summary-empty");
|
|
407
|
+
} else {
|
|
408
|
+
syncSummaryCellText(valueCell, "");
|
|
409
|
+
delete valueCell.dataset.value;
|
|
410
|
+
valueCell.removeAttribute("data-summary-total");
|
|
411
|
+
valueCell.dataset.summaryEmpty = "";
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
while (row.cells.length > logicalColumnCount) {
|
|
416
|
+
row.deleteCell(-1);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
return true;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
export class SummaryTableElement extends TableElement {
|
|
423
|
+
static observedAttributes = [
|
|
424
|
+
...TableElement.observedAttributes,
|
|
425
|
+
"data-locale",
|
|
426
|
+
"data-summary-columns",
|
|
427
|
+
"data-total-label",
|
|
428
|
+
];
|
|
429
|
+
|
|
430
|
+
#summaryObserver = null;
|
|
431
|
+
#summaryObserving = false;
|
|
432
|
+
#scheduledFrame = 0;
|
|
433
|
+
|
|
434
|
+
connectedCallback() {
|
|
435
|
+
super.connectedCallback();
|
|
436
|
+
this.#syncSummaryObserver();
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
disconnectedCallback() {
|
|
440
|
+
super.disconnectedCallback();
|
|
441
|
+
this.#stopSummaryObserving();
|
|
442
|
+
this.#summaryObserver = null;
|
|
443
|
+
|
|
444
|
+
if (this.#scheduledFrame !== 0 && typeof window !== "undefined") {
|
|
445
|
+
window.cancelAnimationFrame(this.#scheduledFrame);
|
|
446
|
+
this.#scheduledFrame = 0;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
refresh() {
|
|
451
|
+
this.#stopSummaryObserving();
|
|
452
|
+
|
|
453
|
+
try {
|
|
454
|
+
const table = getTable(this);
|
|
455
|
+
|
|
456
|
+
if (!(table instanceof HTMLTableElementBase)) {
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
const labelColumnIndex = normalizeTableRowHeaderColumn(
|
|
461
|
+
this.getAttribute("data-row-header-column"),
|
|
462
|
+
) - 1;
|
|
463
|
+
const summaryColumns = collectSummaryColumns(
|
|
464
|
+
table,
|
|
465
|
+
normalizeSummaryColumns(this.getAttribute("data-summary-columns")),
|
|
466
|
+
labelColumnIndex,
|
|
467
|
+
);
|
|
468
|
+
const totals = calculateSummaryTotals(table, summaryColumns);
|
|
469
|
+
|
|
470
|
+
syncSummaryFooter(table, {
|
|
471
|
+
labelColumnIndex,
|
|
472
|
+
locale: normalizeSummaryLocale(this.getAttribute("data-locale")),
|
|
473
|
+
summaryColumns,
|
|
474
|
+
totalLabel: normalizeSummaryTotalLabel(this.getAttribute("data-total-label")),
|
|
475
|
+
totals,
|
|
476
|
+
});
|
|
477
|
+
|
|
478
|
+
super.refresh();
|
|
479
|
+
} finally {
|
|
480
|
+
this.#startSummaryObserving();
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
#scheduleRefresh() {
|
|
485
|
+
if (this.#scheduledFrame !== 0 || typeof window === "undefined") {
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
this.#scheduledFrame = window.requestAnimationFrame(() => {
|
|
490
|
+
this.#scheduledFrame = 0;
|
|
491
|
+
this.refresh();
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
#syncSummaryObserver() {
|
|
496
|
+
if (this.#summaryObserver || typeof MutationObserver === "undefined") {
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
this.#summaryObserver = new MutationObserver(() => {
|
|
501
|
+
this.#scheduleRefresh();
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
this.#startSummaryObserving();
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
#startSummaryObserving() {
|
|
508
|
+
if (!this.#summaryObserver || this.#summaryObserving) {
|
|
509
|
+
return;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
this.#summaryObserver.observe(this, SUMMARY_OBSERVER_OPTIONS);
|
|
513
|
+
this.#summaryObserving = true;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
#stopSummaryObserving() {
|
|
517
|
+
if (!this.#summaryObserver || !this.#summaryObserving) {
|
|
518
|
+
return;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
this.#summaryObserver.disconnect();
|
|
522
|
+
this.#summaryObserving = false;
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
export function defineSummaryTable(registry = globalThis.customElements) {
|
|
527
|
+
if (!registry?.get || !registry?.define) {
|
|
528
|
+
return SummaryTableElement;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
if (!registry.get(SUMMARY_TABLE_TAG_NAME)) {
|
|
532
|
+
registry.define(SUMMARY_TABLE_TAG_NAME, SummaryTableElement);
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
return SummaryTableElement;
|
|
536
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
export const TABLE_TAG_NAME: "basic-table";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Normalizes unsupported or empty labels back to the default `"Tabell"`.
|
|
5
|
+
*/
|
|
6
|
+
export function normalizeTableLabel(
|
|
7
|
+
value?: string | null,
|
|
8
|
+
): string;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Normalizes the generated caption text. Empty values disable caption generation.
|
|
12
|
+
*/
|
|
13
|
+
export function normalizeTableCaption(
|
|
14
|
+
value?: string | null,
|
|
15
|
+
): string;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Normalizes the generated description text. Empty values disable description generation.
|
|
19
|
+
*/
|
|
20
|
+
export function normalizeTableDescription(
|
|
21
|
+
value?: string | null,
|
|
22
|
+
): string;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Normalizes the root `data-row-headers` attribute into a boolean flag.
|
|
26
|
+
*/
|
|
27
|
+
export function normalizeTableRowHeaders(
|
|
28
|
+
value?: string | boolean | null,
|
|
29
|
+
): boolean;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Normalizes the root `data-column-headers` attribute into a boolean flag.
|
|
33
|
+
*/
|
|
34
|
+
export function normalizeTableColumnHeaders(
|
|
35
|
+
value?: string | boolean | null,
|
|
36
|
+
): boolean;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Normalizes invalid row-header-column values back to `1`.
|
|
40
|
+
*/
|
|
41
|
+
export function normalizeTableRowHeaderColumn(
|
|
42
|
+
value?: string | null,
|
|
43
|
+
): number;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Normalizes invalid `rowspan` and `colspan` values back to `1`.
|
|
47
|
+
*/
|
|
48
|
+
export function normalizeTableCellSpan(
|
|
49
|
+
value?: string | null,
|
|
50
|
+
): number;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Custom element that upgrades existing table markup with stronger accessible
|
|
54
|
+
* naming and header associations.
|
|
55
|
+
*
|
|
56
|
+
* Attributes:
|
|
57
|
+
* - `data-caption`: optional generated `<caption>` text when the table has none
|
|
58
|
+
* - `data-column-headers`: promotes the first row to column headers
|
|
59
|
+
* - `data-description`: optional generated description wired through `aria-describedby`
|
|
60
|
+
* - `data-label`: fallback accessible name when the table has neither a caption
|
|
61
|
+
* nor its own label
|
|
62
|
+
* - `data-row-header-column`: one-based column index to use for generated row headers
|
|
63
|
+
* - `data-row-headers`: promotes the first cell in each body row to a row header
|
|
64
|
+
*/
|
|
65
|
+
export class TableElement extends HTMLElement {
|
|
66
|
+
static observedAttributes: string[];
|
|
67
|
+
refresh(): void;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Registers the `basic-table` custom element if it is not already defined.
|
|
72
|
+
*/
|
|
73
|
+
export function defineTable(
|
|
74
|
+
registry?: CustomElementRegistry,
|
|
75
|
+
): typeof TableElement;
|