get.expanded) { this.#focusParentHeader(event.target); } else { event.target.expanded = false; } break; case "ArrowRight": if (event.target.expanded) { focusedRow = this.#focusFirstRow(event.target); } else { event.target.expanded = true; } break; case "Home": this.cards[0]?.summaryEl?.focus(); break; case "End": this.#focusLastVisibleRow(); break; } if (this.multiSelect) { this.updateSelection(event, focusedRow); } } /** * Check if we should handle this event, or if it should be handled by a * child element such as ``. * * @param {KeyboardEvent} event * @returns {boolean} */ #shouldHandleEvent(event) { if (event.keyCode === "Home" || event.keyCode === "End") { // Keys that scroll the entire tree should always be handled. return true; } const headerIsSelected = event.originalTarget === event.target.summaryEl; return headerIsSelected; } /** * Focus the first row of this card (either a URL or nested card header). * * @param {MozCard} card * @returns {SidebarTabRow} */ #focusFirstRow(card) { let focusedRow = null; let innerElement = card.contentSlotEl.assignedElements()[0]; if (innerElement.classList.contains("nested-card")) { // Focus the first nested card header. innerElement.summaryEl.focus(); } else { // Focus the first URL. focusedRow = innerElement.rowEls[0]; focusedRow?.focus(); } return focusedRow; } /** * Focus the last row of this card (either a URL or nested card header). * * @param {MozCard} card * @returns {SidebarTabRow} */ #focusLastRow(card) { let focusedRow = null; let innerElement = card.contentSlotEl.assignedElements()[0]; if (innerElement.classList.contains("nested-card")) { // Focus the last nested card header (or URL, if nested card is expanded). const lastNestedCard = card.lastElementChild; if (lastNestedCard.expanded) { focusedRow = this.#focusLastRow(lastNestedCard); } else { lastNestedCard.summaryEl.focus(); } } else { // Focus the last URL. focusedRow = innerElement.rowEls[innerElement.rowEls.length - 1]; focusedRow?.focus(); } return focusedRow; } /** * Focus the last visible row of the entire tree. */ #focusLastVisibleRow() { const lastCard = this.cards[this.cards.length - 1]; if ( lastCard.classList.contains("nested-card") && !lastCard.parentElement.expanded ) { // If this is an inner card, and the outer card is collapsed, then focus // the outer header. lastCard.parentElement.summaryEl.focus(); } else if (lastCard.expanded) { this.#focusLastRow(lastCard); } else { lastCard.summaryEl.focus(); } } /** * If we're currently on a nested card, focus the "outer" card's header. * * @param {MozCard} card */ #focusParentHeader(card) { if (card.classList.contains("nested-card")) { card.parentElement.summaryEl.focus(); } } /** * When a row is focused while the shift key is held down, add it to the * selection. If shift key was not held down, clear the selection. * * @param {KeyboardEvent} event * @param {SidebarTabRow} rowEl */ updateSelection(event, rowEl) { if (event.code !== "ArrowUp" && event.code !== "ArrowDown") { return; } if (!event.shiftKey) { this.clearSelection(); return; } if (rowEl != null) { const listForRow = rowEl.getRootNode().host; listForRow.selectedGuids.add(rowEl.guid); listForRow.requestVirtualListUpdate(); this.selectedLists.add(listForRow); } } /** * Clear the selection from all lists. */ clearSelection() { for (const list of this.selectedLists) { list.clearSelection(); } this.selectedLists.clear(); } } PK