periodic push

This commit is contained in:
2023-06-29 16:14:36 +02:00
parent c948b95622
commit 831f676068
267 changed files with 17705 additions and 10864 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,158 +0,0 @@
import {cssData} from './styles.js?v=1.3.0';
import ThermostatUI from './thermostat_card.lib.js?v=1.3.0';
console.info("%c Thermostat Card \n%c Version 1.3.0 ", "color: orange; font-weight: bold; background: black", "color: white; font-weight: bold; background: dimgray");
class ThermostatCard extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
}
set hass(hass) {
const config = this._config;
const entity = hass.states[config.entity];
if(!entity)return;
let ambient_temperature = entity.attributes.current_temperature || 0;
if (config.ambient_temperature && hass.states[config.ambient_temperature])
ambient_temperature = hass.states[config.ambient_temperature].state;
let hvac_state = entity.state;
const new_state = {
entity: entity,
min_value: entity.attributes.min_temp,
max_value: entity.attributes.max_temp,
ambient_temperature: ambient_temperature,
target_temperature: entity.attributes.temperature,
target_temperature_low: entity.attributes.target_temp_low,
target_temperature_high: entity.attributes.target_temp_high,
hvac_state: entity.state,
hvac_modes:entity.attributes.hvac_modes,
preset_mode: entity.attributes.preset_mode,
away: (entity.attributes.away_mode == 'on' ? true : false)
}
if (!this._saved_state ||
(this._saved_state.min_value != new_state.min_value ||
this._saved_state.max_value != new_state.max_value ||
this._saved_state.ambient_temperature != new_state.ambient_temperature ||
this._saved_state.target_temperature != new_state.target_temperature ||
this._saved_state.target_temperature_low != new_state.target_temperature_low ||
this._saved_state.target_temperature_high != new_state.target_temperature_high ||
this._saved_state.hvac_state != new_state.hvac_state ||
this._saved_state.preset_mode != new_state.preset_mode ||
this._saved_state.away != new_state.away)) {
this._saved_state = new_state;
this.thermostat.updateState(new_state,hass);
}
this._hass = hass;
}
openProp(entityId) {
this.fire('hass-more-info', { entityId });
}
fire(type, detail, options) {
options = options || {}
detail = detail === null || detail === undefined ? {} : detail
const e = new Event(type, {
bubbles: options.bubbles === undefined ? true : options.bubbles,
cancelable: Boolean(options.cancelable),
composed: options.composed === undefined ? true : options.composed,
})
e.detail = detail
this.dispatchEvent(e)
return e
}
_controlSetPoints() {
if (this.thermostat.dual) {
if (this.thermostat.temperature.high != this._saved_state.target_temperature_high ||
this.thermostat.temperature.low != this._saved_state.target_temperature_low)
this._hass.callService('climate', 'set_temperature', {
entity_id: this._config.entity,
target_temp_high: this.thermostat.temperature.high,
target_temp_low: this.thermostat.temperature.low,
});
} else {
if (this.thermostat.temperature.target != this._saved_state.target_temperature)
this._hass.callService('climate', 'set_temperature', {
entity_id: this._config.entity,
temperature: this.thermostat.temperature.target,
});
}
}
setConfig(config) {
// Check config
if (!config.entity && config.entity.split(".")[0] === 'climate') {
throw new Error('Please define an entity');
}
// Cleanup DOM
const root = this.shadowRoot;
if (root.lastChild) root.removeChild(root.lastChild);
// Prepare config defaults
const cardConfig = deepClone(config);
// cardConfig.hvac = Object.assign({}, config.hvac);
if (!cardConfig.diameter) cardConfig.diameter = 400;
if (!cardConfig.pending) cardConfig.pending = 3;
if (!cardConfig.idle_zone) cardConfig.idle_zone = 2;
if (!cardConfig.step) cardConfig.step = 0.5;
if (!cardConfig.highlight_tap) cardConfig.highlight_tap = false;
if (!cardConfig.no_card) cardConfig.no_card = false;
if (!cardConfig.chevron_size) cardConfig.chevron_size = 50;
if (!cardConfig.num_ticks) cardConfig.num_ticks = 150;
if (!cardConfig.tick_degrees) cardConfig.tick_degrees = 300;
// Extra config values generated for simplicity of updates
cardConfig.radius = cardConfig.diameter / 2;
cardConfig.ticks_outer_radius = cardConfig.diameter / 30;
cardConfig.ticks_inner_radius = cardConfig.diameter / 8;
cardConfig.offset_degrees = 180 - (360 - cardConfig.tick_degrees) / 2;
cardConfig.control = this._controlSetPoints.bind(this);
cardConfig.propWin = this.openProp.bind(this);
this.thermostat = new ThermostatUI(cardConfig);
if (cardConfig.no_card === true) {
const card = document.createElement('ha-card');
card.className = "no_card";
const style = document.createElement('style');
style.textContent = cssData();
card.appendChild(style);
card.appendChild(this.thermostat.container);
root.appendChild(card);
}
else {
const card = document.createElement('ha-card');
const style = document.createElement('style');
style.textContent = cssData();
card.appendChild(style);
card.appendChild(this.thermostat.container);
root.appendChild(card);
}
this._config = cardConfig;
}
}
customElements.define('thermostat-card', ThermostatCard);
function deepClone(value) {
if (!(!!value && typeof value == 'object')) {
return value;
}
if (Object.prototype.toString.call(value) == '[object Date]') {
return new Date(value.getTime());
}
if (Array.isArray(value)) {
return value.map(deepClone);
}
var result = {};
Object.keys(value).forEach(
function(key) { result[key] = deepClone(value[key]); });
return result;
}

View File

