mirror of
https://github.com/Matysh/houseplan-card
synced 2026-09-29 03:09:36 +00:00
DIALOGS: native ha-switch / ha-slider with a hard fallback to plain inputs
Boolean .srcrow checkboxes (14 rows: sun rays, vacuum live position, opening invert/flipH/flipV, marker show-entities/tap-confirm/climate-temp/ is-light/hide-from-plan, space borders/names/lqi + the 4 room-card label flags) and every dialog range slider (9: fill opacities, kiosk icon/font, ripple size, marker size/angle, card font, room opacity, room name/label scale) render through _boolInput/_rangeInput. Each helper picks the native HA element via customElements.get(...) at render time - the ha-* API is undocumented and drifts between HA releases, so the presence check is the only coupling; without the element (old HA, smoke env) the EXISTING input renders unchanged, and both branches feed one handler (change/.checked, input+change/.value). Radios, selects, the decorbar fill flag and the import-dialog floor rows stay native on purpose. New smoke_ha_controls covers both branches with ha-* stubs (two-way value flow); the other 83 smokes keep exercising the fallback, which stays pixel-identical.
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -0,0 +1,86 @@
|
||||
// Native HA controls in dialogs (ha-switch / ha-slider) + the fallback.
|
||||
// The demo env has no HA frontend, so every OTHER smoke already runs the
|
||||
// fallback branch (plain input[type=checkbox|range]). Here we check both:
|
||||
// 1) fallback: without ha-* the dialogs render the classic inputs;
|
||||
// 2) ha branch: after registering lightweight ha-switch/ha-slider stubs
|
||||
// (checked/value + `change`/`input`, the real contract of the HA ones)
|
||||
// a re-opened dialog renders them, values flow BOTH ways.
|
||||
import { launch, checkAll, finish } from './serve.mjs';
|
||||
const { page, browser } = await launch();
|
||||
const res = await page.evaluate(async () => {
|
||||
const out = {};
|
||||
const c = window.__card;
|
||||
const sr = () => c.shadowRoot || c.renderRoot;
|
||||
|
||||
// --- 1) fallback branch: no ha-* registered -> classic inputs ----------
|
||||
c._openMarkerDialog(c._devices[0]); await c.updateComplete;
|
||||
out.fallbackCheckbox = !!sr().querySelector('.dialog .srcrow input[type=checkbox]');
|
||||
out.fallbackRange = !!sr().querySelector('.dialog input[type=range]');
|
||||
out.fallbackNoHaSwitch = !sr().querySelector('.dialog ha-switch');
|
||||
c._markerDialog = null; await c.updateComplete;
|
||||
|
||||
// --- 2) register stubs mimicking the HA contract -----------------------
|
||||
customElements.define('ha-switch', class extends HTMLElement {
|
||||
#on = false;
|
||||
get checked() { return this.#on; }
|
||||
set checked(v) { this.#on = !!v; }
|
||||
get disabled() { return this.hasAttribute('disabled'); }
|
||||
set disabled(v) { v ? this.setAttribute('disabled', '') : this.removeAttribute('disabled'); }
|
||||
});
|
||||
customElements.define('ha-slider', class extends HTMLElement {
|
||||
#v = 0;
|
||||
get value() { return this.#v; }
|
||||
set value(v) { this.#v = Number(v); }
|
||||
set min(_) {} set max(_) {} set step(_) {}
|
||||
});
|
||||
|
||||
c._openMarkerDialog(c._devices[0]); await c.updateComplete;
|
||||
const switches = [...sr().querySelectorAll('.dialog ha-switch')];
|
||||
out.haSwitchRendered = switches.length >= 2; // is_light + hide-from-plan at minimum
|
||||
out.haNoPlainCheckbox = !sr().querySelector('.dialog .srcrow input[type=checkbox]');
|
||||
const sliders = [...sr().querySelectorAll('.dialog ha-slider')];
|
||||
out.haSliderRendered = sliders.length >= 2; // size + angle
|
||||
|
||||
// --- 3) card -> control: state is pushed into the element --------------
|
||||
c._markerDialog = { ...c._markerDialog, isLight: true, hideFromPlan: false };
|
||||
await c.updateComplete;
|
||||
out.downstreamChecked = [...sr().querySelectorAll('.dialog ha-switch')].some((el) => el.checked === true);
|
||||
|
||||
// --- 4) control -> card: change with .checked lands in the dialog ------
|
||||
const before = c._markerDialog.hideFromPlan;
|
||||
const sw = [...sr().querySelectorAll('.dialog ha-switch')].find((el) => el.checked === before);
|
||||
sw.checked = !before;
|
||||
sw.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
await c.updateComplete;
|
||||
out.upstreamChecked = c._markerDialog.hideFromPlan === !before || c._markerDialog.isLight === !before
|
||||
|| c._markerDialog.useClimateTemp === !before || c._markerDialog.tapConfirm === !before;
|
||||
|
||||
// --- 5) slider both ways ----------------------------------------------
|
||||
const sizeBefore = c._markerDialog.size;
|
||||
const sl = [...sr().querySelectorAll('.dialog ha-slider')].find((el) => Number(el.value) === Number(sizeBefore));
|
||||
out.downstreamValue = !!sl;
|
||||
sl.value = 2;
|
||||
sl.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
await c.updateComplete;
|
||||
out.upstreamValue = c._markerDialog.size === 2;
|
||||
// `change` (fired on release, HA-version dependent) reaches the same handler
|
||||
sl.value = 2.5;
|
||||
sl.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
await c.updateComplete;
|
||||
out.upstreamValueChangeEvt = c._markerDialog.size === 2.5;
|
||||
|
||||
c._markerDialog = null; await c.updateComplete;
|
||||
|
||||
// --- 6) general settings: opacity sliders take the ha branch too -------
|
||||
c._openSettingsDialog(); await c.updateComplete;
|
||||
out.gsHaSliders = sr().querySelectorAll('.dialog ha-slider').length >= 9;
|
||||
const gsl = sr().querySelector('.dialog ha-slider');
|
||||
gsl.value = 55;
|
||||
gsl.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
await c.updateComplete;
|
||||
out.gsUpstream = Math.round((Object.values(c._settingsDialog.colors)[0].a) * 100) === 55;
|
||||
c._settingsDialog = null; await c.updateComplete;
|
||||
return out;
|
||||
});
|
||||
checkAll(res, {});
|
||||
await finish(browser, res);
|
||||
File diff suppressed because one or more lines are too long
Vendored
+33
-52
File diff suppressed because one or more lines are too long
+53
-48
@@ -4596,6 +4596,34 @@ class HouseplanCard extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
/** Boolean toggle for dialog rows: the native ha-switch when the HA
|
||||
* frontend provides it, the classic checkbox otherwise (older HA, the
|
||||
* smoke env). The ha-* API is undocumented and shifts between HA
|
||||
* releases, so the presence check is the ONLY coupling: both branches
|
||||
* fire `change` and both are read back via `.checked` off the event
|
||||
* target - one handler, two renderers. */
|
||||
private _boolInput(checked: boolean, onChange: (v: boolean) => void, disabled = false): TemplateResult {
|
||||
const h = (e: Event) => onChange(!!(e.target as HTMLInputElement).checked);
|
||||
return customElements.get('ha-switch')
|
||||
? html`<ha-switch .checked=${checked} .disabled=${disabled} @change=${h}></ha-switch>`
|
||||
: html`<input type="checkbox" .checked=${checked} ?disabled=${disabled} @change=${h} />`;
|
||||
}
|
||||
|
||||
/** Range slider for dialog rows: ha-slider when available, plain
|
||||
* input[type=range] otherwise. Same fallback contract as _boolInput;
|
||||
* ha-slider emits `input` while dragging and `change` on release
|
||||
* (which of the two carries the final value differs between HA
|
||||
* versions - listen to both, the handler is idempotent). */
|
||||
private _rangeInput(min: number, max: number, step: number, value: number, onInput: (v: number) => void): TemplateResult {
|
||||
const h = (e: Event) => {
|
||||
const n = Number((e.target as HTMLInputElement).value);
|
||||
if (Number.isFinite(n)) onInput(n);
|
||||
};
|
||||
return customElements.get('ha-slider')
|
||||
? html`<ha-slider .min=${min} .max=${max} .step=${step} .value=${value} @input=${h} @change=${h}></ha-slider>`
|
||||
: html`<input type="range" min=${min} max=${max} step=${step} .value=${String(value)} @input=${h} />`;
|
||||
}
|
||||
|
||||
private _renderColorRow(key: keyof FillColors, labelKey: string): TemplateResult {
|
||||
const d = this._settingsDialog!;
|
||||
const v = d.colors[key];
|
||||
@@ -4603,8 +4631,7 @@ class HouseplanCard extends LitElement {
|
||||
<span class="gsl">${this._t(labelKey as any)}</span>
|
||||
<input type="color" .value=${v.c}
|
||||
@input=${(e: Event) => this._setFillColor(key, { c: (e.target as HTMLInputElement).value })} />
|
||||
<input type="range" min="0" max="100" .value=${String(Math.round(v.a * 100))}
|
||||
@input=${(e: Event) => this._setFillColor(key, { a: Number((e.target as HTMLInputElement).value) / 100 })} />
|
||||
${this._rangeInput(0, 100, 1, Math.round(v.a * 100), (n) => this._setFillColor(key, { a: n / 100 }))}
|
||||
<span class="opv">${Math.round(v.a * 100)}%</span>
|
||||
</div>`;
|
||||
}
|
||||
@@ -4786,9 +4813,8 @@ class HouseplanCard extends LitElement {
|
||||
</div>
|
||||
</div>
|
||||
<label class="srcrow">
|
||||
<input type="checkbox" .checked=${this._settingsDialog!.sunRays}
|
||||
@change=${(e: Event) =>
|
||||
(this._settingsDialog = { ...this._settingsDialog!, sunRays: (e.target as HTMLInputElement).checked })} />
|
||||
${this._boolInput(this._settingsDialog!.sunRays, (v) =>
|
||||
(this._settingsDialog = { ...this._settingsDialog!, sunRays: v }))}
|
||||
<span>${this._t('gs.sun_rays')}</span>
|
||||
</label>
|
||||
<div class="colorrow gsrow">
|
||||
@@ -4942,8 +4968,7 @@ class HouseplanCard extends LitElement {
|
||||
const k = this._kioskScale;
|
||||
const row = (key: 'icon' | 'font', label: string) => html`<label>${label}</label>
|
||||
<div class="colorrow">
|
||||
<input type="range" min="50" max="300" step="5" .value=${String(Math.round(k[key] * 100))}
|
||||
@input=${(e: Event) => this._saveKioskScale({ [key]: Number((e.target as HTMLInputElement).value) / 100 })} />
|
||||
${this._rangeInput(50, 300, 5, Math.round(k[key] * 100), (n) => this._saveKioskScale({ [key]: n / 100 }))}
|
||||
<span class="opv">${Math.round(k[key] * 100)}%</span>
|
||||
</div>`;
|
||||
return html`<div class="menuwrap dialogwrap" @click=${() => (this._kioskDialog = false)}>
|
||||
@@ -5466,8 +5491,7 @@ class HouseplanCard extends LitElement {
|
||||
<button class="btn ghostbtn" @click=${() => this._vacStartFit(dev)}>${this._t('vac.fit')}</button>
|
||||
</div>
|
||||
<label class="srcrow">
|
||||
<input type="checkbox" .checked=${v.live !== false}
|
||||
@change=${(e: Event) => setVac({ live: (e.target as HTMLInputElement).checked ? null : false })} />
|
||||
${this._boolInput(v.live !== false, (on) => setVac({ live: on ? null : false }))}
|
||||
<span>${this._t('vac.live')}</span>
|
||||
</label>
|
||||
<label>${this._t('vac.trail')}</label>
|
||||
@@ -6556,8 +6580,7 @@ class HouseplanCard extends LitElement {
|
||||
<label>${this._t('opening.contact_label')}</label>
|
||||
${opt(this._contactCandidates(), d.contact, (v) => (this._openingDialog = { ...d, contact: v }))}
|
||||
${d.contact
|
||||
? html`<label class="srcrow"><input type="checkbox" .checked=${d.invert}
|
||||
@change=${(e: Event) => (this._openingDialog = { ...d, invert: (e.target as HTMLInputElement).checked })} />
|
||||
? html`<label class="srcrow">${this._boolInput(d.invert, (v) => (this._openingDialog = { ...d, invert: v }))}
|
||||
<span>${this._t('opening.invert')}</span></label>`
|
||||
: nothing}
|
||||
|
||||
@@ -6566,11 +6589,9 @@ class HouseplanCard extends LitElement {
|
||||
${opt(this._lockCandidates(), d.lock, (v) => (this._openingDialog = { ...d, lock: v }))}`
|
||||
: nothing}
|
||||
|
||||
<label class="srcrow"><input type="checkbox" .checked=${d.flipH}
|
||||
@change=${(e: Event) => (this._openingDialog = { ...d, flipH: (e.target as HTMLInputElement).checked })} />
|
||||
<label class="srcrow">${this._boolInput(d.flipH, (v) => (this._openingDialog = { ...d, flipH: v }))}
|
||||
<span>${this._t('opening.flip_h')}</span></label>
|
||||
<label class="srcrow"><input type="checkbox" .checked=${d.flipV}
|
||||
@change=${(e: Event) => (this._openingDialog = { ...d, flipV: (e.target as HTMLInputElement).checked })} />
|
||||
<label class="srcrow">${this._boolInput(d.flipV, (v) => (this._openingDialog = { ...d, flipV: v }))}
|
||||
<span>${this._t('opening.flip_v')}</span></label>
|
||||
</div>
|
||||
<div class="row">
|
||||
@@ -6874,9 +6895,8 @@ class HouseplanCard extends LitElement {
|
||||
<span>${this._t('marker.from_ha_option')}</span>
|
||||
</label>
|
||||
<label class="srcrow inline entcheck" title=${this._t('marker.show_entities_tip')}>
|
||||
<input type="checkbox" .checked=${d.showEntities}
|
||||
?disabled=${d.bindingMode !== 'ha'}
|
||||
@change=${(e: Event) => (this._markerDialog = { ...d, showEntities: (e.target as HTMLInputElement).checked })} />
|
||||
${this._boolInput(d.showEntities, (v) => (this._markerDialog = { ...d, showEntities: v }),
|
||||
d.bindingMode !== 'ha')}
|
||||
<span>${this._t('marker.show_entities')}</span>
|
||||
</label>
|
||||
</div>
|
||||
@@ -6956,8 +6976,7 @@ class HouseplanCard extends LitElement {
|
||||
: nothing}
|
||||
${d.tapAction === 'run' || d.tapAction === 'toggle' || (!d.tapAction && d.defaultTap === 'toggle')
|
||||
? html`<label class="srcrow" title=${this._t('marker.tap_confirm_tip')}>
|
||||
<input type="checkbox" .checked=${d.tapConfirm}
|
||||
@change=${(e: Event) => (this._markerDialog = { ...d, tapConfirm: (e.target as HTMLInputElement).checked })} />
|
||||
${this._boolInput(d.tapConfirm, (v) => (this._markerDialog = { ...d, tapConfirm: v }))}
|
||||
<span>${this._t('marker.tap_confirm')}</span>
|
||||
</label>`
|
||||
: nothing}
|
||||
@@ -6997,19 +7016,16 @@ class HouseplanCard extends LitElement {
|
||||
|
||||
${this._bindingHasClimate(d.binding)
|
||||
? html`<label class="srcrow climrow" title=${this._t('marker.use_climate_temp_tip')}>
|
||||
<input type="checkbox" .checked=${d.useClimateTemp}
|
||||
@change=${(e: Event) => (this._markerDialog = { ...d, useClimateTemp: (e.target as HTMLInputElement).checked })} />
|
||||
${this._boolInput(d.useClimateTemp, (v) => (this._markerDialog = { ...d, useClimateTemp: v }))}
|
||||
<span>${this._t('marker.use_climate_temp')}</span>
|
||||
</label>`
|
||||
: nothing}
|
||||
<label class="srcrow" title=${this._t('marker.is_light_tip')}>
|
||||
<input type="checkbox" .checked=${d.isLight}
|
||||
@change=${(e: Event) => (this._markerDialog = { ...d, isLight: (e.target as HTMLInputElement).checked })} />
|
||||
${this._boolInput(d.isLight, (v) => (this._markerDialog = { ...d, isLight: v }))}
|
||||
<span>${this._t('marker.is_light')}</span>
|
||||
</label>
|
||||
<label class="srcrow" title=${this._t('marker.hide_tip')}>
|
||||
<input type="checkbox" .checked=${d.hideFromPlan}
|
||||
@change=${(e: Event) => (this._markerDialog = { ...d, hideFromPlan: (e.target as HTMLInputElement).checked })} />
|
||||
${this._boolInput(d.hideFromPlan, (v) => (this._markerDialog = { ...d, hideFromPlan: v }))}
|
||||
<span>${this._t('marker.hide')}</span>
|
||||
</label>
|
||||
<label>${this._t('marker.glow_radius_label')}</label>
|
||||
@@ -7049,20 +7065,17 @@ class HouseplanCard extends LitElement {
|
||||
<input type="color" .value=${d.rippleColor || '#3ea6ff'}
|
||||
@input=${(e: Event) => (this._markerDialog = { ...d, rippleColor: (e.target as HTMLInputElement).value })} />
|
||||
<span class="opl">${this._t('marker.ripple_size')}</span>
|
||||
<input type="range" min="2" max="8" step="0.5" .value=${String(d.rippleSize)}
|
||||
@input=${(e: Event) => (this._markerDialog = { ...d, rippleSize: Number((e.target as HTMLInputElement).value) })} />
|
||||
${this._rangeInput(2, 8, 0.5, d.rippleSize, (n) => (this._markerDialog = { ...d, rippleSize: n }))}
|
||||
<span class="opv">×${d.rippleSize}</span>
|
||||
</div>`
|
||||
: nothing}
|
||||
|
||||
<label>${this._t('marker.size_label')}</label>
|
||||
<div class="colorrow">
|
||||
<input type="range" min="0.5" max="3" step="0.1" .value=${String(d.size)}
|
||||
@input=${(e: Event) => (this._markerDialog = { ...d, size: Number((e.target as HTMLInputElement).value) })} />
|
||||
${this._rangeInput(0.5, 3, 0.1, d.size, (n) => (this._markerDialog = { ...d, size: n }))}
|
||||
<span class="opv">×${d.size.toFixed(1)}</span>
|
||||
<span class="opl">${this._t('marker.angle_label')}</span>
|
||||
<input type="range" min="0" max="350" step="10" .value=${String(d.angle)}
|
||||
@input=${(e: Event) => (this._markerDialog = { ...d, angle: Number((e.target as HTMLInputElement).value) })} />
|
||||
${this._rangeInput(0, 350, 10, d.angle, (n) => (this._markerDialog = { ...d, angle: n }))}
|
||||
<span class="opv">${d.angle}°</span>
|
||||
</div>
|
||||
|
||||
@@ -7173,33 +7186,28 @@ class HouseplanCard extends LitElement {
|
||||
|
||||
<label class="dispsection">${this._t('space.display_section')}</label>
|
||||
<label class="srcrow">
|
||||
<input type="checkbox" .checked=${d.showBorders}
|
||||
@change=${(e: Event) => (this._spaceDialog = { ...d, showBorders: (e.target as HTMLInputElement).checked })} />
|
||||
${this._boolInput(d.showBorders, (v) => (this._spaceDialog = { ...d, showBorders: v }))}
|
||||
<span>${this._t('space.show_borders')}</span>
|
||||
</label>
|
||||
<label class="srcrow">
|
||||
<input type="checkbox" .checked=${d.showNames}
|
||||
@change=${(e: Event) => (this._spaceDialog = { ...d, showNames: (e.target as HTMLInputElement).checked })} />
|
||||
${this._boolInput(d.showNames, (v) => (this._spaceDialog = { ...d, showNames: v }))}
|
||||
<span>${this._t('space.show_names')}</span>
|
||||
</label>
|
||||
<label class="srcrow">
|
||||
<input type="checkbox" .checked=${d.showLqi}
|
||||
@change=${(e: Event) => (this._spaceDialog = { ...d, showLqi: (e.target as HTMLInputElement).checked })} />
|
||||
${this._boolInput(d.showLqi, (v) => (this._spaceDialog = { ...d, showLqi: v }))}
|
||||
<span>${this._t('space.show_lqi')}</span>
|
||||
</label>
|
||||
<label class="dispsection">${this._t('space.roomcard_section')}</label>
|
||||
${([['labelTemp', 'space.label_temp'], ['labelHum', 'space.label_hum'],
|
||||
['labelLqi', 'space.label_lqi'], ['labelLight', 'space.label_light']] as const).map(
|
||||
([f, k]) => html`<label class="srcrow">
|
||||
<input type="checkbox" .checked=${d[f]}
|
||||
@change=${(e: Event) => (this._spaceDialog = { ...d, [f]: (e.target as HTMLInputElement).checked })} />
|
||||
${this._boolInput(d[f], (v) => (this._spaceDialog = { ...d, [f]: v }))}
|
||||
<span>${this._t(k)}</span>
|
||||
</label>`,
|
||||
)}
|
||||
<label>${this._t('space.card_font')}</label>
|
||||
<div class="colorrow gsrow">
|
||||
<input type="range" min="50" max="300" step="5" .value=${String(Math.round(d.cardFontScale * 100))}
|
||||
@input=${(e: Event) => (this._spaceDialog = { ...d, cardFontScale: Number((e.target as HTMLInputElement).value) / 100 })} />
|
||||
${this._rangeInput(50, 300, 5, Math.round(d.cardFontScale * 100), (n) => (this._spaceDialog = { ...d, cardFontScale: n / 100 }))}
|
||||
<span class="opv">${Math.round(d.cardFontScale * 100)}%</span>
|
||||
</div>
|
||||
${this._renderCardPreview(d.cardFontScale, 1, 1)}
|
||||
@@ -7208,8 +7216,7 @@ class HouseplanCard extends LitElement {
|
||||
<input type="color" .value=${d.roomColor}
|
||||
@input=${(e: Event) => (this._spaceDialog = { ...d, roomColor: (e.target as HTMLInputElement).value })} />
|
||||
<span class="opl">${this._t('space.opacity')}</span>
|
||||
<input type="range" min="0" max="100" .value=${String(Math.round(d.roomOpacity * 100))}
|
||||
@input=${(e: Event) => (this._spaceDialog = { ...d, roomOpacity: Number((e.target as HTMLInputElement).value) / 100 })} />
|
||||
${this._rangeInput(0, 100, 1, Math.round(d.roomOpacity * 100), (n) => (this._spaceDialog = { ...d, roomOpacity: n / 100 }))}
|
||||
<span class="opv">${Math.round(d.roomOpacity * 100)}%</span>
|
||||
</div>
|
||||
<label>${this._t('space.bg_mode')}</label>
|
||||
@@ -7444,14 +7451,12 @@ class HouseplanCard extends LitElement {
|
||||
<label class="dispsection">${this._t('room.sizes_section')}</label>
|
||||
<label>${this._t('room.name_scale')}</label>
|
||||
<div class="colorrow gsrow">
|
||||
<input type="range" min="50" max="300" step="5" .value=${String(Math.round(this._roomNameScale * 100))}
|
||||
@input=${(e: Event) => { this._roomNameScale = Number((e.target as HTMLInputElement).value) / 100; this.requestUpdate(); }} />
|
||||
${this._rangeInput(50, 300, 5, Math.round(this._roomNameScale * 100), (n) => { this._roomNameScale = n / 100; this.requestUpdate(); })}
|
||||
<span class="opv">${Math.round(this._roomNameScale * 100)}%</span>
|
||||
</div>
|
||||
<label>${this._t('room.label_scale')}</label>
|
||||
<div class="colorrow gsrow">
|
||||
<input type="range" min="50" max="300" step="5" .value=${String(Math.round(this._roomLabelScale * 100))}
|
||||
@input=${(e: Event) => { this._roomLabelScale = Number((e.target as HTMLInputElement).value) / 100; this.requestUpdate(); }} />
|
||||
${this._rangeInput(50, 300, 5, Math.round(this._roomLabelScale * 100), (n) => { this._roomLabelScale = n / 100; this.requestUpdate(); })}
|
||||
<span class="opv">${Math.round(this._roomLabelScale * 100)}%</span>
|
||||
</div>
|
||||
${this._renderCardPreview(
|
||||
|
||||
@@ -1347,6 +1347,11 @@ export const cardStyles = css`
|
||||
.dialog .body .temprange .tempin { width: 56px; flex: none; padding: var(--sp-2) var(--sp-3); }
|
||||
.dialog .body .colorrow .tempin { width: 72px; flex: none; }
|
||||
.srcrow { flex-wrap: nowrap; }
|
||||
/* native HA controls (rendered only when the HA frontend defines them;
|
||||
old HA and the smoke env keep the plain inputs). ha-switch is taller
|
||||
than a checkbox - cap its footprint so .srcrow keeps its rhythm. */
|
||||
.srcrow ha-switch { flex: none; }
|
||||
.colorrow ha-slider { flex: 1; min-width: 0; }
|
||||
.srcrow > span:first-of-type { white-space: nowrap; }
|
||||
.colorrow .opl { color: var(--hp-muted); font-size: var(--fs-s); }
|
||||
.colorrow .opv { font-size: var(--fs-s); min-width: 34px; text-align: right; }
|
||||
|
||||
Reference in New Issue
Block a user