From b248e466968ceb146dbc48a0e905d3a0c7509753 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Tue, 3 Sep 2024 12:06:14 +0200 Subject: [PATCH] Switch overflow menues to ha-menu (#782) * Switch quick action to ha-menu * Update the rest * Update src/dashboards/hacs-dashboard.ts Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- .../hacs-repository-owerflow-menu.ts | 42 +-- src/dashboards/hacs-dashboard.ts | 291 +++++++++++------- src/dashboards/hacs-repository-dashboard.ts | 54 +++- src/styles/hacs-common-style.ts | 16 + 4 files changed, 265 insertions(+), 138 deletions(-) diff --git a/src/components/hacs-repository-owerflow-menu.ts b/src/components/hacs-repository-owerflow-menu.ts index 5ebf0b280..548b8ad21 100644 --- a/src/components/hacs-repository-owerflow-menu.ts +++ b/src/components/hacs-repository-owerflow-menu.ts @@ -24,34 +24,40 @@ import { import type { HacsDashboard } from "../dashboards/hacs-dashboard"; import type { HacsRepositoryDashboard } from "../dashboards/hacs-repository-dashboard"; import { showHacsDownloadDialog, showHacsFormDialog } from "./dialogs/show-hacs-dialog"; +import { LocalizeFunc } from "../../homeassistant-frontend/src/common/translations/localize"; +import { HacsLocalizeKeys } from "../data/localize"; export const repositoryMenuItems = memoizeOne( - (element: HacsRepositoryDashboard | HacsDashboard, repository: RepositoryBase) => [ + ( + element: HacsRepositoryDashboard | HacsDashboard, + repository: RepositoryBase, + localize: LocalizeFunc, + ) => [ ...(element.nodeName === "HACS-DASHBOARD" ? [ { path: mdiInformation, - label: element.hacs.localize("common.show"), + label: localize("common.show"), action: () => navigate(`/hacs/repository/${repository.id}`), }, ] : []), { path: mdiGithub, - label: element.hacs.localize("common.repository"), + label: localize("common.repository"), action: () => mainWindow.open(`https://github.com/${repository.full_name}`, "_blank", "noreferrer=true"), }, { path: mdiArrowDownCircle, - label: element.hacs.localize("repository_card.update_information"), + label: localize("repository_card.update_information"), action: async () => { await repositoryUpdate(element.hass, String(repository.id)); }, }, { path: repository.installed_version ? mdiReload : mdiDownload, - label: element.hacs.localize( + label: localize( repository.installed_version ? "repository_card.redownload" : "common.download", ), action: () => @@ -62,7 +68,7 @@ export const repositoryMenuItems = memoizeOne( ? [ { path: mdiMoonNew, - label: element.hacs.localize("repository_card.dismiss_new"), + label: localize("repository_card.dismiss_new"), action: () => repositoriesClearNewRepository(element.hass, repository.id), }, ] @@ -71,7 +77,7 @@ export const repositoryMenuItems = memoizeOne( ? [ { path: mdiLanguageJavascript, - label: element.hacs.localize("repository_card.open_source"), + label: localize("repository_card.open_source"), action: () => mainWindow.open( `/hacsfiles/${repository.local_path.split("/").pop()}/${repository.file_name}?cachebuster=${Date.now()}`, @@ -84,7 +90,7 @@ export const repositoryMenuItems = memoizeOne( { divider: true }, { path: mdiAlertCircleOutline, - label: element.hacs.localize("repository_card.open_issue"), + label: localize("repository_card.open_issue"), action: () => mainWindow.open( `https://github.com/${repository.full_name}/issues`, @@ -96,7 +102,7 @@ export const repositoryMenuItems = memoizeOne( ? [ { path: mdiAlert, - label: element.hacs.localize("repository_card.report"), + label: localize("repository_card.report"), action: () => mainWindow.open( `https://github.com/hacs/integration/issues/new?assignees=ludeeus&labels=flag&template=removal.yml&repo=${repository.full_name}&title=Request for removal of ${repository.full_name}`, @@ -107,7 +113,7 @@ export const repositoryMenuItems = memoizeOne( }, { path: mdiClose, - label: element.hacs.localize("common.remove"), + label: localize("common.remove"), action: async () => { if (repository.category === "integration" && repository.config_flow) { const configFlows = (await getConfigEntries(element.hass)).some( @@ -115,12 +121,12 @@ export const repositoryMenuItems = memoizeOne( ); if (configFlows) { const ignore = await showConfirmationDialog(element, { - title: element.hacs.localize("dialog.configured.title"), - text: element.hacs.localize("dialog.configured.message", { + title: localize("dialog.configured.title"), + text: localize("dialog.configured.message", { name: repository.name, }), - dismissText: element.hacs.localize("common.ignore"), - confirmText: element.hacs.localize("common.navigate"), + dismissText: localize("common.ignore"), + confirmText: localize("common.navigate"), confirm: () => { navigate("/config/integrations", { replace: true }); }, @@ -132,9 +138,9 @@ export const repositoryMenuItems = memoizeOne( } showHacsFormDialog(element, { hacs: element.hacs, - title: element.hacs.localize("dialog.remove.title"), - saveLabel: element.hacs.localize("dialog.remove.title"), - description: element.hacs.localize("dialog.remove.message", { + title: localize("dialog.remove.title"), + saveLabel: localize("dialog.remove.title"), + description: localize("dialog.remove.message", { name: repository.name, }), saveAction: async () => { @@ -143,7 +149,7 @@ export const repositoryMenuItems = memoizeOne( destructive: true, }); }, - warning: true, + error: true, }, ] : []), diff --git a/src/dashboards/hacs-dashboard.ts b/src/dashboards/hacs-dashboard.ts index 0e444f6e8..df0ae1037 100644 --- a/src/dashboards/hacs-dashboard.ts +++ b/src/dashboards/hacs-dashboard.ts @@ -3,6 +3,7 @@ import "@material/mwc-list/mwc-list"; import "@material/mwc-list/mwc-list-item"; import { mdiAlertCircleOutline, + mdiDotsVertical, mdiFileDocument, mdiGit, mdiGithub, @@ -11,7 +12,7 @@ import { } from "@mdi/js"; import type { CSSResultGroup, TemplateResult } from "lit"; import { LitElement, html, nothing } from "lit"; -import { customElement, property } from "lit/decorators"; +import { customElement, property, query, state } from "lit/decorators"; import memoize from "memoize-one"; import { relativeTime } from "../../homeassistant-frontend/src/common/datetime/relative_time"; import { storage } from "../../homeassistant-frontend/src/common/decorators/storage"; @@ -28,12 +29,14 @@ import "../../homeassistant-frontend/src/components/ha-button-menu"; import "../../homeassistant-frontend/src/components/ha-fab"; import "../../homeassistant-frontend/src/components/ha-form/ha-form"; import "../../homeassistant-frontend/src/components/ha-markdown"; +import "../../homeassistant-frontend/src/components/ha-menu"; +import "../../homeassistant-frontend/src/components/ha-menu-item"; import { LocalizeFunc } from "../../homeassistant-frontend/src/common/translations/localize"; import { HaFormSchema } from "../../homeassistant-frontend/src/components/ha-form/types"; -import "../../homeassistant-frontend/src/components/ha-icon-overflow-menu"; -import { IconOverflowMenuItem } from "../../homeassistant-frontend/src/components/ha-icon-overflow-menu"; +import { HaMenu } from "../../homeassistant-frontend/src/components/ha-menu"; import "../../homeassistant-frontend/src/components/ha-svg-icon"; +import { PageNavigation } from "../../homeassistant-frontend/src/layouts/hass-tabs-subpage"; import { haStyle } from "../../homeassistant-frontend/src/resources/styles"; import type { HomeAssistant, Route } from "../../homeassistant-frontend/src/types"; import { brandsUrl } from "../../homeassistant-frontend/src/util/brands-url"; @@ -51,7 +54,7 @@ import { repositoriesClearNew } from "../data/websocket"; import { HacsStyles } from "../styles/hacs-common-style"; import { documentationUrl } from "../tools/documentation"; import { typeIcon } from "../tools/type-icon"; -import { PageNavigation } from "../../homeassistant-frontend/src/layouts/hass-tabs-subpage"; +import { showAlertDialog } from "../../homeassistant-frontend/src/dialogs/generic/show-dialog-box"; const defaultKeyData = { title: "", @@ -102,6 +105,15 @@ export class HacsDashboard extends LitElement { @storage({ key: "hacs-dashboard-table-columns-ordering", state: true, subscribe: false }) private _orderTableColumns?: string[]; + @query("#overflow-menu") + private _overflowMenu!: HaMenu; + + @query("#repository-overflow-menu") + private _repositoryOverflowMenu!: HaMenu; + + @state() + private _overflowMenuRepository?: RepositoryBase; + protected render = (): TemplateResult | void => { const repositories = this._filterRepositories( this.hacs.repositories, @@ -111,111 +123,148 @@ export class HacsDashboard extends LitElement { const repositoriesContainsNew = repositories.some((repository) => repository.new); return html` - mainWindow.open(documentationUrl({}), "_blank", "noreferrer=true"), - }, - { - path: mdiGithub, - label: "GitHub", - action: () => mainWindow.open(`https://github.com/hacs`, "_blank", "noreferrer=true"), - }, - { - path: mdiAlertCircleOutline, - label: this.hacs.localize("menu.open_issue"), - action: () => - mainWindow.open( - documentationUrl({ - path: "/docs/help/issues", - }), - "_blank", - "noreferrer=true", - ), - }, - { - path: mdiGit, - disabled: Boolean(this.hacs.info.disabled_reason), - label: this.hacs.localize("menu.custom_repositories"), - action: () => { + ?iswide=${this.isWide} + .localizeFunc=${this.hacs.localize as LocalizeFunc} + main-page + .narrow=${this.narrow} + .route=${this.route} + clickable + .filter=${this._activeSearch || ""} + hasFilters + hasFab + .filters=${this._activeFilters?.length} + .noDataText=${this.hacs.localize("dashboard.no_data")} + .initialGroupColumn=${this._activeGrouping || "translated_status"} + .initialCollapsedGroups=${this._activeCollapsed || []} + .groupOrder=${this._groupOrder(this.hacs.localize, this._activeGrouping)} + .initialSorting=${this._activeSorting} + .columnOrder=${this._orderTableColumns} + .hiddenColumns=${this._hiddenTableColumns} + @columns-changed=${this._handleColumnsChanged} + @row-click=${this._handleRowClicked} + @clear-filter=${this._handleClearFilter} + @value-changed=${this._handleSearchFilterChanged} + @sorting-changed=${this._handleSortingChanged} + @grouping-changed=${this._handleGroupingChanged} + @collapsed-changed=${this._handleCollapseChanged} + > + + + filter.startsWith("status_")) || "", + type: this._activeFilters?.find((filter) => filter.startsWith("type_")) || "", + }} + .schema=${this._filterSchema(this.hacs.localize, this.hacs.info.categories)} + .computeLabel=${this._computeFilterFormLabel} + @value-changed=${this._handleFilterChanged} + > + + + ${this._overflowMenuRepository + ? repositoryMenuItems(this, this._overflowMenuRepository, this.hacs.localize).map( + (entry) => + entry.divider + ? html`
  • ` + : html` + { + entry?.action && entry.action(); + }} + > + +
    ${entry.label}
    +
    + `, + ) + : nothing} +
    + + { + mainWindow.open(documentationUrl({}), "_blank", "noreferrer=true"); + }} + > + +
    ${this.hacs.localize("menu.documentation")}
    +
    + { + mainWindow.open("https://github.com/hacs", "_blank", "noreferrer=true"); + }} + > + +
    GitHub
    +
    + { + mainWindow.open( + documentationUrl({ + path: "/docs/help/issues", + }), + "_blank", + "noreferrer=true", + ); + }} + > + +
    ${this.hacs.localize("menu.open_issue")}
    +
    + { + if (!this.hacs.info.disabled_reason) { showHacsCustomRepositoriesDialog(this, { hacs: this.hacs, }); - }, - }, - repositoriesContainsNew - ? { - path: mdiNewBox, - label: this.hacs.localize("menu.dismiss"), - action: () => { - repositoriesClearNew(this.hass, this.hacs); - }, - } - : undefined, - { - path: mdiInformation, - label: this.hacs.localize("menu.about"), - action: () => { - showHacsFormDialog(this, { - hacs: this.hacs, - title: APP_FULL_NAME, - description: html``, + } else { + showAlertDialog(this, { + title: "HACS is disabled", + text: this.hacs.info.disabled_reason, }); - }, - }, - ].filter((item) => item !== undefined) as IconOverflowMenuItem[]} - > - - - filter.startsWith("status_")) || "", - type: this._activeFilters?.find((filter) => filter.startsWith("type_")) || "", - }} - .schema=${this._filterSchema(this.hacs.localize, this.hacs.info.categories)} - .computeLabel=${this._computeFilterFormLabel} - @value-changed=${this._handleFilterChanged} - > - `; + } + }} + > + +
    ${this.hacs.localize("menu.custom_repositories")}
    +
    + ${repositoriesContainsNew + ? html` { + repositoriesClearNew(this.hass, this.hacs); + }} + > + +
    ${this.hacs.localize("menu.dismiss")}
    +
    ` + : nothing} + { + showHacsFormDialog(this, { + hacs: this.hacs, + title: APP_FULL_NAME, + description: html``, + }); + }} + > + +
    ${this.hacs.localize("menu.about")}
    +
    +
    `; }; private _filterRepositories = memoize( @@ -381,17 +430,39 @@ export class HacsDashboard extends LitElement { showNarrow: true, type: "overflow-menu", template: (repository: RepositoryBase) => html` - - + `, }, }), ); + private _showOverflowRepositoryMenu = (ev: any) => { + if ( + this._repositoryOverflowMenu.open && + ev.target === this._repositoryOverflowMenu.anchorElement + ) { + this._repositoryOverflowMenu.close(); + return; + } + this._repositoryOverflowMenu.anchorElement = ev.target; + this._overflowMenuRepository = ev.target.repository; + this._repositoryOverflowMenu.show(); + }; + + private _showOverflowMenu = (ev: any) => { + if (this._overflowMenu.open) { + this._overflowMenu.close(); + return; + } + this._overflowMenu.anchorElement = ev.target; + this._overflowMenu.show(); + }; + private _groupOrder = memoize( (localize: LocalizeFunc, activeGrouping: string | undefined) => activeGrouping === "translated_status" diff --git a/src/dashboards/hacs-repository-dashboard.ts b/src/dashboards/hacs-repository-dashboard.ts index b3cd32763..8aa9d57cb 100644 --- a/src/dashboards/hacs-repository-dashboard.ts +++ b/src/dashboards/hacs-repository-dashboard.ts @@ -2,23 +2,26 @@ import { mdiAccount, mdiArrowDownBold, mdiCube, + mdiDotsVertical, mdiDownload, mdiExclamationThick, mdiStar, } from "@mdi/js"; import type { PropertyValues, TemplateResult } from "lit"; import { LitElement, css, html } from "lit"; -import { customElement, property, state } from "lit/decorators"; +import { customElement, property, query, state } from "lit/decorators"; import memoizeOne from "memoize-one"; import { mainWindow } from "../../homeassistant-frontend/src/common/dom/get_main_window"; import { extractSearchParamsObject } from "../../homeassistant-frontend/src/common/url/search-params"; +import "../../homeassistant-frontend/src/components/chips/ha-assist-chip"; +import "../../homeassistant-frontend/src/components/chips/ha-chip-set"; import "../../homeassistant-frontend/src/components/ha-alert"; import "../../homeassistant-frontend/src/components/ha-card"; -import "../../homeassistant-frontend/src/components/chips/ha-chip-set"; -import "../../homeassistant-frontend/src/components/chips/ha-assist-chip"; import "../../homeassistant-frontend/src/components/ha-fab"; -import "../../homeassistant-frontend/src/components/ha-icon-overflow-menu"; import "../../homeassistant-frontend/src/components/ha-markdown"; +import "../../homeassistant-frontend/src/components/ha-menu"; +import type { HaMenu } from "../../homeassistant-frontend/src/components/ha-menu"; +import "../../homeassistant-frontend/src/components/ha-menu-item"; import { showConfirmationDialog } from "../../homeassistant-frontend/src/dialogs/generic/show-dialog-box"; import "../../homeassistant-frontend/src/layouts/hass-error-screen"; import "../../homeassistant-frontend/src/layouts/hass-loading-screen"; @@ -49,6 +52,9 @@ export class HacsRepositoryDashboard extends LitElement { @state() private _error?: string; + @query("#overflow-menu") + private _repositoryOverflowMenu!: HaMenu; + public connectedCallback() { super.connectedCallback(); document.body.addEventListener("keydown", this._generateMyLink); @@ -188,13 +194,12 @@ export class HacsRepositoryDashboard extends LitElement { .header=${this._repository.name} hasFab > - - + .label=${this.hass.localize("ui.common.overflow_menu") || "overflow_menu"} + .path=${mdiDotsVertical} + @click=${this._showOverflowRepositoryMenu} + >
    @@ -274,9 +279,38 @@ export class HacsRepositoryDashboard extends LitElement { ` : ""} + + ${repositoryMenuItems(this, this._repository, this.hacs.localize).map((entry) => + entry.divider + ? html`
  • ` + : html` + { + entry?.action && entry.action(); + }} + > + +
    ${entry.label}
    +
    + `, + )} +
    `; } + private _showOverflowRepositoryMenu = (ev: any) => { + if ( + this._repositoryOverflowMenu.open && + ev.target === this._repositoryOverflowMenu.anchorElement + ) { + this._repositoryOverflowMenu.close(); + return; + } + this._repositoryOverflowMenu.anchorElement = ev.target; + this._repositoryOverflowMenu.show(); + }; + private _downloadRepositoryDialog() { showHacsDownloadDialog(this, { hacs: this.hacs, diff --git a/src/styles/hacs-common-style.ts b/src/styles/hacs-common-style.ts index 6c193c1a0..4abfc4622 100644 --- a/src/styles/hacs-common-style.ts +++ b/src/styles/hacs-common-style.ts @@ -30,10 +30,26 @@ export const hacsCommonClasses = css` } `; +const hacsOverflowMenuStyle = css` + ha-menu-item.error { + --md-menu-item-label-text-color: var(--error-color); + --hcv-color-icon: var(--error-color); + } + + ha-menu-item.warning { + --md-menu-item-label-text-color: var(--warning-color); + --hcv-color-icon: var(--warning-color); + } + li[role="separator"] { + border-bottom: 1px solid var(--divider-color); + } +`; + export const HacsStyles: CSSResultGroup = [ haStyle, hacsIconStyle, hacsCommonClasses, hacsLinkStyle, hacsButtonStyle, + hacsOverflowMenuStyle, ];