Skip to content
This repository has been archived by the owner on Nov 4, 2023. It is now read-only.

Commit

Permalink
feat(SLIDER): add vertical slider option and improve design (#672)
Browse files Browse the repository at this point in the history
Co-authored-by: Taras Nychko <[email protected]>
Co-authored-by: Rafał Chłodnicki <[email protected]>
  • Loading branch information
3 people authored Apr 20, 2021
1 parent 60fb562 commit e713089
Show file tree
Hide file tree
Showing 6 changed files with 277 additions and 22 deletions.
47 changes: 30 additions & 17 deletions TILE_EXAMPLES.md
Original file line number Diff line number Diff line change
Expand Up @@ -508,23 +508,36 @@ Similar to sensor, but with an icon.<br>
![SLIDER](images/tile-screenshots/SLIDER.png)
```js
{
position: [6, 1],
id: 'input_number.casatunes_volume_6',
type: TYPES.SLIDER,
unit: '%',
state: false,
//bottom: true, // puts slider on bottom
slider: {
//max: 100,
//min: 0,
//step: 2,
request: {
type: "call_service",
domain: "input_number",
service: "set_value",
field: "value"
}
}
position: [0, 0],
id: 'light.entity',
type: TYPES.SLIDER,
unit: '%',
title: 'Kitchen light',
icon: 'mdi-lightbulb', // Optional. Slider size will be adjusted automatically.
// vertical: true, // Show vertical slider (default: false - horizontal).
// singleLine: true, // Makes the optional icon, the slider and the icon be shown on single line (default: false, only works with horizontal slider).
// legacy: true, // Old-style slider that only works in horizontal mode (default: false).
// bottom: true, // puts slider on the bottom (default: false, only work with the legacy slider).
state: false,
// For light entities a filter function can be used to convert the value from 0-255 to 0-100% range.
filter: function (value) {
var num = parseFloat(value) / 2.55;
return num && !isNaN(num) ? num.toFixed() : 0;
},
slider: {
max: 255,
min: 0,
step: 5,
field: 'brightness',
// sliderWidth: '60', // Custom slider width
// sliderHeight: '270', // Custom slider height
request: {
type: "call_service",
domain: "light",
service: "turn_on",
field: "brightness"
},
},
}
```

Expand Down
Binary file modified images/tile-screenshots/SLIDER.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
37 changes: 37 additions & 0 deletions scripts/controllers/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,43 @@ App.controller('Main', function ($scope, $timeout, $location, Api, tmhDynamicLoc
};
};

$scope.sliderStyles = function (page, item) {
return cacheInItem(item, '_c_slider_styles', function () {
const styles = {};

if (item.vertical) {
const width = item.width || 1;
const height = item.height || 1;
const tileSize = page.tileSize || CONFIG.tileSize;
const tileMargin = page.tileMargin || CONFIG.tileMargin;

const itemWidth = tileSize * width + tileMargin * (width - 1);
const itemHeight = tileSize * height + tileMargin * (height - 1);

// defines free space used by icon
const iconSpacer = item.icon ? 0 : 35;

// if `item.slider.sliderHeight` is defined - set SIZE to custom value
// if `item.slider.sliderHeight` is not defined: make calculation and compare if it greater than (0/20).
// This prevents:
// - to show very small sliders
// - negative values, which can break alignment
// If the user would like to override automatic calculation, an appropriate option has to be defined.
const sliderWidth = item.slider.sliderWidth || Math.max(0, itemWidth - 30);
const sliderHeight = item.slider.sliderHeight || (x => x > 22 ? x : 0)(itemHeight - 100 + iconSpacer);

styles.width = sliderHeight + 'px';
styles.height = sliderWidth + 'px';
styles.top = sliderHeight / 2 - sliderWidth / 2 + 'px';
styles.right = sliderWidth / 2 - sliderHeight / 2 + 'px';
} else {
styles.width = item.slider.sliderWidth ? item.slider.sliderWidth + 'px' : '80%';
styles.height = item.slider.sliderHeight ? item.slider.sliderHeight + 'px' : '1rem';
}
return styles;
});
};

$scope.itemStyles = function (page, item, entity) {
const prevSize = item._prevTileSize || page.tileSize || CONFIG.tileSize;
const currentSize = page.tileSize || CONFIG.tileSize;
Expand Down
36 changes: 33 additions & 3 deletions scripts/directives/tile.html
Original file line number Diff line number Diff line change
Expand Up @@ -307,14 +307,15 @@
<div ng-if="item.type === TYPES.SLIDER && (_c = getSliderConf(item, entity))"
class="item-entity-container" ng-class="{'-slider-bottom': item.bottom}">

<div class="item-entity">
<!-- legacy -->

<div ng-if="item.legacy" class="item-entity">
<span ng-bind="getSliderValue(item, entity, _c)"
class="item-entity--value"></span>
<span ng-if="(_unit = entityUnit(item, entity))"
class="item-entity--unit" ng-bind="_unit"></span>
</div>

<div class="item-slider">
<div ng-if="item.legacy" class="item-slider">
<input type="range"
ng-model="_c.getSetValue"
ng-model-options="{ getterSetter: true }"
Expand All @@ -324,6 +325,35 @@
ng-on-pointerdown="$event.stopPropagation()"
step="{{ _c.step }}" min="{{ _c.min }}" max="{{ _c.max }}">
</div>

<!-- vertical/horizontal -->

<div ng-if="!item.legacy && (_c_styles = sliderStyles(page, item))"
class="slider-inner"
ng-class="{'slider-inner-direction-column': item.vertical || (!item.vertical && !item.singleLine),'slider-inner-direction-row': !item.vertical && item.singleLine}">

<div class="slider-inner-icon" ng-if="item.icon">
<span class="item-entity--icon mdi" ng-class="entityIcon(item, entity)"></span>
</div>

<div class="slider-inner-value" ng-style="{'order': !item.vertical && item.singleLine ? 1 : 0}">
{{ getSliderValue(item, entity, _c) }}<span ng-if="(_unit = entityUnit(item, entity))">{{ _unit }}</span>
</div>

<div class="range-holder"
ng-class="{'-vertical': item.vertical,'-horizontal': !item.vertical}"
ng-style="item.vertical ? {height: _c_styles.width, width: _c_styles.height} : {height: _c_styles.height, width: _c_styles.width}">
<input type="range"
ng-model="_c.getSetValue"
ng-model-options="{ getterSetter: true }"
ng-change="sliderChanged(item, entity, _c)"
ng-on-touchstart="$event.stopPropagation()"
ng-on-touchmove="$event.stopPropagation()"
ng-on-pointerdown="$event.stopPropagation()"
step="{{ _c.step }}" min="{{ _c.min }}" max="{{ _c.max }}"
ng-style="item.vertical && _c_styles">
</div>
</div>
</div>

<div ng-if="item.type === TYPES.IFRAME"
Expand Down
156 changes: 155 additions & 1 deletion styles/main.less
Original file line number Diff line number Diff line change
Expand Up @@ -1558,6 +1558,7 @@ camera_stream {
-webkit-appearance: none;
}

// Used in light tile.
&-container {
margin: 10px 5px 0;

Expand All @@ -1566,6 +1567,7 @@ camera_stream {
}
}

// Used in light tile.
&-title {
text-align: left;
font-size: 0.9em;
Expand All @@ -1583,7 +1585,7 @@ camera_stream {
box-sizing: border-box;
margin: 0;
cursor: pointer;
}
}
}

.item-entity-sliders {
Expand Down Expand Up @@ -2243,6 +2245,158 @@ input[type=range]::-moz-range-thumb {
cursor: pointer;
}

@sliderBorderRadius: 2px;
@sliderColor: #FFF;
@sliderTrackColor: #808080;
@sliderThumbColor: #DDD;
@sliderTrackBg: #808080;

.slider-inner {
align-items: center;
display: flex;
height: 100%;
width: 100%;

&-icon {
height: 35px;
margin-top: -10px;
text-align: center;
width: 35px;
}

&-value {
font-weight: 300;
margin-bottom: 5px;
margin-top: -10px;
}

.range-holder {
margin-bottom: 8px;
position: relative;

input[type="range"] {
-webkit-appearance: none;
background-color: @sliderTrackColor;
border: 0;
border-radius: @sliderBorderRadius;
margin: 0;
outline: 0;
overflow: hidden;
transition: box-shadow 0.2s ease-in-out;
width: 100%;
height: 100%;

&::-webkit-slider-runnable-track {
-webkit-appearance: none;
background-color: @sliderTrackBg;
height: 100%;
transition: box-shadow 0.2s ease-in-out;
}

&::-moz-range-track {
background-color: @sliderTrackBg;
height: 100%;
margin-top: -1px;
transition: box-shadow 0.2s ease-in-out;
}

&::-webkit-slider-thumb {
-webkit-appearance: none;
background: @sliderColor;
border-bottom: 1em solid @sliderColor;
border-left: 1.2em solid @sliderColor;
border-right: 1.2em solid @sliderColor;
border-top: 1em solid @sliderColor;
border-radius: 0;
box-shadow: -350px 0 0 350px @sliderColor, inset 0 0 0 80px @sliderThumbColor;
cursor: ns-resize;
font-size: 9px;
height: 100%;
margin-top: 0px;
transition: box-shadow 0.2s ease-in-out;
width: 20px;
}

&::-moz-range-thumb {
background: @sliderColor;
border-bottom: 1em solid @sliderColor;
border-left: 1.2em solid @sliderColor;
border-right: 1.2em solid @sliderColor;
border-top: 1em solid @sliderColor;
border-radius: 0;
box-shadow: -350px 0 0 350px @sliderColor, inset 0 0 0 80px @sliderThumbColor;
cursor: ns-resize;
font-size: 9px;
height: 70%;
transition: box-shadow 0.2s ease-in-out;
width: 3px;
}
}

&.-vertical input[type="range"] {
position: absolute;
transform: rotate(270deg);

&::-webkit-slider-thumb {
width: 25px;
}
}

&.-horizontal input[type="range"] {
&::-webkit-slider-thumb {
border-left: 0.9em solid @sliderColor;
border-right: 0.9em solid @sliderColor;
cursor: ew-resize;
}
}
}

&-direction-row {
flex-direction: row;
justify-content: space-between;

.slider-inner-icon {
.item-entity--icon {
font-size: 1.5rem;
line-height: 25px;
}
}

.slider-inner-value {
font-size: 1rem;
margin-left: 10px;
margin-right: 10px;
padding-bottom: 5px;
}

.range-holder {
margin-left: 5px;
margin-top: -12px;
width: 100%;
}
}

&-direction-column {
flex-direction: column;
justify-content: center;

.slider-inner-value {
font-size: 1rem;
padding-top: 7px;
}

.slider-inner-icon {
.item-entity--icon {
font-size: 1.7rem;
line-height: 30px;
}
}

.range-holder.-horizontal {
padding-bottom: 5px;
}
}
}

@keyframes slides2 {
0% { transform: translate(0, 0); }
Expand Down
23 changes: 22 additions & 1 deletion styles/themes.less
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,16 @@
.noty {
box-shadow: 0 19px 38px rgba(0, 0, 0, 0.30), 0 15px 12px rgba(0, 0, 0, 0.22);
}

@sliderThumbColor: #808080;

.slider-inner {
.range-holder {
input[type="range"] {
border: 1px solid @sliderThumbColor;
}
}
}
}

.-theme-transparent {
Expand Down Expand Up @@ -608,6 +618,18 @@
}
}

@sliderBorderRadius: 6px;
@sliderThumbColor: #808080;

.slider-inner {
.range-holder {
& input[type="range"] {
border-radius: @sliderBorderRadius;
border: 1px solid @sliderThumbColor;
}
}
}

&.-theme-mobile {
.item {
&-state {
Expand All @@ -617,7 +639,6 @@
}
}


.-theme-fresh-air {
background: rgb(21, 89, 208);
color: #fff;
Expand Down

0 comments on commit e713089

Please sign in to comment.