diff --git a/src/components/editor/panel-images.ts b/src/components/editor/panel-images.ts index ec6d123..7185c2d 100644 --- a/src/components/editor/panel-images.ts +++ b/src/components/editor/panel-images.ts @@ -10,6 +10,7 @@ import Sortable from 'sortablejs'; import { VehicleCardEditor } from '../../editor'; import { ImageConfig, VehicleCardConfig } from '../../types'; import { imageInputChange, handleFilePicked } from '../../utils/editor-image-handler'; +import { Picker } from '../../utils/create'; import editorcss from '../../css/editor.css'; @@ -76,11 +77,98 @@ export class PanelImages extends LitElement { this.toggleUpload()} class="upload-btn"> ${this.editor.hass.localize('ui.components.selectors.image.upload')} + this.toggleSwiperConfig()} class="swiper-btn"> Swiper Config `; return urlInput; } + private _renderSwiperConfig(): TemplateResult { + const image = this.config?.extra_configs?.images_swipe || {}; + const sharedConfig = { + component: this, + configType: 'images_swipe', + }; + + const swiperConfig = [ + { + value: image.max_height || 150, + configValue: 'max_height', + label: 'Max Height (px)', + options: { selector: { number: { min: 100, max: 500, mode: 'slider', step: 1 } } }, + pickerType: 'number' as 'number', + }, + { + value: image.max_width || 450, + configValue: 'max_width', + label: 'Max Width (px)', + options: { selector: { number: { min: 100, max: 500, mode: 'slider', step: 1 } } }, + pickerType: 'number' as 'number', + }, + + { + value: image.delay || 3000, + configValue: 'delay', + label: 'Delay (ms)', + options: { selector: { number: { min: 500, max: 10000, mode: 'slider', step: 50 } } }, + pickerType: 'number' as 'number', + }, + { + value: image.speed || 500, + configValue: 'speed', + label: 'Speed (ms)', + options: { selector: { number: { min: 100, max: 5000, mode: 'slider', step: 50 } } }, + pickerType: 'number' as 'number', + }, + { + value: image.effect || 'slide', + configValue: 'effect', + label: 'Effect', + items: [ + { + value: 'slide', + label: 'Slide', + }, + { + value: 'fade', + label: 'Fade', + }, + { + value: 'coverflow', + label: 'Coverflow', + }, + ], + pickerType: 'attribute' as 'attribute', + }, + ]; + const swiperBooleanConfig = [ + { + value: image.autoplay || false, + configValue: 'autoplay', + label: 'Autoplay', + pickerType: 'selectorBoolean' as 'selectorBoolean', + }, + { + value: image.loop || true, + configValue: 'loop', + label: 'Loop', + pickerType: 'selectorBoolean' as 'selectorBoolean', + }, + ]; + + return html` `; + } + private _imageList(): TemplateResult { if (this._reindexImages) { return html`Loading...`; @@ -141,6 +229,7 @@ export class PanelImages extends LitElement { return html`${dropArea}${imageList}`; } + private _renderDropArea(): TemplateResult { const errorMsg = this.editor.localize('card.common.toastImageError'); @@ -185,8 +274,9 @@ export class PanelImages extends LitElement { protected render(): TemplateResult { const imageList = this._imageList(); const addNewImage = this._renderUploadAddNewImage(); + const swiperConfig = this._renderSwiperConfig(); - const content = html`${imageList}${addNewImage}`; + const content = html`${imageList}${addNewImage} ${swiperConfig}`; return content; } @@ -195,6 +285,25 @@ export class PanelImages extends LitElement { fireEvent(this.editor, 'config-changed', { config: this.config }); } + private toggleSwiperConfig(): void { + const swiperConfig = this.shadowRoot?.querySelector('.image-swiper-config') as HTMLElement; + const imageList = this.shadowRoot?.getElementById('images-list') as HTMLElement; + const swiperBtn = this.shadowRoot?.querySelector('.swiper-btn') as HTMLElement; + const uploadBtn = this.shadowRoot?.querySelector('.upload-btn') as HTMLElement; + const isHidden = swiperConfig?.style.display === 'none'; + if (isHidden) { + swiperConfig.style.display = 'block'; + imageList.style.display = 'none'; + uploadBtn.style.visibility = 'hidden'; + swiperBtn.innerHTML = 'Cancel'; + } else { + swiperConfig.style.display = 'none'; + uploadBtn.style.visibility = 'visible'; + imageList.style.display = 'block'; + swiperBtn.innerHTML = 'Swiper Config'; + } + } + private toggleUpload(): void { const dropArea = this.shadowRoot?.getElementById('drop-area') as HTMLElement; const imageList = this.shadowRoot?.getElementById('images-list') as HTMLElement; @@ -210,6 +319,17 @@ export class PanelImages extends LitElement { addImageBtn.innerHTML = 'Add Image'; } } + + private generateItemPicker(config: any, wrapperClass = 'item-content'): TemplateResult { + return html` +
+ ${Picker({ + ...config, + })} +
+ `; + } + private _handleDragOver(event: DragEvent) { event.preventDefault(); this.isDragging = true; @@ -218,6 +338,7 @@ export class PanelImages extends LitElement { private _handleDragLeave() { this.isDragging = false; } + private _handleDrop(event: DragEvent): void { event.preventDefault(); this.isDragging = false; @@ -353,4 +474,28 @@ export class PanelImages extends LitElement { this._newImageUrl = ''; this._debouncedConfigChanged(); } + + _valueChanged(ev: any): void { + ev.stopPropagation(); + if (!this.config) return; + + const target = ev.target; + const configValue = target.configValue; + const configType = target.configType; + let newValue: any = ev.detail.value; + + const updates: Partial = {}; + + if (configType === 'images_swipe') { + let imagesSwipe = this.config.extra_configs?.images_swipe || {}; + imagesSwipe = { ...imagesSwipe, [configValue]: newValue }; + updates.extra_configs = { ...this.config.extra_configs, images_swipe: imagesSwipe }; + this.config = { ...this.config, ...updates }; + this._debouncedConfigChanged(); + } else { + updates[configValue] = newValue; + this.config = { ...this.config, ...updates }; + this._debouncedConfigChanged(); + } + } } diff --git a/src/css/styles.css b/src/css/styles.css index 3853531..bd6c3ec 100644 --- a/src/css/styles.css +++ b/src/css/styles.css @@ -238,7 +238,6 @@ header h1 { } } - .grid-container { display: grid; grid-template-columns: repeat(2, minmax(140px, 1fr)); @@ -251,13 +250,9 @@ header h1 { .grid-item { display: flex; position: relative; - gap: var(--vic-card-padding); padding: var(--vic-gutter-gap) var(--vic-card-padding); - height: 100%; - flex-direction: row; - align-items: center; background: var(--secondary-background-color, var(--card-background-color, #fff)); - box-shadow: var(--ha-card-box-shadow, none); + box-shadow: var(--ha-card-box-shadow); box-sizing: border-box; border-radius: var(--ha-card-border-radius, 12px); border-width: var(--ha-card-border-width, 1px); @@ -266,18 +261,22 @@ header h1 { transition: all 0.3s ease-out; opacity: 1; cursor: pointer; +} - &:hover { - box-shadow: 0 0 8px 0 rgba(0, 0, 0, 0.3); - } +.grid-item:hover { + box-shadow: 0 0 8px 0 rgba(0, 0, 0, 0.3); } -/* @media screen and (max-width: 768px) { - .grid-item { - padding: 8px; - gap: 0.5rem; - } -} */ + +.grid-item .click-container { + display: flex; + height: 100%; + flex-direction: row; + align-items: center; + gap: var(--vic-card-padding); +} + + .grid-item .item-notify { display: flex; position: absolute; @@ -293,12 +292,6 @@ header h1 { display: none; } -/* .grid-item .item-icon { - display: inline-block; - border-radius: 50% 50%; - background-color: var(--chip-background-color); - padding: 0.5rem; -} */ .grid-item .item-icon { position: relative; @@ -326,23 +319,23 @@ header h1 { flex-direction: column; min-width: 0; overflow: hidden; +} - .primary { - font-weight: 500; - font-size: 1rem; - white-space: nowrap; - position: relative; - text-overflow: ellipsis; - overflow: hidden; - } +.grid-item .item-content .primary { + font-weight: 500; + font-size: 1rem; + white-space: nowrap; + position: relative; + text-overflow: ellipsis; + overflow: hidden; +} - .secondary { - color: var(--secondary-text-color); - /* text-transform: capitalize; */ - letter-spacing: 0.5px; - font-size: smaller; - text-wrap: nowrap; - } +.grid-item .item-content .secondary { + color: var(--secondary-text-color); + /* text-transform: capitalize; */ + letter-spacing: 0.5px; + font-size: smaller; + text-wrap: nowrap; } .primary.title-wrap { @@ -515,6 +508,7 @@ header h1 { transition: all 0.3s ease-out; /* margin-bottom: 1rem; */ position: relative; + overflow: hidden; } .default-card:not(:first-child) { @@ -701,10 +695,10 @@ dialog::backdrop { opacity: 0.5; cursor: pointer; transition: opacity 0.3s; +} - &:hover { - opacity: 1; - } +.tyre-toggle-btn:hover { + opacity: 1; } /* TYRE WRAP ROTATED */ @@ -718,13 +712,13 @@ dialog::backdrop { .tyre-wrapper .background { position: absolute; - width: 100%; - height: 100%; + width: var(--vic-tire-size, 100%); + height: var(--vic-tire-size, 100%); z-index: 0; - top: 50%; - left: 50%; + top: var(--vic-tire-top, 50%); + left: var(--vic-tire-left, 50%); transform: translate(-50%, -50%); - background-size: cover; + background-size: contain; background-repeat: no-repeat; overflow: hidden; filter: drop-shadow(2px 4px 1rem #000000d8); @@ -732,8 +726,8 @@ dialog::backdrop { .tyre-wrapper .tyre-box { position: absolute; - width: 30%; - height: 60%; + width: 35%; + height: 50%; z-index: 1; display: flex; align-items: center; @@ -742,6 +736,7 @@ dialog::backdrop { gap: 0.5rem; text-shadow: 0 1px 0 rgba(255, 255, 255, 0.3); transition: all 400ms cubic-bezier(0.3, 0.0, 0.8, 0.15); + scale: var(--vic-tire-value-size); } .tyre-value { @@ -753,11 +748,11 @@ dialog::backdrop { .tyre-name { color: var(--secondary-text-color); - text-align: center; + text-align: left; margin: 0; text-transform: uppercase; letter-spacing: 1.5px; - text-wrap: pretty; + text-wrap: nowrap; } .tyre-info { diff --git a/src/editor.ts b/src/editor.ts index 49e1537..444f7c2 100644 --- a/src/editor.ts +++ b/src/editor.ts @@ -27,6 +27,7 @@ import { uploadImage } from './utils/editor-image-handler'; import { handleFirstUpdated, deepMerge } from './utils/ha-helpers'; import { compareVersions } from './utils/helpers'; import { loadHaComponents, stickyPreview } from './utils/loader'; +import { Create } from './utils'; // Import the custom card components import './components/editor'; @@ -42,14 +43,15 @@ export class VehicleCardEditor extends LitElement implements LovelaceCardEditor @property() private baseCardTypes: CardTypeConfig[] = []; @state() private _activeSubcardType: string | null = null; - @state() private _confirmDeleteType: string | null = null; @state() private _yamlConfig: { [key: string]: any } = {}; + @state() private _confirmDeleteType: string | null = null; @state() private _customBtns: { [key: string]: BaseButtonConfig } = {}; @state() private _selectedLanguage: string = 'system'; @state() _latestRelease: string = ''; @state() private _visiblePanel: Set = new Set(); @state() private _newCardType: Map = new Map(); + @state() private _isTirePreview: boolean = false; @query('panel-images') private _panelImages!: PanelImages; @@ -73,9 +75,9 @@ export class VehicleCardEditor extends LitElement implements LovelaceCardEditor } private _cleanConfig(): void { - if (['btn_preview', 'card_preview'].some((key) => this._config[key])) { + if (['btn_preview', 'card_preview', 'tire_preview'].some((key) => this._config[key])) { console.log('Cleaning config of preview keys'); - this._config = { ...this._config, btn_preview: null, card_preview: null }; + this._config = { ...this._config, btn_preview: null, card_preview: null, tire_preview: null }; this.configChanged(); } } @@ -90,7 +92,7 @@ export class VehicleCardEditor extends LitElement implements LovelaceCardEditor protected updated(changedProperties: PropertyValues): void { super.updated(changedProperties); - if (!this._btnPreview && !this._cardPreview) { + if (!this._btnPreview && !this._cardPreview && !this._isTirePreview) { this._cleanConfig(); } } @@ -741,13 +743,13 @@ export class VehicleCardEditor extends LitElement implements LovelaceCardEditor
- ${this._config.extra_configs?.tire_background - ? html` this._removeTireBackground()}> Remove image ` + ${this._config.extra_configs?.tire_card_custom.background + ? html` this._removeTireBackground()}> Use Defaut image ` : html` this.shadowRoot?.getElementById('file-upload-new')?.click()}> Upload image @@ -758,10 +760,63 @@ export class VehicleCardEditor extends LitElement implements LovelaceCardEditor @change=${(ev: any) => this._handleTireBackgroundUpload(ev)} accept="image/*" />`} + this._toggleTirePreview()}> + ${this._isTirePreview ? 'Close preview' : 'Preview'}
`; - return this.panelTemplate('customTireBackground', 'customTireBackground', 'mdi:car-tire-alert', urlInput); + const tireCard = this._config.extra_configs?.tire_card_custom || {}; + + const imageSizeDirection = [ + { + value: tireCard.image_size || 100, + label: 'Base image size', + configValue: 'image_size', + pickerType: 'number' as 'number', + options: { selector: { number: { max: 200, min: 0, mode: 'slider', step: 1 } } }, + }, + { + value: tireCard.value_size || 100, + label: 'Name & Value size', + configValue: 'value_size', + pickerType: 'number' as 'number', + options: { selector: { number: { max: 150, min: 50, mode: 'slider', step: 1 } } }, + }, + { + value: tireCard.top || 50, + label: `${tireCard.horizontal ? 'Horizontal' : 'Vertical'} position`, + configValue: 'top', + pickerType: 'number' as 'number', + options: { selector: { number: { max: 100, min: 0, mode: 'slider', step: 1 } } }, + }, + { + value: tireCard.left || 50, + label: `${tireCard.horizontal ? 'Vertical' : 'Horizontal'} position`, + configValue: 'left', + pickerType: 'number' as 'number', + options: { selector: { number: { max: 100, min: 0, mode: 'slider', step: 1 } } }, + }, + { + value: tireCard.horizontal || false, + label: 'Horizontal layout', + configValue: 'horizontal', + pickerType: 'selectorBoolean' as 'selectorBoolean', + }, + ]; + + const imageSizeWrapper = html`
+ ${imageSizeDirection.map((config) => + this.generateItemPicker({ ...config, configIndex: 'extra_configs', configType: 'tire_card_custom' }) + )} + this.resetTireImageSizes()} + >Reset +
`; + + const content = html`
${urlInput} ${imageSizeWrapper}
`; + + return this.panelTemplate('customTireBackground', 'customTireBackground', 'mdi:car-tire-alert', content); } /* ---------------------------- TEMPLATE HELPERS ---------------------------- */ @@ -833,8 +888,37 @@ export class VehicleCardEditor extends LitElement implements LovelaceCardEditor `; } + private generateItemPicker(config: any, wrapperClass = 'item-content'): TemplateResult { + return html` +
+ ${Create.Picker({ + ...config, + component: this, + })} +
+ `; + } + /* ----------------------------- EVENT HANDLERS ----------------------------- */ + private resetTireImageSizes(): void { + this._config = { + ...this._config, + extra_configs: { + ...this._config.extra_configs, + tire_card_custom: { + ...this._config.extra_configs?.tire_card_custom, + image_size: 100, + value_size: 100, + top: 50, + left: 50, + horizontal: false, + }, + }, + }; + this.configChanged(); + } + private async _handleTireBackgroundUpload(ev: any): Promise { if (!ev.target.files || ev.target.files.length === 0) { return; @@ -843,7 +927,13 @@ export class VehicleCardEditor extends LitElement implements LovelaceCardEditor const file = ev.target.files[0]; const url = await uploadImage(this.hass, file); if (url) { - this._config = { ...this._config, extra_configs: { ...this._config.extra_configs, tire_background: url } }; + this._config = { + ...this._config, + extra_configs: { + ...this._config.extra_configs, + tire_card_custom: { ...this._config.extra_configs?.tire_card_custom, background: url }, + }, + }; this.configChanged(); } else { return; @@ -851,7 +941,13 @@ export class VehicleCardEditor extends LitElement implements LovelaceCardEditor } private _removeTireBackground(): void { - this._config = { ...this._config, extra_configs: { ...this._config.extra_configs, tire_background: '' } }; + this._config = { + ...this._config, + extra_configs: { + ...this._config.extra_configs, + tire_card_custom: { ...this._config.extra_configs?.tire_card_custom, background: '' }, + }, + }; this.configChanged(); } @@ -1169,6 +1265,7 @@ export class VehicleCardEditor extends LitElement implements LovelaceCardEditor const target = ev.target; const configValue = target.configValue; const configBtnType = target.configBtnType; + const configIndex = target.configIndex; let newValue: any = target.value; const updates: Partial = {}; @@ -1204,6 +1301,16 @@ export class VehicleCardEditor extends LitElement implements LovelaceCardEditor [key]: parseInt(newValue) || newValue, }; console.log('Button grid config changed:', key, newValue); + } else if (configIndex === 'extra_configs') { + newValue = ev.detail.value; + const key = configValue as keyof VehicleCardConfig['extra_configs']['tire_card_custom']; + updates.extra_configs = { + ...this._config.extra_configs, + tire_card_custom: { + ...this._config.extra_configs?.tire_card_custom, + [key]: parseInt(newValue) || newValue, + }, + }; } else { newValue = target.checked !== undefined ? target.checked : ev.detail.value; updates[configValue] = newValue; @@ -1346,6 +1453,46 @@ export class VehicleCardEditor extends LitElement implements LovelaceCardEditor } } + public _toggleTirePreview(): void { + if (this._cardPreview) { + this._dispatchCardEvent('close_card_preview'); + this._cardPreview = false; + } + + if (this._isTirePreview) { + console.log('Closing tire preview'); + if (this._config) { + this._config = { + ...this._config, + tire_preview: null, + }; + } + + this._isTirePreview = false; + this.configChanged(); + setTimeout(() => { + this._dispatchCardEvent('close_preview'); + }, 50); + } else { + let tireConfig = this._config.extra_configs?.tire_card_custom; + console.log('Setting tire preview'); + if (this._config) { + this._config = { + ...this._config, + tire_preview: { + ...tireConfig, + }, + }; + } + + this._isTirePreview = true; + this.configChanged(); + setTimeout(() => { + this._dispatchCardEvent('toggle_preview_tire'); + }, 50); + } + } + private _closeSubCardEditor(card: CardTypeConfig): void { const resetState = () => { this._activeSubcardType = null; diff --git a/src/types/config.ts b/src/types/config.ts index 2231016..a5c4b8b 100644 --- a/src/types/config.ts +++ b/src/types/config.ts @@ -130,7 +130,23 @@ export interface ShowOptionsConfig extends VehicleCardConfig { } export type ExtraConfigs = { - tire_background: string; + tire_card_custom: { + background: string; + horizontal: boolean; + image_size: number; + value_size: number; + top: number; + left: number; + }; + images_swipe: { + max_height: number; + max_width: number; + autoplay: boolean; + loop: boolean; + delay: number; + speed: number; + effect: 'slide' | 'fade' | 'coverflow'; + }; }; export interface VehicleCardConfig extends LovelaceCardConfig { @@ -216,7 +232,23 @@ export const defaultConfig: Partial = { mode: THEME_MODE.Auto, }, extra_configs: { - tire_background: '', + tire_card_custom: { + background: '', + horizontal: false, + image_size: 100, + value_size: 100, + top: 50, + left: 50, + }, + images_swipe: { + max_height: 150, + max_width: 450, + autoplay: false, + loop: true, + delay: 5000, + speed: 500, + effect: 'slide', + }, }, button_grid: { use_swiper: false, diff --git a/src/vehicle-info-card.ts b/src/vehicle-info-card.ts index 0dd3752..fd9fc05 100644 --- a/src/vehicle-info-card.ts +++ b/src/vehicle-info-card.ts @@ -13,6 +13,7 @@ import { import { LitElement, html, TemplateResult, PropertyValues, CSSResultGroup, nothing } from 'lit'; import { customElement, property, state, query } from 'lit/decorators.js'; import { classMap } from 'lit/directives/class-map.js'; +import { styleMap } from 'lit-html/directives/style-map.js'; import './components/cards'; import { VehicleButtons } from './components/cards'; @@ -55,14 +56,14 @@ export class VehicleCard extends LitElement implements LovelaceCard { @state() public _entityNotFound: boolean = false; // Active card type - @state() private _currentCardType: string | null = null; + @state() public _currentCardType: string | null = null; @state() private _activeSubCard: Set = new Set(); @state() private _mapPopupLovelace: LovelaceCardConfig[] = []; @state() private chargingInfoVisible!: boolean; @state() private isTyreHorizontal!: boolean; // Preview states - @state() private _currentPreviewType: 'button' | 'card' | null = null; + @state() private _currentPreviewType: 'button' | 'card' | 'tire' | null = null; @state() private _cardPreviewElement: LovelaceCardConfig[] = []; @state() private _buttonEntityPreview: Partial = {}; @@ -161,9 +162,6 @@ export class VehicleCard extends LitElement implements LovelaceCard { protected updated(changedProps: PropertyValues): void { super.updated(changedProps); - if (changedProps.has('_buttonReady')) { - console.log('Button ready:', this._buttonReady); - } if ( changedProps.has('activeCardType') && this._currentCardType !== 'mapDialog' && @@ -293,6 +291,8 @@ export class VehicleCard extends LitElement implements LovelaceCard { this._currentPreviewType = 'card'; } else if (!this._currentPreviewType && this.config?.btn_preview) { this._currentPreviewType = 'button'; + } else if (!this._currentPreviewType && this.config?.tire_preview) { + this._currentPreviewType = 'tire'; } if (this._currentPreviewType !== null) { @@ -302,7 +302,7 @@ export class VehicleCard extends LitElement implements LovelaceCard { } } - private async _configurePreview(cardType: 'button' | 'card' | null): Promise { + private async _configurePreview(cardType: 'button' | 'card' | 'tire' | null): Promise { if (!cardType && !this.isEditorPreview) return; let cardConfig: LovelaceCardConfig[] | BaseButtonConfig = []; @@ -321,11 +321,14 @@ export class VehicleCard extends LitElement implements LovelaceCard { cardElement = await createCardElement(this._hass, cardConfig as LovelaceCardConfig[]); this._cardPreviewElement = cardElement; break; + case 'tire': + this._currentPreviewType = 'tire'; + break; default: return; } - if (!cardElement) { + if (cardType === null) { this._resetCardPreview(); // Reset preview return; } @@ -394,6 +397,7 @@ export class VehicleCard extends LitElement implements LovelaceCard { const typeMap = { button: this._renderBtnPreview(this._buttonEntityPreview as CustomButtonEntity), card: html`${this._cardPreviewElement}`, + tire: this._renderDefaultTyreCard(), }; return typeMap[type]; @@ -808,9 +812,22 @@ export class VehicleCard extends LitElement implements LovelaceCard { } private _renderDefaultTyreCard(): TemplateResult { - const customTyreBg = this.config.extra_configs?.tire_background - ? this.config.extra_configs.tire_background - : IMG.tyreBg; + const tireConfig = this.config.extra_configs.tire_card_custom; + const customTyreBg = tireConfig?.background || IMG.tyreBg; + const isHorizontal = tireConfig?.horizontal ?? false; + const tireImageSize = tireConfig?.image_size ?? 100; + const tireValueSize = tireConfig?.value_size ?? 100; + const tireTop = tireConfig?.top ?? 50; + const tireLeft = tireConfig?.left ?? 50; + + const sizeStyle = { + '--vic-tire-top': `${tireTop}%`, + '--vic-tire-left': `${tireLeft}%`, + '--vic-tire-size': `${tireImageSize}%`, + '--vic-tire-value-size': tireValueSize / 100, + }; + const directionClass = isHorizontal ? 'rotated' : ''; + const lang = this.userLang; const isPressureWarning = this.getBooleanState(this.vehicleEntities.tirePressureWarning?.entity_id); @@ -821,22 +838,17 @@ export class VehicleCard extends LitElement implements LovelaceCard { const tyreInfo = isPressureWarning ? tireWarningProblem : tireWarningOk; const infoClass = isPressureWarning ? 'warning' : ''; - const isHorizontal = this.isTyreHorizontal ? 'rotated' : ''; - const toggleHorizontal = () => { - this.isTyreHorizontal = !this.isTyreHorizontal; - }; - return html`
${tireCardTitle}
-
+
this.toggleTireDirection(ev)}>
-
+
${DataKeys.tyrePressures(lang).map( (tyre) => - html`
+ html`
${this.getStateDisplay(this.vehicleEntities[tyre.key]?.entity_id)} ${tyre.name}
` @@ -849,6 +861,21 @@ export class VehicleCard extends LitElement implements LovelaceCard { `; } + private toggleTireDirection(ev: Event): void { + ev.stopPropagation(); + const target = ev.target as HTMLElement; + const tyreWrapper = target.closest('.default-card')?.querySelector('.tyre-wrapper'); + const tyreBoxex = tyreWrapper?.querySelectorAll('.tyre-box'); + if (!tyreWrapper || !tyreBoxex) return; + + const isHorizontal = tyreWrapper.classList.contains('rotated'); + + tyreWrapper.classList.toggle('rotated', !isHorizontal); + tyreBoxex.forEach((el) => { + el.classList.toggle('rotated', !isHorizontal); + }); + } + private _renderServiceControl(): TemplateResult | void { const hass = this._hass; const serviceControl = this.config.services;