Merge branch 'core-range' into core

This commit is contained in:
Manuel Mtz-Almeida
2017-09-18 20:01:11 +02:00
10 changed files with 1371 additions and 7 deletions

View File

@@ -1,10 +1,5 @@
<<<<<<< HEAD
import { Element, Component, Listen, Prop } from '@stencil/core';
import { Nav, NavContainer } from '../../navigation/nav-interfaces';
=======
import { Component, Element, Listen, Prop } from '@stencil/core';
import { Nav, NavContainer, OverlayPortal } from '../../navigation/nav-interfaces';
>>>>>>> fffa9b54606f47336abfbb232405186787180b12
import { Nav, NavContainer } from '../../navigation/nav-interfaces';
import { Config } from '../..';
import { App } from './app-interfaces';
import { isReady } from '../../utils/helpers';

View File

@@ -0,0 +1,74 @@
import { Component, Event, EventEmitter, Listen, Prop } from '@stencil/core';
@Component({
tag: `ion-range-knob`
})
export class RangeKnob {
@Prop() pressed: boolean;
@Prop() pin: boolean;
@Prop() min: number;
@Prop() max: number;
@Prop() val: number;
@Prop() disabled: boolean;
@Prop() labelId: string;
@Prop() knob: string;
@Prop() ratio: number;
@Event() ionIncrease: EventEmitter;
@Event() ionDecrease: EventEmitter;
@Listen('keydown')
handleKeyBoard(ev: KeyboardEvent) {
const keyCode = ev.keyCode;
if (keyCode === KEY_LEFT || keyCode === KEY_DOWN) {
this.ionDecrease.emit({isIncrease: false, knob: this.knob});
ev.preventDefault();
ev.stopPropagation();
} else if (keyCode === KEY_RIGHT || keyCode === KEY_UP) {
this.ionIncrease.emit({isIncrease: true, knob: this.knob});
ev.preventDefault();
ev.stopPropagation();
}
}
leftPos(val: number) {
return `${val * 100}%`;
}
hostData() {
return {
class: {
'range-knob-pressed': this.pressed,
'range-knob-min': this.val === this.min || this.val === undefined,
'range-knob-max': this.val === this.max
},
style: {
'left': this.leftPos(this.ratio)
},
attrs: {
'role': 'slider',
'tabindex': this.disabled ? -1 : 0,
'aria-valuemin': this.min,
'aria-valuemax': this.max,
'aria-disabled': this.disabled,
'aria-labelledby': this.labelId,
'aria-valuenow': this.val
}
};
}
render() {
if (this.pin) {
return [
<div class='range-pin' role='presentation'>{this.val}</div>,
<div class='range-knob' role='presentation' />
];
}
return <div class='range-knob' role='presentation' />;
}
}
export const KEY_LEFT = 37;
export const KEY_UP = 38;
export const KEY_RIGHT = 39;
export const KEY_DOWN = 40;

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

