mirror of
https://github.com/home-assistant/frontend.git
synced 2026-04-02 00:27:49 +01:00
Add scrollbar support for cards with fixed grid row height (#30209)
* Add scrollbar support for cards with fixed grid row height * Fix default sizing * Add warning for card that doesn't support resizing well * Remove not used explanation
This commit is contained in:
@@ -24,6 +24,7 @@ import type {
|
||||
LovelaceHeaderFooter,
|
||||
} from "../types";
|
||||
import type { EntitiesCardConfig } from "./types";
|
||||
import { haStyleScrollbar } from "../../../resources/styles";
|
||||
|
||||
export const computeShowHeaderToggle = <
|
||||
T extends EntityConfig | LovelaceRowConfig,
|
||||
@@ -242,7 +243,7 @@ class HuiEntitiesCard extends LitElement implements LovelaceCard {
|
||||
`}
|
||||
</h1>
|
||||
`}
|
||||
<div id="states" class="card-content">
|
||||
<div id="states" class="card-content ha-scrollbar">
|
||||
${this._configEntities!.map((entityConf) =>
|
||||
this._renderEntity(entityConf)
|
||||
)}
|
||||
@@ -255,71 +256,76 @@ class HuiEntitiesCard extends LitElement implements LovelaceCard {
|
||||
`;
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
ha-card {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
static styles = [
|
||||
haStyleScrollbar,
|
||||
css`
|
||||
ha-card {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.card-header .name {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.card-header .name {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
#states {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--entities-card-row-gap, var(--card-row-gap, 8px));
|
||||
}
|
||||
#states {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow-x: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--entities-card-row-gap, var(--card-row-gap, 8px));
|
||||
}
|
||||
|
||||
#states > div > * {
|
||||
overflow: clip visible;
|
||||
}
|
||||
#states > div > * {
|
||||
overflow: clip visible;
|
||||
}
|
||||
|
||||
#states > div {
|
||||
position: relative;
|
||||
}
|
||||
#states > div {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.icon {
|
||||
padding: 0px 18px 0px 8px;
|
||||
}
|
||||
.icon {
|
||||
padding: 0px 18px 0px 8px;
|
||||
}
|
||||
|
||||
.header {
|
||||
border-top-left-radius: var(
|
||||
--ha-card-border-radius,
|
||||
var(--ha-border-radius-lg)
|
||||
);
|
||||
border-top-right-radius: var(
|
||||
--ha-card-border-radius,
|
||||
var(--ha-border-radius-lg)
|
||||
);
|
||||
overflow: hidden;
|
||||
}
|
||||
.header:not(:has(> hui-buttons-header-footer)) {
|
||||
margin-bottom: var(--ha-space-4);
|
||||
}
|
||||
.header {
|
||||
border-top-left-radius: var(
|
||||
--ha-card-border-radius,
|
||||
var(--ha-border-radius-lg)
|
||||
);
|
||||
border-top-right-radius: var(
|
||||
--ha-card-border-radius,
|
||||
var(--ha-border-radius-lg)
|
||||
);
|
||||
overflow: hidden;
|
||||
}
|
||||
.header:not(:has(> hui-buttons-header-footer)) {
|
||||
margin-bottom: var(--ha-space-4);
|
||||
}
|
||||
|
||||
.footer {
|
||||
border-bottom-left-radius: var(
|
||||
--ha-card-border-radius,
|
||||
var(--ha-border-radius-lg)
|
||||
);
|
||||
border-bottom-right-radius: var(
|
||||
--ha-card-border-radius,
|
||||
var(--ha-border-radius-lg)
|
||||
);
|
||||
margin-top: -16px;
|
||||
overflow: hidden;
|
||||
}
|
||||
`;
|
||||
.footer {
|
||||
border-bottom-left-radius: var(
|
||||
--ha-card-border-radius,
|
||||
var(--ha-border-radius-lg)
|
||||
);
|
||||
border-bottom-right-radius: var(
|
||||
--ha-card-border-radius,
|
||||
var(--ha-border-radius-lg)
|
||||
);
|
||||
margin-top: -16px;
|
||||
overflow: hidden;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
private _renderEntity(entityConf: LovelaceRowConfig): TemplateResult {
|
||||
const element = createRowElement(
|
||||
|
||||
@@ -75,7 +75,14 @@ class HuiGridCard extends HuiStackCard<GridCardConfig> {
|
||||
return [
|
||||
super.sharedStyles,
|
||||
css`
|
||||
:host {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
}
|
||||
#root {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(
|
||||
var(--grid-card-column-count, ${DEFAULT_COLUMNS}),
|
||||
|
||||
@@ -26,9 +26,15 @@ export class HuiHorizontalStackCard extends HuiStackCard {
|
||||
return [
|
||||
super.sharedStyles,
|
||||
css`
|
||||
:host {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
}
|
||||
#root {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
gap: var(--horizontal-stack-card-gap, var(--stack-card-gap, 8px));
|
||||
}
|
||||
#root > hui-card {
|
||||
|
||||
@@ -2,6 +2,7 @@ import { LitElement, css, html, nothing } from "lit";
|
||||
import { property, state } from "lit/decorators";
|
||||
import { computeRTLDirection } from "../../../common/util/compute_rtl";
|
||||
import type { LovelaceCardConfig } from "../../../data/lovelace/config/card";
|
||||
import { haStyleScrollbar } from "../../../resources/styles";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import type {
|
||||
LovelaceCard,
|
||||
@@ -116,31 +117,38 @@ export abstract class HuiStackCard<T extends StackCardConfig = StackCardConfig>
|
||||
${this._config.title
|
||||
? html`<h1 class="card-header">${this._config.title}</h1>`
|
||||
: ""}
|
||||
<div id="root" dir=${this.hass ? computeRTLDirection(this.hass) : "ltr"}>
|
||||
<div
|
||||
id="root"
|
||||
class="ha-scrollbar"
|
||||
dir=${this.hass ? computeRTLDirection(this.hass) : "ltr"}
|
||||
>
|
||||
${this._cards}
|
||||
${this.preview && this._errorCard ? this._errorCard : nothing}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
static sharedStyles = css`
|
||||
.card-header {
|
||||
color: var(--ha-card-header-color, var(--primary-text-color));
|
||||
text-align: var(--ha-stack-title-text-align, start);
|
||||
font-family: var(--ha-card-header-font-family, inherit);
|
||||
font-size: var(--ha-card-header-font-size, var(--ha-font-size-2xl));
|
||||
font-weight: var(--ha-font-weight-normal);
|
||||
margin-block-start: 0px;
|
||||
margin-block-end: 0px;
|
||||
letter-spacing: -0.012em;
|
||||
line-height: var(--ha-line-height-condensed);
|
||||
display: block;
|
||||
padding: 24px 16px 16px;
|
||||
}
|
||||
:host([ispanel]) #root {
|
||||
--ha-card-border-radius: var(--restore-card-border-radius);
|
||||
--ha-card-border-width: var(--restore-card-border-width);
|
||||
--ha-card-box-shadow: var(--restore-card-box-shadow);
|
||||
}
|
||||
`;
|
||||
static sharedStyles = [
|
||||
haStyleScrollbar,
|
||||
css`
|
||||
.card-header {
|
||||
color: var(--ha-card-header-color, var(--primary-text-color));
|
||||
text-align: var(--ha-stack-title-text-align, start);
|
||||
font-family: var(--ha-card-header-font-family, inherit);
|
||||
font-size: var(--ha-card-header-font-size, var(--ha-font-size-2xl));
|
||||
font-weight: var(--ha-font-weight-normal);
|
||||
margin-block-start: 0px;
|
||||
margin-block-end: 0px;
|
||||
letter-spacing: -0.012em;
|
||||
line-height: var(--ha-line-height-condensed);
|
||||
display: block;
|
||||
padding: 24px 16px 16px;
|
||||
}
|
||||
:host([ispanel]) #root {
|
||||
--ha-card-border-radius: var(--restore-card-border-radius);
|
||||
--ha-card-border-width: var(--restore-card-border-width);
|
||||
--ha-card-box-shadow: var(--restore-card-box-shadow);
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -26,10 +26,16 @@ class HuiVerticalStackCard extends HuiStackCard {
|
||||
return [
|
||||
super.sharedStyles,
|
||||
css`
|
||||
#root {
|
||||
:host {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
}
|
||||
#root {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
gap: var(--vertical-stack-card-gap, var(--stack-card-gap, 8px));
|
||||
}
|
||||
`,
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { mdiDotsVertical, mdiPlaylistEdit } from "@mdi/js";
|
||||
import type { PropertyValues } from "lit";
|
||||
import { css, html, LitElement } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { styleMap } from "lit/directives/style-map";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import "../../../../components/ha-alert";
|
||||
import "../../../../components/ha-button";
|
||||
import "../../../../components/ha-dropdown";
|
||||
import type { HaDropdownSelectEvent } from "../../../../components/ha-dropdown";
|
||||
@@ -24,6 +25,7 @@ import type { HuiCard } from "../../cards/hui-card";
|
||||
import type { CardGridSize } from "../../common/compute-card-grid-size";
|
||||
import {
|
||||
computeCardGridSize,
|
||||
DEFAULT_GRID_SIZE,
|
||||
GRID_COLUMN_MULTIPLIER,
|
||||
isPreciseMode,
|
||||
migrateLayoutToGridOptions,
|
||||
@@ -50,6 +52,7 @@ export class HuiCardLayoutEditor extends LitElement {
|
||||
|
||||
private _mergedOptions = memoizeOne(
|
||||
(options?: LovelaceGridOptions, defaultOptions?: LovelaceGridOptions) => ({
|
||||
...DEFAULT_GRID_SIZE,
|
||||
...defaultOptions,
|
||||
...options,
|
||||
})
|
||||
@@ -86,12 +89,17 @@ export class HuiCardLayoutEditor extends LitElement {
|
||||
const gridTotalColumns = 12 * columnSpan;
|
||||
|
||||
return html`
|
||||
${this._defaultGridOptions &&
|
||||
Object.keys(this._defaultGridOptions).length === 0
|
||||
? html`
|
||||
<ha-alert alert-type="info">
|
||||
${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.edit_card.layout.no_grid_support"
|
||||
)}
|
||||
</ha-alert>
|
||||
`
|
||||
: nothing}
|
||||
<div class="header">
|
||||
<p class="intro">
|
||||
${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.edit_card.layout.explanation"
|
||||
)}
|
||||
</p>
|
||||
<ha-dropdown
|
||||
slot="icons"
|
||||
@wa-select=${this._handleAction}
|
||||
@@ -284,13 +292,17 @@ export class HuiCardLayoutEditor extends LitElement {
|
||||
const checked = ev.target.checked;
|
||||
|
||||
let columns: number | "full" | undefined;
|
||||
const defaultGridOptions = {
|
||||
...DEFAULT_GRID_SIZE,
|
||||
...this._defaultGridOptions,
|
||||
};
|
||||
if (checked) {
|
||||
columns = "full";
|
||||
} else if (this._defaultGridOptions?.columns === "full") {
|
||||
} else if (defaultGridOptions.columns === "full") {
|
||||
// Default is full width, so we need to set a specific value
|
||||
const columnSpan = this.sectionConfig.column_span ?? 1;
|
||||
const gridTotalColumns = 12 * columnSpan;
|
||||
columns = this._defaultGridOptions?.max_columns ?? gridTotalColumns;
|
||||
columns = defaultGridOptions.max_columns ?? gridTotalColumns;
|
||||
} else {
|
||||
columns = undefined;
|
||||
}
|
||||
@@ -306,11 +318,15 @@ export class HuiCardLayoutEditor extends LitElement {
|
||||
const checked = ev.target.checked;
|
||||
|
||||
let rows: number | "auto" | undefined;
|
||||
const defaultGridOptions = {
|
||||
...DEFAULT_GRID_SIZE,
|
||||
...this._defaultGridOptions,
|
||||
};
|
||||
if (checked) {
|
||||
rows = "auto";
|
||||
} else if (this._defaultGridOptions?.rows === "auto") {
|
||||
} else if (defaultGridOptions.rows === "auto") {
|
||||
// Default is auto height, so we need to set a specific value
|
||||
rows = this._defaultGridOptions?.min_rows ?? 1;
|
||||
rows = defaultGridOptions.min_rows ?? 1;
|
||||
} else {
|
||||
rows = undefined;
|
||||
}
|
||||
@@ -368,11 +384,7 @@ export class HuiCardLayoutEditor extends LitElement {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: flex-start;
|
||||
}
|
||||
.header .intro {
|
||||
flex: 1;
|
||||
margin: 0;
|
||||
color: var(--secondary-text-color);
|
||||
justify-content: flex-end;
|
||||
}
|
||||
.header ha-dropdown {
|
||||
--mdc-theme-text-primary-on-background: var(--primary-text-color);
|
||||
|
||||
@@ -8637,7 +8637,8 @@
|
||||
"auto_height": "Auto height",
|
||||
"auto_height_helper": "Adjust the card height based on its content",
|
||||
"precise_mode": "Precise mode",
|
||||
"precise_mode_helper": "Change the card width with more precision"
|
||||
"precise_mode_helper": "Change the card width with more precision",
|
||||
"no_grid_support": "This card does not fully support resizing yet and may not display correctly with custom sizes."
|
||||
}
|
||||
},
|
||||
"edit_badge": {
|
||||
|
||||
Reference in New Issue
Block a user