mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
Merge branch 'core-range' into core
This commit is contained in:
@@ -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';
|
||||
|
||||
74
packages/core/src/components/range/range-knob.tsx
Normal file
74
packages/core/src/components/range/range-knob.tsx
Normal 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;
|
||||
227
packages/core/src/components/range/range.ios.scss
Normal file
227
packages/core/src/components/range/range.ios.scss
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
279
packages/core/src/components/range/range.md.scss
Normal file
279
packages/core/src/components/range/range.md.scss
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
56
packages/core/src/components/range/range.scss
Normal file
56
packages/core/src/components/range/range.scss
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
401
packages/core/src/components/range/range.tsx
Normal file
401
packages/core/src/components/range/range.tsx
Normal 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
|
||||
};
|
||||
}
|
||||
223
packages/core/src/components/range/range.wp.scss
Normal file
223
packages/core/src/components/range/range.wp.scss
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
104
packages/core/src/components/range/test/basic.html
Normal file
104
packages/core/src/components/range/test/basic.html
Normal 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>
|
||||
@@ -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 {
|
||||
|
||||
@@ -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',
|
||||
|
||||
Reference in New Issue
Block a user