feat: Add support for overriding card border radius (#2298)
- Closes #2287 [skip ci]
This commit is contained in:
@@ -1184,6 +1184,26 @@ automations:
|
||||
|
||||
See [Trigger Templates](./configuration/templates.md?id=triggers).
|
||||
|
||||
## Theme
|
||||
|
||||
### Custom border radius
|
||||
|
||||
This example shows how to customize the border radius for both the card and buttons to create a cohesive squared-off look:
|
||||
|
||||
```yaml
|
||||
type: custom:advanced-camera-card
|
||||
cameras:
|
||||
- camera_entity: camera.office
|
||||
view:
|
||||
theme:
|
||||
overrides:
|
||||
# Set a custom border radius for the card
|
||||
'--advanced-camera-card-border-radius': '12px'
|
||||
|
||||
# Match the button border radius to complement the card
|
||||
'--advanced-camera-card-button-border-radius': '12px'
|
||||
```
|
||||
|
||||
## Trigger actions
|
||||
|
||||
You can control the card itself with the `custom:advanced-camera-card-action` action.
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import { StyleInfo } from 'lit/directives/style-map';
|
||||
import { AdvancedCameraCardConfig, configDefaults } from '../config/schema/types';
|
||||
import { ThemeConfig, ThemeName } from '../config/schema/view';
|
||||
import { aspectRatioToStyle, setOrRemoveAttribute } from '../utils/basic';
|
||||
import {
|
||||
aspectRatioToStyle,
|
||||
setOrRemoveAttribute,
|
||||
setOrRemoveStyleProperty,
|
||||
} from '../utils/basic';
|
||||
import { View } from '../view/view';
|
||||
import { CardStyleAPI } from './types';
|
||||
|
||||
@@ -94,20 +98,27 @@ export class StyleManager {
|
||||
|
||||
protected _setPerformance(): void {
|
||||
const STYLE_DISABLE_MAP = {
|
||||
box_shadow: 'none',
|
||||
border_radius: '0px',
|
||||
box_shadow: {
|
||||
cssKey: '--advanced-camera-card-box-shadow-override',
|
||||
value: 'none',
|
||||
},
|
||||
border_radius: {
|
||||
cssKey: '--advanced-camera-card-border-radius-override',
|
||||
value: '0px',
|
||||
},
|
||||
};
|
||||
const element = this._api.getCardElementManager().getElement();
|
||||
const performance = this._api.getConfigManager().getCardWideConfig()?.performance;
|
||||
|
||||
const styles = performance?.style ?? {};
|
||||
for (const configKey of Object.keys(styles)) {
|
||||
const CSSKey = `--advanced-camera-card-css-${configKey.replaceAll('_', '-')}`;
|
||||
if (styles[configKey] === false) {
|
||||
element.style.setProperty(CSSKey, STYLE_DISABLE_MAP[configKey]);
|
||||
} else {
|
||||
element.style.removeProperty(CSSKey);
|
||||
}
|
||||
const mapping = STYLE_DISABLE_MAP[configKey];
|
||||
setOrRemoveStyleProperty(
|
||||
element,
|
||||
!styles[configKey],
|
||||
mapping.cssKey,
|
||||
mapping.value,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@
|
||||
// this ensures the same experience across all browsers.
|
||||
background-color: var(--advanced-camera-card-background);
|
||||
|
||||
border-radius: var(--ha-card-border-radius, 4px);
|
||||
border-radius: var(--advanced-camera-card-border-radius-final);
|
||||
overflow: auto;
|
||||
|
||||
height: var(--advanced-camera-card-height);
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
|
||||
:host([location='right'][open]) #d {
|
||||
transform: none;
|
||||
box-shadow: var(--advanced-camera-card-css-box-shadow, 0px 0px 25px 0px black);
|
||||
box-shadow: var(--advanced-camera-card-box-shadow-override, 0px 0px 25px 0px black);
|
||||
}
|
||||
|
||||
#ifs {
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
position: relative;
|
||||
|
||||
// See: https://github.com/dermotduffy/advanced-camera-card/issues/2107
|
||||
border-radius: var(--ha-card-border-radius, 4px);
|
||||
border-radius: var(--advanced-camera-card-border-radius-final);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,8 +25,5 @@ advanced-camera-card-thumbnail.selected {
|
||||
// this outer border, we need to add the size of the border to the thumbnail
|
||||
// image border radius.
|
||||
// Related: https://www.30secondsofcode.org/articles/s/css-nested-border-radius
|
||||
border-radius: calc(
|
||||
var(--advanced-camera-card-css-border-radius, var(--ha-card-border-radius, 4px)) +
|
||||
4px
|
||||
);
|
||||
border-radius: calc(var(--advanced-camera-card-border-radius-final) + 4px);
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
border-radius: 50%;
|
||||
height: var(--advanced-camera-card-next-prev-size);
|
||||
top: calc(50% - (var(--advanced-camera-card-next-prev-size) / 2));
|
||||
box-shadow: var(--advanced-camera-card-css-box-shadow, 0px 0px 20px 5px black);
|
||||
box-shadow: var(--advanced-camera-card-box-shadow-override, 0px 0px 20px 5px black);
|
||||
transition: all 0.2s ease-out;
|
||||
opacity: 0.8;
|
||||
aspect-ratio: 1 / 1;
|
||||
|
||||
+1
-1
@@ -80,7 +80,7 @@
|
||||
|
||||
.ptz-zoom,
|
||||
.ptz-presets {
|
||||
border-radius: var(--ha-card-border-radius, 4px);
|
||||
border-radius: var(--advanced-camera-card-border-radius-final);
|
||||
}
|
||||
|
||||
/***********
|
||||
|
||||
@@ -18,6 +18,16 @@
|
||||
--advanced-camera-card-text-color: var(--primary-text-color);
|
||||
--advanced-camera-card-divider-color: var(--divider-color);
|
||||
|
||||
// Border radius variable cascade:
|
||||
// 1. --advanced-camera-card-border-radius-override (set by JS for performance config)
|
||||
// 2. --advanced-camera-card-border-radius (user theme override)
|
||||
// 3. --ha-card-border-radius (Home Assistant theme)
|
||||
// 4. 4px (default)
|
||||
--advanced-camera-card-border-radius-final: var(
|
||||
--advanced-camera-card-border-radius-override,
|
||||
var(--advanced-camera-card-border-radius, var(--ha-card-border-radius, 4px))
|
||||
);
|
||||
|
||||
--advanced-camera-card-control-background: var(--app-header-background-color);
|
||||
--advanced-camera-card-control-background-opacity: 60%;
|
||||
|
||||
|
||||
@@ -24,10 +24,7 @@ img {
|
||||
vertical-align: top;
|
||||
margin: 0;
|
||||
|
||||
border-radius: var(
|
||||
--advanced-camera-card-css-border-radius,
|
||||
var(--ha-card-border-radius, 4px)
|
||||
);
|
||||
border-radius: var(--advanced-camera-card-border-radius-final);
|
||||
|
||||
// Not 'contain' as some thumbnails may vary in aspect-ratio slightly and
|
||||
// should be clipped to fill the thumbnail div whilst maintaining
|
||||
|
||||
@@ -11,10 +11,7 @@
|
||||
|
||||
border: 1px solid var(--secondary-color);
|
||||
background-color: var(--secondary-background-color);
|
||||
border-radius: var(
|
||||
--advanced-camera-card-css-border-radius,
|
||||
var(--ha-card-border-radius, 4px)
|
||||
);
|
||||
border-radius: var(--advanced-camera-card-border-radius-final);
|
||||
|
||||
box-sizing: border-box;
|
||||
|
||||
|
||||
@@ -16,10 +16,7 @@
|
||||
|
||||
:host([details]) {
|
||||
border: 1px solid var(--advanced-camera-card-thumbnail-border-color);
|
||||
border-radius: var(
|
||||
--advanced-camera-card-css-border-radius,
|
||||
var(--ha-card-border-radius, 4px)
|
||||
);
|
||||
border-radius: var(--advanced-camera-card-border-radius-final);
|
||||
padding: 2px;
|
||||
|
||||
// When details are enabled, use a background color so that the details have
|
||||
|
||||
@@ -69,7 +69,7 @@ div.timeline {
|
||||
border-color: var(--advanced-camera-card-active-color);
|
||||
background-color: var(--advanced-camera-card-active-color);
|
||||
box-shadow: var(
|
||||
--advanced-camera-card-css-box-shadow,
|
||||
--advanced-camera-card-box-shadow-override,
|
||||
0px 0px 5px 1px var(--advanced-camera-card-active-color)
|
||||
);
|
||||
}
|
||||
@@ -109,12 +109,12 @@ div.timeline {
|
||||
color: var(--advanced-camera-card-timeline-text-color);
|
||||
background-color: var(--advanced-camera-card-timeline-background);
|
||||
box-shadow: var(
|
||||
--advanced-camera-card-css-box-shadow,
|
||||
--advanced-camera-card-box-shadow-override,
|
||||
0px 0px 5px 1px var(--advanced-camera-card-timeline-item-color)
|
||||
);
|
||||
}
|
||||
.vis-item.vis-range {
|
||||
border-radius: var(--advanced-camera-card-css-border-radius, unset);
|
||||
border-radius: var(--advanced-camera-card-border-radius-final);
|
||||
}
|
||||
|
||||
.vis-time-axis .vis-grid.vis-minor {
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
position: relative;
|
||||
|
||||
// See: https://github.com/dermotduffy/advanced-camera-card/issues/2107
|
||||
border-radius: var(--ha-card-border-radius, 4px);
|
||||
border-radius: var(--advanced-camera-card-border-radius-final);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
|
||||
@@ -191,10 +191,10 @@ describe('StyleManager', () => {
|
||||
manager.updateFromConfig();
|
||||
|
||||
expect(
|
||||
element.style.getPropertyValue('--advanced-camera-card-css-box-shadow'),
|
||||
element.style.getPropertyValue('--advanced-camera-card-box-shadow-override'),
|
||||
).toBeFalsy();
|
||||
expect(
|
||||
element.style.getPropertyValue('--advanced-camera-card-css-border-radius'),
|
||||
element.style.getPropertyValue('--advanced-camera-card-border-radius-override'),
|
||||
).toBeFalsy();
|
||||
});
|
||||
|
||||
@@ -217,12 +217,56 @@ describe('StyleManager', () => {
|
||||
manager.updateFromConfig();
|
||||
|
||||
expect(
|
||||
element.style.getPropertyValue('--advanced-camera-card-css-box-shadow'),
|
||||
element.style.getPropertyValue('--advanced-camera-card-box-shadow-override'),
|
||||
).toEqual('none');
|
||||
expect(
|
||||
element.style.getPropertyValue('--advanced-camera-card-css-border-radius'),
|
||||
element.style.getPropertyValue('--advanced-camera-card-border-radius-override'),
|
||||
).toBeFalsy();
|
||||
});
|
||||
|
||||
it('box_shadow disabled sets override', () => {
|
||||
const api = createCardAPI();
|
||||
const element = document.createElement('div');
|
||||
vi.mocked(api.getCardElementManager().getElement).mockReturnValue(element);
|
||||
vi.mocked(api.getConfigManager().getCardWideConfig).mockReturnValue(
|
||||
createConfig({
|
||||
performance: {
|
||||
style: {
|
||||
box_shadow: false,
|
||||
},
|
||||
},
|
||||
}),
|
||||
);
|
||||
const manager = new StyleManager(api);
|
||||
|
||||
manager.updateFromConfig();
|
||||
|
||||
expect(
|
||||
element.style.getPropertyValue('--advanced-camera-card-box-shadow-override'),
|
||||
).toEqual('none');
|
||||
});
|
||||
|
||||
it('border_radius disabled sets override', () => {
|
||||
const api = createCardAPI();
|
||||
const element = document.createElement('div');
|
||||
vi.mocked(api.getCardElementManager().getElement).mockReturnValue(element);
|
||||
vi.mocked(api.getConfigManager().getCardWideConfig).mockReturnValue(
|
||||
createConfig({
|
||||
performance: {
|
||||
style: {
|
||||
border_radius: false,
|
||||
},
|
||||
},
|
||||
}),
|
||||
);
|
||||
const manager = new StyleManager(api);
|
||||
|
||||
manager.updateFromConfig();
|
||||
|
||||
expect(
|
||||
element.style.getPropertyValue('--advanced-camera-card-border-radius-override'),
|
||||
).toEqual('0px');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getAspectRatioStyle', () => {
|
||||
|
||||
Reference in New Issue
Block a user