@lmfaole/basics 0.3.0 → 0.5.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/LICENSE +21 -0
- package/README.md +78 -350
- package/basic-components/basic-accordion/README.md +53 -0
- package/{components → basic-components}/basic-accordion/index.d.ts +5 -5
- package/basic-components/basic-accordion/index.js +413 -0
- package/basic-components/basic-alert/README.md +48 -0
- package/basic-components/basic-alert/index.d.ts +53 -0
- package/basic-components/basic-alert/index.js +189 -0
- package/basic-components/basic-alert/register.js +3 -0
- package/basic-components/basic-carousel/README.md +108 -0
- package/basic-components/basic-carousel/index.d.ts +73 -0
- package/basic-components/basic-carousel/index.js +255 -0
- package/basic-components/basic-carousel/register.js +3 -0
- package/basic-components/basic-dialog/README.md +57 -0
- package/basic-components/basic-popover/README.md +56 -0
- package/basic-components/basic-summary-table/README.md +93 -0
- package/{components → basic-components}/basic-summary-table/index.js +188 -42
- package/basic-components/basic-table/README.md +89 -0
- package/{components → basic-components}/basic-table/index.js +203 -145
- package/basic-components/basic-tabs/README.md +63 -0
- package/basic-components/basic-tabs/register.d.ts +1 -0
- package/basic-components/basic-toast/README.md +62 -0
- package/basic-components/basic-toast/index.d.ts +68 -0
- package/basic-components/basic-toast/index.js +690 -0
- package/basic-components/basic-toast/register.d.ts +1 -0
- package/basic-components/basic-toast/register.js +3 -0
- package/basic-components/basic-toc/README.md +43 -0
- package/basic-components/basic-toc/register.d.ts +1 -0
- package/basic-styling/components/basic-accordion.css +99 -0
- package/basic-styling/components/basic-alert.css +27 -0
- package/basic-styling/components/basic-carousel.css +183 -0
- package/basic-styling/components/basic-dialog.css +41 -0
- package/basic-styling/components/basic-popover.css +52 -0
- package/basic-styling/components/basic-summary-table.css +98 -0
- package/basic-styling/components/basic-table.css +66 -0
- package/basic-styling/components/basic-tabs.css +61 -0
- package/basic-styling/components/basic-toast.css +102 -0
- package/basic-styling/components/basic-toc.css +30 -0
- package/basic-styling/components.css +11 -0
- package/basic-styling/forms.css +55 -0
- package/basic-styling/global.css +62 -0
- package/basic-styling/index.css +2 -0
- package/basic-styling/interaction.css +90 -0
- package/basic-styling/tokens/base.css +19 -0
- package/basic-styling/tokens/palette.css +229 -0
- package/basic-styling/tokens/palette.tokens.json +1787 -0
- package/index.d.ts +10 -7
- package/index.js +10 -7
- package/package.json +61 -76
- package/components/basic-accordion/index.js +0 -387
- package/readme.mdx +0 -6
- /package/{components → basic-components}/basic-accordion/register.d.ts +0 -0
- /package/{components → basic-components}/basic-accordion/register.js +0 -0
- /package/{components/basic-dialog → basic-components/basic-alert}/register.d.ts +0 -0
- /package/{components/basic-popover → basic-components/basic-carousel}/register.d.ts +0 -0
- /package/{components → basic-components}/basic-dialog/index.d.ts +0 -0
- /package/{components → basic-components}/basic-dialog/index.js +0 -0
- /package/{components/basic-summary-table → basic-components/basic-dialog}/register.d.ts +0 -0
- /package/{components → basic-components}/basic-dialog/register.js +0 -0
- /package/{components → basic-components}/basic-popover/index.d.ts +0 -0
- /package/{components → basic-components}/basic-popover/index.js +0 -0
- /package/{components/basic-table → basic-components/basic-popover}/register.d.ts +0 -0
- /package/{components → basic-components}/basic-popover/register.js +0 -0
- /package/{components → basic-components}/basic-summary-table/index.d.ts +0 -0
- /package/{components/basic-tabs → basic-components/basic-summary-table}/register.d.ts +0 -0
- /package/{components → basic-components}/basic-summary-table/register.js +0 -0
- /package/{components → basic-components}/basic-table/index.d.ts +0 -0
- /package/{components/basic-toc → basic-components/basic-table}/register.d.ts +0 -0
- /package/{components → basic-components}/basic-table/register.js +0 -0
- /package/{components → basic-components}/basic-tabs/index.d.ts +0 -0
- /package/{components → basic-components}/basic-tabs/index.js +0 -0
- /package/{components → basic-components}/basic-tabs/register.js +0 -0
- /package/{components → basic-components}/basic-toc/index.d.ts +0 -0
- /package/{components → basic-components}/basic-toc/index.js +0 -0
- /package/{components → basic-components}/basic-toc/register.js +0 -0
|
@@ -12,6 +12,11 @@ const DEFAULT_TOTAL_LABEL = "Totalt";
|
|
|
12
12
|
const GENERATED_SUMMARY_ROW_ATTRIBUTE = "data-basic-summary-table-generated-row";
|
|
13
13
|
const GENERATED_SUMMARY_CELL_ATTRIBUTE = "data-basic-summary-table-generated-cell";
|
|
14
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
|
+
};
|
|
15
20
|
|
|
16
21
|
export function normalizeSummaryColumns(value) {
|
|
17
22
|
if (!value?.trim()) {
|
|
@@ -36,8 +41,30 @@ export function normalizeSummaryLocale(value) {
|
|
|
36
41
|
return value?.trim() || undefined;
|
|
37
42
|
}
|
|
38
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
|
+
|
|
39
65
|
function getDecimalSeparator(value) {
|
|
40
|
-
const
|
|
66
|
+
const numberMatch = findSummaryNumberMatch(value);
|
|
67
|
+
const text = numberMatch?.numberText.trim() ?? String(value ?? "").trim();
|
|
41
68
|
const lastComma = text.lastIndexOf(",");
|
|
42
69
|
const lastDot = text.lastIndexOf(".");
|
|
43
70
|
|
|
@@ -59,7 +86,8 @@ function getDecimalSeparator(value) {
|
|
|
59
86
|
}
|
|
60
87
|
|
|
61
88
|
function countFractionDigits(value) {
|
|
62
|
-
const
|
|
89
|
+
const numberMatch = findSummaryNumberMatch(value);
|
|
90
|
+
const text = numberMatch?.numberText.trim() ?? String(value ?? "").trim();
|
|
63
91
|
|
|
64
92
|
if (!text) {
|
|
65
93
|
return 0;
|
|
@@ -75,7 +103,8 @@ function countFractionDigits(value) {
|
|
|
75
103
|
}
|
|
76
104
|
|
|
77
105
|
export function parseSummaryNumber(value) {
|
|
78
|
-
const
|
|
106
|
+
const numberMatch = findSummaryNumberMatch(value);
|
|
107
|
+
const text = numberMatch?.numberText.trim() ?? String(value ?? "").trim();
|
|
79
108
|
|
|
80
109
|
if (!text) {
|
|
81
110
|
return null;
|
|
@@ -107,6 +136,28 @@ export function formatSummaryNumber(value, { locale, fractionDigits = 0 } = {})
|
|
|
107
136
|
}).format(value);
|
|
108
137
|
}
|
|
109
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
|
+
|
|
110
161
|
function findCellAtColumnIndex(row, targetColumnIndex) {
|
|
111
162
|
let columnIndex = 0;
|
|
112
163
|
|
|
@@ -134,6 +185,10 @@ function getLogicalColumnCount(table) {
|
|
|
134
185
|
let maxColumns = 0;
|
|
135
186
|
|
|
136
187
|
for (const row of Array.from(table.rows)) {
|
|
188
|
+
if (row.hasAttribute(GENERATED_SUMMARY_ROW_ATTRIBUTE)) {
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
|
|
137
192
|
let columnCount = 0;
|
|
138
193
|
|
|
139
194
|
for (const cell of Array.from(row.cells)) {
|
|
@@ -192,6 +247,10 @@ function calculateSummaryTotals(table, summaryColumns) {
|
|
|
192
247
|
totals.set(columnIndex, {
|
|
193
248
|
total: 0,
|
|
194
249
|
fractionDigits: 0,
|
|
250
|
+
prefix: "",
|
|
251
|
+
suffix: "",
|
|
252
|
+
affixInitialized: false,
|
|
253
|
+
affixConsistent: true,
|
|
195
254
|
});
|
|
196
255
|
}
|
|
197
256
|
|
|
@@ -213,6 +272,25 @@ function calculateSummaryTotals(table, summaryColumns) {
|
|
|
213
272
|
|
|
214
273
|
current.total += parsedValue;
|
|
215
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
|
+
}
|
|
216
294
|
}
|
|
217
295
|
}
|
|
218
296
|
|
|
@@ -238,6 +316,31 @@ function ensureGeneratedSummaryRow(table) {
|
|
|
238
316
|
return row;
|
|
239
317
|
}
|
|
240
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
|
+
|
|
241
344
|
function removeGeneratedSummaryRow(table) {
|
|
242
345
|
table.querySelector(`tr[${GENERATED_SUMMARY_ROW_ATTRIBUTE}]`)?.remove();
|
|
243
346
|
}
|
|
@@ -259,34 +362,58 @@ function syncSummaryFooter(table, {
|
|
|
259
362
|
const summaryColumnSet = new Set(summaryColumns.filter((column) => column < logicalColumnCount));
|
|
260
363
|
const row = ensureGeneratedSummaryRow(table);
|
|
261
364
|
|
|
262
|
-
row.replaceChildren();
|
|
263
|
-
|
|
264
365
|
for (let columnIndex = 0; columnIndex < logicalColumnCount; columnIndex += 1) {
|
|
265
366
|
if (columnIndex === effectiveLabelColumnIndex) {
|
|
266
|
-
const labelCell =
|
|
267
|
-
|
|
268
|
-
labelCell.
|
|
367
|
+
const labelCell = ensureSummaryRowCell(row, columnIndex, "th");
|
|
368
|
+
|
|
369
|
+
if (labelCell.getAttribute("scope") !== "row") {
|
|
370
|
+
labelCell.setAttribute("scope", "row");
|
|
371
|
+
}
|
|
372
|
+
|
|
269
373
|
labelCell.setAttribute(GENERATED_SUMMARY_LABEL_ATTRIBUTE, "");
|
|
270
|
-
|
|
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);
|
|
271
379
|
continue;
|
|
272
380
|
}
|
|
273
381
|
|
|
274
|
-
const valueCell =
|
|
382
|
+
const valueCell = ensureSummaryRowCell(row, columnIndex, "td");
|
|
275
383
|
valueCell.setAttribute(GENERATED_SUMMARY_CELL_ATTRIBUTE, "");
|
|
384
|
+
valueCell.removeAttribute(GENERATED_SUMMARY_LABEL_ATTRIBUTE);
|
|
385
|
+
valueCell.removeAttribute("scope");
|
|
276
386
|
|
|
277
387
|
if (summaryColumnSet.has(columnIndex)) {
|
|
278
|
-
const summary = totals.get(columnIndex) ?? {
|
|
279
|
-
|
|
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, {
|
|
280
397
|
locale,
|
|
281
398
|
fractionDigits: summary.fractionDigits,
|
|
399
|
+
prefix: summary.affixConsistent ? summary.prefix : "",
|
|
400
|
+
suffix: summary.affixConsistent ? summary.suffix : "",
|
|
282
401
|
});
|
|
402
|
+
|
|
403
|
+
syncSummaryCellText(valueCell, formattedValue);
|
|
283
404
|
valueCell.dataset.value = String(summary.total);
|
|
284
405
|
valueCell.dataset.summaryTotal = "";
|
|
406
|
+
valueCell.removeAttribute("data-summary-empty");
|
|
285
407
|
} else {
|
|
408
|
+
syncSummaryCellText(valueCell, "");
|
|
409
|
+
delete valueCell.dataset.value;
|
|
410
|
+
valueCell.removeAttribute("data-summary-total");
|
|
286
411
|
valueCell.dataset.summaryEmpty = "";
|
|
287
412
|
}
|
|
413
|
+
}
|
|
288
414
|
|
|
289
|
-
|
|
415
|
+
while (row.cells.length > logicalColumnCount) {
|
|
416
|
+
row.deleteCell(-1);
|
|
290
417
|
}
|
|
291
418
|
|
|
292
419
|
return true;
|
|
@@ -301,6 +428,7 @@ export class SummaryTableElement extends TableElement {
|
|
|
301
428
|
];
|
|
302
429
|
|
|
303
430
|
#summaryObserver = null;
|
|
431
|
+
#summaryObserving = false;
|
|
304
432
|
#scheduledFrame = 0;
|
|
305
433
|
|
|
306
434
|
connectedCallback() {
|
|
@@ -310,7 +438,7 @@ export class SummaryTableElement extends TableElement {
|
|
|
310
438
|
|
|
311
439
|
disconnectedCallback() {
|
|
312
440
|
super.disconnectedCallback();
|
|
313
|
-
this.#
|
|
441
|
+
this.#stopSummaryObserving();
|
|
314
442
|
this.#summaryObserver = null;
|
|
315
443
|
|
|
316
444
|
if (this.#scheduledFrame !== 0 && typeof window !== "undefined") {
|
|
@@ -320,33 +448,37 @@ export class SummaryTableElement extends TableElement {
|
|
|
320
448
|
}
|
|
321
449
|
|
|
322
450
|
refresh() {
|
|
323
|
-
|
|
451
|
+
this.#stopSummaryObserving();
|
|
324
452
|
|
|
325
|
-
|
|
453
|
+
try {
|
|
454
|
+
const table = getTable(this);
|
|
326
455
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
456
|
+
if (!(table instanceof HTMLTableElementBase)) {
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
330
459
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
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
|
+
});
|
|
348
477
|
|
|
349
|
-
|
|
478
|
+
super.refresh();
|
|
479
|
+
} finally {
|
|
480
|
+
this.#startSummaryObserving();
|
|
481
|
+
}
|
|
350
482
|
}
|
|
351
483
|
|
|
352
484
|
#scheduleRefresh() {
|
|
@@ -369,11 +501,25 @@ export class SummaryTableElement extends TableElement {
|
|
|
369
501
|
this.#scheduleRefresh();
|
|
370
502
|
});
|
|
371
503
|
|
|
372
|
-
this.#
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
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;
|
|
377
523
|
}
|
|
378
524
|
}
|
|
379
525
|
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# `basic-table`
|
|
2
|
+
|
|
3
|
+
Accessible naming and header relationships for regular tables.
|
|
4
|
+
|
|
5
|
+
## Register
|
|
6
|
+
|
|
7
|
+
```js
|
|
8
|
+
import "@lmfaole/basics/basic-components/basic-table/register";
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Example
|
|
12
|
+
|
|
13
|
+
```html
|
|
14
|
+
<basic-table
|
|
15
|
+
data-caption="Bemanning per sprint"
|
|
16
|
+
data-description="Viser team, lokasjon og ledig kapasitet per sprint."
|
|
17
|
+
data-row-headers
|
|
18
|
+
data-row-header-column="2"
|
|
19
|
+
>
|
|
20
|
+
<table>
|
|
21
|
+
<thead>
|
|
22
|
+
<tr>
|
|
23
|
+
<th>Statuskode</th>
|
|
24
|
+
<th>Team</th>
|
|
25
|
+
<th>Lokasjon</th>
|
|
26
|
+
<th>Sprint</th>
|
|
27
|
+
<th>Ledige timer</th>
|
|
28
|
+
</tr>
|
|
29
|
+
</thead>
|
|
30
|
+
<tbody>
|
|
31
|
+
<tr>
|
|
32
|
+
<td>A1</td>
|
|
33
|
+
<td>Plattform</td>
|
|
34
|
+
<td>Oslo</td>
|
|
35
|
+
<td>14</td>
|
|
36
|
+
<td>18</td>
|
|
37
|
+
</tr>
|
|
38
|
+
<tr>
|
|
39
|
+
<td>B4</td>
|
|
40
|
+
<td>Designsystem</td>
|
|
41
|
+
<td>Trondheim</td>
|
|
42
|
+
<td>14</td>
|
|
43
|
+
<td>10</td>
|
|
44
|
+
</tr>
|
|
45
|
+
<tr>
|
|
46
|
+
<td>C2</td>
|
|
47
|
+
<td>Innsikt</td>
|
|
48
|
+
<td>Bergen</td>
|
|
49
|
+
<td>15</td>
|
|
50
|
+
<td>26</td>
|
|
51
|
+
</tr>
|
|
52
|
+
</tbody>
|
|
53
|
+
</table>
|
|
54
|
+
</basic-table>
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Props
|
|
58
|
+
|
|
59
|
+
| Prop | Description | Type | Default | Options |
|
|
60
|
+
| --- | --- | --- | --- | --- |
|
|
61
|
+
| `data-caption` | Generates a visible `<caption>` when the table does not already define one. | string | none | any string |
|
|
62
|
+
| `data-column-headers` | Promotes the first row to column headers when the author provides a plain table without a header row. | boolean attribute | off | `present`, `omitted` |
|
|
63
|
+
| `data-description` | Generates hidden helper text and connects it with `aria-describedby`. | string | none | any string |
|
|
64
|
+
| `data-label` | Fallback accessible name when the table has no caption, `aria-label`, or `aria-labelledby`. | string | `Tabell` | any string |
|
|
65
|
+
| `data-row-header-column` | One-based body column that should become the generated row header. | positive integer | `1` | positive integer |
|
|
66
|
+
| `data-row-headers` | Enables generated row headers in body rows. This is also enabled automatically when `data-row-header-column` is present. | boolean attribute | off | `present`, `omitted` |
|
|
67
|
+
|
|
68
|
+
## Starter Styling Prop
|
|
69
|
+
|
|
70
|
+
| Prop | Description | Type | Default | Options |
|
|
71
|
+
| --- | --- | --- | --- | --- |
|
|
72
|
+
| `data-zebra` | Optional starter-CSS hook that adds alternating body-row backgrounds when you import `basic-styling`. | boolean attribute | off | `present`, `omitted` |
|
|
73
|
+
| `data-separators` | Optional starter-CSS hook that chooses whether interior dividers appear between rows, columns, or both when you import `basic-styling`. | enum string | `rows` | `rows`, `columns`, `both` |
|
|
74
|
+
|
|
75
|
+
## Behavior
|
|
76
|
+
|
|
77
|
+
- Preserves author-provided captions and only generates one when needed
|
|
78
|
+
- Can generate hidden helper text for extra context
|
|
79
|
+
- Can promote a plain first row to column headers
|
|
80
|
+
- Assigns missing header ids and populates `headers` on data cells
|
|
81
|
+
- Re-runs automatically when the wrapped table changes
|
|
82
|
+
|
|
83
|
+
## Markup Contract
|
|
84
|
+
|
|
85
|
+
- Provide one descendant `<table>`
|
|
86
|
+
- Use real table sections and header cells where possible
|
|
87
|
+
- Add `data-row-headers` when one body column identifies each row
|
|
88
|
+
- Add `data-column-headers` only when you want the component to promote a plain first row
|
|
89
|
+
- Keep layout and styling outside the package
|