mirror of
https://github.com/home-assistant/frontend.git
synced 2025-11-11 12:01:07 +00:00
Compare commits
7 Commits
copilot/fi
...
dropdown
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b70d1492b3 | ||
|
|
fba38cc6ec | ||
|
|
a8b0291a9a | ||
|
|
a240bd2634 | ||
|
|
9d4bf30753 | ||
|
|
10b99433ea | ||
|
|
f0d4c9cb72 |
55
gallery/src/pages/components/ha-dropdown.markdown
Normal file
55
gallery/src/pages/components/ha-dropdown.markdown
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
---
|
||||||
|
title: Dropdown
|
||||||
|
---
|
||||||
|
|
||||||
|
# Dropdown `<ha-dropdown>`
|
||||||
|
|
||||||
|
## Implementation
|
||||||
|
|
||||||
|
A compact, accessible dropdown menu for choosing actions or settings. `ha-dropdown` supports composed menu items (`<ha-dropdown-item>`) for icons, submenus, checkboxes, disabled entries, and destructive variants. Use composition with `slot="trigger"` to control the trigger button and use `<ha-dropdown-item>` for rich item content.
|
||||||
|
|
||||||
|
### Example usage (composition)
|
||||||
|
|
||||||
|
```html
|
||||||
|
<ha-dropdown open>
|
||||||
|
<ha-button slot="trigger" with-caret>Dropdown</ha-button>
|
||||||
|
|
||||||
|
<ha-dropdown-item>
|
||||||
|
<ha-svg-icon .path="${mdiContentCut}" slot="icon"></ha-svg-icon>
|
||||||
|
Cut
|
||||||
|
</ha-dropdown-item>
|
||||||
|
|
||||||
|
<ha-dropdown-item>
|
||||||
|
<ha-svg-icon .path="${mdiContentCopy}" slot="icon"></ha-svg-icon>
|
||||||
|
Copy
|
||||||
|
</ha-dropdown-item>
|
||||||
|
|
||||||
|
<ha-dropdown-item disabled>
|
||||||
|
<ha-svg-icon .path="${mdiContentPaste}" slot="icon"></ha-svg-icon>
|
||||||
|
Paste
|
||||||
|
</ha-dropdown-item>
|
||||||
|
|
||||||
|
<ha-dropdown-item>
|
||||||
|
Show images
|
||||||
|
<ha-dropdown-item slot="submenu" value="show-all-images"
|
||||||
|
>Show all images</ha-dropdown-item
|
||||||
|
>
|
||||||
|
<ha-dropdown-item slot="submenu" value="show-thumbnails"
|
||||||
|
>Show thumbnails</ha-dropdown-item
|
||||||
|
>
|
||||||
|
</ha-dropdown-item>
|
||||||
|
|
||||||
|
<ha-dropdown-item type="checkbox" checked>Emoji shortcuts</ha-dropdown-item>
|
||||||
|
<ha-dropdown-item type="checkbox" checked>Word wrap</ha-dropdown-item>
|
||||||
|
|
||||||
|
<ha-dropdown-item variant="danger">
|
||||||
|
<ha-svg-icon .path="${mdiDelete}" slot="icon"></ha-svg-icon>
|
||||||
|
Delete
|
||||||
|
</ha-dropdown-item>
|
||||||
|
</ha-dropdown>
|
||||||
|
```
|
||||||
|
|
||||||
|
### API
|
||||||
|
|
||||||
|
This component is based on the webawesome dropdown component.
|
||||||
|
Check the [webawesome documentation](https://webawesome.com/docs/components/dropdown/) for more details.
|
||||||
133
gallery/src/pages/components/ha-dropdown.ts
Normal file
133
gallery/src/pages/components/ha-dropdown.ts
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
import {
|
||||||
|
mdiContentCopy,
|
||||||
|
mdiContentCut,
|
||||||
|
mdiContentPaste,
|
||||||
|
mdiDelete,
|
||||||
|
} from "@mdi/js";
|
||||||
|
import type { TemplateResult } from "lit";
|
||||||
|
import { css, html, LitElement } from "lit";
|
||||||
|
import { customElement } from "lit/decorators";
|
||||||
|
import "../../../../src/components/ha-button";
|
||||||
|
import "../../../../src/components/ha-card";
|
||||||
|
import "../../../../src/components/ha-svg-icon";
|
||||||
|
import "../../../../src/components/ha-dropdown-item";
|
||||||
|
import "@home-assistant/webawesome/dist/components/icon/icon";
|
||||||
|
import "@home-assistant/webawesome/dist/components/button/button";
|
||||||
|
import "@home-assistant/webawesome/dist/components/dropdown/dropdown";
|
||||||
|
import "../../../../src/components/ha-dropdown";
|
||||||
|
import "@home-assistant/webawesome/dist/components/popup/popup";
|
||||||
|
import { applyThemesOnElement } from "../../../../src/common/dom/apply_themes_on_element";
|
||||||
|
import "../../../../src/components/ha-icon-button";
|
||||||
|
|
||||||
|
@customElement("demo-components-ha-dropdown")
|
||||||
|
export class DemoHaDropdown extends LitElement {
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
return html`
|
||||||
|
${["light", "dark"].map(
|
||||||
|
(mode) => html`
|
||||||
|
<div class=${mode}>
|
||||||
|
<ha-card header="ha-button in ${mode}">
|
||||||
|
<div class="card-content">
|
||||||
|
<ha-dropdown open>
|
||||||
|
<ha-button slot="trigger" with-caret>Dropdown</ha-button>
|
||||||
|
|
||||||
|
<ha-dropdown-item>
|
||||||
|
<ha-svg-icon
|
||||||
|
.path=${mdiContentCut}
|
||||||
|
slot="icon"
|
||||||
|
></ha-svg-icon>
|
||||||
|
Cut
|
||||||
|
</ha-dropdown-item>
|
||||||
|
<ha-dropdown-item>
|
||||||
|
<ha-svg-icon
|
||||||
|
.path=${mdiContentCopy}
|
||||||
|
slot="icon"
|
||||||
|
></ha-svg-icon>
|
||||||
|
Copy
|
||||||
|
</ha-dropdown-item>
|
||||||
|
<ha-dropdown-item disabled>
|
||||||
|
<ha-svg-icon
|
||||||
|
.path=${mdiContentPaste}
|
||||||
|
slot="icon"
|
||||||
|
></ha-svg-icon>
|
||||||
|
Paste
|
||||||
|
</ha-dropdown-item>
|
||||||
|
<ha-dropdown-item>
|
||||||
|
Show images
|
||||||
|
<ha-dropdown-item slot="submenu" value="show-all-images"
|
||||||
|
>Show All Images</ha-dropdown-item
|
||||||
|
>
|
||||||
|
<ha-dropdown-item slot="submenu" value="show-thumbnails"
|
||||||
|
>Show Thumbnails</ha-dropdown-item
|
||||||
|
>
|
||||||
|
</ha-dropdown-item>
|
||||||
|
<ha-dropdown-item type="checkbox" checked
|
||||||
|
>Emoji Shortcuts</ha-dropdown-item
|
||||||
|
>
|
||||||
|
<ha-dropdown-item type="checkbox" checked
|
||||||
|
>Word Wrap</ha-dropdown-item
|
||||||
|
>
|
||||||
|
<ha-dropdown-item variant="danger">
|
||||||
|
<ha-svg-icon .path=${mdiDelete} slot="icon"></ha-svg-icon>
|
||||||
|
Delete
|
||||||
|
</ha-dropdown-item>
|
||||||
|
</ha-dropdown>
|
||||||
|
</div>
|
||||||
|
</ha-card>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
firstUpdated(changedProps) {
|
||||||
|
super.firstUpdated(changedProps);
|
||||||
|
applyThemesOnElement(
|
||||||
|
this.shadowRoot!.querySelector(".dark"),
|
||||||
|
{
|
||||||
|
default_theme: "default",
|
||||||
|
default_dark_theme: "default",
|
||||||
|
themes: {},
|
||||||
|
darkMode: true,
|
||||||
|
theme: "default",
|
||||||
|
},
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static styles = css`
|
||||||
|
:host {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
.dark,
|
||||||
|
.light {
|
||||||
|
display: block;
|
||||||
|
background-color: var(--primary-background-color);
|
||||||
|
padding: 0 50px;
|
||||||
|
}
|
||||||
|
.button {
|
||||||
|
padding: unset;
|
||||||
|
}
|
||||||
|
ha-card {
|
||||||
|
margin: 24px auto;
|
||||||
|
}
|
||||||
|
.card-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 24px;
|
||||||
|
}
|
||||||
|
.card-content div {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"demo-components-ha-dropdown": DemoHaDropdown;
|
||||||
|
}
|
||||||
|
}
|
||||||
33
src/components/ha-dropdown-item.ts
Normal file
33
src/components/ha-dropdown-item.ts
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import DropdownItem from "@home-assistant/webawesome/dist/components/dropdown-item/dropdown-item";
|
||||||
|
import { css, type CSSResultGroup } from "lit";
|
||||||
|
import { customElement } from "lit/decorators";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Home Assistant dropdown item component
|
||||||
|
*
|
||||||
|
* @element ha-dropdown-item
|
||||||
|
* @extends {DropdownItem}
|
||||||
|
*
|
||||||
|
* @summary
|
||||||
|
* A stylable dropdown item component supporting Home Assistant theming, variants, and appearances based on webawesome dropdown item.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@customElement("ha-dropdown-item")
|
||||||
|
export class HaDropdownItem extends DropdownItem {
|
||||||
|
static get styles(): CSSResultGroup {
|
||||||
|
return [
|
||||||
|
DropdownItem.styles,
|
||||||
|
css`
|
||||||
|
:host {
|
||||||
|
min-height: 40px;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-dropdown-item": HaDropdownItem;
|
||||||
|
}
|
||||||
|
}
|
||||||
45
src/components/ha-dropdown.ts
Normal file
45
src/components/ha-dropdown.ts
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import Dropdown from "@home-assistant/webawesome/dist/components/dropdown/dropdown";
|
||||||
|
import { css, type CSSResultGroup } from "lit";
|
||||||
|
import { customElement, property } from "lit/decorators";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Home Assistant dropdown component
|
||||||
|
*
|
||||||
|
* @element ha-dropdown
|
||||||
|
* @extends {Dropdown}
|
||||||
|
*
|
||||||
|
* @summary
|
||||||
|
* A stylable dropdown component supporting Home Assistant theming, variants, and appearances based on webawesome dropdown.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@customElement("ha-dropdown")
|
||||||
|
export class HaDropdown extends Dropdown {
|
||||||
|
@property({ attribute: false }) dropdownTag = "ha-dropdown";
|
||||||
|
|
||||||
|
@property({ attribute: false }) dropdownItemTag = "ha-dropdown-item";
|
||||||
|
|
||||||
|
static get styles(): CSSResultGroup {
|
||||||
|
return [
|
||||||
|
Dropdown.styles,
|
||||||
|
css`
|
||||||
|
:host {
|
||||||
|
--wa-color-surface-border: var(--ha-color-border-normal);
|
||||||
|
--wa-color-surface-raised: var(
|
||||||
|
--card-background-color,
|
||||||
|
var(--ha-dialog-surface-background, var(--mdc-theme-surface, #fff)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#menu {
|
||||||
|
--wa-shadow-m: 0px 4px 8px 0px var(--ha-color-shadow);
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-dropdown": HaDropdown;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -29,6 +29,8 @@ import {
|
|||||||
} from "../../dialogs/generic/show-dialog-box";
|
} from "../../dialogs/generic/show-dialog-box";
|
||||||
import { haStyle } from "../../resources/styles";
|
import { haStyle } from "../../resources/styles";
|
||||||
import type { HomeAssistant } from "../../types";
|
import type { HomeAssistant } from "../../types";
|
||||||
|
import "../../components/ha-dropdown-item";
|
||||||
|
import "../../components/ha-dropdown";
|
||||||
|
|
||||||
// Client ID used by iOS app
|
// Client ID used by iOS app
|
||||||
const iOSclientId = "https://home-assistant.io/iOS";
|
const iOSclientId = "https://home-assistant.io/iOS";
|
||||||
@@ -146,19 +148,18 @@ class HaRefreshTokens extends LitElement {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<ha-md-button-menu positioning="popover">
|
<ha-dropdown>
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
slot="trigger"
|
slot="trigger"
|
||||||
.label=${this.hass.localize("ui.common.menu")}
|
.label=${this.hass.localize("ui.common.menu")}
|
||||||
.path=${mdiDotsVertical}
|
.path=${mdiDotsVertical}
|
||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
<ha-md-menu-item
|
<ha-dropdown-item
|
||||||
graphic="icon"
|
|
||||||
@click=${this._toggleTokenExpiration}
|
@click=${this._toggleTokenExpiration}
|
||||||
.token=${token}
|
.token=${token}
|
||||||
>
|
>
|
||||||
<ha-svg-icon
|
<ha-svg-icon
|
||||||
slot="start"
|
slot="icon"
|
||||||
.path=${token.expire_at
|
.path=${token.expire_at
|
||||||
? mdiClockRemoveOutline
|
? mdiClockRemoveOutline
|
||||||
: mdiClockCheckOutline}
|
: mdiClockCheckOutline}
|
||||||
@@ -170,24 +171,20 @@ class HaRefreshTokens extends LitElement {
|
|||||||
: this.hass.localize(
|
: this.hass.localize(
|
||||||
"ui.panel.profile.refresh_tokens.enable_token_expiration"
|
"ui.panel.profile.refresh_tokens.enable_token_expiration"
|
||||||
)}
|
)}
|
||||||
</ha-md-menu-item>
|
</ha-dropdown-item>
|
||||||
<ha-md-menu-item
|
<ha-dropdown-item
|
||||||
graphic="icon"
|
variant="danger"
|
||||||
class="warning"
|
|
||||||
.disabled=${token.is_current}
|
.disabled=${token.is_current}
|
||||||
@click=${this._deleteToken}
|
@click=${this._deleteToken}
|
||||||
.token=${token}
|
.token=${token}
|
||||||
>
|
>
|
||||||
<ha-svg-icon
|
<ha-svg-icon
|
||||||
class="warning"
|
slot="icon"
|
||||||
slot="start"
|
|
||||||
.path=${mdiDelete}
|
.path=${mdiDelete}
|
||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
<div slot="headline">
|
${this.hass.localize("ui.common.delete")}
|
||||||
${this.hass.localize("ui.common.delete")}
|
</ha-dropdown-item>
|
||||||
</div>
|
</ha-dropdown>
|
||||||
</ha-md-menu-item>
|
|
||||||
</ha-md-button-menu>
|
|
||||||
</div>
|
</div>
|
||||||
</ha-settings-row>
|
</ha-settings-row>
|
||||||
`
|
`
|
||||||
|
|||||||
@@ -155,6 +155,10 @@ export const semanticColorStyles = css`
|
|||||||
|
|
||||||
/* Surfaces */
|
/* Surfaces */
|
||||||
--ha-color-surface-default: var(--ha-color-neutral-95);
|
--ha-color-surface-default: var(--ha-color-neutral-95);
|
||||||
|
--ha-color-on-surface-default: var(--ha-color-neutral-05);
|
||||||
|
|
||||||
|
/* shadow */
|
||||||
|
--ha-color-shadow: rgba(0, 0, 0, 0.2);
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@@ -286,5 +290,9 @@ export const darkSemanticColorStyles = css`
|
|||||||
|
|
||||||
/* Surfaces */
|
/* Surfaces */
|
||||||
--ha-color-surface-default: var(--ha-color-neutral-10);
|
--ha-color-surface-default: var(--ha-color-neutral-10);
|
||||||
|
--ha-color-on-surface-default: var(--ha-color-neutral-95);
|
||||||
|
|
||||||
|
/* shadow */
|
||||||
|
--ha-color-shadow: rgba(255, 255, 255, 0.2);
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -52,7 +52,9 @@ export const waColorStyles = css`
|
|||||||
--wa-color-danger-on-normal: var(--ha-color-on-danger-normal);
|
--wa-color-danger-on-normal: var(--ha-color-on-danger-normal);
|
||||||
--wa-color-danger-on-quiet: var(--ha-color-on-danger-quiet);
|
--wa-color-danger-on-quiet: var(--ha-color-on-danger-quiet);
|
||||||
|
|
||||||
--wa-color-surface-default: var(--card-background-color);
|
--wa-color-text-normal: var(--ha-color-text-primary);
|
||||||
|
--wa-color-surface-default: var(--card-background-color);
|
||||||
|
--wa-color-surface-raised: var(--ha-dialog-surface-background, var(--mdc-theme-surface, #fff));
|
||||||
--wa-panel-border-radius: var(--ha-border-radius-3xl);
|
--wa-panel-border-radius: var(--ha-border-radius-3xl);
|
||||||
--wa-panel-border-style: solid;
|
--wa-panel-border-style: solid;
|
||||||
--wa-panel-border-width: 1px;
|
--wa-panel-border-width: 1px;
|
||||||
|
|||||||
@@ -16,7 +16,17 @@ export const waMainStyles = css`
|
|||||||
--wa-font-weight-action: var(--ha-font-weight-medium);
|
--wa-font-weight-action: var(--ha-font-weight-medium);
|
||||||
--wa-transition-fast: 75ms;
|
--wa-transition-fast: 75ms;
|
||||||
--wa-transition-easing: ease;
|
--wa-transition-easing: ease;
|
||||||
--wa-border-width-l: var(--ha-border-radius-lg);
|
|
||||||
|
--wa-border-style: solid;
|
||||||
|
--wa-border-width-s: var(--ha-border-width-sm);
|
||||||
|
--wa-border-width-m: var(--ha-border-width-md);
|
||||||
|
--wa-border-width-l: var(--ha-border-width-lg);
|
||||||
|
--wa-border-radius-s: var(--ha-border-radius-sm);
|
||||||
|
--wa-border-radius-m: var(--ha-border-radius-md);
|
||||||
|
--wa-border-radius-l: var(--ha-border-radius-lg);
|
||||||
|
|
||||||
|
--wa-line-height-condensed: 1.25;
|
||||||
|
|
||||||
--wa-space-xl: 32px;
|
--wa-space-xl: 32px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user