174 lines
5.4 KiB
TypeScript
174 lines
5.4 KiB
TypeScript
import type { StyleInfo } from 'lit/directives/style-map.js';
|
|
|
|
import { configDefaults, type AdvancedCameraCardConfig } from '../config/schema/types';
|
|
import type { ThemeConfig, ThemeName } from '../config/schema/view';
|
|
import {
|
|
aspectRatioToStyle,
|
|
setOrRemoveAttribute,
|
|
setOrRemoveStyleProperty,
|
|
} from '../utils/basic';
|
|
import type { View } from '../view/view';
|
|
import type { CardStyleAPI } from './types';
|
|
|
|
export class StyleManager {
|
|
private _api: CardStyleAPI;
|
|
|
|
constructor(api: CardStyleAPI) {
|
|
this._api = api;
|
|
}
|
|
|
|
public setExpandedMode(): void {
|
|
const card = this._api.getCardElementManager().getElement();
|
|
const view = this._api.getViewManager().getView();
|
|
|
|
// When a new media loads, set the aspect ratio for when the card is
|
|
// expanded/popped-up. This is based exclusively on last media content,
|
|
// as dimension configuration does not apply in fullscreen or expanded mode.
|
|
const lastKnown = this._api.getMediaLoadedInfoManager().getLastKnown();
|
|
card.style.setProperty(
|
|
'--advanced-camera-card-expand-aspect-ratio',
|
|
view?.isAnyMediaView() && lastKnown
|
|
? `${lastKnown.width} / ${lastKnown.height}`
|
|
: 'unset',
|
|
);
|
|
// Non-media may have no intrinsic dimensions (or multiple media items in a
|
|
// grid) and so we need to explicit request the dialog to use all available
|
|
// space.
|
|
const isGrid = view?.isGrid();
|
|
card.style.setProperty(
|
|
'--advanced-camera-card-expand-width',
|
|
!isGrid && view?.isAnyMediaView()
|
|
? 'none'
|
|
: 'var(--advanced-camera-card-expand-max-width)',
|
|
);
|
|
card.style.setProperty(
|
|
'--advanced-camera-card-expand-height',
|
|
!isGrid && view?.isAnyMediaView()
|
|
? 'none'
|
|
: 'var(--advanced-camera-card-expand-max-height)',
|
|
);
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
private _getThemeNames(themeConfig: ThemeConfig): ThemeName[] | null {
|
|
return themeConfig.themes.length
|
|
? themeConfig.themes
|
|
: configDefaults.view.theme.themes;
|
|
}
|
|
|
|
private _setDimmable(): void {
|
|
const config = this._api.getConfigManager().getConfig();
|
|
this._api
|
|
.getCardElementManager()
|
|
.getElement()
|
|
.toggleAttribute('dimmable', !!config?.view.dim);
|
|
}
|
|
|
|
private _setMinMaxHeight(): void {
|
|
const config = this._api.getConfigManager().getConfig();
|
|
if (config) {
|
|
const card = this._api.getCardElementManager().getElement();
|
|
card.style.setProperty('--advanced-camera-card-height', config.dimensions.height);
|
|
}
|
|
}
|
|
|
|
private _setPerformance(): void {
|
|
const STYLE_DISABLE_MAP = {
|
|
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 mapping = STYLE_DISABLE_MAP[configKey];
|
|
setOrRemoveStyleProperty(
|
|
element,
|
|
!styles[configKey],
|
|
mapping.cssKey,
|
|
mapping.value,
|
|
);
|
|
}
|
|
}
|
|
|
|
private _isAspectRatioEnforced(
|
|
config: AdvancedCameraCardConfig,
|
|
view?: View | null,
|
|
): boolean {
|
|
const aspectRatioMode = config.dimensions.aspect_ratio_mode;
|
|
|
|
// Do not artifically constrain aspect ratio if:
|
|
// - It's fullscreen.
|
|
// - It's in expanded mode.
|
|
// - Aspect ratio enforcement is disabled.
|
|
// - Aspect ratio enforcement is dynamic and it's a media view (i.e. not the
|
|
// gallery) or diagnostics / timeline.
|
|
return !(
|
|
this._api.getFullscreenManager().isInFullscreen() ||
|
|
this._api.getExpandManager().isExpanded() ||
|
|
aspectRatioMode === 'unconstrained' ||
|
|
(aspectRatioMode === 'dynamic' &&
|
|
(!view ||
|
|
view?.isAnyMediaView() ||
|
|
view?.is('timeline') ||
|
|
view?.is('diagnostics')))
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Get the aspect ratio padding required to enforce the aspect ratio (if it is
|
|
* required).
|
|
* @returns A padding percentage.
|
|
*/
|
|
public getAspectRatioStyle(): StyleInfo {
|
|
const config = this._api.getConfigManager().getConfig();
|
|
const view = this._api.getViewManager().getView();
|
|
|
|
if (config) {
|
|
if (!this._isAspectRatioEnforced(config, view)) {
|
|
return aspectRatioToStyle();
|
|
}
|
|
|
|
const aspectRatioMode = config.dimensions.aspect_ratio_mode;
|
|
|
|
const lastKnown = this._api.getMediaLoadedInfoManager().getLastKnown();
|
|
if (lastKnown && aspectRatioMode === 'dynamic') {
|
|
return aspectRatioToStyle({ ratio: [lastKnown.width, lastKnown.height] });
|
|
}
|
|
return aspectRatioToStyle({ ratio: config.dimensions.aspect_ratio });
|
|
}
|
|
return aspectRatioToStyle({ defaultStatic: true });
|
|
}
|
|
}
|