@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,22 @@ const GENERATED_ROW_HEADER_ATTRIBUTE = "data-basic-table-generated-row-header";
|
|
|
12
12
|
const GENERATED_DESCRIPTION_ATTRIBUTE = "data-basic-table-generated-description";
|
|
13
13
|
const MANAGED_HEADERS_ATTRIBUTE = "data-basic-table-managed-headers";
|
|
14
14
|
const MANAGED_LABEL_ATTRIBUTE = "data-basic-table-managed-label";
|
|
15
|
+
const TABLE_OBSERVER_OPTIONS = {
|
|
16
|
+
childList: true,
|
|
17
|
+
subtree: true,
|
|
18
|
+
characterData: true,
|
|
19
|
+
attributes: true,
|
|
20
|
+
attributeFilter: [
|
|
21
|
+
"aria-describedby",
|
|
22
|
+
"aria-label",
|
|
23
|
+
"aria-labelledby",
|
|
24
|
+
"colspan",
|
|
25
|
+
"headers",
|
|
26
|
+
"id",
|
|
27
|
+
"rowspan",
|
|
28
|
+
"scope",
|
|
29
|
+
],
|
|
30
|
+
};
|
|
15
31
|
|
|
16
32
|
let nextTableInstanceId = 1;
|
|
17
33
|
|
|
@@ -198,6 +214,21 @@ function replaceCellTag(cell, tagName, generatedAttributeName) {
|
|
|
198
214
|
return replacement;
|
|
199
215
|
}
|
|
200
216
|
|
|
217
|
+
function demoteManagedHeaderCell(cell, generatedAttributeName) {
|
|
218
|
+
const replacement = document.createElement("td");
|
|
219
|
+
|
|
220
|
+
for (const attribute of Array.from(cell.attributes)) {
|
|
221
|
+
if (attribute.name === generatedAttributeName || attribute.name === "scope") {
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
replacement.setAttribute(attribute.name, attribute.value);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
replacement.replaceChildren(...Array.from(cell.childNodes));
|
|
229
|
+
cell.parentElement?.replaceChild(replacement, cell);
|
|
230
|
+
}
|
|
231
|
+
|
|
201
232
|
function promoteFirstRowCellsToHeaders(table) {
|
|
202
233
|
const firstRow = table.rows[0];
|
|
203
234
|
|
|
@@ -214,38 +245,41 @@ function promoteFirstRowCellsToHeaders(table) {
|
|
|
214
245
|
}
|
|
215
246
|
}
|
|
216
247
|
|
|
217
|
-
function promoteBodyCellsToRowHeaders(table, rowHeaderColumnIndex) {
|
|
218
|
-
for (const section of Array.from(table.tBodies)) {
|
|
219
|
-
for (const row of Array.from(section.rows)) {
|
|
220
|
-
const targetCell = findCellAtColumnIndex(row, rowHeaderColumnIndex);
|
|
221
|
-
|
|
222
|
-
if (!(targetCell instanceof HTMLTableCellElementBase) || targetCell.tagName === "TH") {
|
|
223
|
-
continue;
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
replaceCellTag(targetCell, "th", GENERATED_ROW_HEADER_ATTRIBUTE);
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
|
|
231
248
|
function demoteManagedHeaders(table, generatedAttributeName) {
|
|
232
249
|
for (const cell of Array.from(table.querySelectorAll(`th[${generatedAttributeName}]`))) {
|
|
233
250
|
if (!(cell instanceof HTMLTableCellElementBase)) {
|
|
234
251
|
continue;
|
|
235
252
|
}
|
|
236
253
|
|
|
237
|
-
|
|
254
|
+
demoteManagedHeaderCell(cell, generatedAttributeName);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
238
257
|
|
|
239
|
-
|
|
240
|
-
|
|
258
|
+
function syncBodyRowHeaders(table, rowHeaderColumnIndex, rowHeadersEnabled) {
|
|
259
|
+
for (const section of Array.from(table.tBodies)) {
|
|
260
|
+
for (const row of Array.from(section.rows)) {
|
|
261
|
+
const targetCell = rowHeadersEnabled
|
|
262
|
+
? findCellAtColumnIndex(row, rowHeaderColumnIndex)
|
|
263
|
+
: null;
|
|
264
|
+
|
|
265
|
+
for (const cell of Array.from(row.cells)) {
|
|
266
|
+
if (
|
|
267
|
+
!(cell instanceof HTMLTableCellElementBase)
|
|
268
|
+
|| !cell.hasAttribute(GENERATED_ROW_HEADER_ATTRIBUTE)
|
|
269
|
+
|| cell === targetCell
|
|
270
|
+
) {
|
|
271
|
+
continue;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
demoteManagedHeaderCell(cell, GENERATED_ROW_HEADER_ATTRIBUTE);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (!(targetCell instanceof HTMLTableCellElementBase) || targetCell.tagName === "TH") {
|
|
241
278
|
continue;
|
|
242
279
|
}
|
|
243
280
|
|
|
244
|
-
|
|
281
|
+
replaceCellTag(targetCell, "th", GENERATED_ROW_HEADER_ATTRIBUTE);
|
|
245
282
|
}
|
|
246
|
-
|
|
247
|
-
replacement.replaceChildren(...Array.from(cell.childNodes));
|
|
248
|
-
cell.parentElement?.replaceChild(replacement, cell);
|
|
249
283
|
}
|
|
250
284
|
}
|
|
251
285
|
|
|
@@ -256,6 +290,12 @@ function createGeneratedCaption(table) {
|
|
|
256
290
|
return caption;
|
|
257
291
|
}
|
|
258
292
|
|
|
293
|
+
function syncTextContent(node, text) {
|
|
294
|
+
if (node.textContent !== text) {
|
|
295
|
+
node.textContent = text;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
259
299
|
function syncTableCaption(table, captionText) {
|
|
260
300
|
const existingCaption = table.caption;
|
|
261
301
|
const generatedCaption = existingCaption?.hasAttribute(GENERATED_CAPTION_ATTRIBUTE)
|
|
@@ -272,18 +312,12 @@ function syncTableCaption(table, captionText) {
|
|
|
272
312
|
}
|
|
273
313
|
|
|
274
314
|
const caption = generatedCaption ?? createGeneratedCaption(table);
|
|
275
|
-
caption
|
|
315
|
+
syncTextContent(caption, captionText);
|
|
276
316
|
}
|
|
277
317
|
|
|
278
318
|
function syncFallbackAccessibleName(table, label) {
|
|
279
319
|
const hasCaption = Boolean(table.caption);
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
if (hasManagedLabel && table.getAttribute("aria-label") !== label) {
|
|
283
|
-
table.removeAttribute(MANAGED_LABEL_ATTRIBUTE);
|
|
284
|
-
hasManagedLabel = false;
|
|
285
|
-
}
|
|
286
|
-
|
|
320
|
+
const hasManagedLabel = table.hasAttribute(MANAGED_LABEL_ATTRIBUTE);
|
|
287
321
|
const hasOwnAriaLabel = table.hasAttribute("aria-label") && !hasManagedLabel;
|
|
288
322
|
const hasOwnLabelledBy = table.hasAttribute("aria-labelledby");
|
|
289
323
|
|
|
@@ -296,8 +330,13 @@ function syncFallbackAccessibleName(table, label) {
|
|
|
296
330
|
return;
|
|
297
331
|
}
|
|
298
332
|
|
|
299
|
-
table.
|
|
300
|
-
|
|
333
|
+
if (table.getAttribute("aria-label") !== label) {
|
|
334
|
+
table.setAttribute("aria-label", label);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
if (!hasManagedLabel) {
|
|
338
|
+
table.setAttribute(MANAGED_LABEL_ATTRIBUTE, "");
|
|
339
|
+
}
|
|
301
340
|
}
|
|
302
341
|
|
|
303
342
|
function getGeneratedDescription(root) {
|
|
@@ -321,7 +360,11 @@ function syncTableDescription(root, table, descriptionText, baseId) {
|
|
|
321
360
|
);
|
|
322
361
|
|
|
323
362
|
if (tokens.length > 0) {
|
|
324
|
-
|
|
363
|
+
const nextValue = tokens.join(" ");
|
|
364
|
+
|
|
365
|
+
if (table.getAttribute("aria-describedby") !== nextValue) {
|
|
366
|
+
table.setAttribute("aria-describedby", nextValue);
|
|
367
|
+
}
|
|
325
368
|
} else {
|
|
326
369
|
table.removeAttribute("aria-describedby");
|
|
327
370
|
}
|
|
@@ -343,13 +386,17 @@ function syncTableDescription(root, table, descriptionText, baseId) {
|
|
|
343
386
|
description.id = `${baseId}-description`;
|
|
344
387
|
}
|
|
345
388
|
|
|
346
|
-
description
|
|
389
|
+
syncTextContent(description, descriptionText);
|
|
347
390
|
|
|
348
391
|
const tokens = getAriaReferenceTokens(table.getAttribute("aria-describedby")).filter(
|
|
349
392
|
(token) => token !== description.id,
|
|
350
393
|
);
|
|
351
394
|
tokens.push(description.id);
|
|
352
|
-
|
|
395
|
+
const nextValue = tokens.join(" ");
|
|
396
|
+
|
|
397
|
+
if (table.getAttribute("aria-describedby") !== nextValue) {
|
|
398
|
+
table.setAttribute("aria-describedby", nextValue);
|
|
399
|
+
}
|
|
353
400
|
}
|
|
354
401
|
|
|
355
402
|
export class TableElement extends HTMLElementBase {
|
|
@@ -364,6 +411,7 @@ export class TableElement extends HTMLElementBase {
|
|
|
364
411
|
|
|
365
412
|
#instanceId = `${TABLE_TAG_NAME}-${nextTableInstanceId++}`;
|
|
366
413
|
#observer = null;
|
|
414
|
+
#observing = false;
|
|
367
415
|
#scheduledFrame = 0;
|
|
368
416
|
|
|
369
417
|
connectedCallback() {
|
|
@@ -372,7 +420,7 @@ export class TableElement extends HTMLElementBase {
|
|
|
372
420
|
}
|
|
373
421
|
|
|
374
422
|
disconnectedCallback() {
|
|
375
|
-
this.#
|
|
423
|
+
this.#stopObserving();
|
|
376
424
|
this.#observer = null;
|
|
377
425
|
|
|
378
426
|
if (this.#scheduledFrame !== 0 && typeof window !== "undefined") {
|
|
@@ -386,119 +434,126 @@ export class TableElement extends HTMLElementBase {
|
|
|
386
434
|
}
|
|
387
435
|
|
|
388
436
|
refresh() {
|
|
389
|
-
|
|
437
|
+
this.#stopObserving();
|
|
390
438
|
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
}
|
|
439
|
+
try {
|
|
440
|
+
const table = collectOwnedTables(this)[0] ?? null;
|
|
394
441
|
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
const columnHeadersEnabled = normalizeTableColumnHeaders(
|
|
399
|
-
this.getAttribute("data-column-headers") ?? this.hasAttribute("data-column-headers"),
|
|
400
|
-
);
|
|
401
|
-
const rowHeaderColumnIndex = normalizeTableRowHeaderColumn(
|
|
402
|
-
this.getAttribute("data-row-header-column"),
|
|
403
|
-
) - 1;
|
|
404
|
-
const rowHeadersEnabled = normalizeTableRowHeaders(
|
|
405
|
-
this.getAttribute("data-row-headers") ?? this.hasAttribute("data-row-header-column"),
|
|
406
|
-
);
|
|
407
|
-
const baseId = this.id || this.#instanceId;
|
|
408
|
-
|
|
409
|
-
syncTableCaption(table, caption);
|
|
410
|
-
syncFallbackAccessibleName(table, label);
|
|
411
|
-
syncTableDescription(this, table, description, baseId);
|
|
412
|
-
|
|
413
|
-
if (columnHeadersEnabled) {
|
|
414
|
-
promoteFirstRowCellsToHeaders(table);
|
|
415
|
-
} else {
|
|
416
|
-
demoteManagedHeaders(table, GENERATED_COLUMN_HEADER_ATTRIBUTE);
|
|
417
|
-
}
|
|
442
|
+
if (!(table instanceof HTMLTableElementBase)) {
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
418
445
|
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
446
|
+
const label = normalizeTableLabel(this.getAttribute("data-label"));
|
|
447
|
+
const caption = normalizeTableCaption(this.getAttribute("data-caption"));
|
|
448
|
+
const description = normalizeTableDescription(this.getAttribute("data-description"));
|
|
449
|
+
const columnHeadersEnabled = normalizeTableColumnHeaders(
|
|
450
|
+
this.getAttribute("data-column-headers") ?? this.hasAttribute("data-column-headers"),
|
|
451
|
+
);
|
|
452
|
+
const rowHeaderColumnIndex = normalizeTableRowHeaderColumn(
|
|
453
|
+
this.getAttribute("data-row-header-column"),
|
|
454
|
+
) - 1;
|
|
455
|
+
const rowHeadersEnabled = normalizeTableRowHeaders(
|
|
456
|
+
this.getAttribute("data-row-headers") ?? this.hasAttribute("data-row-header-column"),
|
|
457
|
+
);
|
|
458
|
+
const baseId = this.id || this.#instanceId;
|
|
425
459
|
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
460
|
+
syncTableCaption(table, caption);
|
|
461
|
+
syncFallbackAccessibleName(table, label);
|
|
462
|
+
syncTableDescription(this, table, description, baseId);
|
|
429
463
|
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
464
|
+
if (columnHeadersEnabled) {
|
|
465
|
+
promoteFirstRowCellsToHeaders(table);
|
|
466
|
+
} else {
|
|
467
|
+
demoteManagedHeaders(table, GENERATED_COLUMN_HEADER_ATTRIBUTE);
|
|
433
468
|
}
|
|
434
469
|
|
|
435
|
-
|
|
436
|
-
columnHeadersEnabled,
|
|
437
|
-
rowHeadersEnabled,
|
|
438
|
-
rowHeaderColumnIndex,
|
|
439
|
-
});
|
|
470
|
+
syncBodyRowHeaders(table, rowHeaderColumnIndex, rowHeadersEnabled);
|
|
440
471
|
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
472
|
+
const placements = buildTablePlacements(table);
|
|
473
|
+
const headerPlacements = [];
|
|
474
|
+
let nextHeaderIndex = 1;
|
|
444
475
|
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
});
|
|
450
|
-
nextHeaderIndex += 1;
|
|
451
|
-
}
|
|
476
|
+
for (const placement of placements) {
|
|
477
|
+
if (placement.cell.tagName !== "TH") {
|
|
478
|
+
continue;
|
|
479
|
+
}
|
|
452
480
|
|
|
453
|
-
|
|
481
|
+
const scope = inferHeaderScope(placement.cell, placement, {
|
|
482
|
+
columnHeadersEnabled,
|
|
483
|
+
rowHeadersEnabled,
|
|
484
|
+
rowHeaderColumnIndex,
|
|
485
|
+
});
|
|
454
486
|
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
487
|
+
if (scope) {
|
|
488
|
+
if (placement.cell.getAttribute("scope") !== scope) {
|
|
489
|
+
placement.cell.setAttribute("scope", scope);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
headerPlacements.push({
|
|
494
|
+
...placement,
|
|
495
|
+
scope,
|
|
496
|
+
id: ensureHeaderId(placement.cell, this.id || this.#instanceId, nextHeaderIndex),
|
|
497
|
+
});
|
|
498
|
+
nextHeaderIndex += 1;
|
|
458
499
|
}
|
|
459
500
|
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
header.rowIndex < placement.rowIndex
|
|
466
|
-
&& header.columnIndex < placement.columnIndex + placement.colSpan
|
|
467
|
-
&& header.columnIndex + header.colSpan > placement.columnIndex
|
|
468
|
-
);
|
|
469
|
-
case "row":
|
|
470
|
-
case "rowgroup":
|
|
471
|
-
return (
|
|
472
|
-
header.columnIndex < placement.columnIndex
|
|
473
|
-
&& header.rowIndex < placement.rowIndex + placement.rowSpan
|
|
474
|
-
&& header.rowIndex + header.rowSpan > placement.rowIndex
|
|
475
|
-
);
|
|
476
|
-
default:
|
|
477
|
-
return false;
|
|
501
|
+
headerPlacements.sort(sortPlacementsInDocumentOrder);
|
|
502
|
+
|
|
503
|
+
for (const placement of placements) {
|
|
504
|
+
if (placement.cell.tagName !== "TD") {
|
|
505
|
+
continue;
|
|
478
506
|
}
|
|
479
|
-
});
|
|
480
507
|
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
508
|
+
const associatedHeaders = headerPlacements.filter((header) => {
|
|
509
|
+
switch (header.scope) {
|
|
510
|
+
case "col":
|
|
511
|
+
case "colgroup":
|
|
512
|
+
return (
|
|
513
|
+
header.rowIndex < placement.rowIndex
|
|
514
|
+
&& header.columnIndex < placement.columnIndex + placement.colSpan
|
|
515
|
+
&& header.columnIndex + header.colSpan > placement.columnIndex
|
|
516
|
+
);
|
|
517
|
+
case "row":
|
|
518
|
+
case "rowgroup":
|
|
519
|
+
return (
|
|
520
|
+
header.columnIndex < placement.columnIndex
|
|
521
|
+
&& header.rowIndex < placement.rowIndex + placement.rowSpan
|
|
522
|
+
&& header.rowIndex + header.rowSpan > placement.rowIndex
|
|
523
|
+
);
|
|
524
|
+
default:
|
|
525
|
+
return false;
|
|
526
|
+
}
|
|
527
|
+
});
|
|
528
|
+
|
|
529
|
+
if (associatedHeaders.length === 0) {
|
|
530
|
+
if (placement.cell.hasAttribute(MANAGED_HEADERS_ATTRIBUTE)) {
|
|
531
|
+
placement.cell.removeAttribute("headers");
|
|
532
|
+
placement.cell.removeAttribute(MANAGED_HEADERS_ATTRIBUTE);
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
continue;
|
|
485
536
|
}
|
|
486
537
|
|
|
487
|
-
|
|
488
|
-
|
|
538
|
+
if (
|
|
539
|
+
placement.cell.hasAttribute("headers")
|
|
540
|
+
&& !placement.cell.hasAttribute(MANAGED_HEADERS_ATTRIBUTE)
|
|
541
|
+
) {
|
|
542
|
+
continue;
|
|
543
|
+
}
|
|
489
544
|
|
|
490
|
-
|
|
491
|
-
placement.cell.hasAttribute("headers")
|
|
492
|
-
&& !placement.cell.hasAttribute(MANAGED_HEADERS_ATTRIBUTE)
|
|
493
|
-
) {
|
|
494
|
-
continue;
|
|
495
|
-
}
|
|
545
|
+
const nextHeaders = associatedHeaders.map((header) => header.id).join(" ");
|
|
496
546
|
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
547
|
+
if (placement.cell.getAttribute("headers") !== nextHeaders) {
|
|
548
|
+
placement.cell.setAttribute("headers", nextHeaders);
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
if (!placement.cell.hasAttribute(MANAGED_HEADERS_ATTRIBUTE)) {
|
|
552
|
+
placement.cell.setAttribute(MANAGED_HEADERS_ATTRIBUTE, "");
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
} finally {
|
|
556
|
+
this.#startObserving();
|
|
502
557
|
}
|
|
503
558
|
}
|
|
504
559
|
|
|
@@ -522,22 +577,25 @@ export class TableElement extends HTMLElementBase {
|
|
|
522
577
|
this.#scheduleRefresh();
|
|
523
578
|
});
|
|
524
579
|
|
|
525
|
-
this.#
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
}
|
|
580
|
+
this.#startObserving();
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
#startObserving() {
|
|
584
|
+
if (!this.#observer || this.#observing) {
|
|
585
|
+
return;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
this.#observer.observe(this, TABLE_OBSERVER_OPTIONS);
|
|
589
|
+
this.#observing = true;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
#stopObserving() {
|
|
593
|
+
if (!this.#observer || !this.#observing) {
|
|
594
|
+
return;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
this.#observer.disconnect();
|
|
598
|
+
this.#observing = false;
|
|
541
599
|
}
|
|
542
600
|
}
|
|
543
601
|
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# `basic-tabs`
|
|
2
|
+
|
|
3
|
+
Accessible tablists and panels from existing markup.
|
|
4
|
+
|
|
5
|
+
## Register
|
|
6
|
+
|
|
7
|
+
```js
|
|
8
|
+
import "@lmfaole/basics/basic-components/basic-tabs/register";
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Example
|
|
12
|
+
|
|
13
|
+
```html
|
|
14
|
+
<basic-tabs data-label="Eksempelkode">
|
|
15
|
+
<div data-tabs-list>
|
|
16
|
+
<button type="button" data-tab>Oversikt</button>
|
|
17
|
+
<button type="button" data-tab>Implementasjon</button>
|
|
18
|
+
<button type="button" data-tab>Tilgjengelighet</button>
|
|
19
|
+
</div>
|
|
20
|
+
|
|
21
|
+
<section data-tab-panel>
|
|
22
|
+
<p>Viser en kort oppsummering.</p>
|
|
23
|
+
</section>
|
|
24
|
+
<section data-tab-panel>
|
|
25
|
+
<p>Viser implementasjonsdetaljer.</p>
|
|
26
|
+
</section>
|
|
27
|
+
<section data-tab-panel>
|
|
28
|
+
<p>Viser tilgjengelighetsnotater.</p>
|
|
29
|
+
</section>
|
|
30
|
+
</basic-tabs>
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Props
|
|
34
|
+
|
|
35
|
+
| Prop | Description | Type | Default | Options |
|
|
36
|
+
| --- | --- | --- | --- | --- |
|
|
37
|
+
| `data-label` | Accessible name for the generated tablist when it does not already have one. | string | `Faner` | any string |
|
|
38
|
+
| `data-activation` | Chooses whether arrow-key focus changes also activate the selected panel. | enum string | `automatic` | `automatic`, `manual` |
|
|
39
|
+
| `data-selected-index` | Sets the initially selected tab by index. Invalid values fall back to the first enabled tab. | zero-based integer | first enabled tab | zero-based integer |
|
|
40
|
+
|
|
41
|
+
## Markup Hooks
|
|
42
|
+
|
|
43
|
+
| Hook | Description | Type | Default | Options |
|
|
44
|
+
| --- | --- | --- | --- | --- |
|
|
45
|
+
| `data-tabs-list` | Holds the interactive tab controls. | descendant container attribute | required | present on one descendant container |
|
|
46
|
+
| `data-tab` | Marks each tab control. Prefer `<button>` elements. | descendant control attribute | required | present on matching descendant controls |
|
|
47
|
+
| `data-tab-panel` | Marks each panel in the same order as the tabs. | descendant panel attribute | required | present on matching descendant panels |
|
|
48
|
+
| `disabled` on a `[data-tab]` control | Removes that tab from keyboard navigation and selection. | boolean attribute | off | `present`, `omitted` |
|
|
49
|
+
|
|
50
|
+
## Behavior
|
|
51
|
+
|
|
52
|
+
- Generates missing tab and panel ids
|
|
53
|
+
- Keeps `aria-selected`, `aria-controls`, `aria-labelledby`, `hidden`, and `data-selected` in sync
|
|
54
|
+
- Supports click, `ArrowLeft`, `ArrowRight`, `Home`, and `End`
|
|
55
|
+
- Skips disabled tabs during keyboard navigation
|
|
56
|
+
- In `manual` mode, arrow keys move focus and `Enter` or `Space` activates
|
|
57
|
+
|
|
58
|
+
## Markup Contract
|
|
59
|
+
|
|
60
|
+
- Provide one descendant element with `data-tabs-list`
|
|
61
|
+
- Provide matching counts of `[data-tab]` and `[data-tab-panel]` in the same order
|
|
62
|
+
- Prefer `<button>` elements for tabs
|
|
63
|
+
- Keep layout and styling outside the package
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# `basic-toast`
|
|
2
|
+
|
|
3
|
+
Toast notifications with managed open state and live-region announcements.
|
|
4
|
+
|
|
5
|
+
## Register
|
|
6
|
+
|
|
7
|
+
```js
|
|
8
|
+
import "@lmfaole/basics/basic-components/basic-toast/register";
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Example
|
|
12
|
+
|
|
13
|
+
```html
|
|
14
|
+
<basic-toast data-label="Lagring fullfort" data-duration="5000">
|
|
15
|
+
<button type="button" data-toast-open>Show toast</button>
|
|
16
|
+
|
|
17
|
+
<section data-toast-panel>
|
|
18
|
+
<h2 data-toast-title>Lagret</h2>
|
|
19
|
+
<p>Meldingen ble lagret uten feil.</p>
|
|
20
|
+
</section>
|
|
21
|
+
</basic-toast>
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Props
|
|
25
|
+
|
|
26
|
+
| Prop | Description | Type | Default | Options |
|
|
27
|
+
| --- | --- | --- | --- | --- |
|
|
28
|
+
| `data-label` | Fallback accessible name when the toast panel has no `aria-label`, `aria-labelledby`, or `[data-toast-title]`. | string | `Toast` | any string |
|
|
29
|
+
| `data-live` | Chooses whether the announcement behaves like `status` or `alert`. | enum string | `polite` | `polite`, `assertive` |
|
|
30
|
+
| `data-duration` | Auto-dismiss timeout for the toast. | non-negative integer | `5000` | non-negative integer milliseconds, `0` disables auto-dismiss |
|
|
31
|
+
| `data-open` | Optional initial open state for the managed toast panel. | boolean-ish attribute | closed | `present`, `omitted`, `false` |
|
|
32
|
+
|
|
33
|
+
## Starter Styling Prop
|
|
34
|
+
|
|
35
|
+
| Prop | Description | Type | Default | Options |
|
|
36
|
+
| --- | --- | --- | --- | --- |
|
|
37
|
+
| `data-toast-position` | Optional starter-CSS hook for viewport placement when you import `basic-styling`. | enum string | none | `top-left`, `top-center`, `top-right`, `center-left`, `center`, `center-right`, `bottom-left`, `bottom-center`, `bottom-right` |
|
|
38
|
+
|
|
39
|
+
## Markup Hooks
|
|
40
|
+
|
|
41
|
+
| Hook | Description | Type | Default | Options |
|
|
42
|
+
| --- | --- | --- | --- | --- |
|
|
43
|
+
| `data-toast-open` | Shows or toggles the toast. | descendant control attribute | none | present on a descendant button or control |
|
|
44
|
+
| `data-toast-panel` | Marks the message container managed by the component. | descendant element attribute | required | present on one descendant element |
|
|
45
|
+
| `data-toast-title` | Makes the visible heading the toast's accessible name. | descendant heading attribute | none | present on a descendant heading |
|
|
46
|
+
| `data-toast-close` | Closes the toast when activated. | descendant control attribute | none | present on a descendant control outside `[data-toast-panel]` |
|
|
47
|
+
|
|
48
|
+
## Behavior
|
|
49
|
+
|
|
50
|
+
- Uses the Popover API in manual mode when available so the toast can render in the top layer
|
|
51
|
+
- Announces the current toast text through an internal live region whenever the toast opens
|
|
52
|
+
- Syncs `hidden` and `data-open` on the panel and root element
|
|
53
|
+
- Uses `[data-toast-title]` as the accessible name when present, otherwise falls back to `data-label`
|
|
54
|
+
- Supports `show()`, `hide()`, and `toggle()`
|
|
55
|
+
|
|
56
|
+
## Markup Contract
|
|
57
|
+
|
|
58
|
+
- Provide one descendant `[data-toast-panel]`
|
|
59
|
+
- Keep `[data-toast-panel]` to non-interactive message content
|
|
60
|
+
- Use `[data-toast-open]` on buttons that should show or toggle the toast
|
|
61
|
+
- If you need an explicit dismiss control, place `[data-toast-close]` outside `[data-toast-panel]`
|
|
62
|
+
- Keep layout and styling outside the package
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
export const TOAST_TAG_NAME: "basic-toast";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Normalizes unsupported or empty labels back to the default `"Toast"`.
|
|
5
|
+
*/
|
|
6
|
+
export function normalizeToastLabel(
|
|
7
|
+
value?: string | null,
|
|
8
|
+
): string;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Normalizes unsupported live-region values back to `"polite"`.
|
|
12
|
+
*/
|
|
13
|
+
export function normalizeToastLive(
|
|
14
|
+
value?: string | null,
|
|
15
|
+
): "assertive" | "polite";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Maps a toast live setting to the matching ARIA role.
|
|
19
|
+
*/
|
|
20
|
+
export function getToastRoleForLive(
|
|
21
|
+
value?: string | null,
|
|
22
|
+
): "alert" | "status";
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Normalizes the optional `data-duration` attribute into milliseconds.
|
|
26
|
+
* A value of `0` disables auto-dismiss.
|
|
27
|
+
*/
|
|
28
|
+
export function normalizeToastDuration(
|
|
29
|
+
value?: string | null,
|
|
30
|
+
): number;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Normalizes the optional `data-open` attribute into a boolean flag.
|
|
34
|
+
*/
|
|
35
|
+
export function normalizeToastOpen(
|
|
36
|
+
value?: string | null,
|
|
37
|
+
): boolean;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Custom element that upgrades trigger-and-panel markup into a toast
|
|
41
|
+
* notification flow with optional auto-dismiss.
|
|
42
|
+
*
|
|
43
|
+
* Attributes:
|
|
44
|
+
* - `data-label`: fallback accessible name when the toast has no title
|
|
45
|
+
* - `data-live`: chooses between `status` and `alert` semantics
|
|
46
|
+
* - `data-duration`: auto-dismiss timeout in milliseconds, `0` disables it
|
|
47
|
+
* - `data-open`: optional initial open state
|
|
48
|
+
*
|
|
49
|
+
* Behavior:
|
|
50
|
+
* - uses the Popover API in manual mode when available so the toast panel can
|
|
51
|
+
* render in the top layer
|
|
52
|
+
* - announces the current toast text through an internal live region whenever
|
|
53
|
+
* the toast opens
|
|
54
|
+
* - expects `[data-toast-panel]` to contain non-interactive message content
|
|
55
|
+
*/
|
|
56
|
+
export class ToastElement extends HTMLElement {
|
|
57
|
+
static observedAttributes: string[];
|
|
58
|
+
show(opener?: HTMLElement | null): boolean;
|
|
59
|
+
hide(): boolean;
|
|
60
|
+
toggle(opener?: HTMLElement | null): boolean;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Registers the `basic-toast` custom element if it is not already defined.
|
|
65
|
+
*/
|
|
66
|
+
export function defineToast(
|
|
67
|
+
registry?: CustomElementRegistry,
|
|
68
|
+
): typeof ToastElement;
|