@@ -1,303 +0,0 @@
export function cssData(user) {
var css =`
ha-card {
overflow: hidden;
--rail_border_color: transparent;
--auto_color: rgb(227, 99, 4, 1);
--cool_color: rgba(0, 122, 241, 0.6);
--cool_colorc: rgba(0, 122, 241, 1);
--heat_color: #ff8100;
--heat_colorc: rgb(227, 99, 4, 1);
--manual_color: #44739e;
--off_color: #8a8a8a;
--fan_only_color: #D7DBDD;
--dry_color: #efbd07;
--idle_color: #808080;
--unknown_color: #bac;
--text-color: white;
}
ha-card.no_card{
background-color: transparent;
border: none;
box-shadow: none;
}
ha-card.no_card .prop{
display: none;
}
.auto, .heat_cool {
--mode_color: var(--auto_color);
}
.cool {
--mode_color: var(--cool_color);
}
.heat {
--mode_color: var(--heat_color);
}
.manual {
--mode_color: var(--manual_color);
}
.off {
--mode_color: var(--off_color);
}
.more {
--mode_color: var(--off_color);
}
.fan_only {
--mode_color: var(--fan_only_color);
}
.eco {
--mode_color: var(--auto_color);
}
.dry {
--mode_color: var(--dry_color);
}
.idle {
--mode_color: var(--idle_color);
}
.unknown-mode {
--mode_color: var(--unknown_color);
}
.c_body {
padding: 5% 5% 5% 5%;
}
.c_icon{
position: absolute;
cursor: pointer;
top: 0;
right: 0;
z-index: 25;
}
.climate_info {
position: absolute;
top: 82%;
left: 50%;
transform: translate(-50%, -50%);
width: 14%;
height: 14%;
--background-color: white;
}
.modes, .mode_color{
position: absolute;
top: 50%;
left: 50%;
width: 100%;
height: 100%;
transform: translate(-50%, -50%);
}
.modes ha-icon {
color: var(--mode_color);
--mdc-icon-size: 100%;
}
.dialog{
background-color:#0000008c;
width: 90%;
height: 90%;
margin: 5%;
border-radius: 50%;
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: center;
align-content: center;
top: 45%;
left: 45%;
backdrop-filter: blur(6px) grayscale(50%);
box-shadow: 0px 0px 10px 0px #696969;
border: 1px solid #ffffff;
}
.dialog span {
width:33%;
margin: 3% 0;
}
.dialog ha-icon {
color: var(--mode_color);
--mdc-icon-size: 70%;
margin: 15%;
}
.dialog.hide{
display: none;
}
.dialog.pending{
border: 1px solid var(--mode_color);
box-shadow: 0px 0px 10px 0px var(--mode_color);
animation: dialog-pending .8s infinite alternate;
}
@keyframes dialog-pending
{
from {box-shadow: 0px 0px 10px 0px var(--mode_color);}
to {box-shadow: 0px 0px 0px 0px var(--mode_color);}
}
.dot_r{
height: 100%;
width: 100%;
background-color: white;
border-radius: 50%;
display: inline-block;
opacity: 0.1;
}
.dot_h{
visibility: hidden;
}
.dial {
user-select: none;
--thermostat-off-fill: #000000c2;
--thermostat-path-color: rgba(255, 255, 255, 0.3);
--thermostat-path-active-color: rgba(255, 255, 255, 0.8);
--thermostat-path-active-color-large: rgba(255, 255, 255, 1);
--thermostat-text-color: white;
}
.dial.has-thermo .dial__ico__leaf {
visibility: hidden;
}
.dial .dial__shape {
transition: fill 0.5s;
fill: var(--thermostat-off-fill);
}
.dial__ico__thermo {
fill: var(--thermostat-path-active-color);
opacity: 0;
transition: opacity 0.5s;
pointer-events: none;
}
.dial.has-thermo .dial__ico__thermo {
display: block;
opacity: 1;
pointer-events: initial;
}
.dial__editableIndicator {
fill: white;
fill-rule: evenodd;
opacity: 0;
transition: opacity 0.5s;
}
.dial__temperatureControl {
fill: white;
opacity: 0;
transition: opacity 0.2s;
}
.dial__temperatureControl.control-visible {
opacity: 0.2;
}
.dial--edit .dial__editableIndicator {
opacity: 1;
}
.dial--state--off .dial__shape {
fill: var(--thermostat-off-fill);
}
.dial--state--heat .dial__shape {
fill: var(--heat_colorc);
}
.dial--state--cool .dial__shape {
fill: var(--cool_colorc);
}
.dial--state--auto .dial__shape {
fill: var(--auto_color);
}
.dial--state--fan_only .dial__shape {
fill: var(--fan_only_color);
}
.dial--state--dry .dial__shape {
fill: var(--dry_color);
}
.dial--state--idle .dial__shape {
fill: var(--idle_color);
}
.dial__ticks path {
fill: var(--thermostat-path-color);
}
.dial__ticks path.active {
fill: var(--mode_color);
}
.dial__ticks path.active.large {
fill: var(--mode_color);
}
.dial text, .dial text tspan {
fill: var(--thermostat-text-color);
text-anchor: middle;
font-family: Helvetica, sans-serif;
alignment-baseline: central;
dominant-baseline: central;
}
.dial__lbl--target {
font-size: 120px;
font-weight: bold;
visibility: hidden;
}
.dial__lbl--low, .dial__lbl--high {
font-size: 90px;
font-weight: bold;
visibility: hidden;
}
.dial.in_control .dial__lbl--target {
visibility: visible;
}
.dial.in_control .dial__lbl--low {
visibility: visible;
}
.dial.in_control .dial__lbl--high {
visibility: visible;
}
.dial__lbl--ambient {
font-size: 120px;
font-weight: bold;
visibility: visible;
}
.dial.in_control.has_dual .dial__chevron--low,
.dial.in_control.has_dual .dial__chevron--high {
visibility: visible;
}
.dial.in_control .dial__chevron--target {
visibility: visible;
}
.dial.in_control.has_dual .dial__chevron--target {
visibility: hidden;
}
.dial .dial__chevron {
visibility: hidden;
fill: none;
stroke: var(--thermostat-text-color);
stroke-width: 4px;
opacity: 0.3;
}
.dial .dial__chevron.pressed {
opacity: 1;
}
.dial.in_control .dial__lbl--ambient {
visibility: hidden;
}
.dial__lbl--super--ambient, .dial__lbl--super--target {
font-size: 40px;
font-weight: bold;
}
.dial__lbl--super--high, .dial__lbl--super--low {
font-size: 30px;
font-weight: bold;
}
.dial__lbl--ring {
font-size: 22px;
font-weight: bold;
}
.dial__lbl--title {
font-size: 24px;
}
`
return css;
}

View File

