feat: Add UI support for PTZ presets (#1920)

- Closes #1889

Whilst a simple change theoretically, the card has such a collection of
surfaces that can overlap other surfaces, it's challenging to get this
to work right! There's a real chance this will have broken something
z-index related (e.g. X overlaps Y when it should not), or (for related
reasons) broken curver corners on the card.
This commit is contained in:
Dermot Duffy
2025-02-27 20:22:12 -08:00
committed by GitHub
parent bb147a45fd
commit 0432eea4b8
37 changed files with 833 additions and 647 deletions
+13 -43
View File
@@ -1,4 +1,5 @@
@use './themes/base.scss';
@import './z-index.scss';
:host {
display: block;
@@ -10,9 +11,8 @@
// this ensures the same experience across all browsers.
background-color: var(--card-background-color);
// The primary border-radius used is the div.main. This is only useful for
// keeping the background-color within the radius.
border-radius: var(--ha-card-border-radius, 4px);
overflow: auto;
height: var(--advanced-camera-card-height);
min-height: 100px;
@@ -32,10 +32,18 @@
--advanced-camera-card-height: auto;
}
// Without hovering over the card, it is "flattened" to avoid z-index weaving
// from other cards. Tip: Best way to test this is with multiple Advanced Camera
// Cards, opening a submenu on the 1st (e.g. media players) and verifying the
// menu of the 2nd card is not visible through the opened submenu on the 1st.
:host(:not(:hover)) {
z-index: #{$z-index-card-flatten};
}
advanced-camera-card-loading {
position: absolute;
inset: 0;
z-index: 1;
z-index: #{$z-index-loading};
}
:host([dimmable]:not([interaction])) {
@@ -60,17 +68,11 @@ advanced-camera-card-loading {
div.main {
position: relative;
// Required to keep curved corners on the card.
overflow: auto;
width: 100%;
height: 100%;
margin: auto;
display: block;
// Necessary to get Safari to show border-radius correctly.
transform: translateZ(0);
// Hide scrollbar: Firefox
scrollbar-width: none;
// Hide scrollbar: IE and Edge
@@ -82,20 +84,6 @@ div.main::-webkit-scrollbar {
display: none;
}
// Need to apply the border radius on the container level, as the ha-card has
// overflow visible in order to allow a submenu to extend beyond the card
// boundary. Need to be able to selectively curve top or bottom depending on
// whether the outside menu is being shown. There's no way to select 'preceding
// element' in CSS, so this must be implemented in JS.
div.main.curve-top {
border-top-left-radius: var(--ha-card-border-radius, 4px);
border-top-right-radius: var(--ha-card-border-radius, 4px);
}
div.main.curve-bottom {
border-bottom-left-radius: var(--ha-card-border-radius, 4px);
border-bottom-right-radius: var(--ha-card-border-radius, 4px);
}
ha-card {
display: flex;
flex-direction: column;
@@ -181,30 +169,12 @@ web-dialog::part(dialog) {
background: transparent;
}
/*************************************
* "Outside" style for menu/status bar
*************************************/
// Style is set on the children themselves, to avoid the need for the parent
// outlay to prevent overflow (which needs to be enabled to menu items to be
// visible). See similar approach in overlay.scss for overlay.
.outerlay[data-position='top'] > *:first-child {
border-top-left-radius: var(--ha-card-border-radius, 4px);
border-top-right-radius: var(--ha-card-border-radius, 4px);
}
.outerlay[data-position='bottom'] > *:last-child {
border-bottom-left-radius: var(--ha-card-border-radius, 4px);
border-bottom-right-radius: var(--ha-card-border-radius, 4px);
}
/*******************
* Menu hover styles
*******************/
advanced-camera-card-menu {
z-index: 2;
z-index: #{$z-index-menu};
}
advanced-camera-card-menu[data-style*='hover'] {
@@ -223,7 +193,7 @@ ha-card:hover
*************************/
advanced-camera-card-status-bar {
z-index: 1;
z-index: #{$z-index-status-bar};
}
advanced-camera-card-status-bar[data-style*='hover'] {
+3 -1
View File
@@ -1,3 +1,5 @@
@import './z-index.scss';
:host {
// Drawer width sizes to contents.
width: unset;
@@ -28,7 +30,7 @@
// Drawer renders behind the menu/status-bar overlay (otherwise the drawer
// controls render on top of menu items)
z-index: 10;
z-index: #{$z-index-drawer};
}
:host([location='right']) #d {
-8
View File
@@ -9,7 +9,6 @@ div.control-surround {
position: absolute;
bottom: 50%;
transform: translateY(50%);
z-index: 0;
padding-top: $drawer-padding-extend;
padding-bottom: $drawer-padding-extend;
}
@@ -48,13 +47,6 @@ advanced-camera-card-icon.control {
transition: opacity 0.5s ease;
}
:host([open]) advanced-camera-card-icon.control,
advanced-camera-card-icon.control:hover {
// When the drawer is open or hovered make the button to close it more
// prominent.
opacity: 1;
}
:host([location='left']) advanced-camera-card-icon.control {
border-top-right-radius: $drawer-icon-size;
border-bottom-right-radius: $drawer-icon-size;
-4
View File
@@ -43,10 +43,6 @@
var(--advanced-camera-card-grid-column-size)
)
);
// When at item is selected, it may be enlarged -- it should render "in-front"
// of the other cameras that will then "move out of the way".
z-index: 2;
}
slot {
+12
View File
@@ -0,0 +1,12 @@
@use './button.scss';
ha-icon-button {
// Icons in the menu are expected to follow Advanced Camera Card theming
// unless they are active (in which case we want to take advantage of the
// whatever styling is appropriate, e.g. light icon partially lit).
--state-unavailable-color: var(--advanced-camera-card-button-color);
--state-inactive-color: var(--advanced-camera-card-button-color);
color: var(--advanced-camera-card-menu-button-inactive-color);
background-color: var(--advanced-camera-card-menu-button-background);
}
+1 -12
View File
@@ -1,4 +1,4 @@
@use './button.scss';
@use './menu-button.scss';
:host {
--advanced-camera-card-menu-button-size: 40px;
@@ -10,9 +10,6 @@
display: flex;
flex-direction: row;
justify-content: space-between;
// Allow submenus to overflow the menu "bar".
overflow: visible;
}
:host([data-style='outside']) {
@@ -142,13 +139,5 @@ div.opposing {
background: var(--advanced-camera-card-menu-background);
}
// Icons in the menu are expected to follow Advanced Camera Card theming unless they are
// active (in which case we want to take advantage of the whatever styling is
// appropriate, e.g. light icon partially lit).
:host ha-icon-button {
--state-unavailable-color: var(--advanced-camera-card-button-color);
--state-inactive-color: var(--advanced-camera-card-button-color);
}
// Further theme related styling is dynamically applied by `menu.ts`, see
// `_renderPerInstanceStyle`.
-28
View File
@@ -55,31 +55,3 @@ slot[name='bottom'],
slot[name='right'] {
justify-content: flex-end;
}
/*******************************
* Match rounded corners to card
*******************************/
// Style is set on the children themselves, to avoid the need for the parent
// outlay to prevent overflow (which needs to be enabled to menu items to be
// visible). See similar approach in card.scss for outerlay.
::slotted([slot='top']:first-child),
::slotted([slot='left']:first-child) {
border-top-left-radius: var(--ha-card-border-radius, 4px);
}
::slotted([slot='top']:first-child),
::slotted([slot='right']:first-child) {
border-top-right-radius: var(--ha-card-border-radius, 4px);
}
::slotted([slot='bottom']:last-child),
::slotted([slot='left']:last-child) {
border-bottom-left-radius: var(--ha-card-border-radius, 4px);
}
::slotted([slot='bottom']:last-child),
::slotted([slot='right']:last-child) {
border-bottom-right-radius: var(--ha-card-border-radius, 4px);
}
+18 -34
View File
@@ -55,7 +55,7 @@
.ptz-move,
.ptz-zoom,
.ptz-home {
.ptz-presets {
position: relative;
background-color: rgba(0, 0, 0, 0.3);
}
@@ -68,27 +68,28 @@
}
:host([data-orientation='horizontal']) .ptz .ptz-zoom,
:host([data-orientation='horizontal']) .ptz .ptz-home {
:host([data-orientation='horizontal']) .ptz .ptz-presets {
width: calc(var(--advanced-camera-card-ptz-icon-size) * 1.5);
}
:host([data-orientation='vertical']) .ptz .ptz-zoom,
:host([data-orientation='vertical']) .ptz .ptz-home {
:host([data-orientation='vertical']) .ptz .ptz-presets {
height: calc(var(--advanced-camera-card-ptz-icon-size) * 1.5);
}
.ptz-zoom,
.ptz-home {
.ptz-presets {
border-radius: var(--ha-card-border-radius, 4px);
}
/***********
* PTZ Icons
***********/
advanced-camera-card-icon {
.ptz-move advanced-camera-card-icon {
position: absolute;
--mdc-icon-size: var(--advanced-camera-card-ptz-icon-size);
}
advanced-camera-card-icon:not(.disabled) {
advanced-camera-card-icon:not(.disabled),
advanced-camera-card-submenu:not(.disabled) {
cursor: pointer;
}
.disabled {
@@ -115,34 +116,17 @@ advanced-camera-card-icon:not(.disabled) {
transform: translateY(-50%);
}
:host([data-orientation='vertical']) .zoom_in {
right: 5px;
top: 50%;
.ptz-presets,
.ptz-zoom {
display: flex;
align-items: center;
justify-content: space-evenly;
}
:host([data-orientation='vertical']) .zoom_out {
left: 5px;
top: 50%;
:host([data-orientation='vertical']) .ptz-presets,
:host([data-orientation='vertical']) .ptz-zoom {
flex-direction: row;
}
:host([data-orientation='horizontal']) .zoom_in {
left: 50%;
top: 5px;
}
:host([data-orientation='horizontal']) .zoom_out {
left: 50%;
bottom: 5px;
}
:host([data-orientation='vertical']) .zoom_in,
:host([data-orientation='vertical']) .zoom_out {
transform: translateY(-50%);
}
:host([data-orientation='horizontal']) .zoom_in,
:host([data-orientation='horizontal']) .zoom_out {
transform: translateX(-50%);
}
.home {
top: 50%;
left: 50%;
transform: translateX(-50%) translateY(-50%);
:host([data-orientation='horizontal']) .ptz-presets,
:host([data-orientation='horizontal']) .ptz-zoom {
flex-direction: column;
}
+2 -3
View File
@@ -1,11 +1,10 @@
@use './button.scss';
@import './z-index.scss';
:host {
pointer-events: auto;
}
mwc-list-item {
z-index: 20;
--mdc-menu-z-index: #{$z-index-submenu};
}
ha-icon-button {
+22
View File
@@ -0,0 +1,22 @@
/************
* Managing z-indicies and stacking contexts is very challenging on the card,
* due to the volume of different potentially overlapping surfaces. In
* particular, care must be taken to not generate new stacking contexts
* inadvertently which would make "z-index" weaving challenging (e.g. submenu
* shown for PTZ presets, needs to render "over" the media drawer open/close
* control).
*************/
// More-info dialog box has a z-index of 8, so everything meaningful needs to be
// below that.
$z-index-loading: 6;
$z-index-submenu: 5;
// Need menu to render above drawer (so the drawer button is below menu-submenus)
$z-index-menu: 4;
$z-index-drawer: 3;
$z-index-status-bar: 2;
// The base z-index when the card is not hovered over. See note in card.scss .
$z-index-card-flatten: 0;