feat(range): improve range and gesture components

This commit is contained in:
mhartington
2017-09-08 12:04:05 -04:00
parent 663deb2694
commit 84d8ab4fee
6 changed files with 691 additions and 108 deletions

View File

@ -3,6 +3,42 @@ import { BlockerDelegate, GestureController, GestureDelegate, BLOCK_ALL } from '
import { Component, Element, Event, EventEmitter, Listen, Prop, PropDidChange } from '@stencil/core'; import { Component, Element, Event, EventEmitter, Listen, Prop, PropDidChange } from '@stencil/core';
import { PanRecognizer } from './recognizers'; import { PanRecognizer } from './recognizers';
/**
* @name Range
* @description
* The Range slider lets users select from a range of values by moving
* the slider knob. It can accept dual knobs, but by default one knob
* controls the value of the range.
*
* ### Range Labels
* Labels can be placed on either side of the range by adding the
* `range-start` or `range-end` property to the element. The element
* doesn't have to be an `ion-label`, it can be added to any element
* to place it to the left or right of the range. See [usage](#usage)
* below for examples.
*
*
* ### Minimum and Maximum Values
* Minimum and maximum values can be passed to the range through the `min`
* and `max` properties, respectively. By default, the range sets the `min`
* to `0` and the `max` to `100`.
*
*
* ### Steps and Snaps
* The `step` property specifies the value granularity of the range's value.
* It can be useful to set the `step` when the value isn't in increments of `1`.
* Setting the `step` property will show tick marks on the range for each step.
* The `snaps` property can be set to automatically move the knob to the nearest
* tick mark based on the step property value.
*
*
* ### Dual Knobs
* Setting the `dual-knobs` property to `true` on the range component will
* enable two knobs on the range. If the range has two knobs, the value will
* be an object containing two properties: `lower` and `upper`.
*
*
*/
@Component({ @Component({
tag: 'ion-gesture' tag: 'ion-gesture'
@ -44,6 +80,8 @@ export class Gesture {
@Prop() onMove: GestureCallback; @Prop() onMove: GestureCallback;
@Prop() onEnd: GestureCallback; @Prop() onEnd: GestureCallback;
@Prop() onPress: GestureCallback; @Prop() onPress: GestureCallback;
@Prop() onDown: GestureCallback;
@Prop() onUp: GestureCallback;
@Prop() notCaptured: GestureCallback; @Prop() notCaptured: GestureCallback;
@ -149,7 +187,7 @@ export class Gesture {
this.pan.start(detail.startX, detail.startY); this.pan.start(detail.startX, detail.startY);
} }
this.onDown(detail)
return true; return true;
} }
@ -305,7 +343,7 @@ export class Gesture {
detail.event = ev; detail.event = ev;
this.calcGestureData(ev); this.calcGestureData(ev);
this.onUp(detail)
if (this.pan) { if (this.pan) {
if (this.hasCapturedPan) { if (this.hasCapturedPan) {
detail.type = 'pan'; detail.type = 'pan';

View File

@ -11,6 +11,7 @@ export class RangeKnob {
@Prop() val: number; @Prop() val: number;
@Prop() disabled: boolean; @Prop() disabled: boolean;
@Prop() labelId: string; @Prop() labelId: string;
@Prop() knob: string;
@Prop() ratio: number; @Prop() ratio: number;
@Event() ionIncrease: EventEmitter; @Event() ionIncrease: EventEmitter;
@ -20,11 +21,11 @@ export class RangeKnob {
handleKeyBoard(ev: KeyboardEvent) { handleKeyBoard(ev: KeyboardEvent) {
const keyCode = ev.keyCode; const keyCode = ev.keyCode;
if (keyCode === KEY_LEFT || keyCode === KEY_DOWN) { if (keyCode === KEY_LEFT || keyCode === KEY_DOWN) {
this.ionDecrease.emit({isIncrease: false}); this.ionDecrease.emit({isIncrease: false, knob: this.knob});
ev.preventDefault(); ev.preventDefault();
ev.stopPropagation(); ev.stopPropagation();
} else if (keyCode === KEY_RIGHT || keyCode === KEY_UP) { } else if (keyCode === KEY_RIGHT || keyCode === KEY_UP) {
this.ionIncrease.emit({isIncrease: true}); this.ionIncrease.emit({isIncrease: true, knob: this.knob});
ev.preventDefault(); ev.preventDefault();
ev.stopPropagation(); ev.stopPropagation();
} }
@ -42,16 +43,16 @@ export class RangeKnob {
'range-knob-max': this.val === this.max 'range-knob-max': this.val === this.max
}, },
style: { style: {
left: this.leftPos(this.ratio) 'left': this.leftPos(this.ratio)
}, },
attrs: { attrs: {
role: 'slider', 'role': 'slider',
tabindex: this.disabled ? -1 : 0, 'tabindex': this.disabled ? -1 : 0,
'aria-valuemin': `${this.min}`, 'aria-valuemin': this.min,
'aria-valuemax': `${this.max}`, 'aria-valuemax': this.max,
'aria-disabled': `${this.disabled}`, 'aria-disabled': this.disabled,
'aria-labelledby': `${this.labelId}`, 'aria-labelledby': this.labelId,
'aria-valuenow': `${this.val}` 'aria-valuenow': this.val
} }
}; };
} }

View File

@ -0,0 +1,227 @@
@import "../../themes/ionic.globals.ios";
// iOS Range
// --------------------------------------------------
/// @prop - Padding top/bottom of the range
$range-ios-padding-vertical: 8px !default;
/// @prop - Padding start/end of the range
$range-ios-padding-horizontal: 16px !default;
/// @prop - Height of the range slider
$range-ios-slider-height: 42px !default;
/// @prop - Width of the area that will select the range knob
$range-ios-hit-width: 42px !default;
/// @prop - Height of the area that will select the range knob
$range-ios-hit-height: $range-ios-slider-height !default;
/// @prop - Height of the range bar
$range-ios-bar-height: 1px !default;
/// @prop - Background of the range bar
$range-ios-bar-background-color: #bdbdbd !default;
/// @prop - Background of the active range bar
$range-ios-bar-active-background-color: color($colors-ios, primary) !default;
/// @prop - Width of the range knob
$range-ios-knob-width: 28px !default;
/// @prop - Height of the range knob
$range-ios-knob-height: $range-ios-knob-width !default;
/// @prop - Box shadow of the range knob
$range-ios-knob-box-shadow: 0 3px 1px rgba(0, 0, 0, .1), 0 4px 8px rgba(0, 0, 0, .13), 0 0 0 1px rgba(0, 0, 0, .02) !default;
/// @prop - Border radius of the range knob
$range-ios-knob-border-radius: 50% !default;
/// @prop - Background of the range knob
$range-ios-knob-background-color: #fff !default;
/// @prop - Width of the range tick
$range-ios-tick-width: $range-ios-bar-height !default;
/// @prop - Height of the range tick
$range-ios-tick-height: 8px !default;
/// @prop - Border radius of the range tick
$range-ios-tick-border-radius: 0 !default;
/// @prop - Background of the range tick
$range-ios-tick-background-color: $range-ios-bar-background-color !default;
/// @prop - Background of the active range tick
$range-ios-tick-active-background-color: $range-ios-bar-active-background-color !default;
/// @prop - Background of the range pin
$range-ios-pin-background-color: transparent !default;
/// @prop - Color of the range pin
$range-ios-pin-color: $text-ios-color !default;
/// @prop - Font size of the range pin
$range-ios-pin-font-size: 12px !default;
// deprecated
$range-ios-pin-padding: null !default;
/// @prop - Padding top of the range pin
$range-ios-pin-padding-top: 8px !default;
/// @prop - Padding end of the range pin
$range-ios-pin-padding-end: $range-ios-pin-padding-top !default;
/// @prop - Padding bottom of the range pin
$range-ios-pin-padding-bottom: $range-ios-pin-padding-top !default;
/// @prop - Padding start of the range pin
$range-ios-pin-padding-start: $range-ios-pin-padding-end !default;
.range-ios {
@include padding($range-ios-padding-vertical, $range-ios-padding-horizontal);
}
.range-ios [range-left] {
@include margin(0, 20px, 0, 0);
}
.range-ios [range-right] {
@include margin(0, 0, 0, 20px);
}
.range-ios.range-has-pin {
@include padding($range-ios-padding-vertical + $range-ios-pin-font-size, null, null, null);
}
.range-ios .range-slider {
height: $range-ios-slider-height;
}
.range-ios .range-bar {
@include position(($range-ios-slider-height / 2), null, null, 0);
@include border-radius(1px);
position: absolute;
width: 100%;
height: $range-ios-bar-height;
background: $range-ios-bar-background-color;
pointer-events: none;
}
.range-ios.range-pressed .range-bar-active {
will-change: left, right;
}
.range-ios.range-pressed .range-knob-handle {
will-change: left;
}
.range-ios .range-bar-active {
bottom: 0;
width: auto;
background: $range-ios-bar-active-background-color;
}
.range-ios .range-knob-handle {
@include position(($range-ios-slider-height / 2), null, null, 0);
@include margin(-($range-ios-hit-height / 2), null, null, -($range-ios-hit-width / 2));
@include text-align(center);
position: absolute;
width: $range-ios-hit-width;
height: $range-ios-hit-height;
}
.range-ios .range-knob {
@include position(($range-ios-hit-height / 2) - ($range-ios-knob-height / 2) + ($range-ios-bar-height / 2) - .5px,
null, null, ($range-ios-hit-width / 2) - ($range-ios-knob-width / 2));
@include border-radius($range-ios-knob-border-radius);
position: absolute;
width: $range-ios-knob-width;
height: $range-ios-knob-height;
background: $range-ios-knob-background-color;
box-shadow: $range-ios-knob-box-shadow;
pointer-events: none;
}
.range-ios .range-tick {
@include margin-horizontal(-($range-ios-tick-width / 2), null);
@include border-radius($range-ios-tick-border-radius);
position: absolute;
top: ($range-ios-hit-height / 2) - ($range-ios-tick-height / 2) + ($range-ios-bar-height / 2);
width: $range-ios-tick-width;
height: $range-ios-tick-height;
background: $range-ios-tick-background-color;
pointer-events: none;
}
.range-ios .range-tick-active {
background: $range-ios-tick-active-background-color;
}
.range-ios .range-pin {
@include text-align(center);
@include border-radius(50px);
@include transform(translate3d(0, 28px, 0), scale(.01));
position: relative;
top: -20px;
display: inline-block;
min-width: 28px;
font-size: $range-ios-pin-font-size;
color: $range-ios-pin-color;
background: $range-ios-pin-background-color;
transition: transform 120ms ease;
@include deprecated-variable(padding, $range-ios-pin-padding) {
@include padding($range-ios-pin-padding-top, $range-ios-pin-padding-end, $range-ios-pin-padding-bottom, $range-ios-pin-padding-start);
}
}
.range-ios .range-knob-pressed .range-pin {
@include transform(translate3d(0, 0, 0), scale(1));
}
.range-ios.range-disabled {
opacity: .5;
}
// Generate iOS Range Colors
// --------------------------------------------------
@each $color-name, $color-base, $color-contrast in get-colors($colors-ios) {
.range-ios-#{$color-name} {
.range-bar-active,
.range-tick-active {
background: $color-base;
}
}
}

View File

@ -1,23 +1,13 @@
import { import { Component, Element, Event, EventEmitter, Listen, Method, Prop, PropDidChange, State } from '@stencil/core';
Component,
Element,
Event,
EventEmitter,
Listen,
Method,
Prop,
PropDidChange,
State
} from '@stencil/core';
import { BaseInputComponent, GestureDetail } from '../../index'; import { BaseInputComponent, GestureDetail } from '../../index';
import { clamp } from '../../utils/helpers'; import { clamp } from '../../utils/helpers';
@Component({ @Component({
tag: 'ion-range', tag: 'ion-range',
styleUrls: { styleUrls: {
// ios: 'toggle.ios.scss', ios: 'range.ios.scss',
md: 'range.md.scss' md: 'range.md.scss',
// wp: 'toggle.wp.scss' wp: 'range.wp.scss'
}, },
host: { host: {
theme: 'range' theme: 'range'
@ -39,7 +29,7 @@ export class Range implements BaseInputComponent {
@State() _valB: number = 0; @State() _valB: number = 0;
@State() _ratioA: number = 0; @State() _ratioA: number = 0;
@State() _ratioB: number = 0; @State() _ratioB: number = 0;
@State() _ticks: any[]; @State() _ticks: any[] = [];
@State() _activeB: boolean; @State() _activeB: boolean;
@State() _rect: ClientRect; @State() _rect: ClientRect;
@ -51,12 +41,13 @@ export class Range implements BaseInputComponent {
@Event() ionStyle: EventEmitter; @Event() ionStyle: EventEmitter;
@Event() ionFocus: EventEmitter; @Event() ionFocus: EventEmitter;
@Event() ionBlur: EventEmitter; @Event() ionBlur: EventEmitter;
//
// @Prop() color: string; @Prop() color: string;
// @Prop() mode: string; @Prop() mode: string;
@Prop({ state: true }) @Prop({ state: true })
value: any; value: any;
@Prop() disabled: boolean = false; @Prop() disabled: boolean = false;
@Prop() min: number = 0; @Prop() min: number = 0;
@Prop() max: number = 100; @Prop() max: number = 100;
@ -66,10 +57,6 @@ export class Range implements BaseInputComponent {
@Prop() snaps: boolean = false; @Prop() snaps: boolean = false;
@Prop() debounce: number = 0; @Prop() debounce: number = 0;
private canStart() {
return !this.disabled;
}
fireBlur() { fireBlur() {
if (this.hasFocus) { if (this.hasFocus) {
this.hasFocus = false; this.hasFocus = false;
@ -83,8 +70,15 @@ export class Range implements BaseInputComponent {
this.emitStyle(); this.emitStyle();
} }
@PropDidChange('value')
valueChanged(val: boolean) {
this.ionChange.emit({ value: val });
this.emitStyle();
}
ionViewWillLoad() { ionViewWillLoad() {
this._inputUpdated(); this.inputUpdated();
this.createTicks();
this.emitStyle(); this.emitStyle();
} }
@ -106,22 +100,21 @@ export class Range implements BaseInputComponent {
} }
} }
_inputUpdated() { inputUpdated() {
const val = this.value; const val = this.value;
if (this.dualKnobs) { if (this.dualKnobs) {
this._valA = val.lower; this._valA = val.lower;
this._valB = val.upper; this._valB = val.upper;
this._ratioA = this._valueToRatio(val.lower); this._ratioA = this.valueToRatio(val.lower);
this._ratioB = this._valueToRatio(val.upper); this._ratioB = this.valueToRatio(val.upper);
} else { } else {
this._valA = val; this._valA = val;
this._ratioA = this._valueToRatio(val); this._ratioA = this.valueToRatio(val);
} }
this.updateBar();
this._updateBar();
} }
_updateBar() { updateBar() {
const ratioA = this._ratioA; const ratioA = this._ratioA;
const ratioB = this._ratioB; const ratioB = this._ratioB;
@ -133,27 +126,25 @@ export class Range implements BaseInputComponent {
this._barR = `${100 - ratioA * 100}%`; this._barR = `${100 - ratioA * 100}%`;
} }
this._updateTicks(); this.updateTicks();
} }
_creatTicks() { createTicks() {
if (this.snaps) { if (this.snaps) {
this._ticks = [];
for (let value = this.min; value <= this.max; value += this.steps) { for (let value = this.min; value <= this.max; value += this.steps) {
let ratio = this._valueToRatio(value); let ratio = this.valueToRatio(value);
this._ticks.push({ this._ticks.push({
ratio, ratio,
left: `${ratio * 100}%` left: `${ratio * 100}%`
}); });
} }
this._updateTicks(); this.updateTicks();
} }
} }
_updateTicks() { updateTicks() {
const ticks = this._ticks; const ticks = this._ticks;
const ratio = this.ratio; const ratio = this.ratio;
if (this.snaps && ticks) { if (this.snaps && ticks) {
if (this.dualKnobs) { if (this.dualKnobs) {
let upperRatio = this.ratioUpper(); let upperRatio = this.ratioUpper();
@ -169,19 +160,19 @@ export class Range implements BaseInputComponent {
} }
} }
_valueToRatio(value: number) { valueToRatio(value: number) {
value = Math.round((value - this.min) / this.steps) * this.steps; value = Math.round((value - this.min) / this.steps) * this.steps;
value = value / (this.max - this.min); value = value / (this.max - this.min);
return clamp(0, value, 1); return clamp(0, value, 1);
} }
_ratioToValue(ratio: number) { ratioToValue(ratio: number) {
ratio = Math.round((this.max - this.min) * ratio); ratio = Math.round((this.max - this.min) * ratio);
ratio = Math.round(ratio / this.steps) * this.steps + this.min; ratio = Math.round(ratio / this.steps) * this.steps + this.min;
return clamp(this.min, ratio, this.max); return clamp(this.min, ratio, this.max);
} }
_inputNormalize(val: any): any { inputNormalize(val: any): any {
if (this.dualKnobs) { if (this.dualKnobs) {
return val; return val;
} else { } else {
@ -190,21 +181,16 @@ export class Range implements BaseInputComponent {
} }
} }
_update( update(current: { x?: number; y?: number }, rect: ClientRect, isPressed: boolean) {
current: { x?: number; y?: number },
rect: ClientRect,
isPressed: boolean
) {
// figure out where the pointer is currently at // figure out where the pointer is currently at
// update the knob being interacted with // update the knob being interacted with
let ratio = clamp(0, (current.x - rect.left) / rect.width, 1); let ratio = clamp(0, (current.x - rect.left) / rect.width, 1);
let val = this._ratioToValue(ratio); let val = this.ratioToValue(ratio);
if (this.snaps) { if (this.snaps) {
// snaps the ratio to the current value // snaps the ratio to the current value
ratio = this._valueToRatio(val); ratio = this.valueToRatio(val);
} }
// update which knob is pressed // update which knob is pressed
this._pressed = isPressed; this._pressed = isPressed;
let valChanged = false; let valChanged = false;
@ -224,7 +210,8 @@ export class Range implements BaseInputComponent {
valChanged = val === this._valA; valChanged = val === this._valA;
this._valA = val; this._valA = val;
} }
this._updateBar();
this.updateBar();
if (valChanged) { if (valChanged) {
return false; return false;
} }
@ -271,72 +258,83 @@ export class Range implements BaseInputComponent {
} }
@Listen('ionIncrease, ionDecrease') @Listen('ionIncrease, ionDecrease')
_keyChg(ev: any) { keyChng(ev: RangeEvent) {
const step = this.steps; const step = this.steps;
// if (isKnobB) { if (ev.detail.knob === 'knobB') {
// if (isIncrease) { if (!!ev.detail.isIncrease) {
// this._valB += step; this._valB += step;
// } else { } else {
// this._valB -= step; this._valB -= step;
// } }
// this._valB = clamp(this.min, this._valB, this.max); this._valB = clamp(this.min, this._valB, this.max);
// this._ratioB = this._valueToRatio(this._valB); this._ratioB = this.valueToRatio(this._valB);
// } else {
if (!!ev.detail.isIncrease) {
this._valA += step;
} else { } else {
this._valA -= step; if (!!ev.detail.isIncrease) {
this._valA += step;
} else {
this._valA -= step;
}
this._valA = clamp(this.min, this._valA, this.max);
this._ratioA = this.valueToRatio(this._valA);
} }
this._valA = clamp(this.min, this._valA, this.max); this.updateBar();
this._ratioA = this._valueToRatio(this._valA);
// }
this._updateBar();
} }
onPress(detail: GestureDetail) { canStart() {
console.log('on press') const el = this.rangeEl.querySelector('.range-slider');
if (this.disabled) { this._rect = el.getBoundingClientRect();
return false; return !this.disabled;
} }
onDown(detail: GestureDetail) {
if (this.disabled) return false;
this.fireFocus(); this.fireFocus();
const current = { x: detail.currentX, y: detail.currentY }; const current = { x: detail.currentX, y: detail.currentY };
const rect = (this._rect = this.rangeEl.getBoundingClientRect()); const rect = this._rect;
// figure out which knob they started closer to // figure out which knob they started closer to
const ratio = clamp(0, (current.x - rect.left) / rect.width, 1); const ratio = clamp(0, (current.x - rect.left) / rect.width, 1);
this._activeB = this._activeB =
this.dualKnobs && this.dualKnobs &&
Math.abs(ratio - this._ratioA) > Math.abs(ratio - this._ratioB); Math.abs(ratio - this._ratioA) > Math.abs(ratio - this._ratioB);
// update the active knob's position // update the active knob's position
this._update(current, rect, true); this.update(current, rect, true);
// return true so the pointer events // return true so the pointer events
// know everything's still valid // know everything's still valid
return true; return true;
} }
onUp(detail: GestureDetail) {
if (this.disabled) {
return;
}
// update the active knob's position
this.update({ x: detail.currentX, y: detail.currentY }, this._rect, false);
// trigger ionBlur event
this.fireBlur();
}
onDragMove(detail: GestureDetail) { onDragMove(detail: GestureDetail) {
console.log('drag start')
if (this.disabled) { if (this.disabled) {
return; return;
} }
const current = { x: detail.currentX, y: detail.currentY }; const current = { x: detail.currentX, y: detail.currentY };
// update the active knob's position // update the active knob's position
this._update(current, this._rect, true); this.update(current, this._rect, true);
} }
onDragEnd(detail: GestureDetail) {
console.log('drag end')
if (this.disabled) {
return;
}
// update the active knob's position
this._update({ x: detail.currentX, y: detail.currentY }, this._rect, false);
// trigger ionBlur event hostData() {
this.fireBlur(); return {
class: {
'range-disabled': this.disabled,
'range-pressed': this._pressed,
'range-has-pin': this.pin
}
};
} }
render() { render() {
@ -346,17 +344,26 @@ export class Range implements BaseInputComponent {
<ion-gesture <ion-gesture
props={{ props={{
disableScroll: true, disableScroll: true,
onPress: this.onPress.bind(this), canStart: this.canStart.bind(this),
onDown: this.onDown.bind(this),
onMove: this.onDragMove.bind(this), onMove: this.onDragMove.bind(this),
onEnd: this.onDragEnd.bind(this), onUp: this.onUp.bind(this),
gestureName: 'range', gestureName: 'range',
gesturePriority: 30, gesturePriority: 30,
type: 'pan,press', type: 'press,pan',
direction: 'x', direction: 'x',
threshold: 0 threshold: 0
}} }}
> >
<div class="range-slider"> <div class="range-slider">
{this._ticks.map(t =>
<div
style={{ left: t.left }}
role="presentation"
class={{ 'range-tick': true, 'range-tick-active': t.active }}
/>
)}
<div class="range-bar" role="presentation" /> <div class="range-bar" role="presentation" />
<div <div
class="range-bar range-bar-active" class="range-bar range-bar-active"
@ -368,6 +375,7 @@ export class Range implements BaseInputComponent {
/> />
<ion-range-knob <ion-range-knob
class="range-knob-handle" class="range-knob-handle"
knob="knobA"
pressed={this._pressedA} pressed={this._pressedA}
ratio={this._ratioA} ratio={this._ratioA}
val={this._valA} val={this._valA}
@ -375,9 +383,29 @@ export class Range implements BaseInputComponent {
min={this.min} min={this.min}
max={this.max} max={this.max}
/> />
{this.dualKnobs
? <ion-range-knob
class="range-knob-handle"
knob="knobB"
pressed={this._pressedB}
ratio={this._ratioB}
val={this._valB}
pin={this.pin}
min={this.min}
max={this.max}
/>
: null}
</div> </div>
</ion-gesture>, </ion-gesture>,
<slot name="range-end" /> <slot name="range-end" />
]; ];
} }
} }
export interface RangeEvent {
detail: {
isIncrease: boolean,
knob: string
};
}

View File

@ -0,0 +1,223 @@
@import "../../themes/ionic.globals.wp";
// Windows Range
// --------------------------------------------------
/// @prop - Padding top/bottom of the range
$range-wp-padding-vertical: 8px !default;
/// @prop - Padding start/end of the range
$range-wp-padding-horizontal: 8px !default;
/// @prop - Height of the range slider
$range-wp-slider-height: 42px !default;
/// @prop - Width of the area that will select the range knob
$range-wp-hit-width: 42px !default;
/// @prop - Height of the area that will select the range knob
$range-wp-hit-height: $range-wp-slider-height !default;
/// @prop - Height of the range bar
$range-wp-bar-height: 2px !default;
/// @prop - Background of the range bar
$range-wp-bar-background-color: #bdbdbd !default;
/// @prop - Background of the active range bar
$range-wp-bar-active-background-color: color($colors-wp, primary) !default;
/// @prop - Width of the range knob
$range-wp-knob-width: 8px !default;
/// @prop - Height of the range knob
$range-wp-knob-height: $range-wp-knob-width * 3 !default;
/// @prop - Background of the range knob
$range-wp-knob-background-color: $range-wp-bar-active-background-color !default;
/// @prop - Border radius of the range knob
$range-wp-knob-border-radius: $range-wp-knob-width / 2 !default;
/// @prop - Width of the range tick
$range-wp-tick-width: $range-wp-bar-height !default;
/// @prop - Height of the range tick
$range-wp-tick-height: $range-wp-tick-width * 3 !default;
/// @prop - Border radius of the range tick
$range-wp-tick-border-radius: $range-wp-knob-width / 2 !default;
/// @prop - Background of the range tick
$range-wp-tick-background-color: $range-wp-bar-background-color !default;
/// @prop - Background of the active range tick
$range-wp-tick-active-background-color: $range-wp-bar-active-background-color !default;
/// @prop - Background of the range pin
$range-wp-pin-background-color: $range-wp-bar-active-background-color !default;
/// @prop - Color of the range pin
$range-wp-pin-color: color-contrast($colors-wp, $range-wp-bar-active-background-color) !default;
/// @prop - Font size of the range pin
$range-wp-pin-font-size: 12px !default;
// deprecated
$range-wp-pin-padding: null !default;
/// @prop - Padding top of the range pin
$range-wp-pin-padding-top: 8px !default;
/// @prop - Padding end of the range pin
$range-wp-pin-padding-end: $range-wp-pin-padding-top !default;
/// @prop - Padding bottom of the range pin
$range-wp-pin-padding-bottom: $range-wp-pin-padding-top !default;
/// @prop - Padding start of the range pin
$range-wp-pin-padding-start: $range-wp-pin-padding-end !default;
.range-wp {
@include padding($range-wp-padding-vertical, $range-wp-padding-horizontal);
}
.range-wp [range-left] {
@include margin(0, 12px, 0, 0);
}
.range-wp [range-right] {
@include margin(0, 0, 0, 12px);
}
.range-wp.range-has-pin {
@include padding($range-wp-padding-vertical + $range-wp-pin-font-size + $range-wp-pin-padding-top, null, null, null);
}
.range-wp .range-slider {
height: $range-wp-slider-height;
}
.range-wp .range-bar {
@include position(($range-wp-slider-height / 2), null, null, 0);
position: absolute;
width: 100%;
height: $range-wp-bar-height;
background: $range-wp-bar-background-color;
pointer-events: none;
}
.range-wp.range-pressed .range-bar-active {
will-change: left, right;
}
.range-wp.range-pressed .range-knob-handle {
will-change: left;
}
.range-wp .range-bar-active {
bottom: 0;
width: auto;
background: $range-wp-bar-active-background-color;
}
.range-wp .range-knob-handle {
@include position(($range-wp-slider-height / 2), null, null, 0);
@include margin(-($range-wp-hit-height / 2), null, null, -($range-wp-hit-width / 2));
@include text-align(center);
position: absolute;
width: $range-wp-hit-width;
height: $range-wp-hit-height;
}
.range-wp .range-knob {
@include position(($range-wp-hit-height / 2) - ($range-wp-knob-height / 2) + ($range-wp-bar-height / 2),
null, null, ($range-wp-hit-width / 2) - ($range-wp-knob-width / 2));
@include border-radius($range-wp-knob-border-radius);
position: absolute;
width: $range-wp-knob-width;
height: $range-wp-knob-height;
background: $range-wp-knob-background-color;
pointer-events: none;
}
.range-wp .range-tick {
@include margin-horizontal(-($range-wp-tick-width / 2), null);
@include border-radius($range-wp-tick-border-radius);
position: absolute;
top: ($range-wp-hit-height / 2) - ($range-wp-tick-height / 2) + ($range-wp-bar-height / 2);
width: $range-wp-tick-width;
height: $range-wp-tick-height;
background: $range-wp-tick-background-color;
pointer-events: none;
}
.range-wp .range-tick-active {
background: $range-wp-tick-active-background-color;
}
.range-wp .range-pin {
@include text-align(center);
@include border-radius(50px);
@include transform(translate3d(0, 28px, 0), scale(.01));
position: relative;
top: -24px;
display: inline-block;
min-width: 28px;
font-size: $range-wp-pin-font-size;
color: $range-wp-pin-color;
background: $range-wp-pin-background-color;
transition: transform 120ms ease;
@include deprecated-variable(padding, $range-wp-pin-padding) {
@include padding($range-wp-pin-padding-top, $range-wp-pin-padding-end, $range-wp-pin-padding-bottom, $range-wp-pin-padding-start);
}
}
.range-wp .range-knob-pressed .range-pin {
@include transform(translate3d(0, 0, 0), scale(1));
}
.range-wp.range-disabled {
opacity: .5;
}
// Generate Windows Range Colors
// --------------------------------------------------
@each $color-name, $color-base, $color-contrast in get-colors($colors-wp) {
.range-wp-#{$color-name} {
.range-bar-active,
.range-tick-active,
.range-knob,
.range-pin {
background: $color-base;
}
}
}

View File

@ -17,20 +17,86 @@
</ion-header> </ion-header>
<ion-content> <ion-content>
<ion-list> <ion-list>
<ion-list-header>
Range color
</ion-list-header>
<ion-item> <ion-item>
<ion-range value="40" id="range"> <ion-range value="20"></ion-range>
<ion-icon small name="sunny" slot="range-start"></ion-icon> </ion-item>
<ion-icon name="sunny" slot="range-end"></ion-icon> <ion-item>
</ion-range> <ion-range value="40" color="secondary"></ion-range>
</ion-item>
<ion-item>
<ion-range value="60" color="light"></ion-range>
</ion-item>
<ion-item>
<ion-range value="80" color="dark"></ion-range>
</ion-item>
<ion-item>
<ion-range value="100" color="danger"></ion-range>
</ion-item> </ion-item>
</ion-list> </ion-list>
<button onClick='elFunction()'> Helllo</button>
<ion-list>
<ion-list-header>
Mode
</ion-list-header>
<ion-item>
<ion-range value="50" mode="md"></ion-range>
</ion-item>
<ion-item>
<ion-range value="50" mode="ios"></ion-range>
</ion-item>
<ion-item>
<ion-range value="50" mode="wp"></ion-range>
</ion-item>
</ion-list>
<ion-list>
<ion-list-header>
Options
</ion-list-header>
<ion-item>
<ion-range pin="true"></ion-range>
</ion-item>
<ion-item>
<ion-range min="1000" max="2000" steps="100" snaps="true" id="range"></ion-range>
</ion-item>
<ion-item>
<ion-range dual-knobs="true" id="multiKnob"></ion-range>
</ion-item>
<ion-item>
<ion-range color="danger">
<ion-icon small name="thermometer" slot="range-start"></ion-icon>
<ion-icon name="thermometer" slot="range-end"></ion-icon>
</ion-range>
</ion-item>
</ion-list>
<ion-button onclick="elTest()">Test</ion-button>
</ion-content> </ion-content>
</ion-app> </ion-app>
<script> <script>
function elFunction(){ var knob = document.getElementById('multiKnob')
var range = document.querySelector('ion-range'); knob.value = {
console.log(range.ratio()) lower: 33,
upper: 60
}
knob.addEventListener('ionFocus', function(ev) {
console.log('focus', ev)
})
knob.addEventListener('ionBlur', function(ev) {
console.log('blur', ev)
})
knob.addEventListener('ionChange', function(ev) {
console.log('change', ev)
})
function elTest() {
var range = document.getElementById('range');
range.disabled = !range.disabled;
} }
</script> </script>
</body> </body>