@@ -1,659 +0,0 @@
export default class ThermostatUI {
get container() {
return this._container
}
set dual(val) {
this._dual = val
}
get dual() {
return this._dual;
}
get in_control() {
return this._in_control;
}
get temperature() {
return {
low: this._low,
high: this._high,
target: this._target,
}
}
get ambient() {
return this._ambient;
}
set temperature(val) {
this._ambient = val.ambient;
this._low = val.low;
this._high = val.high;
this._target = val.target;
if (this._low && this._high) this.dual = true;
}
constructor(config) {
this._config = config; // need certain options for updates
this._ticks = []; // need for dynamic tick updates
this._controls = []; // need for managing highlight and clicks
this._dual = false; // by default is single temperature
this._container = document.createElement('div');
this._main_icon = document.createElement('div');
this._modes_dialog = document.createElement('div');
config.title = config.title === null || config.title === undefined ? 'Title' : config.title
this._ic = document.createElement('div');
this._ic.className = "prop";
this._ic.innerHTML = `<ha-icon-button id="more" icon="mdi:dots-vertical" class="c_icon" role="button" tabindex="0" aria-disabled="false"></ha-icon-button>`;
this._container.appendChild(this._ic)
// this._container.appendChild(this._buildTitle(config.title));
this._ic.addEventListener('click', () => this.openProp());
this._container.appendChild(this._load_icon('',''));
this.c_body = document.createElement('div');
this.c_body.className = 'c_body';
const root = this._buildCore(config.diameter);
root.appendChild(this._buildDial(config.radius));
root.appendChild(this._buildTicks(config.num_ticks));
root.appendChild(this._buildRing(config.radius));
root.appendChild(this._buildThermoIcon(config.radius));
root.appendChild(this._buildDialSlot(1));
root.appendChild(this._buildDialSlot(2));
root.appendChild(this._buildDialSlot(3));
root.appendChild(this._buildText(config.radius, 'title', 0));
root.appendChild(this._buildText(config.radius, 'ambient', 0));
root.appendChild(this._buildText(config.radius, 'target', 0));
root.appendChild(this._buildText(config.radius, 'low', -config.radius / 2.5));
root.appendChild(this._buildText(config.radius, 'high', config.radius / 3));
root.appendChild(this._buildChevrons(config.radius, 0, 'low', 0.7, -config.radius / 2.5));
root.appendChild(this._buildChevrons(config.radius, 0, 'high', 0.7, config.radius / 3));
root.appendChild(this._buildChevrons(config.radius, 0, 'target', 1, 0));
root.appendChild(this._buildChevrons(config.radius, 180, 'low', 0.7, -config.radius / 2.5));
root.appendChild(this._buildChevrons(config.radius, 180, 'high', 0.7, config.radius / 3));
root.appendChild(this._buildChevrons(config.radius, 180, 'target', 1, 0));
this.c_body.appendChild(root);
this._container.appendChild(this.c_body);
this._root = root;
this._buildControls(config.radius);
this._root.addEventListener('click', () => this._enableControls());
this._container.appendChild(this._buildDialog());
this._main_icon.addEventListener('click', () => this._openDialog());
this._modes_dialog.addEventListener('click', () => this._hideDialog());
this._updateText('title', config.title);
}
updateState(options,hass) {
const config = this._config;
const away = options.away || false;
this.entity = options.entity;
this.min_value = options.min_value;
this.max_value = options.max_value;
this.hvac_state = options.hvac_state;
this.preset_mode = options.preset_mode;
this.hvac_modes = options.hvac_modes;
this.temperature = {
low: options.target_temperature_low,
high: options.target_temperature_high,
target: options.target_temperature,
ambient: options.ambient_temperature,
}
this._updateClass('has_dual', this.dual);
let tick_label, from, to;
const tick_indexes = [];
const ambient_index = SvgUtil.restrictToRange(Math.round((this.ambient - this.min_value) / (this.max_value - this.min_value) * config.num_ticks), 0, config.num_ticks - 1);
const target_index = SvgUtil.restrictToRange(Math.round((this._target - this.min_value) / (this.max_value - this.min_value) * config.num_ticks), 0, config.num_ticks - 1);
const high_index = SvgUtil.restrictToRange(Math.round((this._high - this.min_value) / (this.max_value - this.min_value) * config.num_ticks), 0, config.num_ticks - 1);
const low_index = SvgUtil.restrictToRange(Math.round((this._low - this.min_value) / (this.max_value - this.min_value) * config.num_ticks), 0, config.num_ticks - 1);
if (!this.dual) {
tick_label = [this._target, this.ambient].sort();
this._updateTemperatureSlot(tick_label[0], -8, `temperature_slot_1`);
this._updateTemperatureSlot(tick_label[1], 8, `temperature_slot_2`);
switch (this.hvac_state) {
case 'dry':
this._load_icon(this.hvac_state, 'water-percent');
break;
case 'fan_only':
this._load_icon(this.hvac_state, 'fan');
break;
case 'cool':
this._load_icon(this.hvac_state, 'snowflake');
if (target_index <= ambient_index) {
from = target_index;
to = ambient_index;
}
break;
case 'heat':
this._load_icon(this.hvac_state, 'fire');
if (target_index >= ambient_index) {
from = ambient_index;
to = target_index;
}
break;
case 'auto':
this._load_icon(this.hvac_state, 'atom');
if (target_index >= ambient_index) {
from = ambient_index;
to = target_index;
}
break;
case 'off':
this._load_icon(this.hvac_state, 'power');
break;
default:
this._load_icon('more', 'dots-horizontal');
}
} else {
tick_label = [this._low, this._high, this.ambient].sort();
this._updateTemperatureSlot(null, 0, `temperature_slot_1`);
this._updateTemperatureSlot(null, 0, `temperature_slot_2`);
this._updateTemperatureSlot(null, 0, `temperature_slot_3`);
switch (this.hvac_state) {
case 'cool':
if (high_index < ambient_index) {
from = high_index;
to = ambient_index;
this._updateTemperatureSlot(this.ambient, 8, `temperature_slot_3`);
this._updateTemperatureSlot(this._high, -8, `temperature_slot_2`);
}
break;
case 'heat':
if (low_index > ambient_index) {
from = ambient_index;
to = low_index;
this._updateTemperatureSlot(this.ambient, -8, `temperature_slot_1`);
this._updateTemperatureSlot(this._low, 8, `temperature_slot_2`);
}
break;
case 'off':
if (high_index < ambient_index) {
from = high_index;
to = ambient_index;
this._updateTemperatureSlot(this.ambient, 8, `temperature_slot_3`);
this._updateTemperatureSlot(this._high, -8, `temperature_slot_2`);
}
if (low_index > ambient_index) {
from = ambient_index;
to = low_index;
this._updateTemperatureSlot(this.ambient, -8, `temperature_slot_1`);
this._updateTemperatureSlot(this._low, 8, `temperature_slot_2`);
}
break;
default:
}
}
tick_label.forEach(item => tick_indexes.push(SvgUtil.restrictToRange(Math.round((item - this.min_value) / (this.max_value - this.min_value) * config.num_ticks), 0, config.num_ticks - 1)));
this._updateTicks(from, to, tick_indexes, this.hvac_state);
// this._updateColor(this.hvac_state, this.preset_mode);
this._updateText('ambient', this.ambient);
this._updateEdit(false);
this._updateDialog(this.hvac_modes,hass);
}
_temperatureControlClicked(index) {
const config = this._config;
let chevron;
this._root.querySelectorAll('path.dial__chevron').forEach(el => SvgUtil.setClass(el, 'pressed', false));
if (this.in_control) {
if (this.dual) {
switch (index) {
case 0:
// clicked top left
chevron = this._root.querySelectorAll('path.dial__chevron--low')[1];
this._low = this._low + config.step;
if ((this._low + config.idle_zone) >= this._high) this._low = this._high - config.idle_zone;
break;
case 1:
// clicked top right
chevron = this._root.querySelectorAll('path.dial__chevron--high')[1];
this._high = this._high + config.step;
if (this._high > this.max_value) this._high = this.max_value;
break;
case 2:
// clicked bottom right
chevron = this._root.querySelectorAll('path.dial__chevron--high')[0];
this._high = this._high - config.step;
if ((this._high - config.idle_zone) <= this._low) this._high = this._low + config.idle_zone;
break;
case 3:
// clicked bottom left
chevron = this._root.querySelectorAll('path.dial__chevron--low')[0];
this._low = this._low - config.step;
if (this._low < this.min_value) this._low = this.min_value;
break;
}
SvgUtil.setClass(chevron, 'pressed', true);
setTimeout(() => SvgUtil.setClass(chevron, 'pressed', false), 200);
if (config.highlight_tap)
SvgUtil.setClass(this._controls[index], 'control-visible', true);
}
else {
if (index < 2) {
// clicked top
chevron = this._root.querySelectorAll('path.dial__chevron--target')[1];
this._target = this._target + config.step;
if (this._target > this.max_value) this._target = this.max_value;
if (config.highlight_tap) {
SvgUtil.setClass(this._controls[0], 'control-visible', true);
SvgUtil.setClass(this._controls[1], 'control-visible', true);
}
} else {
// clicked bottom
chevron = this._root.querySelectorAll('path.dial__chevron--target')[0];
this._target = this._target - config.step;
if (this._target < this.min_value) this._target = this.min_value;
if (config.highlight_tap) {
SvgUtil.setClass(this._controls[2], 'control-visible', true);
SvgUtil.setClass(this._controls[3], 'control-visible', true);
}
}
SvgUtil.setClass(chevron, 'pressed', true);
setTimeout(() => SvgUtil.setClass(chevron, 'pressed', false), 200);
}
if (config.highlight_tap) {
setTimeout(() => {
SvgUtil.setClass(this._controls[0], 'control-visible', false);
SvgUtil.setClass(this._controls[1], 'control-visible', false);
SvgUtil.setClass(this._controls[2], 'control-visible', false);
SvgUtil.setClass(this._controls[3], 'control-visible', false);
}, 200);
}
} else {
this._enableControls();
}
}
_updateEdit(show_edit) {
SvgUtil.setClass(this._root, 'dial--edit', show_edit);
}
_enableControls() {
const config = this._config;
this._in_control = true;
this._updateClass('in_control', this.in_control);
if (this._timeoutHandler) clearTimeout(this._timeoutHandler);
this._updateEdit(true);
//this._updateClass('has-thermo', true);
this._updateText('target', this.temperature.target);
this._updateText('low', this.temperature.low);
this._updateText('high', this.temperature.high);
this._timeoutHandler = setTimeout(() => {
this._updateText('ambient', this.ambient);
this._updateEdit(false);
//this._updateClass('has-thermo', false);
this._in_control = false;
this._updateClass('in_control', this.in_control);
config.control();
}, config.pending * 1000);
}
_updateClass(class_name, flag) {
SvgUtil.setClass(this._root, class_name, flag);
}
_updateText(id, value) {
const lblTarget = this._root.querySelector(`#${id}`).querySelectorAll('tspan');
const text = Math.floor(value);
if (value) {
lblTarget[0].textContent = text;
if (value % 1 != 0) {
lblTarget[1].textContent = Math.round(value % 1 * 10);
} else {
lblTarget[1].textContent = '';
}
}
if (this.in_control && id == 'target' && this.dual) {
lblTarget[0].textContent = '·';
}
if(id =='title'){
lblTarget[0].textContent = value;
lblTarget[1].textContent = '';
}
}
_updateTemperatureSlot(value, offset, slot) {
const config = this._config;
const lblSlot1 = this._root.querySelector(`#${slot}`)
lblSlot1.textContent = value != null ? SvgUtil.superscript(value) : '';
const peggedValue = SvgUtil.restrictToRange(value, this.min_value, this.max_value);
const position = [config.radius, config.ticks_outer_radius - (config.ticks_outer_radius - config.ticks_inner_radius) / 2];
let degs = config.tick_degrees * (peggedValue - this.min_value) / (this.max_value - this.min_value) - config.offset_degrees + offset;
const pos = SvgUtil.rotatePoint(position, degs, [config.radius, config.radius]);
SvgUtil.attributes(lblSlot1, {
x: pos[0],
y: pos[1]
});
}
_updateColor(state, preset_mode) {
if(Object.prototype.toString.call(preset_mode) === "[object String]"){
if(state != 'off' && preset_mode.toLowerCase() == 'idle')
state = 'idle'
this._root.classList.forEach(c => {
if (c.indexOf('dial--state--') != -1)
this._root.classList.remove(c);
});
this._root.classList.add('dial--state--' + state);
}
}
_updateTicks(from, to, large_ticks, hvac_state) {
const config = this._config;
const tickPoints = [
[config.radius - 1, config.ticks_outer_radius],
[config.radius + 1, config.ticks_outer_radius],
[config.radius + 1, config.ticks_inner_radius],
[config.radius - 1, config.ticks_inner_radius]
];
const tickPointsLarge = [
[config.radius - 1.5, config.ticks_outer_radius],
[config.radius + 1.5, config.ticks_outer_radius],
[config.radius + 1.5, config.ticks_inner_radius + 20],
[config.radius - 1.5, config.ticks_inner_radius + 20]
];
this._ticks.forEach((tick, index) => {
let isLarge = false;
let isActive = (index >= from && index <= to) ? 'active '+hvac_state : '';
large_ticks.forEach(i => isLarge = isLarge || (index == i));
if (isLarge) isActive += ' large';
const theta = config.tick_degrees / config.num_ticks;
SvgUtil.attributes(tick, {
d: SvgUtil.pointsToPath(SvgUtil.rotatePoints(isLarge ? tickPointsLarge : tickPoints, index * theta - config.offset_degrees, [config.radius, config.radius])),
class: isActive
});
});
}
_updateDialog(modes,hass){
this._modes_dialog.innerHTML = "";
for(var i=0;i<modes.length;i++){
let icon;
let mode = modes[i];
switch (mode) {
case 'dry':
icon = 'water-percent';
break;
case 'fan_only':
icon = 'fan';
break;
case 'cool':
icon = 'snowflake';
break;
case 'heat':
icon = 'fire';
break;
case 'auto':
icon = 'atom';
break;
case 'off':
icon = 'power';
break;
}
let d = document.createElement('span');
d.innerHTML = `<ha-icon class="modeicon ${mode}" icon="mdi:${icon}"></ha-icon>`
d.addEventListener('click', (e) => this._setMode(e,mode,hass));
//this._modes[i].push(d);
this._modes_dialog.appendChild(d)
}
}
_buildCore(diameter) {
return SvgUtil.createSVGElement('svg', {
width: '100%',
height: '100%',
viewBox: '0 0 ' + diameter + ' ' + diameter,
class: 'dial'
})
}
openProp() {
this._config.propWin(this.entity.entity_id)
}
_openDialog(){
this._modes_dialog.className = "dialog modes";
}
_hideDialog(){
this._modes_dialog.className = "dialog modes hide";
}
_setMode(e, mode,hass){
console.log(mode);
let config = this._config;
if (this._timeoutHandlerMode) clearTimeout(this._timeoutHandlerMode);
hass.callService('climate', 'set_hvac_mode', {
entity_id: this._config.entity,
hvac_mode: mode,
});
this._modes_dialog.className = "dialog modes "+mode+" pending";
this._timeoutHandlerMode = setTimeout(() => {
this._modes_dialog.className = "dialog modes hide";
}, config.pending * 1000);
e.stopPropagation();
}
_load_icon(state, ic_name){
let ic_dot = 'dot_r'
if(ic_name == ''){
ic_dot = 'dot_h'
}
this._main_icon.innerHTML = `
<div class="climate_info">
<div class="mode_color"><span class="${ic_dot}"></span></div>
<div class="modes"><ha-icon class="${state}" icon="mdi:${ic_name}"></ha-icon></div>
</div>
`;
return this._main_icon;
}
_buildDialog(){
this._modes_dialog.className = "dialog modes hide";
return this._modes_dialog;
}
// build black dial
_buildDial(radius) {
return SvgUtil.createSVGElement('circle', {
cx: radius,
cy: radius,
r: radius,
class: 'dial__shape'
})
}
// build circle around
_buildRing(radius) {
return SvgUtil.createSVGElement('path', {
d: SvgUtil.donutPath(radius, radius, radius - 4, radius - 8),
class: 'dial__editableIndicator',
})
}
_buildTicks(num_ticks) {
const tick_element = SvgUtil.createSVGElement('g', {
class: 'dial__ticks'
});
for (let i = 0; i < num_ticks; i++) {
const tick = SvgUtil.createSVGElement('path', {})
this._ticks.push(tick);
tick_element.appendChild(tick);
}
return tick_element;
}
_buildChevrons(radius, rotation, id, scale, offset) {
const config = this._config;
const translation = rotation > 0 ? -1 : 1;
const width = config.chevron_size;
const chevron_def = ["M", 0, 0, "L", width / 2, width * 0.3, "L", width, 0].map((x) => isNaN(x) ? x : x * scale).join(' ');
const translate = [radius - width / 2 * scale * translation + offset, radius + 70 * scale * 1.1 * translation];
const chevron = SvgUtil.createSVGElement('path', {
class: `dial__chevron dial__chevron--${id}`,
d: chevron_def,
transform: `translate(${translate[0]},${translate[1]}) rotate(${rotation})`
});
return chevron;
}
_buildThermoIcon(radius) {
const thermoScale = radius / 3 / 100;
const thermoDef = 'M 37.999 38.261 V 7 c 0 -3.859 -3.141 -7 -7 -7 s -7 3.141 -7 7 v 31.261 c -3.545 2.547 -5.421 6.769 -4.919 11.151 c 0.629 5.482 5.066 9.903 10.551 10.512 c 0.447 0.05 0.895 0.074 1.339 0.074 c 2.956 0 5.824 -1.08 8.03 -3.055 c 2.542 -2.275 3.999 -5.535 3.999 -8.943 C 42.999 44.118 41.14 40.518 37.999 38.261 Z M 37.666 55.453 c -2.146 1.921 -4.929 2.8 -7.814 2.482 c -4.566 -0.506 -8.261 -4.187 -8.785 -8.752 c -0.436 -3.808 1.28 -7.471 4.479 -9.56 l 0.453 -0.296 V 38 h 1 c 0.553 0 1 -0.447 1 -1 s -0.447 -1 -1 -1 h -1 v -3 h 1 c 0.553 0 1 -0.447 1 -1 s -0.447 -1 -1 -1 h -1 v -3 h 1 c 0.553 0 1 -0.447 1 -1 s -0.447 -1 -1 -1 h -1 v -3 h 1 c 0.553 0 1 -0.447 1 -1 s -0.447 -1 -1 -1 h -1 v -3 h 1 c 0.553 0 1 -0.447 1 -1 s -0.447 -1 -1 -1 h -1 v -3 h 1 c 0.553 0 1 -0.447 1 -1 s -0.447 -1 -1 -1 h -1 V 8 h 1 c 0.553 0 1 -0.447 1 -1 s -0.447 -1 -1 -1 H 26.1 c 0.465 -2.279 2.484 -4 4.899 -4 c 2.757 0 5 2.243 5 5 v 1 h -1 c -0.553 0 -1 0.447 -1 1 s 0.447 1 1 1 h 1 v 3 h -1 c -0.553 0 -1 0.447 -1 1 s 0.447 1 1 1 h 1 v 3 h -1 c -0.553 0 -1 0.447 -1 1 s 0.447 1 1 1 h 1 v 3 h -1 c -0.553 0 -1 0.447 -1 1 s 0.447 1 1 1 h 1 v 3 h -1 c -0.553 0 -1 0.447 -1 1 s 0.447 1 1 1 h 1 v 3 h -1 c -0.553 0 -1 0.447 -1 1 s 0.447 1 1 1 h 1 v 4.329 l 0.453 0.296 c 2.848 1.857 4.547 4.988 4.547 8.375 C 40.999 50.841 39.784 53.557 37.666 55.453 Z'.split(' ').map((x) => isNaN(x) ? x : x * thermoScale).join(' ');
const translate = [radius - (thermoScale * 100 * 0.3), radius * 1.65]
return SvgUtil.createSVGElement('path', {
class: 'dial__ico__thermo',
d: thermoDef,
transform: 'translate(' + translate[0] + ',' + translate[1] + ')'
});
}
_buildDialSlot(index) {
return SvgUtil.createSVGElement('text', {
class: 'dial__lbl dial__lbl--ring',
id: `temperature_slot_${index}`
})
}
_buildText(radius, name, offset) {
const target = SvgUtil.createSVGElement('text', {
x: radius + offset,
y: radius-(name=='title'?radius/2:0),
class: `dial__lbl dial__lbl--${name}`,
id: name
});
const text = SvgUtil.createSVGElement('tspan', {
});
// hack
if (name == 'target' || name == 'ambient') offset += 20;
const superscript = SvgUtil.createSVGElement('tspan', {
x: radius + radius / 3.1 + offset,
y: radius - radius / 6,
class: `dial__lbl--super--${name}`
});
target.appendChild(text);
target.appendChild(superscript);
return target;
}
_buildControls(radius) {
let startAngle = 270;
let loop = 4;
for (let index = 0; index < loop; index++) {
const angle = 360 / loop;
const sector = SvgUtil.anglesToSectors(radius, startAngle, angle);
const controlsDef = 'M' + sector.L + ',' + sector.L + ' L' + sector.L + ',0 A' + sector.L + ',' + sector.L + ' 1 0,1 ' + sector.X + ', ' + sector.Y + ' z';
const path = SvgUtil.createSVGElement('path', {
class: 'dial__temperatureControl',
fill: 'blue',
d: controlsDef,
transform: 'rotate(' + sector.R + ', ' + sector.L + ', ' + sector.L + ')'
});
this._controls.push(path);
path.addEventListener('click', () => this._temperatureControlClicked(index));
this._root.appendChild(path);
startAngle = startAngle + angle;
}
}
}
class SvgUtil {
static createSVGElement(tag, attributes) {
const element = document.createElementNS('http://www.w3.org/2000/svg', tag);
this.attributes(element, attributes)
return element;
}
static attributes(element, attrs) {
for (let i in attrs) {
element.setAttribute(i, attrs[i]);
}
}
// Rotate a cartesian point about given origin by X degrees
static rotatePoint(point, angle, origin) {
const radians = angle * Math.PI / 180;
const x = point[0] - origin[0];
const y = point[1] - origin[1];
const x1 = x * Math.cos(radians) - y * Math.sin(radians) + origin[0];
const y1 = x * Math.sin(radians) + y * Math.cos(radians) + origin[1];
return [x1, y1];
}
// Rotate an array of cartesian points about a given origin by X degrees
static rotatePoints(points, angle, origin) {
return points.map((point) => this.rotatePoint(point, angle, origin));
}
// Given an array of points, return an SVG path string representing the shape they define
static pointsToPath(points) {
return points.map((point, iPoint) => (iPoint > 0 ? 'L' : 'M') + point[0] + ' ' + point[1]).join(' ') + 'Z';
}
static circleToPath(cx, cy, r) {
return [
"M", cx, ",", cy,
"m", 0 - r, ",", 0,
"a", r, ",", r, 0, 1, ",", 0, r * 2, ",", 0,
"a", r, ",", r, 0, 1, ",", 0, 0 - r * 2, ",", 0,
"z"
].join(' ').replace(/\s,\s/g, ",");
}
static donutPath(cx, cy, rOuter, rInner) {
return this.circleToPath(cx, cy, rOuter) + " " + this.circleToPath(cx, cy, rInner);
}
static superscript(n) {
if ((n - Math.floor(n)) !== 0)
n = Number(n).toFixed(1);;
const x = `${n}${n == 0 ? '' : ''}`;
return x;
}
// Restrict a number to a min + max range
static restrictToRange(val, min, max) {
if (val < min) return min;
if (val > max) return max;
return val;
}
static setClass(el, className, state) {
el.classList[state ? 'add' : 'remove'](className);
}
static anglesToSectors(radius, startAngle, angle) {
let aRad = 0 // Angle in Rad
let z = 0 // Size z
let x = 0 // Side x
let X = 0 // SVG X coordinate
let Y = 0 // SVG Y coordinate
const aCalc = (angle > 180) ? 360 - angle : angle;
aRad = aCalc * Math.PI / 180;
z = Math.sqrt(2 * radius * radius - (2 * radius * radius * Math.cos(aRad)));
if (aCalc <= 90) {
x = radius * Math.sin(aRad);
}
else {
x = radius * Math.sin((180 - aCalc) * Math.PI / 180);
}
Y = Math.sqrt(z * z - x * x);
if (angle <= 180) {
X = radius + x;
}
else {
X = radius - x;
}
return {
L: radius,
X: X,
Y: Y,
R: startAngle
}
}
}