@@ -0,0 +1,279 @@
@import "../../themes/ionic.globals.md";
@import "./range";
// Material Design Range
// --------------------------------------------------
/// @prop - Padding top/bottom of the range
$range-md-padding-vertical: 8px !default;
/// @prop - Padding start/end of the range
$range-md-padding-horizontal: 8px !default;
/// @prop - Height of the range slider
$range-md-slider-height: 42px !default;
/// @prop - Width of the area that will select the range knob
$range-md-hit-width: 42px !default;
/// @prop - Height of the area that will select the range knob
$range-md-hit-height: $range-md-slider-height !default;
/// @prop - Height of the range bar
$range-md-bar-height: 2px !default;
/// @prop - Background of the range bar
$range-md-bar-background-color: #bdbdbd !default;
/// @prop - Background of the active range bar
$range-md-bar-active-background-color: color($colors-md, primary) !default;
/// @prop - Width of the range knob
$range-md-knob-width: 18px !default;
/// @prop - Height of the range knob
$range-md-knob-height: $range-md-knob-width !default;
/// @prop - Background of the range knob
$range-md-knob-background-color: $range-md-bar-active-background-color !default;
/// @prop - Background of the range knob when the value is the minimum
$range-md-knob-min-background-color: $background-md-color !default;
/// @prop - Border of the range knob when the value is the minimum
$range-md-knob-min-border: 2px solid $range-md-bar-background-color !default;
/// @prop - Width of the range tick
$range-md-tick-width: 2px !default;
/// @prop - Height of the range tick
$range-md-tick-height: $range-md-tick-width !default;
/// @prop - Border radius of the range tick
$range-md-tick-border-radius: 50% !default;
/// @prop - Background of the range tick
$range-md-tick-background-color: #000 !default;
/// @prop - Background of the active range tick
$range-md-tick-active-background-color: $range-md-tick-background-color !default;
/// @prop - Background of the range pin
$range-md-pin-background-color: $range-md-bar-active-background-color !default;
/// @prop - Color of the range pin
$range-md-pin-color: color-contrast($colors-md, $range-md-bar-active-background-color) !default;
/// @prop - Font size of the range pin
$range-md-pin-font-size: 12px !default;
/// @prop - Padding top/bottom of the range pin
$range-md-pin-padding-vertical: 8px !default;
/// @prop - Padding start/end of the range pin
$range-md-pin-padding-horizontal: 0 !default;
/// @prop - Background of the range pin when the value is the minimum
$range-md-pin-min-background-color: $range-md-bar-background-color !default;
.range-md {
@include padding($range-md-padding-vertical, $range-md-padding-horizontal);
}
.range-md [slot="range-start"] {
@include margin(0, 12px, 0, 0);
}
.range-md [slot="range-end"] {
@include margin(0, 0, 0, 12px);
}
.range-md.range-has-pin {
@include padding($range-md-padding-vertical + $range-md-pin-font-size + $range-md-pin-padding-vertical, null, null, null);
}
.range-md .range-slider {
height: $range-md-slider-height;
}
.range-md .range-bar {
@include position(($range-md-slider-height / 2), null, null, 0);
position: absolute;
width: 100%;
height: $range-md-bar-height;
background: $range-md-bar-background-color;
pointer-events: none;
}
.range-md.range-pressed .range-bar-active {
will-change: left, right;
}
.range-md.range-pressed .range-knob-handle {
will-change: left;
}
.range-md .range-bar-active {
bottom: 0;
width: auto;
background: $range-md-bar-active-background-color;
}
.range-md .range-knob-handle {
@include position(($range-md-slider-height / 2), null, null, 0);
@include margin(-($range-md-hit-height / 2), null, null, -($range-md-hit-width / 2));
@include text-align(center);
position: absolute;
width: $range-md-hit-width;
height: $range-md-hit-height;
}
.range-md .range-knob {
@include position(($range-md-hit-height / 2) - ($range-md-knob-height / 2) + ($range-md-bar-height / 2),
null, null, ($range-md-hit-width / 2) - ($range-md-knob-width / 2));
@include border-radius(50%);
position: absolute;
z-index: 2;
width: $range-md-knob-width;
height: $range-md-knob-height;
background: $range-md-knob-background-color;
transform: scale(.67);
transition-duration: 120ms;
transition-property: transform, background-color, border;
transition-timing-function: ease;
pointer-events: none;
}
.range-md .range-tick {
@include margin-horizontal(-($range-md-tick-width / 2), null);
@include border-radius($range-md-tick-border-radius);
position: absolute;
top: ($range-md-hit-height / 2) - ($range-md-tick-height / 2) + ($range-md-bar-height / 2);
z-index: 1;
width: $range-md-tick-width;
height: $range-md-tick-height;
background: $range-md-tick-background-color;
pointer-events: none;
}
.range-md .range-tick-active {
background: $range-md-tick-active-background-color;
}
.range-md .range-pin {
@include padding($range-md-pin-padding-vertical, $range-md-pin-padding-horizontal);
@include text-align(center);
@include border-radius(50%);
@include transform(translate3d(0, 28px, 0), scale(.01));
position: relative;
top: -20px;
display: inline-block;
min-width: 28px;
height: 28px;
font-size: $range-md-pin-font-size;
color: $range-md-pin-color;
background: $range-md-pin-background-color;
transition: transform 120ms ease, background-color 120ms ease;
&::before {
@include position(3px, null, null, 50%);
@include border-radius(50%, 50%, 50%, 0);
@include margin-horizontal(-13px, null);
position: absolute;
z-index: -1;
width: 26px;
height: 26px;
background: $range-md-pin-background-color;
content: "";
transform: rotate(-45deg);
transition: background-color 120ms ease;
}
}
.range-md .range-knob-pressed .range-pin {
@include transform(translate3d(0, 0, 0), scale(1));
}
.range-md:not(.range-has-pin) .range-knob-pressed .range-knob {
transform: scale(1);
}
@mixin md-range-min() {
.range-md .range-knob-min.range-knob-min {
.range-knob {
border: $range-md-knob-min-border;
background: $range-md-knob-min-background-color;
}
.range-pin,
.range-pin::before {
color: color-contrast($colors-md, $range-md-pin-min-background-color);
background: $range-md-pin-min-background-color;
}
}
}
@include md-range-min();
.range-md.range-disabled {
.range-bar-active {
background-color: $range-md-bar-background-color;
}
.range-knob {
outline: 5px solid #fff;
background-color: $range-md-bar-background-color;
transform: scale(.55);
}
}
// Generate Material Design Range Colors
// --------------------------------------------------
@each $color-name, $color-base, $color-contrast in get-colors($colors-md) {
.range-md-#{$color-name} {
@include md-range-min();
.range-bar-active,
.range-knob,
.range-pin,
.range-pin::before {
background: $color-base;
}
}
}

