UNPKG

@material/web

Version:
128 lines 4.86 kB
/** * @license * Copyright 2023 Google LLC * SPDX-License-Identifier: Apache-2.0 */ import { __decorate } from "tslib"; import { html, isServer, LitElement } from 'lit'; import { queryAssignedElements } from 'lit/decorators.js'; import { Chip } from './chip.js'; /** * A chip set component. */ export class ChipSet extends LitElement { get chips() { return this.childElements.filter((child) => child instanceof Chip); } constructor() { super(); this.internals = // Cast needed for closure this.attachInternals(); if (!isServer) { this.addEventListener('focusin', this.updateTabIndices.bind(this)); this.addEventListener('update-focus', this.updateTabIndices.bind(this)); this.addEventListener('keydown', this.handleKeyDown.bind(this)); this.internals.role = 'toolbar'; } } render() { return html `<slot @slotchange=${this.updateTabIndices}></slot>`; } handleKeyDown(event) { const isLeft = event.key === 'ArrowLeft'; const isRight = event.key === 'ArrowRight'; const isHome = event.key === 'Home'; const isEnd = event.key === 'End'; // Ignore non-navigation keys if (!isLeft && !isRight && !isHome && !isEnd) { return; } const { chips } = this; // Don't try to select another chip if there aren't any. if (chips.length < 2) { return; } // Prevent default interactions, such as scrolling. event.preventDefault(); if (isHome || isEnd) { const index = isHome ? 0 : chips.length - 1; chips[index].focus({ trailing: isEnd }); this.updateTabIndices(); return; } // Check if moving forwards or backwards const isRtl = getComputedStyle(this).direction === 'rtl'; const forwards = isRtl ? isLeft : isRight; const focusedChip = chips.find((chip) => chip.matches(':focus-within')); if (!focusedChip) { // If there is not already a chip focused, select the first or last chip // based on the direction we're traveling. const nextChip = forwards ? chips[0] : chips[chips.length - 1]; nextChip.focus({ trailing: !forwards }); this.updateTabIndices(); return; } const currentIndex = chips.indexOf(focusedChip); let nextIndex = forwards ? currentIndex + 1 : currentIndex - 1; // Search for the next sibling that is not disabled to select. // If we return to the host index, there is nothing to select. while (nextIndex !== currentIndex) { if (nextIndex >= chips.length) { // Return to start if moving past the last item. nextIndex = 0; } else if (nextIndex < 0) { // Go to end if moving before the first item. nextIndex = chips.length - 1; } // Check if the next sibling is disabled. If so, // move the index and continue searching. // // Some toolbar items may be focusable when disabled for increased // visibility. const nextChip = chips[nextIndex]; if (nextChip.disabled && !nextChip.alwaysFocusable) { if (forwards) { nextIndex++; } else { nextIndex--; } continue; } nextChip.focus({ trailing: !forwards }); this.updateTabIndices(); break; } } updateTabIndices() { // The chip that should be focusable is either the chip that currently has // focus or the first chip that can be focused. const { chips } = this; let chipToFocus; for (const chip of chips) { const isChipFocusable = chip.alwaysFocusable || !chip.disabled; const chipIsFocused = chip.matches(':focus-within'); if (chipIsFocused && isChipFocusable) { // Found the first chip that is actively focused. This overrides the // first focusable chip found. chipToFocus = chip; continue; } if (isChipFocusable && !chipToFocus) { chipToFocus = chip; } // Disable non-focused chips. If we disable all of them, we'll grant focus // to the first focusable child that was found. chip.tabIndex = -1; } if (chipToFocus) { chipToFocus.tabIndex = 0; } } } __decorate([ queryAssignedElements() ], ChipSet.prototype, "childElements", void 0); //# sourceMappingURL=chip-set.js.map