View File

@@ -1,590 +0,0 @@
/*
* Author : duytruong
* Github : https://github.com/konnectedvn
* Description :
* Date : 01 Feb 2021 08:44:30+07:00
* Based on : github.com/DBuit/hass-smart-home-panel-card (Thanks to DBuit!)
*/
console.info("%c [konnected.vn] Vertical Slider Cover Card \n%c Version v0.1.5","color: red; font-weight: bold; background: black", "color: white; font-weight: bold; background: dimgray");
import {
LitElement,
html,
css
} from "https://unpkg.com/lit-element@2.0.1/lit-element.js?module";
class VerticalSliderCoverCard extends LitElement {
static get properties() {
return {
hass: {},
config: {},
active: {},
sliderVal: { type: Array }
};
}
constructor() {
super();
this.sliderVal = [];
}
render() {
var primaryTextColor = "var(--primary-text-color)";
var icon = this.config.icon ? this.config.icon : "mdi:blinds";
var iconSize = this.config.iconSize ? this.config.iconSize: "28px";
var iconColor = this.config.iconColor ? this.config.iconColor : primaryTextColor;
var positionWidth = this.config.positionWidth ? this.config.positionWidth : "100px";
var positionHeight = this.config.positionHeight ? this.config.positionHeight : "300px";
var showName = this.config.showName ? this.config.showName : true;
var showPosition = this.config.showPosition ? this.config.showPosition : true;
var switchWidth = this.config.switchWidth ? this.config.switchWidth : positionWidth;
var switchHeight = this.config.switchHeight ? this.config.switchHeight : switchWidth;
var showSwitch = this.config.showSwitch;
var switchFontColor = this.config.switchFontColor ? this.config.switchFontColor : primaryTextColor;
var gapWidth = this.config.gapWidth ? this.config.gapWidth : "50px";
var countText = this.config.countText ? this.config.countText : "covers open";
var countTextFontColor = this.config.countTextFontColor ? this.config.countTextFontColor : primaryTextColor;
var openBaseline = this.config.closedBaseline ? this.config.closedBaseline : 0;
var entityCounter = 0;
var showButton = this.config.showButton ? this.config.showButton : false;
var buttonText = this.config.buttonText ? this.config.buttonText : "Home";
var buttonPath = this.config.buttonPath ? this.config.buttonPath : "/lovelace/0";
var buttonService = this.config.buttonService ? this.config.buttonService: "";
var buttonData = this.config.buttonData ? this.config.buttonData : "";
var buttonFontColor = this.config.buttonFontColor ? this.config.buttonFontColor : primaryTextColor;
var background = this.config.background ? this.config.background : "transparent";
var sideColor1 = this.config.sideColor1 ? this.config.sideColor1 : "#ffcccc";
var sideColor2 = this.config.sideColor2 ? this.config.sideColor2 : '#b30000';
var switchColor = this.config.switchColor ? this.config.switchColor : sideColor2;
var closedColor = this.config.closedColor ? this.config.closedColor : "hsl(0, 0%, 20%)";
var openColor = this.config.openColor ? this.config.openColor : "hsl(0, 0%, 90%, 0.6)";
var panelType = this.config.panelType;
var showSidebar = this.config.showSidebar;
var titleSize = this.config.titleSize ? this.config.titleSize : "40px";
var titleFontColor = this.config.titleFontColor ? this.config.titleFontColor : primaryTextColor;
return html`
<ha-card>
<div class="page" style="background:${background};">
<div class="side" style="${this._panelSize(panelType)};--show-sidebar:${this._showFlex(showSidebar)};--sideColor-1:${sideColor1};--sideColor-2:${sideColor2};">
<div class="header">
</div>
<div class="center">
<div class="icon">
<ha-icon style="--mdc-icon-size:${iconSize};--icon-color:${iconColor};" icon="${icon}" />
</div>
<h1 style="--title-size:${titleSize};--title-font-color:${titleFontColor};">${this.config.title}</h1>
<h3 style="--count-font-color:${countTextFontColor};">${this._stateCount(openBaseline)} ${countText}</h3>
</div>
<div class="bottom">
${showButton ? html`<button class="back-btn" style="--button-size:${this._buttonFont(titleSize,buttonText)}px;--button-font-color:${buttonFontColor};" @click=${e => this._navigate(buttonPath,buttonService,buttonData)}>${buttonText}</button>` : html``}
</div>
</div>
<div class="main">
<div class="inner-main">
${this.config.entities.map(ent => {
entityCounter++;
var switchValue = 0;
const stateObj = this.hass.states[ent.entity];
switch(stateObj.state) {
case 'open':
switchValue = 100;
break;
case 'closed':
switchValue = 0;
break;
default:
switchValue = 0;
}
return stateObj ? html`
<div class="cover" style="--cover-width:${this._coverSize(positionWidth,gapWidth,panelType)};--center-slider:${this._centerSliders(panelType)};">
<div class="cover-slider">
<p class="cover-name" style="--show-name: ${this._showBlock(showName)};--cover-fontSize: ${this._coverNameFont(positionWidth,gapWidth)}px;">${ent.name || stateObj.attributes.friendly_name}</p>
${stateObj.attributes.supported_features > 6 ? html`
<p class="cover-position" style="--show-position: ${this._showBlock(showPosition)};--cover-fontSize: ${parseInt(positionWidth.replace(/px/,"")) / 4 - (parseInt(positionWidth.replace(/px/,"")) - 60) / 4}px;">${this._coverPosition(stateObj.state, stateObj.attributes.current_position, stateObj.entity_id)}</p>
<div class="range-holder" style="--slider-height: ${positionHeight};--closed-color: ${closedColor};">
<input type="range" class="${stateObj.state}" style="--slider-width: ${positionWidth};--slider-height: ${positionHeight};--closed-color: ${closedColor};--open-color: ${openColor};" .value="${stateObj.state === "closed" ? 0 : Math.round(stateObj.attributes.current_position)}" @input=${e => this._sliderChange(e.target.value, stateObj.entity_id)}} @change=${e => this._setPosition(stateObj.entity_id, e.target.value, ent.script)}>
</div>
` : html`
<h4>${stateObj.state}</h4>
<div class="switch-holder" style="--switch-height: ${switchHeight};">
<input type="range" class="${stateObj.state}" style="--switch-width: ${switchWidth};--switch-height: ${switchHeight};" value="0" min="0" max="1" .value="${switchValue}" @change=${e => this._switch(stateObj)}>
</div>
`}
<div class="toggle" style="--show-switch: ${this._showFlex(showSwitch)};">
<input ?checked=${stateObj.state == "open"} type="checkbox" id="toggle${entityCounter}" class="toggle-btn" @change=${e => this._switch(stateObj)} />
<label for="toggle${entityCounter}" style="--switch-width: ${switchWidth};--switch-height: ${switchHeight};--switch-font-color: ${switchFontColor};--switch-color: ${switchColor};--switch-labelSize: ${parseInt(switchWidth.replace(/px/,"")) / 5}px;"><span></span></label>
</div>
</div>
</div>
`: html``;
})}
</div>
</div>
</div>
</ha-card>
`;
}
updated() {}
_sliderChange(value, entity_id){
this.sliderVal[entity_id] = {val: value, active: true};
this.requestUpdate();
}
_coverPosition(coverState, coverPos, entity_id){
if (coverState === "closed") {
return '0';
} else if (typeof this.sliderVal[entity_id] === 'undefined' || !this.sliderVal[entity_id]['active']) {
return Math.round(coverPos);
} else {
return this.sliderVal[entity_id]['val'];
}
}
_setPosition(entity_id, value, script) {
if (this.hass.states[entity_id].attributes.current_position === value) {
return;
}
this.hass.callService("cover", "set_cover_position", {
entity_id: entity_id,
position: value
});
this.sliderVal[entity_id]['active'] = false;
if (script) {
this.hass.callService("script", "turn_on", {
entity_id: script
});
}
}
_stateCount(baseline) {
let count = 0;
this.config.entities.map(ent => {
const stateObj = this.hass.states[ent.entity];
if(stateObj.state === "open" && baseline === 0) {
count++;
} else if (stateObj.attributes.current_position >= baseline && baseline > 0) {
count++;
}
})
return count;
}
_panelSize(panelType) {
let sideWidth = 40;
if (panelType === true) {
sideWidth = 30;
}
return "--side-width:" + sideWidth + "px";
}
_showFlex(showSidebar) {
if (showSidebar === false) {
return "none";
}
return "flex";
}
_showBlock(confValue) {
if(confValue === false) {
return "none";
}
return "block";
}
_coverSize(positionWidth, gapWidth, panelType) {
if (panelType === false) {
return (parseInt(positionWidth.replace(/px/,"")) + parseInt(gapWidth.replace(/px/,""))) + "px";
} else {
return "50%";
}
}
_buttonFont(titleSize,buttonText) {
let fieldSize = parseInt(titleSize.replace(/px/,"")) * this.config.title.length;
let buttonSize = fieldSize / buttonText.length;
return buttonSize * 0.5;
}
_centerSliders(panelType) {
if (panelType === true) {
return "0 auto";
} else {
return "0";
}
}
_coverNameFont(positionWidth, gapWidth) {
let maxLength = 0;
this.config.entities.map(ent => {
const stateObj = this.hass.states[ent.entity];
let name = ent.name || stateObj.attributes.friendly_name;
if(name.length > maxLength) {
maxLength = name.length;
}
})
let fontsize = parseInt(positionWidth.replace(/px/,""));
if (parseInt(gapWidth.replace(/px/,"")) > 50) {
fontsize = fontsize + (parseInt(gapWidth.replace(/px/,"")) / 2);
} else {
fontsize = fontsize + parseInt(gapWidth.replace(/px/,""));
}
return ((( fontsize - 4 ) / maxLength) * 1.8) | 0;
}
_switch(state) {
this.hass.callService("cover", "stop_cover", {
entity_id: state.entity_id
});
}
_navigate(path,service,data) {
if (service.length === 0) {
window.location.href = path;
} else {
let domain = service.split(".",2)[0];
let ser = service.split(".",2)[1];
this.hass.callService(domain,ser, {
entity_id: data
});
}
}
setConfig(config) {
if (!config.entities) {
throw new Error("You need to define entities");
}
if (!config.title) {
throw new Error("You need to define a title");
}
for (var i = 0, len = config.entities.length; i < len; i++) {
if (config.entities[i].entity === undefined) {
throw new Error(config.entities[i] + " is INVALID! Should be object: - entity: " + config.entities[i] + ".");
}
}
this.config = config;
}
getCardSize() {
return this.config.entities.length + 1;
}
static get styles() {
return css`
:host([is-panel]) ha-card {
left: 50;
top: 0;
width: 100%;
height: 100%;
position: absolute;
}
ha-card {
overflow: hidden;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
}
.page {
width:100%;
height:100%;
display:flex;
flex-direction: row;
}
.page > .side {
padding: 10px;
width: var(--side-width)%;
display:var(--show-sidebar);
flex-direction:column;
background: rgb(28,122,226);
background: linear-gradient(145deg, var(--sideColor-1) 0%, var(--sideColor-2) 90%);
justify-content:space-between;
}
.side .header {
}
.side .center {
display:flex;
flex-direction:column;
}
.side .center .icon {
display:block;
overflow:hidden;
text-align:center;
}
.side .center .icon ha-icon {
color:var(--icon-color);
}
.side .center h1 {
color:var(--title-font-color);
margin:10px 0 0 5px;
font-weight:400;
font-size: var(--title-size);
line-height: var(--title-size);
text-align: center;
}
.side .center h3 {
color:var(--count-font-color);
margin:5px 0 5px 0;
font-size: 120%;
font-weight: 400;
line-height: 100%;
text-align: center;
}
.side .bottom {
}
.back-btn {
border:2px solid var(--button-font-color);
color:var(--button-font-color);
background:transparent;
font-size:var(--button-size);
border-radius: 4px;
width:100%;
display:block;
padding: 10px 0;
}
.page > .main {
width:100%;
overflow-x:scroll;
padding-bottom: 0px;
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */
}
.page > .main::-webkit-scrollbar {
display: none;
}
.page > .main > .inner-main {
display:flex;
flex-direction:row;
align-items:center;
height:100%;
margin: auto;
padding-right: 0px;
}
.page > .main > .inner-main > .cover {
width: var(--cover-width);
display:inline-block;
margin: var(--center-slider);
padding-bottom: 0px;
}
.cover .icon {
margin: 0 auto;
text-align:center;
display:block;
height: 50px;
width: 50px;
color: rgba(255,255,255,0.3);
font-size: 30px;
padding-top:5px;
}
.cover .icon ha-icon {
width: 30px;
height: 30px;
text-align:center;
}
.cover .icon.on ha-icon {
fill: #f7d959;
}
h2 {
color: #FFF;
display: block;
font-weight: 300;
margin-bottom: 10px;
text-align: center;
font-size:20px;
margin-top:0;
}
h3 {
color: #FFF;
display: block;
font-weight: 300;
margin-top: 5px;
margin-bottom: 5px;
text-align: center;
font-size:18px;
}
.cover-name {
display: var(--show-name);
font-weight: 300;
margin-top: calc(var(--cover-fontSize) / 3);
margin-bottom: calc(var(--cover-fontSize) / 2);
text-align: center;
font-size: var(--cover-fontSize);
}
.cover-position {
display: var(--show-position);
font-weight: 300;
margin-top: calc(var(--cover-fontsize) / 2);
margin-bottom: var(--cover-fontsize);
text-align: center;
font-size: var(--cover-fontSize);
}
.cover-slider .cover-name, .cover-position {
color: var(--primary-text-color);
}
h4 {
color: var(--primary-text-color);
display: block;
font-weight: 300;
margin-bottom: 20px;
text-align: center;
font-size:16px;
margin-top:0;
}
.cover-position:after {
content: "%";
padding-left: 1px;
}
.range-holder {
height: var(--slider-height);
position:relative;
display: block;
}
.range-holder input[type="range"] {
outline: 0;
border: 0;
border-radius: 4px;
width: var(--slider-height);
margin: 0;
transition: box-shadow 0.2s ease-in-out;
-webkit-transform:rotate(270deg);
-moz-transform:rotate(270deg);
-o-transform:rotate(270deg);
-ms-transform:rotate(270deg);
transform:rotate(270deg);
overflow: hidden;
height: var(--slider-width);
-webkit-appearance: none;
background-color: var(--closed-color);
position: absolute;
top: calc(50% - (var(--slider-width) / 2));
right: calc(50% - (var(--slider-height) / 2));
}
.range-holder input[type="range"]::-webkit-slider-runnable-track {
height: var(--slider-width);
-webkit-appearance: none;
color: var(--open-color);
margin-top: 0px;
transition: box-shadow 0.2s ease-in-out;
}
.range-holder input[type="range"]::-webkit-slider-thumb {
width: calc((var(--slider-width) / 5) + 2px);
border-right:8px solid var(--closed-color);
border-left:8px solid var(--closed-color);
border-top:20px solid var(--closed-color);
border-bottom:20px solid var(--closed-color);
-webkit-appearance: none;
height: var(--slider-width);
cursor: ew-resize;
background: var(--closed-color);
box-shadow: -350px 0 0 350px var(--open-color), inset 0 0 0 80px var(--open-color);
border-radius: 0;
transition: box-shadow 0.2s ease-in-out;
position: relative;
top: 0;
}
// .range-holder input[type="range"].on::-webkit-slider-thumb {
// border-color: #1c7ae2;
// box-shadow: -350px 0 0 350px #1c7ae2, inset 0 0 0 80px #FFF;
// }
.switch-holder {
height: var(--switch-height);
position:relative;
display: block;
}
.switch-holder input[type="range"] {
outline: 0;
border: 0;
border-radius: 4px;
width: calc(var(--switch-height) - 20px);
margin: 0;
transition: box-shadow 0.2s ease-in-out;
-webkit-transform: rotate(270deg);
-moz-transform: rotate(270deg);
-o-transform: rotate(270deg);
-ms-transform: rotate(270deg);
transform: rotate(270deg);
overflow: hidden;
height: calc(var(--switch-width) - 20px);
-webkit-appearance: none;
background-color: var(--switch-color);
color: var(--switch-font-color);
padding: 10px;
position: absolute;
top: calc(50% - (var(--switch-width) / 2));
right: calc(50% - (var(--switch-height) / 2));
}
.switch-holder input[type="range"]::-webkit-slider-runnable-track {
height: calc(var(--switch-width) - 20px);
-webkit-appearance: none;
color: var(--switch-color);
margin-top: -1px;
transition: box-shadow 0.2s ease-in-out;
}
.switch-holder input[type="range"]::-webkit-slider-thumb {
width: calc(var(--switch-height) / 2);
-webkit-appearance: none;
height: calc(var(--switch-width) - 20px);
cursor: ew-resize;
background: var(--switch-color);
color: var(--switch-font-color);
transition: box-shadow 0.2s ease-in-out;
box-shadow: -340px 0 0 350px #4d4d4d, inset 0 0 0 80px #969696;
position: relative;
top: 0;
border-radius: 4px;
}
// .switch-holder input[type="range"].on::-webkit-slider-thumb {
// box-shadow: -340px 0 0 350px #4d4d4d, inset 0 0 0 80px #1c7ae2;
// }
.toggle {
margin-top: 20px;
margin-bottom: 10px;
display: var(--show-switch);
align-items: center;
justify-content: center;
}
.toggle > input.toggle-btn {
display: none;
}
.toggle > input.toggle-btn + label {
border: 1px solid #FFF;
background: transparent;
width: var(--switch-width);
height: var(--switch-height);
text-align:center;
line-height: var(--switch-height);
cursor: pointer;
border-radius: 4px;
color: var(--switch-font-color);
display:block;
font-size:var(--switch-labelSize);
}
.toggle > input.toggle-btn + label:active,
.toggle > input.toggle-btn + label {
background: var(--switch-color);
border-color: var(--switch-color);
}
.toggle > input.toggle-btn + label> span:before {
content: 'STOP';
}
`;
}
}
customElements.define('vertical-slider-cover-card', VerticalSliderCoverCard);

File diff suppressed because one or more lines are too long