View File

@@ -0,0 +1,56 @@
@import "../../themes/ionic.globals";
// Range
// --------------------------------------------------
.item .item-inner {
overflow: visible;
width: 100%;
}
.item .input-wrapper {
overflow: visible;
flex-direction: column;
width: 100%;
}
.item ion-range {
width: 100%;
}
.item ion-range ion-label {
align-self: center;
}
ion-range {
position: relative;
display: flex;
align-items: center;
ion-label {
flex: initial;
}
ion-icon {
min-height: 2.4rem;
font-size: 2.4rem;
line-height: 1;
}
ion-gesture,
.range-slider {
position: relative;
flex: 1;
cursor: pointer;
}
}

View File

@@ -0,0 +1,401 @@
import { Component, Element, Event, EventEmitter, Listen, Method, Prop, PropDidChange, State } from '@stencil/core';
import { BaseInputComponent, GestureDetail } from '../../index';
import { clamp } from '../../utils/helpers';
@Component({
tag: 'ion-range',
styleUrls: {
ios: 'range.ios.scss',
md: 'range.md.scss',
wp: 'range.wp.scss'
},
host: {
theme: 'range'
}
})
export class Range implements BaseInputComponent {
activated: boolean = false;
hasFocus: boolean = false;
id: string;
labelId: string;
startX: number;
styleTmr: any;
@Element() rangeEl: HTMLElement;
@State() _barL: string;
@State() _barR: string;
@State() _valA: number = 0;
@State() _valB: number = 0;
@State() _ratioA: number = 0;
@State() _ratioB: number = 0;
@State() _ticks: any[] = [];
@State() _activeB: boolean;
@State() _rect: ClientRect;
@State() _pressed: boolean;
@State() _pressedA: boolean;
@State() _pressedB: boolean;
@Event() ionChange: EventEmitter;
@Event() ionStyle: EventEmitter;
@Event() ionFocus: EventEmitter;
@Event() ionBlur: EventEmitter;
@Prop() color: string;
@Prop() mode: string;
@Prop({ mutable: true }) value: any;
@Prop() disabled: boolean = false;
@Prop() min: number = 0;
@Prop() max: number = 100;
@Prop() steps: number = 1;
@Prop() dualKnobs: boolean = false;
@Prop() pin: boolean = false;
@Prop() snaps: boolean = false;
@Prop() debounce: number = 0;
fireBlur() {
if (this.hasFocus) {
this.hasFocus = false;
this.ionBlur.emit();
this.emitStyle();
}
}
@PropDidChange('disabled')
disabledChanged() {
this.emitStyle();
}
@PropDidChange('value')
valueChanged(val: boolean) {
this.ionChange.emit({ value: val });
this.emitStyle();
}
ionViewWillLoad() {
this.inputUpdated();
this.createTicks();
this.emitStyle();
}
private emitStyle() {
clearTimeout(this.styleTmr);
this.styleTmr = setTimeout(() => {
this.ionStyle.emit({
'range-disabled': this.disabled
});
});
}
fireFocus() {
if (!this.hasFocus) {
this.hasFocus = true;
this.ionFocus.emit();
this.emitStyle();
}
}
inputUpdated() {
const val = this.value;
if (this.dualKnobs) {
this._valA = val.lower;
this._valB = val.upper;
this._ratioA = this.valueToRatio(val.lower);
this._ratioB = this.valueToRatio(val.upper);
} else {
this._valA = val;
this._ratioA = this.valueToRatio(val);
}
this.updateBar();
}
updateBar() {
const ratioA = this._ratioA;
const ratioB = this._ratioB;
if (this.dualKnobs) {
this._barL = `${Math.min(ratioA, ratioB) * 100}%`;
this._barR = `${100 - Math.max(ratioA, ratioB) * 100}%`;
} else {
this._barL = '';
this._barR = `${100 - ratioA * 100}%`;
}
this.updateTicks();
}
createTicks() {
if (this.snaps) {
for (let value = this.min; value <= this.max; value += this.steps) {
let ratio = this.valueToRatio(value);
this._ticks.push({
ratio,
left: `${ratio * 100}%`
});
}
this.updateTicks();
}
}
updateTicks() {
const ticks = this._ticks;
const ratio = this.ratio;
if (this.snaps && ticks) {
if (this.dualKnobs) {
let upperRatio = this.ratioUpper();
ticks.forEach(t => {
t.active = t.ratio >= ratio && t.ratio <= upperRatio;
});
} else {
ticks.forEach(t => {
t.active = t.ratio <= ratio;
});
}
}
}
valueToRatio(value: number) {
value = Math.round((value - this.min) / this.steps) * this.steps;
value = value / (this.max - this.min);
return clamp(0, value, 1);
}
ratioToValue(ratio: number) {
ratio = Math.round((this.max - this.min) * ratio);
ratio = Math.round(ratio / this.steps) * this.steps + this.min;
return clamp(this.min, ratio, this.max);
}
inputNormalize(val: any): any {
if (this.dualKnobs) {
return val;
} else {
val = parseFloat(val);
return isNaN(val) ? undefined : val;
}
}
update(current: { x?: number; y?: number }, rect: ClientRect, isPressed: boolean) {
// figure out where the pointer is currently at
// update the knob being interacted with
let ratio = clamp(0, (current.x - rect.left) / rect.width, 1);
let val = this.ratioToValue(ratio);
if (this.snaps) {
// snaps the ratio to the current value
ratio = this.valueToRatio(val);
}
// update which knob is pressed
this._pressed = isPressed;
let valChanged = false;
if (this._activeB) {
// when the pointer down started it was determined
// that knob B was the one they were interacting with
this._pressedB = isPressed;
this._pressedA = false;
this._ratioB = ratio;
valChanged = val === this._valB;
this._valB = val;
} else {
// interacting with knob A
this._pressedA = isPressed;
this._pressedB = false;
this._ratioA = ratio;
valChanged = val === this._valA;
this._valA = val;
}
this.updateBar();
if (valChanged) {
return false;
}
// value has been updated
let value;
if (this.dualKnobs) {
// dual knobs have an lower and upper value
value = {
lower: Math.min(this._valA, this._valB),
upper: Math.max(this._valA, this._valB)
};
} else {
// single knob only has one value
value = this._valA;
}
// Update input value
this.value = value;
return true;
}
@Method()
ratio(): number {
if (this.dualKnobs) {
return Math.min(this._ratioA, this._ratioB);
}
return this._ratioA;
}
@Method()
ratioUpper() {
if (this.dualKnobs) {
return Math.max(this._ratioA, this._ratioB);
}
return null;
}
@Listen('ionIncrease, ionDecrease')
keyChng(ev: RangeEvent) {
const step = this.steps;
if (ev.detail.knob === 'knobB') {
if (!!ev.detail.isIncrease) {
this._valB += step;
} else {
this._valB -= step;
}
this._valB = clamp(this.min, this._valB, this.max);
this._ratioB = this.valueToRatio(this._valB);
} else {
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.updateBar();
}
onDragStart(detail: GestureDetail) {
if (this.disabled) return false;
this.fireFocus();
const current = { x: detail.currentX, y: detail.currentY };
const el = this.rangeEl.querySelector('.range-slider');
this._rect = el.getBoundingClientRect();
const rect = this._rect;
// figure out which knob they started closer to
const ratio = clamp(0, (current.x - rect.left) / rect.width, 1);
this._activeB =
this.dualKnobs &&
Math.abs(ratio - this._ratioA) > Math.abs(ratio - this._ratioB);
// update the active knob's position
this.update(current, rect, true);
// return true so the pointer events
// know everything's still valid
return true;
}
onDragEnd(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) {
if (this.disabled) {
return;
}
const current = { x: detail.currentX, y: detail.currentY };
// update the active knob's position
this.update(current, this._rect, true);
}
hostData() {
return {
class: {
'range-disabled': this.disabled,
'range-pressed': this._pressed,
'range-has-pin': this.pin
}
};
}
render() {
return [
<slot name='range-start' />,
<ion-gesture
props={{
disableScroll: true,
onStart: this.onDragStart.bind(this),
onMove: this.onDragMove.bind(this),
onEnd: this.onDragEnd.bind(this),
enabled: !this.disabled,
gestureName: 'range',
gesturePriority: 30,
type: 'pan',
direction: 'x',
threshold: 0
}}
>
<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 range-bar-active'
style={{
left: this._barL,
right: this._barR
}}
role='presentation'
/>
<ion-range-knob
class='range-knob-handle'
knob='knobA'
pressed={this._pressedA}
ratio={this._ratioA}
val={this._valA}
pin={this.pin}
min={this.min}
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>
</ion-gesture>,
<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

@@ -0,0 +1,104 @@
<!DOCTYPE html>
<html dir="ltr">
<head>
<meta charset="UTF-8">
<title>Ionic Range</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
<script src="/dist/ionic.js"></script>
</head>
<body>
<ion-app>
<ion-header>
<ion-toolbar>
<ion-title>Range</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-list>
<ion-list-header>
Range color
</ion-list-header>
<ion-item>
<ion-range value="20"></ion-range>
</ion-item>
<ion-item>
<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-list>
<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-app>
<script>
var knob = document.getElementById('multiKnob')
knob.value = {
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>
</body>
</html>

View File

@@ -1,5 +1,9 @@
import { StencilElement } from '..';
export function clamp(min: number, n: number, max: number) {
return Math.max(min, Math.min(n, max));
}
export function isDef(v: any): boolean { return v !== undefined && v !== null; }
export function isUndef(v: any): boolean { return v === undefined || v === null; }
@@ -161,6 +165,7 @@ export function getToolbarHeight(toolbarTagName: string, pageChildren: HTMLEleme
return '';
}
/** @hidden */
export type Side = 'left' | 'right' | 'start' | 'end';
export function checkEdgeSide(posX: number, isRightSide: boolean, maxEdgeStart: number): boolean {

View File

@@ -29,10 +29,10 @@ exports.config = {
{ components: ['ion-slides', 'ion-slide'] },
{ components: ['ion-spinner'] },
{ components: ['ion-split-pane'] },
{ components: ['ion-range', 'ion-range-knob']},
{ components: ['ion-tabs', 'ion-tab', 'ion-tab-bar', 'ion-tab-button', 'ion-tab-highlight'] },
{ components: ['ion-toggle'] },
{ components: ['ion-nav', 'ion-nav-controller', 'stencil-ion-nav-delegate','page-one', 'page-two', 'page-three'] },
{ components: ['ion-toast', 'ion-toast-controller'] },
],
preamble: '(C) Ionic http://ionicframework.com - MIT License',