feat: Add richer card theme support (#1809)
This commit is contained in:
1 parent
f7ecb91578
commit
278560b5ac
65 files changed
+1187
-352
No files matched your search
@@ -97,7 +97,6 @@ export class ConfigManager {
|
||||
this._api.getViewManager().reset();
|
||||
|
||||
this._api.getMessageManager().reset();
|
||||
this._api.getStyleManager().setPerformance();
|
||||
this._api.getStatusBarItemManager().removeAllDynamicStatusBarItems();
|
||||
|
||||
setKeyboardShortcutsFromConfig(this._api, this);
|
||||
@@ -134,7 +133,7 @@ export class ConfigManager {
|
||||
const previousConfig = this._overriddenConfig;
|
||||
this._overriddenConfig = overriddenConfig;
|
||||
|
||||
this._api.getStyleManager().setMinMaxHeight();
|
||||
this._api.getStyleManager().updateFromConfig();
|
||||
|
||||
if (
|
||||
previousConfig &&
|
||||
|
||||
@@ -53,8 +53,8 @@ export class HASSManager {
|
||||
});
|
||||
}
|
||||
|
||||
// Dark mode may depend on HASS.
|
||||
this._api.getStyleManager().setLightOrDarkMode();
|
||||
// Theme may depend on HASS.
|
||||
this._api.getStyleManager().applyTheme();
|
||||
|
||||
this._stateWatcher.setHASS(oldHass, hass);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import throttle from 'lodash-es/throttle';
|
||||
import { setOrRemoveAttribute } from '../utils/basic';
|
||||
import { Timer } from '../utils/timer';
|
||||
import { CardInteractionAPI } from './types';
|
||||
|
||||
export class InteractionManager {
|
||||
protected _timer = new Timer();
|
||||
protected _api: CardInteractionAPI;
|
||||
protected _interacted = false;
|
||||
|
||||
constructor(api: CardInteractionAPI) {
|
||||
this._api = api;
|
||||
@@ -17,29 +19,32 @@ export class InteractionManager {
|
||||
}, 1 * 1000);
|
||||
|
||||
public initialize(): void {
|
||||
this._api.getConditionsManager().setState({ interaction: false });
|
||||
this._setInteraction(false);
|
||||
}
|
||||
|
||||
public hasInteraction(): boolean {
|
||||
return this._timer.isRunning();
|
||||
return this._interacted;
|
||||
}
|
||||
|
||||
protected _setInteraction(val: boolean): void {
|
||||
this._interacted = val;
|
||||
setOrRemoveAttribute(
|
||||
this._api.getCardElementManager().getElement(),
|
||||
val,
|
||||
'interaction',
|
||||
);
|
||||
this._api.getConditionsManager().setState({ interaction: val });
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the user interaction ('screensaver') timer to reset the view to
|
||||
* default `view.interaction_seconds` after user interaction.
|
||||
*/
|
||||
protected _reportInteraction(): void {
|
||||
this._timer.stop();
|
||||
this._setInteraction(true);
|
||||
|
||||
const timeoutSeconds = this._api.getConfigManager().getConfig()
|
||||
?.view.interaction_seconds;
|
||||
|
||||
if (timeoutSeconds) {
|
||||
this._api.getConditionsManager().setState({ interaction: true });
|
||||
|
||||
this._timer.start(timeoutSeconds, () => {
|
||||
this._api.getConditionsManager().setState({ interaction: false });
|
||||
this._api.getStyleManager().setLightOrDarkMode();
|
||||
this._setInteraction(false);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import { StyleInfo } from 'lit/directives/style-map';
|
||||
import { FrigateCardConfig } from '../config/types';
|
||||
import {
|
||||
FrigateCardConfig,
|
||||
frigateCardConfigDefaults,
|
||||
ThemeConfig,
|
||||
ThemeName,
|
||||
} from '../config/types';
|
||||
import { aspectRatioToStyle, setOrRemoveAttribute } from '../utils/basic';
|
||||
import { View } from '../view/view';
|
||||
import { CardStyleAPI } from './types';
|
||||
@@ -11,21 +16,6 @@ export class StyleManager {
|
||||
this._api = api;
|
||||
}
|
||||
|
||||
public setLightOrDarkMode = (): void => {
|
||||
const config = this._api.getConfigManager().getConfig();
|
||||
const isDarkMode =
|
||||
config?.view.dark_mode === 'on' ||
|
||||
(config?.view.dark_mode === 'auto' &&
|
||||
(!this._api.getInteractionManager().hasInteraction() ||
|
||||
!!this._api.getHASSManager().getHASS()?.themes.darkMode));
|
||||
|
||||
setOrRemoveAttribute(
|
||||
this._api.getCardElementManager().getElement(),
|
||||
isDarkMode,
|
||||
'dark',
|
||||
);
|
||||
};
|
||||
|
||||
public setExpandedMode(): void {
|
||||
const card = this._api.getCardElementManager().getElement();
|
||||
const view = this._api.getViewManager().getView();
|
||||
@@ -58,7 +48,47 @@ export class StyleManager {
|
||||
);
|
||||
}
|
||||
|
||||
public setMinMaxHeight(): void {
|
||||
public updateFromConfig(): void {
|
||||
this.applyTheme();
|
||||
this._setMinMaxHeight();
|
||||
this._setPerformance();
|
||||
this._setDimmable();
|
||||
}
|
||||
|
||||
public applyTheme() {
|
||||
const themeConfig = this._api.getConfigManager().getConfig()?.view.theme;
|
||||
if (!themeConfig) {
|
||||
return;
|
||||
}
|
||||
|
||||
const element = this._api.getCardElementManager().getElement();
|
||||
const themes = this._getThemeNames(themeConfig);
|
||||
|
||||
setOrRemoveAttribute(element, !!themes, 'themes', themes?.join(' '));
|
||||
|
||||
if (themeConfig.overrides) {
|
||||
for (const [key, value] of Object.entries(themeConfig.overrides)) {
|
||||
element.style.setProperty(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected _getThemeNames(themeConfig: ThemeConfig): ThemeName[] | null {
|
||||
return themeConfig.themes.length
|
||||
? themeConfig.themes
|
||||
: frigateCardConfigDefaults.view.theme.themes;
|
||||
}
|
||||
|
||||
protected _setDimmable(): void {
|
||||
const config = this._api.getConfigManager().getConfig();
|
||||
setOrRemoveAttribute(
|
||||
this._api.getCardElementManager().getElement(),
|
||||
!!config?.view.dim,
|
||||
'dimmable',
|
||||
);
|
||||
}
|
||||
|
||||
protected _setMinMaxHeight(): void {
|
||||
const config = this._api.getConfigManager().getConfig();
|
||||
if (config) {
|
||||
const card = this._api.getCardElementManager().getElement();
|
||||
@@ -66,7 +96,7 @@ export class StyleManager {
|
||||
}
|
||||
}
|
||||
|
||||
public setPerformance(): void {
|
||||
protected _setPerformance(): void {
|
||||
const STYLE_DISABLE_MAP = {
|
||||
box_shadow: 'none',
|
||||
border_radius: '0px',
|
||||
|
||||
@@ -180,6 +180,7 @@ export interface CardInitializerAPI {
|
||||
}
|
||||
|
||||
export interface CardInteractionAPI {
|
||||
getCardElementManager(): CardElementManager;
|
||||
getConditionsManager(): ConditionsManager;
|
||||
getConfigManager(): ConfigManager;
|
||||
getStyleManager(): StyleManager;
|
||||
|
||||
+15
-6
@@ -218,12 +218,14 @@ class FrigateCard extends LitElement {
|
||||
const getContents = (kind: 'overlay' | 'outerlay'): TemplateResult => {
|
||||
const shouldRenderMenu =
|
||||
menuStyle !== 'none' &&
|
||||
((menuStyle === 'outside' && kind === 'outerlay') ||
|
||||
((menuStyle === 'outside' && kind === 'outerlay' && menuPosition === position) ||
|
||||
(menuStyle !== 'outside' && kind === 'overlay'));
|
||||
|
||||
const shouldRenderStatusBar =
|
||||
statusBarStyle !== 'none' &&
|
||||
((statusBarStyle === 'outside' && kind === 'outerlay') ||
|
||||
((statusBarStyle === 'outside' &&
|
||||
kind === 'outerlay' &&
|
||||
statusBarPosition === position) ||
|
||||
(statusBarStyle !== 'outside' && kind === 'overlay'));
|
||||
|
||||
// Complex logic to try to always put the menu in the right-looking place.
|
||||
@@ -233,8 +235,7 @@ class FrigateCard extends LitElement {
|
||||
(menuPosition === 'bottom' &&
|
||||
menuStyle === 'hidden' &&
|
||||
statusBarStyle !== 'popup') ||
|
||||
(menuPosition === 'top' &&
|
||||
(menuStyle !== 'hidden' || statusBarStyle === 'popup'));
|
||||
(menuPosition === 'top' && statusBarStyle === 'popup');
|
||||
|
||||
return html`
|
||||
${shouldRenderMenu && renderMenuFirst ? this._renderMenu(menuPosition) : ''}
|
||||
@@ -329,12 +330,20 @@ class FrigateCard extends LitElement {
|
||||
return;
|
||||
}
|
||||
|
||||
const outerlayUsed =
|
||||
this._config?.menu.style === 'outside' ||
|
||||
this._config?.status_bar.style === 'outside';
|
||||
|
||||
const mainClasses = {
|
||||
main: true,
|
||||
'curve-top':
|
||||
this._config?.menu.style !== 'outside' || this._config?.menu.position !== 'top',
|
||||
!outerlayUsed ||
|
||||
(this._config?.menu.position !== 'top' &&
|
||||
this._config?.status_bar.position !== 'top'),
|
||||
'curve-bottom':
|
||||
this._config?.menu.style !== 'outside' || this._config?.menu.position === 'top',
|
||||
!outerlayUsed ||
|
||||
(this._config?.menu.position !== 'bottom' &&
|
||||
this._config?.status_bar.position !== 'bottom'),
|
||||
};
|
||||
|
||||
const actions = this._controller.getActionsManager().getMergedActions();
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { HomeAssistant } from '@dermotduffy/custom-card-helpers';
|
||||
import { HassEntity } from 'home-assistant-js-websocket';
|
||||
import frigateSVG from '../camera-manager/frigate/assets/frigate.svg';
|
||||
import motioneyeSVG from '../camera-manager/motioneye/assets/motioneye.svg';
|
||||
import reolinkSVG from '../camera-manager/reolink/assets/reolink.svg';
|
||||
import irisSVG from '../images/iris.svg';
|
||||
import { Icon } from '../types';
|
||||
import { HassEntity } from 'home-assistant-js-websocket';
|
||||
|
||||
export class IconController {
|
||||
public getCustomIcon(icon?: Icon): string | null {
|
||||
@@ -14,6 +15,8 @@ export class IconController {
|
||||
return motioneyeSVG;
|
||||
case 'reolink':
|
||||
return reolinkSVG;
|
||||
case 'iris':
|
||||
return irisSVG;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -647,11 +647,11 @@ export class MenuButtonController {
|
||||
if (critical) {
|
||||
return {
|
||||
animation: 'pulse 3s infinite',
|
||||
color: 'var(--error-color, white)',
|
||||
color: 'var(--frigate-card-menu-button-critical-color)',
|
||||
};
|
||||
}
|
||||
return {
|
||||
color: 'var(--primary-color, white)',
|
||||
color: 'var(--frigate-card-menu-button-active-color)',
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -67,6 +67,10 @@ export class StatusBarController {
|
||||
this._host.requestUpdate();
|
||||
}
|
||||
|
||||
public getConfig(): StatusBarConfig | null {
|
||||
return this._config;
|
||||
}
|
||||
|
||||
public shouldRender(): boolean {
|
||||
return this._items.some((item) => item.enabled !== false && item.sufficient);
|
||||
}
|
||||
|
||||
+40
-13
@@ -1,7 +1,13 @@
|
||||
import { HomeAssistant } from '@dermotduffy/custom-card-helpers';
|
||||
import { CSSResultGroup, html, LitElement, TemplateResult, unsafeCSS } from 'lit';
|
||||
import {
|
||||
CSSResultGroup,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
TemplateResult,
|
||||
unsafeCSS,
|
||||
} from 'lit';
|
||||
import { customElement, property } from 'lit/decorators.js';
|
||||
import { styleMap } from 'lit/directives/style-map.js';
|
||||
import { IconController } from '../components-lib/icon-controller';
|
||||
import iconStyle from '../scss/icon.scss';
|
||||
import { Icon } from '../types';
|
||||
@@ -14,17 +20,35 @@ export class FrigateCardIcon extends LitElement {
|
||||
@property({ attribute: false })
|
||||
public icon?: Icon;
|
||||
|
||||
// Note: This attribute will allow non-active entity state styles (e.g. 'off',
|
||||
// 'unavailable') to be overriden from outside the icon itself. This is useful
|
||||
// in the menu / submenus where we want icons to follow menu theming, unless
|
||||
// they are 'active'. This attribute is not used in code, but matched in
|
||||
// icon.scss .
|
||||
@property({ attribute: 'allow-override-non-active-styles', type: Boolean })
|
||||
public allowOverrideNonActiveStyles = false;
|
||||
|
||||
private _controller = new IconController();
|
||||
private _svg: HTMLElement | null = null;
|
||||
|
||||
protected willUpdate(changedProps: PropertyValues): void {
|
||||
if (changedProps.has('icon')) {
|
||||
const customIcon = this._controller.getCustomIcon(this.icon);
|
||||
if (customIcon) {
|
||||
const svgElement = document.createElement('svg');
|
||||
svgElement.innerHTML = customIcon;
|
||||
this._svg = svgElement;
|
||||
} else {
|
||||
this._svg = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
const customIconURL = this._controller.getCustomIcon(this.icon);
|
||||
const style = styleMap(this.icon?.style || {});
|
||||
|
||||
if (customIconURL) {
|
||||
return html`<img style="${style}" src="${customIconURL}" />`;
|
||||
}
|
||||
if (this.icon?.icon) {
|
||||
return html`<ha-icon style="${style}" icon="${this.icon.icon}"></ha-icon>`;
|
||||
if (this._svg) {
|
||||
// Use SVG objects (rather than <img>) to ensure styling applies
|
||||
// correctly.
|
||||
return html`${this._svg}`;
|
||||
}
|
||||
if (this.hass && this.icon?.entity) {
|
||||
const stateObj = this._controller.createStateObjectForStateBadge(
|
||||
@@ -35,16 +59,19 @@ export class FrigateCardIcon extends LitElement {
|
||||
// As a special case, need to pass in a color in order for the state
|
||||
// color to be overridden.
|
||||
return html`<state-badge
|
||||
style="${style}"
|
||||
.color="${this.icon.style?.color ?? undefined}"
|
||||
.color="${this.style.color ?? undefined}"
|
||||
.stateColor=${this.icon.stateColor ?? true}
|
||||
.hass=${this.hass}
|
||||
.stateObj=${stateObj}
|
||||
.overrideIcon=${this.icon.icon}
|
||||
></state-badge>`;
|
||||
}
|
||||
}
|
||||
if (this.icon?.icon) {
|
||||
return html`<ha-icon icon="${this.icon.icon}"></ha-icon>`;
|
||||
}
|
||||
if (this.icon?.fallback) {
|
||||
return html`<ha-icon style="${style}" icon="${this.icon.fallback}"></ha-icon>`;
|
||||
return html`<ha-icon icon="${this.icon.fallback}"></ha-icon>`;
|
||||
}
|
||||
return html``;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { CSSResultGroup, LitElement, TemplateResult, html, unsafeCSS } from 'lit';
|
||||
import { customElement } from 'lit/decorators.js';
|
||||
import irisLogo from '../images/camera-iris-transparent.svg';
|
||||
import loadingStyle from '../scss/loading.scss';
|
||||
import './icon';
|
||||
|
||||
@customElement('frigate-card-loading')
|
||||
export class FrigateCardLoading extends LitElement {
|
||||
protected render(): TemplateResult {
|
||||
return html` <img src="${irisLogo}" /> `;
|
||||
return html`<frigate-card-icon .icon=${{ icon: 'iris' }}></frigate-card-icon>`;
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
|
||||
+55
-4
@@ -66,8 +66,6 @@ export class FrigateCardMenu extends LitElement {
|
||||
: button.title;
|
||||
|
||||
return html` <ha-icon-button
|
||||
class="button"
|
||||
style="${styleMap(button.style || {})}"
|
||||
.actionHandler=${actionHandler({
|
||||
hasHold: frigateCardHasAction(button.hold_action),
|
||||
hasDoubleClick: frigateCardHasAction(button.double_tap_action),
|
||||
@@ -76,18 +74,70 @@ export class FrigateCardMenu extends LitElement {
|
||||
@action=${(ev) => this._controller.actionHandler(ev, button)}
|
||||
>
|
||||
<frigate-card-icon
|
||||
?allow-override-non-active-styles=${true}
|
||||
style="${styleMap(button.style || {})}"
|
||||
.hass=${this.hass}
|
||||
.icon=${{
|
||||
icon: button.icon,
|
||||
entity: button.entity,
|
||||
stateColor: button.state_color,
|
||||
style: button.style,
|
||||
fallback: 'mdi:gesture-tap-button',
|
||||
}}
|
||||
></frigate-card-icon>
|
||||
</ha-icon-button>`;
|
||||
}
|
||||
|
||||
/** Theme-related styling is dynamically injected into the menu depending on
|
||||
* the configured position, style and alignment to allow precise theming.
|
||||
* The alternative is a massive (post-sass processing) CSS file would need to
|
||||
* be shipped to account for every possible combination.
|
||||
*
|
||||
* Each rule uses 'var' values that have nested fallbacks of decreasing
|
||||
* specificity, so the most specific theme variable will match, followed by
|
||||
* the next most specific, etc.
|
||||
*/
|
||||
protected _renderPerInstanceStyle(): TemplateResult | void {
|
||||
const config = this._controller.getMenuConfig();
|
||||
if (!config) {
|
||||
return;
|
||||
}
|
||||
|
||||
const position = config.position;
|
||||
const style = config.style;
|
||||
const alignment = config.alignment;
|
||||
|
||||
const generateValue = (suffix: string): string => {
|
||||
return `
|
||||
var(--frigate-card-menu-override-${suffix},
|
||||
var(--frigate-card-menu-position-${position}-alignment-${alignment}-style-${style}-${suffix},
|
||||
var(--frigate-card-menu-position-${position}-alignment-${alignment}-${suffix},
|
||||
var(--frigate-card-menu-position-${position}-${suffix},
|
||||
var(--frigate-card-menu-style-${style}-${suffix},
|
||||
var(--frigate-card-menu-alignment-${alignment}-${suffix},
|
||||
var(--frigate-card-menu-${suffix})))))))`;
|
||||
};
|
||||
|
||||
// By definition `rule` will match the current configuration, the choice is
|
||||
// actually which of the var(...) variables will be used after the match.
|
||||
const expandedRule = style === 'hidden' ? '[expanded]' : '';
|
||||
const rule =
|
||||
`[data-position='${position}']` +
|
||||
`[data-style='${style}']` +
|
||||
`[data-alignment='${alignment}']` +
|
||||
expandedRule;
|
||||
|
||||
return html`<style>
|
||||
:host(${rule}) {
|
||||
background: ${generateValue('background')};
|
||||
|
||||
ha-icon-button {
|
||||
color: ${generateValue('button-inactive-color')};
|
||||
background: ${generateValue('button-background')};
|
||||
}
|
||||
}
|
||||
</style>`;
|
||||
}
|
||||
|
||||
protected render(): TemplateResult | void {
|
||||
const config = this._controller.getMenuConfig();
|
||||
const style = config?.style;
|
||||
@@ -97,7 +147,8 @@ export class FrigateCardMenu extends LitElement {
|
||||
const matchingButtons = this._controller.getButtons('matching');
|
||||
const opposingButtons = this._controller.getButtons('opposing');
|
||||
|
||||
return html` <div
|
||||
return html` ${this._renderPerInstanceStyle()}
|
||||
<div
|
||||
class="matching"
|
||||
style="${styleMap({ flex: String(matchingButtons.length) })}"
|
||||
>
|
||||
|
||||
@@ -63,7 +63,6 @@ export class FrigateCardNextPreviousControl extends LitElement {
|
||||
right: this.side === 'right',
|
||||
thumbnails: !renderIcon,
|
||||
icons: renderIcon,
|
||||
button: renderIcon,
|
||||
};
|
||||
|
||||
if (renderIcon) {
|
||||
|
||||
@@ -36,12 +36,48 @@ export class FrigateCardStatusBar extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
/** Theme-related styling is dynamically injected into the status bar depending on
|
||||
* the configured position and style to allow precise theming.
|
||||
*
|
||||
* Each rule uses 'var' values that have nested fallbacks of decreasing
|
||||
* specificity, so the most specific theme variable will match, followed by
|
||||
* the next most specific, etc.
|
||||
*/
|
||||
protected _renderPerInstanceStyle(): TemplateResult | void {
|
||||
const config = this._controller.getConfig();
|
||||
if (!config) {
|
||||
return;
|
||||
}
|
||||
|
||||
const position = config.position;
|
||||
const style = config.style;
|
||||
|
||||
const generateValue = (suffix: string): string => {
|
||||
return `
|
||||
var(--frigate-card-status-bar-override-${suffix},
|
||||
var(--frigate-card-status-bar-position-${position}-style-${style}-${suffix},
|
||||
var(--frigate-card-status-bar-position-${position}-${suffix},
|
||||
var(--frigate-card-status-bar-style-${style}-${suffix},
|
||||
var(--frigate-card-status-bar-${suffix})))))`;
|
||||
};
|
||||
|
||||
const rule = `[data-position='${position}']` + `[data-style='${style}']`;
|
||||
|
||||
return html`<style>
|
||||
:host(${rule}) {
|
||||
color: ${generateValue('color')};
|
||||
background: ${generateValue('background')};
|
||||
}
|
||||
</style>`;
|
||||
}
|
||||
|
||||
protected render(): TemplateResult | void {
|
||||
if (!this._controller.shouldRender()) {
|
||||
return;
|
||||
}
|
||||
|
||||
return html`
|
||||
${this._renderPerInstanceStyle()}
|
||||
<div class="status">
|
||||
${this._controller.getRenderItems().map((item): TemplateResult | void => {
|
||||
if (item.enabled === false) {
|
||||
|
||||
@@ -43,9 +43,9 @@ export class FrigateCardSubmenu extends LitElement {
|
||||
}
|
||||
|
||||
const title = item.title ?? getEntityTitle(this.hass, item.entity);
|
||||
const style = styleMap(item.style || {});
|
||||
return html`
|
||||
<mwc-list-item
|
||||
style="${styleMap(item.style || {})}"
|
||||
graphic=${ifDefined(item.icon || item.entity ? 'icon' : undefined)}
|
||||
?twoline=${!!item.subtitle}
|
||||
?selected=${item.selected}
|
||||
@@ -61,17 +61,18 @@ export class FrigateCardSubmenu extends LitElement {
|
||||
hasDoubleClick: frigateCardHasAction(item.double_tap_action),
|
||||
})}
|
||||
>
|
||||
<span>${title ?? ''}</span>
|
||||
${item.subtitle ? html`<span slot="secondary">${item.subtitle}</span>` : ''}
|
||||
<span style="${style}">${title ?? ''}</span>
|
||||
${item.subtitle
|
||||
? html`<span slot="secondary" style="${style}">${item.subtitle}</span>`
|
||||
: ''}
|
||||
<frigate-card-icon
|
||||
slot="graphic"
|
||||
.hass=${this.hass}
|
||||
.icon=${{
|
||||
icon: item.icon,
|
||||
entity: item.entity,
|
||||
style: item.style,
|
||||
}}
|
||||
style="${styleMap(item.style || {})}"
|
||||
style="${style}"
|
||||
></frigate-card-icon>
|
||||
</mwc-list-item>
|
||||
`;
|
||||
@@ -96,7 +97,6 @@ export class FrigateCardSubmenu extends LitElement {
|
||||
>
|
||||
<ha-icon-button
|
||||
style="${style}"
|
||||
class="button"
|
||||
slot="trigger"
|
||||
.label=${this.submenu.title || ''}
|
||||
.actionHandler=${actionHandler({
|
||||
@@ -110,6 +110,7 @@ export class FrigateCardSubmenu extends LitElement {
|
||||
})}
|
||||
>
|
||||
<frigate-card-icon
|
||||
?allow-override-non-active-styles=${true}
|
||||
style="${style}"
|
||||
.hass=${this.hass}
|
||||
.icon=${typeof this.submenu.icon === 'string'
|
||||
@@ -217,7 +218,6 @@ export class FrigateCardSubmenuSelect extends LitElement {
|
||||
icon: this.submenuSelect.icon,
|
||||
entity: entityID,
|
||||
fallback: 'mdi:format-list-bulleted',
|
||||
style: this.submenuSelect.style,
|
||||
},
|
||||
|
||||
type: 'custom:frigate-card-menu-submenu',
|
||||
|
||||
@@ -26,6 +26,7 @@ import {
|
||||
CONF_VIEW_DEFAULT_RESET_ENTITIES,
|
||||
CONF_VIEW_DEFAULT_RESET_EVERY_SECONDS,
|
||||
CONF_VIEW_DEFAULT_RESET_INTERACTION_MODE,
|
||||
CONF_VIEW_DIM,
|
||||
CONF_VIEW_INTERACTION_SECONDS,
|
||||
CONF_VIEW_TRIGGERS,
|
||||
CONF_VIEW_TRIGGERS_ACTIONS_TRIGGER,
|
||||
@@ -881,4 +882,9 @@ const UPGRADES = [
|
||||
},
|
||||
upgradeMoveToWithOverrides('dimensions.max_height', CONF_DIMENSIONS_HEIGHT),
|
||||
deleteWithOverrides('dimensions.min_height'),
|
||||
|
||||
// v6.1.2+
|
||||
upgradeMoveToWithOverrides('view.dark_mode', CONF_VIEW_DIM, {
|
||||
transform: (val) => val === 'on',
|
||||
}),
|
||||
];
|
||||
+18
-3
@@ -1515,7 +1515,10 @@ const viewConfigDefault = {
|
||||
interaction_mode: 'inactive' as const,
|
||||
},
|
||||
default_cycle_camera: false,
|
||||
dark_mode: 'off' as const,
|
||||
dim: false,
|
||||
theme: {
|
||||
themes: ['traditional' as const],
|
||||
},
|
||||
triggers: {
|
||||
show_trigger_status: false,
|
||||
filter_selected_camera: true,
|
||||
@@ -1553,6 +1556,15 @@ export const triggersSchema = z.object({
|
||||
});
|
||||
export type TriggersOptions = z.infer<typeof triggersSchema>;
|
||||
|
||||
const themeName = z.enum(['ha', 'dark', 'light', 'traditional']);
|
||||
export type ThemeName = z.infer<typeof themeName>;
|
||||
|
||||
const themeConfigSchema = z.object({
|
||||
themes: themeName.array().default(viewConfigDefault.theme.themes),
|
||||
overrides: z.record(z.string()).optional(),
|
||||
});
|
||||
export type ThemeConfig = z.infer<typeof themeConfigSchema>;
|
||||
|
||||
const viewConfigSchema = z
|
||||
.object({
|
||||
default: z
|
||||
@@ -1578,7 +1590,10 @@ const viewConfigSchema = z
|
||||
.default(viewConfigDefault.default_reset),
|
||||
|
||||
render_entities: z.string().array().optional(),
|
||||
dark_mode: z.enum(['on', 'off', 'auto']).optional(),
|
||||
|
||||
theme: themeConfigSchema.default(viewConfigDefault.theme),
|
||||
|
||||
dim: z.boolean().default(viewConfigDefault.dim),
|
||||
triggers: triggersSchema.default(viewConfigDefault.triggers),
|
||||
keyboard_shortcuts: keyboardShortcutsSchema.default(
|
||||
viewConfigDefault.keyboard_shortcuts,
|
||||
@@ -1721,7 +1736,7 @@ const statusBarItemDefault = {
|
||||
};
|
||||
|
||||
const statusBarConfigDefault = {
|
||||
height: 30,
|
||||
height: 40,
|
||||
items: {
|
||||
engine: statusBarItemDefault,
|
||||
resolution: statusBarItemDefault,
|
||||
|
||||
+2
-1
@@ -117,8 +117,8 @@ export const CONF_ELEMENTS = 'elements' as const;
|
||||
|
||||
const CONF_VIEW = 'view' as const;
|
||||
export const CONF_VIEW_CAMERA_SELECT = `${CONF_VIEW}.camera_select` as const;
|
||||
export const CONF_VIEW_DARK_MODE = `${CONF_VIEW}.dark_mode` as const;
|
||||
export const CONF_VIEW_DEFAULT = `${CONF_VIEW}.default` as const;
|
||||
export const CONF_VIEW_DIM = `${CONF_VIEW}.dim` as const;
|
||||
export const CONF_VIEW_INTERACTION_SECONDS = `${CONF_VIEW}.interaction_seconds` as const;
|
||||
export const CONF_VIEW_KEYBOARD_SHORTCUTS = `${CONF_VIEW}.keyboard_shortcuts` as const;
|
||||
export const CONF_VIEW_KEYBOARD_SHORTCUTS_ENABLED =
|
||||
@@ -148,6 +148,7 @@ export const CONF_VIEW_DEFAULT_RESET_ENTITIES =
|
||||
`${CONF_VIEW_DEFAULT_RESET}.entities` as const;
|
||||
export const CONF_VIEW_DEFAULT_RESET_AFTER_INTERACTION =
|
||||
`${CONF_VIEW_DEFAULT_RESET}.after_interaction` as const;
|
||||
export const CONF_VIEW_THEME_THEMES = `${CONF_VIEW}.theme.themes` as const;
|
||||
export const CONF_VIEW_TRIGGERS = `${CONF_VIEW}.triggers` as const;
|
||||
export const CONF_VIEW_TRIGGERS_SHOW_TRIGGER_STATUS =
|
||||
`${CONF_VIEW_TRIGGERS}.show_trigger_status` as const;
|
||||
|
||||
+15
-12
@@ -200,7 +200,6 @@ import {
|
||||
CONF_TIMELINE_STYLE,
|
||||
CONF_TIMELINE_WINDOW_SECONDS,
|
||||
CONF_VIEW_CAMERA_SELECT,
|
||||
CONF_VIEW_DARK_MODE,
|
||||
CONF_VIEW_DEFAULT,
|
||||
CONF_VIEW_DEFAULT_CYCLE_CAMERA,
|
||||
CONF_VIEW_DEFAULT_RESET,
|
||||
@@ -208,6 +207,7 @@ import {
|
||||
CONF_VIEW_DEFAULT_RESET_ENTITIES,
|
||||
CONF_VIEW_DEFAULT_RESET_EVERY_SECONDS,
|
||||
CONF_VIEW_DEFAULT_RESET_INTERACTION_MODE,
|
||||
CONF_VIEW_DIM,
|
||||
CONF_VIEW_INTERACTION_SECONDS,
|
||||
CONF_VIEW_KEYBOARD_SHORTCUTS,
|
||||
CONF_VIEW_KEYBOARD_SHORTCUTS_ENABLED,
|
||||
@@ -218,6 +218,7 @@ import {
|
||||
CONF_VIEW_KEYBOARD_SHORTCUTS_PTZ_UP,
|
||||
CONF_VIEW_KEYBOARD_SHORTCUTS_PTZ_ZOOM_IN,
|
||||
CONF_VIEW_KEYBOARD_SHORTCUTS_PTZ_ZOOM_OUT,
|
||||
CONF_VIEW_THEME_THEMES,
|
||||
CONF_VIEW_TRIGGERS,
|
||||
CONF_VIEW_TRIGGERS_ACTIONS,
|
||||
CONF_VIEW_TRIGGERS_ACTIONS_INTERACTION_MODE,
|
||||
@@ -552,13 +553,6 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
||||
{ value: 'stack', label: localize('config.common.timeline.styles.stack') },
|
||||
];
|
||||
|
||||
protected _darkModes: EditorSelectOption[] = [
|
||||
{ value: '', label: '' },
|
||||
{ value: 'on', label: localize('config.view.dark_modes.on') },
|
||||
{ value: 'off', label: localize('config.view.dark_modes.off') },
|
||||
{ value: 'auto', label: localize('config.view.dark_modes.auto') },
|
||||
];
|
||||
|
||||
protected _mediaActionNegativeConditions: EditorSelectOption[] = [
|
||||
{ value: '', label: '' },
|
||||
{
|
||||
@@ -912,6 +906,14 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
||||
{ value: 'bottom', label: localize('config.status_bar.positions.bottom') },
|
||||
];
|
||||
|
||||
protected _themes: EditorSelectOption[] = [
|
||||
{ value: '', label: '' },
|
||||
{ value: 'ha', label: localize('config.view.theme.themes.ha') },
|
||||
{ value: 'dark', label: localize('config.view.theme.themes.dark') },
|
||||
{ value: 'light', label: localize('config.view.theme.themes.light') },
|
||||
{ value: 'traditional', label: localize('config.view.theme.themes.traditional') },
|
||||
];
|
||||
|
||||
public setConfig(config: RawFrigateCardConfig): void {
|
||||
// Note: This does not use Zod to parse the full configuration, so it may be
|
||||
// partially or completely invalid. It's more useful to have a partially
|
||||
@@ -1928,7 +1930,6 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
||||
? html` <div class="values">
|
||||
<div class="controls">
|
||||
<ha-icon-button
|
||||
class="button"
|
||||
.label=${localize('editor.move_up')}
|
||||
.disabled=${addNewCamera ||
|
||||
!this._config ||
|
||||
@@ -1950,7 +1951,6 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
||||
></frigate-card-icon>
|
||||
</ha-icon-button>
|
||||
<ha-icon-button
|
||||
class="button"
|
||||
.label=${localize('editor.move_down')}
|
||||
.disabled=${addNewCamera ||
|
||||
!this._config ||
|
||||
@@ -1975,7 +1975,6 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
||||
></frigate-card-icon>
|
||||
</ha-icon-button>
|
||||
<ha-icon-button
|
||||
class="button"
|
||||
.label=${localize('editor.delete')}
|
||||
.disabled=${addNewCamera}
|
||||
@click=${() => {
|
||||
@@ -2525,7 +2524,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
||||
CONF_VIEW_CAMERA_SELECT,
|
||||
this._cameraSelectViewModes,
|
||||
)}
|
||||
${this._renderOptionSelector(CONF_VIEW_DARK_MODE, this._darkModes)}
|
||||
${this._renderSwitch(CONF_VIEW_DIM, this._defaults.view.dim)}
|
||||
${this._renderNumberInput(CONF_VIEW_INTERACTION_SECONDS)}
|
||||
${this._renderSwitch(
|
||||
CONF_VIEW_DEFAULT_CYCLE_CAMERA,
|
||||
@@ -2533,6 +2532,10 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
||||
)}
|
||||
${this._renderViewDefaultResetMenu()} ${this._renderViewTriggersMenu()}
|
||||
${this._renderViewKeyboardShortcutMenu()}
|
||||
${this._renderOptionSelector(CONF_VIEW_THEME_THEMES, this._themes, {
|
||||
label: localize('config.view.theme.themes.editor_label'),
|
||||
multiple: true,
|
||||
})}
|
||||
</div>
|
||||
`
|
||||
: ''}
|
||||
|
||||
@@ -22,8 +22,13 @@ $ convert -extent 2048x1152 -flop -gravity center 47543120431_b285c45ac8_k.jpg f
|
||||
$ mogrify -strip -interlace Plane -quality 85% -scale 492x277 frigate-bird-in-sky.jpg
|
||||
```
|
||||
|
||||
## camera-iris.svg
|
||||
## iris.svg / iris-outline.svg
|
||||
|
||||
**Link**: https://pictogrammers.com/library/mdi/icon/camera-iris/
|
||||
|
||||
**License**: https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
**Image Formatting Process**:
|
||||
|
||||
- Outline addded manually
|
||||
- Opacity adjustmented manually
|
||||
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M13.73,15L9.83,21.76C10.53,21.91 11.25,22 12,22C14.4,22 16.6,21.15 18.32,19.75L14.66,13.4M2.46,15C3.38,17.92 5.61,20.26 8.45,21.34L12.12,15M8.54,12L4.64,5.25C3,7 2,9.39 2,12C2,12.68 2.07,13.35 2.2,14H9.69M21.8,10H14.31L14.6,10.5L19.36,18.75C21,16.97 22,14.6 22,12C22,11.31 21.93,10.64 21.8,10M21.54,9C20.62,6.07 18.39,3.74 15.55,2.66L11.88,9M9.4,10.5L14.17,2.24C13.47,2.09 12.75,2 12,2C9.6,2 7.4,2.84 5.68,4.25L9.34,10.6L9.4,10.5Z" /></svg>
|
||||
|
Before Width: | Height: | Size: 509 B |
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
|
||||
sodipodi:docname="camera-iris.svg"
|
||||
sodipodi:docname="iris-outline.svg"
|
||||
id="svg4"
|
||||
version="1.1"
|
||||
viewBox="0 0 24 24"
|
||||
@@ -21,9 +21,9 @@
|
||||
inkscape:pagecheckerboard="false"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
showgrid="false"
|
||||
inkscape:zoom="26.62506"
|
||||
inkscape:cx="-1.934268"
|
||||
inkscape:cy="15.680716"
|
||||
inkscape:zoom="19.378578"
|
||||
inkscape:cx="8.2823415"
|
||||
inkscape:cy="12.359008"
|
||||
inkscape:window-width="3840"
|
||||
inkscape:window-height="1527"
|
||||
inkscape:window-x="1080"
|
||||
@@ -34,16 +34,16 @@
|
||||
id="g1119"
|
||||
style="fill-opacity:0.05;fill:#ffffff">
|
||||
<circle
|
||||
style="fill:#ffffff;fill-opacity:0.05;stroke-width:1.39729"
|
||||
style="display:inline;fill:#000000;fill-opacity:0.49782825;stroke-width:1.39729;opacity:0.5"
|
||||
id="path170"
|
||||
cx="12"
|
||||
cy="12"
|
||||
inkscape:label="White Background"
|
||||
inkscape:label="Background"
|
||||
r="11.25" />
|
||||
<path
|
||||
d="M 13.730001,15 9.8300003,21.76 C 10.53,21.91 11.25,22 12,22 c 2.400001,0 4.6,-0.85 6.32,-2.25 L 14.660001,13.4 M 2.4600003,15 c 0.92,2.92 3.15,5.26 5.99,6.34 L 12.12,15 m -3.5799997,-3 -3.9,-6.7499996 c -1.64,1.749999 -2.64,4.1399993 -2.64,6.7499996 0,0.68 0.07,1.35 0.2,2 h 7.49 M 21.8,9.9999997 H 14.310001 L 14.600001,10.5 19.36,18.75 C 21,16.97 22,14.6 22,12 22,11.31 21.93,10.64 21.8,9.9999997 m -0.26,-1 C 20.62,6.0700005 18.39,3.7400002 15.550001,2.6600002 L 11.88,8.9999997 M 9.4000003,10.5 14.170001,2.2400002 c -0.7,-0.15 -1.420001,-0.24 -2.170001,-0.24 -2.3999997,0 -4.5999997,0.84 -6.3199997,2.2500003 l 3.66,6.3499995 z"
|
||||
id="path2"
|
||||
inkscape:label="Iris"
|
||||
style="fill-opacity:0.05;fill:#ffffff" />
|
||||
style="fill-opacity:0.50221002;fill:#ffffff;opacity:0.75" />
|
||||
</g>
|
||||
</svg>
|
||||
@@ -0,0 +1,49 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
inkscape:version="1.4 (e7c3feb1, 2024-10-09)"
|
||||
sodipodi:docname="camera-iris-transparent.svg"
|
||||
id="svg4"
|
||||
version="1.1"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs8" />
|
||||
<sodipodi:namedview
|
||||
id="namedview6"
|
||||
pagecolor="#b93e3e"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.60784314"
|
||||
inkscape:pagecheckerboard="false"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
showgrid="false"
|
||||
inkscape:zoom="19.378578"
|
||||
inkscape:cx="12.100991"
|
||||
inkscape:cy="12.307405"
|
||||
inkscape:window-width="1440"
|
||||
inkscape:window-height="848"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="52"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="svg4" />
|
||||
<g
|
||||
id="g1119"
|
||||
style="fill-opacity:0.05;fill:#ffffff;stroke-width:1.0015748;stroke-dasharray:none">
|
||||
<circle
|
||||
style="display:none;opacity:0.5;fill:#000000;fill-opacity:1;stroke-width:1.00157;stroke-dasharray:none"
|
||||
id="path170"
|
||||
cx="12"
|
||||
cy="12"
|
||||
inkscape:label="Background"
|
||||
r="11.25" />
|
||||
<path
|
||||
d="M 13.730001,15 9.8300003,21.76 C 10.53,21.91 11.25,22 12,22 c 2.400001,0 4.6,-0.85 6.32,-2.25 L 14.660001,13.4 M 2.4600003,15 c 0.92,2.92 3.15,5.26 5.99,6.34 L 12.12,15 m -3.5799997,-3 -3.9,-6.7499996 c -1.64,1.749999 -2.64,4.1399993 -2.64,6.7499996 0,0.68 0.07,1.35 0.2,2 h 7.49 M 21.8,9.9999997 H 14.310001 L 14.600001,10.5 19.36,18.75 C 21,16.97 22,14.6 22,12 22,11.31 21.93,10.64 21.8,9.9999997 m -0.26,-1 C 20.62,6.0700005 18.39,3.7400002 15.550001,2.6600002 L 11.88,8.9999997 M 9.4000003,10.5 14.170001,2.2400002 c -0.7,-0.15 -1.420001,-0.24 -2.170001,-0.24 -2.3999997,0 -4.5999997,0.84 -6.3199997,2.2500003 l 3.66,6.3499995 z"
|
||||
id="path2"
|
||||
inkscape:label="Iris"
|
||||
style="fill-opacity:1;fill:#ffffff;opacity:1;stroke-width:1.0015748;stroke-dasharray:none" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
@@ -481,12 +481,6 @@
|
||||
},
|
||||
"view": {
|
||||
"camera_select": "Visualitza les càmeres seleccionades recentment",
|
||||
"dark_mode": "Mode fosc",
|
||||
"dark_modes": {
|
||||
"auto": "Automàtic",
|
||||
"off": "Desactivat",
|
||||
"on": "Activat"
|
||||
},
|
||||
"default": "Vista per defecte",
|
||||
"default_cycle_camera": "Passeu per les càmeres quan s'actualitzi la vista predeterminada",
|
||||
"default_reset": {
|
||||
@@ -501,6 +495,7 @@
|
||||
"inactive": ""
|
||||
}
|
||||
},
|
||||
"dim": "",
|
||||
"interaction_seconds": "Segons després de l'acció de l'usuari per continuar interactuant (0=mai)",
|
||||
"keyboard_shortcuts": {
|
||||
"editor_label": "",
|
||||
@@ -513,6 +508,15 @@
|
||||
"ptz_zoom_in": "",
|
||||
"ptz_zoom_out": ""
|
||||
},
|
||||
"theme": {
|
||||
"themes": {
|
||||
"dark": "",
|
||||
"ha": "",
|
||||
"editor_label": "",
|
||||
"light": "",
|
||||
"traditional": ""
|
||||
}
|
||||
},
|
||||
"triggers": {
|
||||
"actions": {
|
||||
"editor_label": "Activar accions",
|
||||
|
||||
@@ -481,12 +481,6 @@
|
||||
},
|
||||
"view": {
|
||||
"camera_select": "View for newly selected cameras",
|
||||
"dark_mode": "Dark mode",
|
||||
"dark_modes": {
|
||||
"auto": "Auto",
|
||||
"off": "Off",
|
||||
"on": "On"
|
||||
},
|
||||
"default": "Default view",
|
||||
"default_cycle_camera": "Cycle through cameras when default view updates",
|
||||
"default_reset": {
|
||||
@@ -501,6 +495,7 @@
|
||||
"inactive": "Only reset when card has no human interaction"
|
||||
}
|
||||
},
|
||||
"dim": "Dim when no interaction",
|
||||
"interaction_seconds": "Seconds after user action to remain interacted with (0=never)",
|
||||
"keyboard_shortcuts": {
|
||||
"editor_label": "Keyboard shortcuts",
|
||||
@@ -513,6 +508,15 @@
|
||||
"ptz_zoom_in": "PTZ Zoom In",
|
||||
"ptz_zoom_out": "PTZ Zoom Out"
|
||||
},
|
||||
"theme": {
|
||||
"themes": {
|
||||
"dark": "Dark",
|
||||
"ha": "Home Assistant",
|
||||
"editor_label": "Themes",
|
||||
"light": "Light",
|
||||
"traditional": "Traditional"
|
||||
}
|
||||
},
|
||||
"triggers": {
|
||||
"actions": {
|
||||
"editor_label": "Trigger actions",
|
||||
|
||||
@@ -481,12 +481,6 @@
|
||||
},
|
||||
"view": {
|
||||
"camera_select": "Afficher les caméras nouvellement sélectionnées",
|
||||
"dark_mode": "Mode sombre",
|
||||
"dark_modes": {
|
||||
"auto": "Automatique",
|
||||
"off": "Désactivé",
|
||||
"on": "Activé"
|
||||
},
|
||||
"default": "Vue par défaut",
|
||||
"default_cycle_camera": "Parcourez les caméras lorsque la vue par défaut est mise à jour",
|
||||
"default_reset": {
|
||||
@@ -501,6 +495,7 @@
|
||||
"inactive": "Réinitialiser uniquement lorsque la carte n'a pas d'interaction humaine"
|
||||
}
|
||||
},
|
||||
"dim": "",
|
||||
"interaction_seconds": "Secondes après l'action de l'utilisateur pour maintenir l'interaction (0=jamais)",
|
||||
"keyboard_shortcuts": {
|
||||
"editor_label": "Raccourcis clavier",
|
||||
@@ -513,6 +508,15 @@
|
||||
"ptz_zoom_in": "PTZ zoomer",
|
||||
"ptz_zoom_out": "PTZ dézoomer"
|
||||
},
|
||||
"theme": {
|
||||
"themes": {
|
||||
"dark": "",
|
||||
"ha": "",
|
||||
"editor_label": "",
|
||||
"light": "",
|
||||
"traditional": ""
|
||||
}
|
||||
},
|
||||
"triggers": {
|
||||
"actions": {
|
||||
"editor_label": "Actions de déclenchement",
|
||||
|
||||
@@ -481,12 +481,6 @@
|
||||
},
|
||||
"view": {
|
||||
"camera_select": "Visualizza per le telecamere appena selezionate",
|
||||
"dark_mode": "Tema scuro",
|
||||
"dark_modes": {
|
||||
"auto": "auto",
|
||||
"off": "Off",
|
||||
"on": "On"
|
||||
},
|
||||
"default": "Visualizzazione predefinita",
|
||||
"default_cycle_camera": "Scorri le telecamere quando si aggiorna la visualizzazione predefinita",
|
||||
"default_reset": {
|
||||
@@ -501,6 +495,7 @@
|
||||
"inactive": ""
|
||||
}
|
||||
},
|
||||
"dim": "",
|
||||
"interaction_seconds": "",
|
||||
"keyboard_shortcuts": {
|
||||
"editor_label": "",
|
||||
@@ -513,6 +508,15 @@
|
||||
"ptz_zoom_in": "",
|
||||
"ptz_zoom_out": ""
|
||||
},
|
||||
"theme": {
|
||||
"themes": {
|
||||
"dark": "",
|
||||
"ha": "",
|
||||
"editor_label": "",
|
||||
"light": "",
|
||||
"traditional": ""
|
||||
}
|
||||
},
|
||||
"triggers": {
|
||||
"actions": {
|
||||
"editor_label": "",
|
||||
|
||||
@@ -481,12 +481,6 @@
|
||||
},
|
||||
"view": {
|
||||
"camera_select": "Visualização de câmeras recém-selecionadas",
|
||||
"dark_mode": "Modo escuro",
|
||||
"dark_modes": {
|
||||
"auto": "Automático",
|
||||
"off": "Desligado",
|
||||
"on": "Ligado"
|
||||
},
|
||||
"default": "Visualização padrão",
|
||||
"default_cycle_camera": "Percorrer as câmeras quando a visualização padrão for atualizada",
|
||||
"default_reset": {
|
||||
@@ -501,6 +495,7 @@
|
||||
"inactive": ""
|
||||
}
|
||||
},
|
||||
"dim": "",
|
||||
"interaction_seconds": "",
|
||||
"keyboard_shortcuts": {
|
||||
"editor_label": "",
|
||||
@@ -513,6 +508,15 @@
|
||||
"ptz_zoom_in": "",
|
||||
"ptz_zoom_out": ""
|
||||
},
|
||||
"theme": {
|
||||
"themes": {
|
||||
"dark": "",
|
||||
"ha": "",
|
||||
"editor_label": "",
|
||||
"light": "",
|
||||
"traditional": ""
|
||||
}
|
||||
},
|
||||
"triggers": {
|
||||
"actions": {
|
||||
"editor_label": "",
|
||||
|
||||
@@ -481,12 +481,6 @@
|
||||
},
|
||||
"view": {
|
||||
"camera_select": "Visualização de câmeras recém-selecionadas",
|
||||
"dark_mode": "Modo escuro",
|
||||
"dark_modes": {
|
||||
"auto": "Automático",
|
||||
"off": "Desligado",
|
||||
"on": "Ligado"
|
||||
},
|
||||
"default": "Visualização padrão",
|
||||
"default_cycle_camera": "Percorrer as câmeras quando a visualização padrão for atualizada",
|
||||
"default_reset": {
|
||||
@@ -501,6 +495,7 @@
|
||||
"inactive": ""
|
||||
}
|
||||
},
|
||||
"dim": "",
|
||||
"interaction_seconds": "",
|
||||
"keyboard_shortcuts": {
|
||||
"editor_label": "",
|
||||
@@ -513,6 +508,15 @@
|
||||
"ptz_zoom_in": "",
|
||||
"ptz_zoom_out": ""
|
||||
},
|
||||
"theme": {
|
||||
"themes": {
|
||||
"dark": "",
|
||||
"ha": "",
|
||||
"editor_label": "",
|
||||
"light": "",
|
||||
"traditional": ""
|
||||
}
|
||||
},
|
||||
"triggers": {
|
||||
"actions": {
|
||||
"editor_label": "",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
ha-icon-button.button {
|
||||
color: var(--secondary-color, white);
|
||||
background-color: rgba(0, 0, 0, 0.6);
|
||||
ha-icon-button {
|
||||
color: var(--frigate-card-button-color);
|
||||
background-color: var(--frigate-card-button-background);
|
||||
|
||||
border-radius: 50%;
|
||||
padding: 0px;
|
||||
margin: 3px;
|
||||
@@ -8,5 +9,4 @@ ha-icon-button.button {
|
||||
|
||||
/* Buttons can always be clicked */
|
||||
pointer-events: auto;
|
||||
opacity: 0.9;
|
||||
}
|
||||
+3
-1
@@ -1,3 +1,5 @@
|
||||
@use './themes/base.scss';
|
||||
|
||||
:host {
|
||||
display: block;
|
||||
|
||||
@@ -36,7 +38,7 @@ frigate-card-loading {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
:host([dark]) {
|
||||
:host([dimmable]:not([interaction])) {
|
||||
filter: brightness(75%);
|
||||
}
|
||||
:host([panel]:not([casted])) {
|
||||
|
||||
@@ -37,9 +37,8 @@ div.control-surround {
|
||||
}
|
||||
|
||||
frigate-card-icon.control {
|
||||
color: var(--secondary-color, white);
|
||||
background-color: rgba(0, 0, 0, 0.7);
|
||||
opacity: 0.5;
|
||||
color: var(--frigate-card-button-color);
|
||||
background-color: var(--frigate-card-button-background);
|
||||
pointer-events: all;
|
||||
|
||||
--mdc-icon-size: #{$drawer-icon-size};
|
||||
@@ -54,7 +53,6 @@ frigate-card-icon.control:hover {
|
||||
// When the drawer is open or hovered make the button to close it more
|
||||
// prominent.
|
||||
opacity: 1;
|
||||
background-color: black;
|
||||
}
|
||||
|
||||
:host([location='left']) frigate-card-icon.control {
|
||||
|
||||
@@ -98,7 +98,7 @@ div.upgrade span {
|
||||
margin-right: 0px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.submenu .controls ha-icon-button.button {
|
||||
.submenu .controls ha-icon-button {
|
||||
--mdc-icon-button-size: 32px;
|
||||
--mdc-icon-size: calc(var(--mdc-icon-button-size) / 2);
|
||||
}
|
||||
|
||||
@@ -12,3 +12,13 @@
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
svg path {
|
||||
// !important is necessary to overcome the fill set on the path itself.
|
||||
fill: currentColor !important;
|
||||
}
|
||||
|
||||
:host([allow-override-non-active-styles]) state-badge {
|
||||
--state-inactive-color: inherit;
|
||||
--state-unavailable-color: inherit;
|
||||
}
|
||||
@@ -9,8 +9,9 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 10%;
|
||||
frigate-card-icon {
|
||||
height: 50%;
|
||||
width: auto;
|
||||
animation: rotate 8s linear infinite;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
$bg-img: url('../images/camera-iris-transparent.svg');
|
||||
// Use the outlined Iris logo since the background color is unknown.
|
||||
$bg-img: url('../images/iris-background.svg');
|
||||
|
||||
:host {
|
||||
background-color: var(--primary-background-color);
|
||||
|
||||
+22
-41
@@ -15,6 +15,10 @@
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
:host([data-style='outside']) {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/*******************************************************
|
||||
* Non-hover styles should not interact with the pointer
|
||||
*******************************************************/
|
||||
@@ -45,15 +49,6 @@ div.opposing {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
/********************
|
||||
* Outside menu style
|
||||
********************/
|
||||
|
||||
:host([data-style='outside']) {
|
||||
width: 100%;
|
||||
background: var(--secondary-background-color);
|
||||
}
|
||||
|
||||
/********************************************************
|
||||
* Hack: Ensure host & div expand for column flex layouts
|
||||
********************************************************/
|
||||
@@ -126,24 +121,6 @@ div.opposing {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
:host([data-style='overlay'][data-position='top'][data-alignment='left']),
|
||||
:host([data-style='overlay'][data-position='bottom'][data-alignment='left']),
|
||||
:host([data-style*='hover'][data-position='top'][data-alignment='left']),
|
||||
:host([data-style*='hover'][data-position='bottom'][data-alignment='left']),
|
||||
:host([data-style='hidden'][data-position='top'][data-alignment='left'][expanded]),
|
||||
:host([data-style='hidden'][data-position='bottom'][data-alignment='left'][expanded]) {
|
||||
background: linear-gradient(90deg, rgba(0, 0, 0, 0.8), rgba(0, 0, 0, 0));
|
||||
}
|
||||
|
||||
:host([data-style='overlay'][data-position='top'][data-alignment='right']),
|
||||
:host([data-style='overlay'][data-position='bottom'][data-alignment='right']),
|
||||
:host([data-style*='hover'][data-position='top'][data-alignment='right']),
|
||||
:host([data-style*='hover'][data-position='bottom'][data-alignment='right']),
|
||||
:host([data-style='hidden'][data-position='top'][data-alignment='right'][expanded]),
|
||||
:host([data-style='hidden'][data-position='bottom'][data-alignment='right'][expanded]) {
|
||||
background: linear-gradient(270deg, rgba(0, 0, 0, 0.8), rgba(0, 0, 0, 0));
|
||||
}
|
||||
|
||||
:host([data-style='overlay'][data-position='left']),
|
||||
:host([data-style='overlay'][data-position='right']),
|
||||
:host([data-style*='hover'][data-position='left']),
|
||||
@@ -154,20 +131,24 @@ div.opposing {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
:host([data-style='overlay'][data-position='left'][data-alignment='top']),
|
||||
:host([data-style='overlay'][data-position='right'][data-alignment='top']),
|
||||
:host([data-style*='hover'][data-position='left'][data-alignment='top']),
|
||||
:host([data-style*='hover'][data-position='right'][data-alignment='top']),
|
||||
:host([data-style='hidden'][data-position='left'][data-alignment='top'][expanded]),
|
||||
:host([data-style='hidden'][data-position='right'][data-alignment='top'][expanded]) {
|
||||
background: linear-gradient(180deg, rgba(0, 0, 0, 0.8), rgba(0, 0, 0, 0));
|
||||
/************
|
||||
* Menu theme
|
||||
************/
|
||||
|
||||
// When the menu uses the hidden style, and is not expanded, the remaining
|
||||
// permanent menu buttons use the menu background color instead of the button
|
||||
// background color.
|
||||
:host([data-style='hidden']:not([expanded])) ha-icon-button {
|
||||
background: var(--frigate-card-menu-background);
|
||||
}
|
||||
|
||||
:host([data-style='overlay'][data-position='left'][data-alignment='bottom']),
|
||||
:host([data-style='overlay'][data-position='right'][data-alignment='bottom']),
|
||||
:host([data-style*='hover'][data-position='left'][data-alignment='bottom']),
|
||||
:host([data-style*='hover'][data-position='right'][data-alignment='bottom']),
|
||||
:host([data-style='hidden'][data-position='left'][data-alignment='bottom'][expanded]),
|
||||
:host([data-style='hidden'][data-position='right'][data-alignment='bottom'][expanded]) {
|
||||
background: linear-gradient(0deg, rgba(0, 0, 0, 0.8), rgba(0, 0, 0, 0));
|
||||
// Icons in the menu are expected to follow Frigate 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(--frigate-card-button-color);
|
||||
--state-inactive-color: var(--frigate-card-button-color);
|
||||
}
|
||||
|
||||
// Further theme related styling is dynamically applied by `menu.ts`, see
|
||||
// `_renderPerInstanceStyle`.
|
||||
+13
-20
@@ -1,7 +1,7 @@
|
||||
@use './button.scss';
|
||||
|
||||
:host {
|
||||
--mdc-icon-size: calc(var(--frigate-card-status-bar-height) - 6px);
|
||||
--mdc-icon-size: calc(var(--frigate-card-status-bar-height) / 2);
|
||||
|
||||
display: block;
|
||||
width: 100%;
|
||||
@@ -20,23 +20,10 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/***************
|
||||
* Outside style
|
||||
***************/
|
||||
/**********************
|
||||
* Popup style behavior
|
||||
**********************/
|
||||
|
||||
:host([data-style='outside']) {
|
||||
color: var(--primary-text-color);
|
||||
background: var(--secondary-background-color);
|
||||
}
|
||||
|
||||
/*******************
|
||||
* Non-outside style
|
||||
*******************/
|
||||
|
||||
:host(:not([data-style='outside'])) {
|
||||
color: white;
|
||||
background: linear-gradient(90deg, rgba(0, 0, 0, 0.8), rgba(0, 0, 0, 0));
|
||||
}
|
||||
:host([data-style='popup'][hide]) {
|
||||
opacity: 0;
|
||||
}
|
||||
@@ -63,8 +50,6 @@
|
||||
margin: 3px 5px;
|
||||
|
||||
align-content: center;
|
||||
|
||||
opacity: 0.7;
|
||||
}
|
||||
.item:first-child {
|
||||
margin-left: 10px;
|
||||
@@ -81,7 +66,8 @@
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
img.item {
|
||||
img.item,
|
||||
frigate-card-icon.item {
|
||||
display: block;
|
||||
|
||||
// To ensure images render somewhat reasonably looking their height is kept to
|
||||
@@ -89,3 +75,10 @@ img.item {
|
||||
height: var(--mdc-icon-size, 24px);
|
||||
width: auto;
|
||||
}
|
||||
|
||||
/************************
|
||||
* Status bar theme hooks
|
||||
************************/
|
||||
|
||||
// Further theme related styling is dynamically applied by `status-bar.ts`, see
|
||||
// `_renderPerInstanceStyle`.
|
||||
@@ -7,3 +7,14 @@
|
||||
mwc-list-item {
|
||||
z-index: 20;
|
||||
}
|
||||
|
||||
ha-icon-button {
|
||||
// The main submenu icon is expected to follow Frigate card theming unless it
|
||||
// is 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(--frigate-card-button-color);
|
||||
--state-inactive-color: var(--frigate-card-button-color);
|
||||
|
||||
color: var(--frigate-card-menu-button-inactive-color);
|
||||
background-color: var(--frigate-card-menu-button-background);
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
@use 'dark.scss';
|
||||
@use 'light.scss';
|
||||
@use 'traditional.scss';
|
||||
|
||||
:host {
|
||||
/*********
|
||||
* General
|
||||
*********/
|
||||
|
||||
--frigate-card-exterior: var(--secondary-background-color, black);
|
||||
--frigate-card-background: var(--primary-background-color, white);
|
||||
|
||||
--frigate-card-foreground-primary: var(--primary-color, black);
|
||||
|
||||
--frigate-card-active-color: var(--accent-color, orange);
|
||||
--frigate-card-warning-color: var(--error-color, red);
|
||||
|
||||
--frigate-card-text-color: var(--primary-text-color);
|
||||
--frigate-card-divider-color: var(--divider-color);
|
||||
|
||||
--frigate-card-control-background: var(--app-header-background-color);
|
||||
--frigate-card-control-background-transparent: color-mix(
|
||||
in oklab,
|
||||
var(--frigate-card-control-background),
|
||||
transparent 60%
|
||||
);
|
||||
--frigate-card-control-foreground: var(--app-header-text-color);
|
||||
|
||||
/*********
|
||||
* Buttons
|
||||
*********/
|
||||
|
||||
// The color of buttons (in general, menu or otherwise).
|
||||
--frigate-card-button-color: var(--frigate-card-control-foreground);
|
||||
|
||||
// The color of button background "circles".
|
||||
--frigate-card-button-background: var(--frigate-card-control-background-transparent);
|
||||
|
||||
/******
|
||||
* Menu
|
||||
******/
|
||||
|
||||
// The color of an active icon in the menu.
|
||||
--frigate-card-menu-button-active-color: var(--frigate-card-active-color);
|
||||
|
||||
// The color of an inactive icon in the menu.
|
||||
--frigate-card-menu-button-inactive-color: var(--frigate-card-button-color);
|
||||
|
||||
// The color of a critical icon in the menu.
|
||||
--frigate-card-menu-button-critical-color: var(
|
||||
--error-color,
|
||||
var(--frigate-card-warning-color)
|
||||
);
|
||||
|
||||
// The background color of the button "circles" in the menu.
|
||||
--frigate-card-menu-button-background: color-mix(
|
||||
in oklab,
|
||||
var(--frigate-card-menu-button-inactive-color),
|
||||
transparent 60%
|
||||
);
|
||||
|
||||
// The menu background color.
|
||||
--frigate-card-menu-background: var(--frigate-card-control-background-transparent);
|
||||
--frigate-card-menu-style-outside-background: var(--frigate-card-control-background);
|
||||
|
||||
// May be overridden by the user to force a particular color.
|
||||
--frigate-card-menu-override-background: unset;
|
||||
--frigate-card-menu-override-button-inactive-color: unset;
|
||||
--frigate-card-menu-override-button-background: unset;
|
||||
|
||||
// Examples of precise theming for the menu:
|
||||
// --frigate-card-menu-position-left-alignment-left-style-overlay-background: pink;
|
||||
// --frigate-card-menu-position-left-background: purple;
|
||||
// --frigate-card-menu-style-overlay-background: pink;
|
||||
|
||||
/************
|
||||
* Status bar
|
||||
************/
|
||||
|
||||
// May be overridden by the user to force a particular color.
|
||||
--frigate-card-status-bar-override-background: unset;
|
||||
--frigate-card-status-bar-override-color: unset;
|
||||
|
||||
// The color of items on the status bar.
|
||||
--frigate-card-status-bar-color: var(--frigate-card-control-foreground);
|
||||
|
||||
// The background color of the outerlay above/below the card.
|
||||
--frigate-card-status-bar-background: var(
|
||||
--frigate-card-control-background-transparent
|
||||
);
|
||||
--frigate-card-status-bar-style-outside-background: var(
|
||||
--frigate-card-control-background
|
||||
);
|
||||
|
||||
/************
|
||||
* Thumbnails
|
||||
************/
|
||||
|
||||
--frigate-card-thumbnail-icon-color: var(--frigate-card-foreground-primary);
|
||||
--frigate-card-thumbnail-border-color: var(--frigate-card-foreground-primary);
|
||||
--frigate-card-thumbnail-background: var(--frigate-card-background);
|
||||
--frigate-card-thumbnail-icon-favorite-color: color-mix(
|
||||
in oklab,
|
||||
gold,
|
||||
transparent 25%
|
||||
);
|
||||
--frigate-card-thumbnail-text-color: var(--frigate-card-text-color);
|
||||
|
||||
/**********
|
||||
* Timeline
|
||||
**********/
|
||||
|
||||
--frigate-card-timeline-text-color: var(--frigate-card-text-color);
|
||||
--frigate-card-timeline-item-color: var(--frigate-card-foreground-primary);
|
||||
--frigate-card-timeline-tool-color: var(--frigate-card-foreground-primary);
|
||||
--frigate-card-timeline-background: var(--frigate-card-background);
|
||||
|
||||
// style for the 'background' style of item (i.e. recordings).
|
||||
--frigate-card-timeline-background-item-color: color-mix(
|
||||
in oklab,
|
||||
var(--frigate-card-timeline-item-color),
|
||||
transparent 90%
|
||||
);
|
||||
|
||||
--frigate-card-timeline-divider-color: var(--frigate-card-divider-color);
|
||||
--frigate-card-timeline-target-bar-color: var(--frigate-card-active-color);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
:host([themes~='dark']) {
|
||||
// This file should include only, and exactly match, values from
|
||||
// https://github.com/home-assistant/frontend/blob/dev/src/resources/styles-data.ts .
|
||||
|
||||
--primary-background-color: #111111;
|
||||
--secondary-background-color: #282828;
|
||||
--primary-text-color: #e1e1e1;
|
||||
--divider-color: rgba(225, 225, 225, 0.12);
|
||||
--app-header-background-color: #101e24;
|
||||
--app-header-text-color: #e1e1e1;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
:host([themes~='light']) {
|
||||
// This file should include only, and exactly match, values from
|
||||
// https://github.com/home-assistant/frontend/blob/dev/src/resources/ha-style.ts
|
||||
|
||||
--primary-background-color: #fafafa;
|
||||
--secondary-background-color: #e5e5e5;
|
||||
--primary-text-color: #212121;
|
||||
--divider-color: rgba(0, 0, 0, 0.12);
|
||||
--app-header-text-color: #ffffff;
|
||||
|
||||
// HA dynamically sets these values based on choice of primary/secondary
|
||||
// colors and hex blending techniques. The card does not currently
|
||||
// re-implement this logic, rather just uses the default static value.
|
||||
// See: https://github.com/home-assistant/frontend/blob/dev/src/common/dom/apply_themes_on_element.ts
|
||||
|
||||
--app-header-background-color: #03a9f4;
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
@mixin background-gradient($var, $angle) {
|
||||
#{$var}: linear-gradient(
|
||||
$angle,
|
||||
var(--frigate-card-menu-background),
|
||||
var(--frigate-card-menu-background),
|
||||
var(--frigate-card-menu-background),
|
||||
color-mix(in oklab, var(--frigate-card-menu-background), transparent 100%)
|
||||
);
|
||||
}
|
||||
|
||||
:host([themes~='traditional']) {
|
||||
/*********
|
||||
* General
|
||||
*********/
|
||||
|
||||
--frigate-card-control-background: black;
|
||||
|
||||
/******
|
||||
* Menu
|
||||
******/
|
||||
|
||||
--frigate-card-menu-button-active-color: var(--primary-color, white);
|
||||
--frigate-card-menu-button-background: rgba(0, 0, 0, 0.6);
|
||||
|
||||
@include background-gradient(
|
||||
'--frigate-card-menu-position-left-alignment-top-background',
|
||||
180deg
|
||||
);
|
||||
@include background-gradient(
|
||||
'--frigate-card-menu-position-left-alignment-bottom-background',
|
||||
0deg
|
||||
);
|
||||
@include background-gradient(
|
||||
'--frigate-card-menu-position-right-alignment-top-background',
|
||||
180deg
|
||||
);
|
||||
@include background-gradient(
|
||||
'--frigate-card-menu-position-right-alignment-bottom-background',
|
||||
0deg
|
||||
);
|
||||
|
||||
@include background-gradient(
|
||||
'--frigate-card-menu-position-top-alignment-left-background',
|
||||
90deg
|
||||
);
|
||||
@include background-gradient(
|
||||
'--frigate-card-menu-position-top-alignment-right-background',
|
||||
270deg
|
||||
);
|
||||
@include background-gradient(
|
||||
'--frigate-card-menu-position-bottom-alignment-left-background',
|
||||
90deg
|
||||
);
|
||||
@include background-gradient(
|
||||
'--frigate-card-menu-position-bottom-alignment-right-background',
|
||||
270deg
|
||||
);
|
||||
|
||||
--frigate-card-menu-position-top-alignment-left-style-outside-background: var(
|
||||
--secondary-background-color
|
||||
);
|
||||
--frigate-card-menu-position-top-alignment-right-style-outside-background: var(
|
||||
--secondary-background-color
|
||||
);
|
||||
--frigate-card-menu-position-bottom-alignment-left-style-outside-background: var(
|
||||
--secondary-background-color
|
||||
);
|
||||
--frigate-card-menu-position-bottom-alignment-right-style-outside-background: var(
|
||||
--secondary-background-color
|
||||
);
|
||||
|
||||
/************
|
||||
* Status bar
|
||||
************/
|
||||
|
||||
--frigate-card-status-bar-color: white;
|
||||
|
||||
@include background-gradient(
|
||||
'--frigate-card-status-bar-position-top-background',
|
||||
90deg
|
||||
);
|
||||
@include background-gradient(
|
||||
'--frigate-card-status-bar-position-bottom-background',
|
||||
90deg
|
||||
);
|
||||
|
||||
--frigate-card-status-bar-position-top-style-outside-background: var(
|
||||
--secondary-background-color
|
||||
);
|
||||
--frigate-card-status-bar-position-bottom-style-outside-background: var(
|
||||
--secondary-background-color
|
||||
);
|
||||
--frigate-card-status-bar-position-top-style-outside-color: var(
|
||||
--secondary-text-color
|
||||
);
|
||||
--frigate-card-status-bar-position-bottom-style-outside-color: var(
|
||||
--secondary-text-color
|
||||
);
|
||||
}
|
||||
@@ -5,6 +5,8 @@
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
background-color: var(--frigate-card-thumbnail-background);
|
||||
|
||||
--frigate-card-carousel-thumbnail-opacity: 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
width: 100%;
|
||||
margin-left: 5px;
|
||||
padding: 5px;
|
||||
color: var(--primary-text-color);
|
||||
color: var(--frigate-card-thumbnail-text-color);
|
||||
overflow: hidden;
|
||||
column-gap: 5%;
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
}
|
||||
|
||||
:host([details]) {
|
||||
border: 1px solid var(--primary-color);
|
||||
border: 1px solid var(--frigate-card-thumbnail-border-color);
|
||||
border-radius: var(
|
||||
--frigate-card-css-border-radius,
|
||||
var(--ha-card-border-radius, 4px)
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
// When details are enabled, use a background color so that the details have
|
||||
// contrast with the background.
|
||||
background-color: var(--primary-background-color, black);
|
||||
background-color: var(--frigate-card-thumbnail-background);
|
||||
}
|
||||
|
||||
:host(:hover) {
|
||||
@@ -34,8 +34,7 @@
|
||||
frigate-card-icon {
|
||||
position: absolute;
|
||||
border-radius: 50%;
|
||||
opacity: 0.5;
|
||||
color: var(--primary-color);
|
||||
color: var(--frigate-card-thumbnail-icon-color);
|
||||
|
||||
cursor: pointer;
|
||||
|
||||
@@ -52,7 +51,7 @@ frigate-card-icon.star {
|
||||
left: 3px;
|
||||
}
|
||||
frigate-card-icon.star.starred {
|
||||
color: gold;
|
||||
color: var(--frigate-card-thumbnail-icon-favorite-color);
|
||||
}
|
||||
|
||||
frigate-card-icon.timeline {
|
||||
|
||||
+31
-21
@@ -10,6 +10,8 @@
|
||||
|
||||
// So that absolute sidedrawer is relative to this host.
|
||||
position: relative;
|
||||
|
||||
background-color: var(--frigate-card-timeline-background);
|
||||
}
|
||||
|
||||
:host(:not([mini])) {
|
||||
@@ -30,7 +32,7 @@ div.timeline {
|
||||
}
|
||||
|
||||
.vis-text {
|
||||
color: var(--primary-text-color) !important;
|
||||
color: var(--frigate-card-timeline-text-color) !important;
|
||||
}
|
||||
|
||||
.vis-timeline {
|
||||
@@ -39,7 +41,7 @@ div.timeline {
|
||||
|
||||
.vis-labelset .vis-label {
|
||||
// Group labels.
|
||||
color: var(--primary-text-color);
|
||||
color: var(--frigate-card-timeline-text-color);
|
||||
}
|
||||
|
||||
:host([ribbon]:not([groups])) .vis-item:not(.vis-background) {
|
||||
@@ -52,12 +54,11 @@ div.timeline {
|
||||
// Ribbon timelines have a much shorter 'bar'.
|
||||
height: 6px;
|
||||
}
|
||||
|
||||
.vis-item {
|
||||
border-color: var(--primary-color);
|
||||
border-color: var(--frigate-card-timeline-item-color);
|
||||
background: none;
|
||||
color: var(--primary-text-color);
|
||||
background-color: var(--primary-color);
|
||||
color: var(--frigate-card-timeline-text-color);
|
||||
background-color: var(--frigate-card-timeline-item-color);
|
||||
|
||||
transition:
|
||||
background-color ease-in-out 1s,
|
||||
@@ -65,13 +66,15 @@ div.timeline {
|
||||
box-shadow ease-in-out 1s;
|
||||
}
|
||||
.vis-item.vis-selected {
|
||||
border-color: var(--accent-color);
|
||||
background-color: var(--accent-color);
|
||||
box-shadow: var(--frigate-card-css-box-shadow, 0px 0px 5px 1px var(--primary-color));
|
||||
border-color: var(--frigate-card-active-color);
|
||||
background-color: var(--frigate-card-active-color);
|
||||
box-shadow: var(
|
||||
--frigate-card-css-box-shadow,
|
||||
0px 0px 5px 1px var(--frigate-card-active-color)
|
||||
);
|
||||
}
|
||||
.vis-item.vis-background {
|
||||
background-color: var(--primary-color);
|
||||
opacity: 0.1;
|
||||
background-color: var(--frigate-card-timeline-background-item-color);
|
||||
}
|
||||
|
||||
// If there are no timeline groups shown (e.g. mini mode with a single camera),
|
||||
@@ -103,16 +106,19 @@ div.timeline {
|
||||
|
||||
.vis-item.vis-cluster {
|
||||
border-style: dotted;
|
||||
color: var(--primary-text-color);
|
||||
background-color: var(--primary-background-color);
|
||||
box-shadow: var(--frigate-card-css-box-shadow, 0px 0px 5px 1px var(--primary-color));
|
||||
color: var(--frigate-card-timeline-text-color);
|
||||
background-color: var(--frigate-card-timeline-background);
|
||||
box-shadow: var(
|
||||
--frigate-card-css-box-shadow,
|
||||
0px 0px 5px 1px var(--frigate-card-timeline-item-color)
|
||||
);
|
||||
}
|
||||
.vis-item.vis-range {
|
||||
border-radius: var(--frigate-card-css-border-radius, unset);
|
||||
}
|
||||
|
||||
.vis-time-axis .vis-grid.vis-minor {
|
||||
border-color: var(--secondary-color);
|
||||
border-color: var(--frigate-card-timeline-divider-color);
|
||||
}
|
||||
|
||||
// Give an indication that the user can interact with the axes.
|
||||
@@ -121,11 +127,15 @@ div.timeline {
|
||||
transition: background-color 0.5s ease-out;
|
||||
}
|
||||
:host([recordings]) .vis-text.vis-minor:hover {
|
||||
background-color: var(--primary-color);
|
||||
background-color: color-mix(
|
||||
in oklab,
|
||||
var(--frigate-card-timeline-text-color),
|
||||
transparent 80%
|
||||
);
|
||||
}
|
||||
|
||||
.vis-time-axis .vis-grid.vis-major {
|
||||
border-color: var(--secondary-color);
|
||||
border-color: var(--frigate-card-timeline-divider-color);
|
||||
}
|
||||
|
||||
.vis-label {
|
||||
@@ -146,7 +156,7 @@ div.vis-tooltip {
|
||||
}
|
||||
|
||||
.target_bar {
|
||||
background-color: var(--primary-color);
|
||||
background-color: var(--frigate-card-timeline-target-bar-color);
|
||||
width: 2px;
|
||||
|
||||
z-index: 20;
|
||||
@@ -155,8 +165,8 @@ div.vis-tooltip {
|
||||
pointer-events: none;
|
||||
}
|
||||
.target_bar .vis-custom-time-marker {
|
||||
background-color: var(--primary-background-color);
|
||||
color: var(--primary-text-color);
|
||||
background-color: var(--frigate-card-timeline-background);
|
||||
color: var(--frigate-card-timeline-text-color);
|
||||
bottom: 0px;
|
||||
top: unset;
|
||||
}
|
||||
@@ -176,7 +186,7 @@ div.vis-tooltip {
|
||||
position: absolute;
|
||||
right: 0px;
|
||||
bottom: 0px;
|
||||
color: var(--primary-color);
|
||||
color: var(--frigate-card-timeline-tool-color);
|
||||
z-index: 10;
|
||||
}
|
||||
.timeline-tools * {
|
||||
|
||||
@@ -5,7 +5,6 @@ import {
|
||||
LovelaceCardEditor,
|
||||
Themes,
|
||||
} from '@dermotduffy/custom-card-helpers';
|
||||
import { StyleInfo } from 'lit/directives/style-map';
|
||||
import { z } from 'zod';
|
||||
|
||||
export type ClipsOrSnapshots = 'clips' | 'snapshots';
|
||||
@@ -146,9 +145,6 @@ export interface Icon {
|
||||
|
||||
// If an icon is not otherwise resolved / available, this will be used instead.
|
||||
fallback?: string;
|
||||
|
||||
// Styling to apply to the icon.
|
||||
style?: StyleInfo;
|
||||
}
|
||||
|
||||
// *************************************************************************
|
||||
|
||||
Reference in new issue
Block a user