From 2c6e11b10dc53790fd033b1619503adf08a8e87e Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Fri, 27 May 2016 13:20:36 -0500 Subject: [PATCH 1/8] feat(range): create ion-range input --- src/components.ios.scss | 1 + src/components.md.scss | 1 + src/components.wp.scss | 1 + src/components/item/item.ts | 2 +- src/components/range/range.ios.scss | 162 ++++++ src/components/range/range.md.scss | 4 + src/components/range/range.ts | 633 +++++++++++++++++++++ src/components/range/range.wp.scss | 4 + src/components/range/test/basic/e2e.ts | 0 src/components/range/test/basic/index.ts | 22 + src/components/range/test/basic/main.html | 133 +++++ src/components/range/test/basic/page1.html | 86 +++ src/config/directives.ts | 2 + 13 files changed, 1050 insertions(+), 1 deletion(-) create mode 100644 src/components/range/range.ios.scss create mode 100644 src/components/range/range.md.scss create mode 100644 src/components/range/range.ts create mode 100644 src/components/range/range.wp.scss create mode 100644 src/components/range/test/basic/e2e.ts create mode 100644 src/components/range/test/basic/index.ts create mode 100644 src/components/range/test/basic/main.html create mode 100644 src/components/range/test/basic/page1.html diff --git a/src/components.ios.scss b/src/components.ios.scss index 44fd2e067b..9e7dac405a 100644 --- a/src/components.ios.scss +++ b/src/components.ios.scss @@ -25,6 +25,7 @@ "components/picker/picker.ios", "components/popover/popover.ios", "components/radio/radio.ios", + "components/range/range.ios", "components/searchbar/searchbar.ios", "components/segment/segment.ios", "components/select/select.ios", diff --git a/src/components.md.scss b/src/components.md.scss index f5f4a4b93a..8541e9c6b2 100644 --- a/src/components.md.scss +++ b/src/components.md.scss @@ -24,6 +24,7 @@ "components/picker/picker.md", "components/popover/popover.md", "components/radio/radio.md", + "components/range/range.md", "components/searchbar/searchbar.md", "components/segment/segment.md", "components/select/select.md", diff --git a/src/components.wp.scss b/src/components.wp.scss index a9ad7f635e..8bae231b46 100644 --- a/src/components.wp.scss +++ b/src/components.wp.scss @@ -24,6 +24,7 @@ "components/picker/picker.wp", "components/popover/popover.wp", "components/radio/radio.wp", + "components/range/range.wp", "components/searchbar/searchbar.wp", "components/segment/segment.wp", "components/select/select.wp", diff --git a/src/components/item/item.ts b/src/components/item/item.ts index 30cb0edf0b..28a9682fd7 100644 --- a/src/components/item/item.ts +++ b/src/components/item/item.ts @@ -48,7 +48,7 @@ import {Label} from '../label/label'; '' + '' + '' + - '' + + '' + '' + '' + '' + diff --git a/src/components/range/range.ios.scss b/src/components/range/range.ios.scss new file mode 100644 index 0000000000..5c729e782a --- /dev/null +++ b/src/components/range/range.ios.scss @@ -0,0 +1,162 @@ +@import "../../globals.ios"; + +// iOS Range +// -------------------------------------------------- + +$range-ios-slider-height: 42px !default; + +$range-ios-hit-width: 42px !default; +$range-ios-hit-height: $range-ios-slider-height !default; + +$range-ios-bar-height: 2px !default; +$range-ios-bar-background-color: #bdbdbd !default; +$range-ios-bar-active-background-color: color($colors-ios, primary) !default; + +$range-ios-knob-width: 12px !default; +$range-ios-knob-height: $range-ios-knob-width !default; +$range-ios-knob-background-color: $range-ios-bar-active-background-color !default; + +$range-ios-tick-width: 6px !default; +$range-ios-tick-height: $range-ios-tick-width !default; +$range-ios-tick-background-color: $range-ios-bar-background-color !default; +$range-ios-tick-active-background-color: $range-ios-bar-active-background-color !default; + +$range-ios-pin-background-color: $range-ios-bar-active-background-color !default; +$range-ios-pin-color: color-contrast($colors-ios, $range-ios-pin-background-color) !default; +$range-ios-pin-font-size: 12px !default; + + +.item-range .item-inner { + overflow: visible; +} + +.item-range .input-wrapper { + overflow: visible; + + flex-direction: column; +} + +.item-range ion-range { + width: 100%; +} + +ion-range { + position: relative; + display: block; + + margin-top: -16px; + padding: 8px; +} + +.range-slider { + position: relative; + + height: $range-ios-slider-height; + + cursor: pointer; +} + +.range-bar { + position: absolute; + top: ($range-ios-slider-height / 2); + left: 0; + + width: 100%; + height: $range-ios-bar-height; + + background: $range-ios-bar-background-color; + + pointer-events: none; +} + +.range-pressed .range-bar-active { + will-change: left, right; +} + +.range-pressed .range-knob-handle { + will-change: left; +} + +.range-bar-active { + bottom: 0; + + width: auto; + + background: $range-ios-bar-active-background-color; +} + +.range-knob-handle { + position: absolute; + top: ($range-ios-slider-height / 2); + left: 0%; + + margin-top: -($range-ios-hit-height / 2); + margin-left: -($range-ios-hit-width / 2); + + width: $range-ios-hit-width; + height: $range-ios-hit-height; + + text-align: center; +} + +.range-knob { + position: absolute; + top: ($range-ios-hit-height / 2) - ($range-ios-knob-height / 2) + ($range-ios-bar-height / 2); + left: ($range-ios-hit-width / 2) - ($range-ios-knob-width / 2); + + width: $range-ios-knob-width; + height: $range-ios-knob-height; + + border-radius: 50%; + + background: $range-ios-knob-background-color; + + pointer-events: none; +} + +.range-tick { + position: absolute; + top: ($range-ios-hit-height / 2) - ($range-ios-tick-height / 2) + ($range-ios-bar-height / 2); + + margin-left: ($range-ios-tick-width / 2) * -1; + + width: $range-ios-tick-width; + height: $range-ios-tick-height; + + border-radius: 50%; + + background: $range-ios-tick-background-color; + + pointer-events: none; +} + +.range-tick-active { + background: $range-ios-tick-active-background-color; +} + +.range-pin { + position: relative; + top: -20px; + display: inline-block; + + padding: 8px; + + min-width: 28px; + + border-radius: 50px; + + font-size: $range-ios-pin-font-size; + + text-align: center; + + color: $range-ios-pin-color; + + background: $range-ios-pin-background-color; + + transform: translate3d(0, 28px, 0) scale(.01); + transition: transform 120ms ease; +} + +.range-knob-pressed .range-pin { + transform: translate3d(0, 0, 0) scale(1); +} diff --git a/src/components/range/range.md.scss b/src/components/range/range.md.scss new file mode 100644 index 0000000000..7fb37a2c58 --- /dev/null +++ b/src/components/range/range.md.scss @@ -0,0 +1,4 @@ +@import "../../globals.md"; + +// Material Design Range +// -------------------------------------------------- diff --git a/src/components/range/range.ts b/src/components/range/range.ts new file mode 100644 index 0000000000..3249b02777 --- /dev/null +++ b/src/components/range/range.ts @@ -0,0 +1,633 @@ +import {Component, Optional, Input, Output, EventEmitter, ViewChild, ViewChildren, QueryList, Renderer, ElementRef, Provider, Inject, forwardRef, ViewEncapsulation} from '@angular/core'; +import {NG_VALUE_ACCESSOR} from '@angular/common'; + +import {Form} from '../../util/form'; +import {isTrueProperty, isNumber, isString, isPresent, clamp} from '../../util/util'; +import {Item} from '../item/item'; +import {pointerCoord} from '../../util/dom'; + + +const RANGE_VALUE_ACCESSOR = new Provider( + NG_VALUE_ACCESSOR, {useExisting: forwardRef(() => Range), multi: true}); + + +@Component({ + selector: '.range-knob-handle', + template: + '
{{_val}}
' + + '
', + host: { + '[class.range-knob-pressed]': 'pressed', + '[style.left]': '_x', + '[style.top]': '_y', + '[style.transform]': '_trns', + '[attr.aria-valuenow]': '_val', + '[attr.aria-valuemin]': 'range.min', + '[attr.aria-valuemax]': 'range.max', + 'role': 'slider', + 'tabindex': '0' + } +}) +export class RangeKnob { + private _ratio: number; + private _val: number; + private _x: string; + pressed: boolean; + + @Input() upper: boolean; + + constructor(@Inject(forwardRef(() => Range)) private range: Range) {} + + get ratio(): number { + return this._ratio; + } + set ratio(ratio: number) { + this._ratio = clamp(0, ratio, 1); + this._val = this.range.ratioToValue(this._ratio); + + if (this.range.snaps) { + this._ratio = this.range.valueToRatio(this._val); + } + } + + get value(): number { + return this._val; + } + set value(val: number) { + if (isString(val)) { + val = Math.round(val); + } + if (isNumber(val) && !isNaN(val)) { + this._ratio = this.range.valueToRatio(val); + this._val = this.range.ratioToValue(this._ratio); + } + } + + position() { + this._x = `${this._ratio * 100}%`; + } + + ngOnInit() { + if (isPresent(this.range.value)) { + // we already have a value + if (this.range.dualKnobs) { + // we have a value and there are two knobs + if (this.upper) { + // this is the upper knob + this.value = this.range.value.upper; + + } else { + // this is the lower knob + this.value = this.range.value.lower; + } + + } else { + // we have a value and there is only one knob + this.value = this.range.value; + } + + } else { + // we do not have a value so set defaults + this.ratio = ((this.range.dualKnobs && this.upper) ? 1 : 0); + } + + this.position(); + } + +} + + +/** + * @name Range + * + * @description + */ +@Component({ + selector: 'ion-range', + template: + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
', + host: { + '[class.range-disabled]': '_disabled', + '[class.range-pressed]': '_pressed', + }, + directives: [RangeKnob], + providers: [RANGE_VALUE_ACCESSOR], + encapsulation: ViewEncapsulation.None, +}) +export class Range { + private _dual: boolean = false; + private _pin: boolean; + private _disabled: boolean = false; + private _pressed: boolean; + private _labelId: string; + private _fn: Function; + + private _active: RangeKnob; + private _start: Coordinates = null; + private _rect: ClientRect; + private _ticks: any[]; + private _barL: string; + private _barR: string; + + private _min: number = 0; + private _max: number = 100; + private _step: number = 1; + private _snaps: boolean = false; + private _removes: Function[] = []; + private _mouseRemove: Function; + + value: any; + + @ViewChild('bar') private _bar: ElementRef; + @ViewChild('slider') private _slider: ElementRef; + @ViewChildren(RangeKnob) private _knobs: QueryList; + + /** + * @private + */ + id: string; + + /** + * @input {number} Minimum integer value of the range. Defaults to `0`. + */ + @Input() + get min(): number { + return this._min; + } + set min(val: number) { + val = Math.round(val); + if (!isNaN(val)) { + this._min = val; + } + } + + /** + * @input {number} Maximum integer value of the range. Defaults to `100`. + */ + @Input() + get max(): number { + return this._max; + } + set max(val: number) { + val = Math.round(val); + if (!isNaN(val)) { + this._max = val; + } + } + + /** + * @input {number} Specifies the value granularity. Defaults to `1`. + */ + @Input() + get step(): number { + return this._step; + } + set step(val: number) { + val = Math.round(val); + if (!isNaN(val) && val > 0) { + this._step = val; + } + } + + /** + * @input {number} If true, the knob snaps to tick marks evenly spaced based on the step property value. Defaults to `false`. + */ + @Input() + get snaps(): boolean { + return this._snaps; + } + set snaps(val: boolean) { + this._snaps = isTrueProperty(val); + } + + /** + * @input {number} If true, a pin with integer value is shown when the knob is pressed. Defaults to `false`. + */ + @Input() + get pin(): boolean { + return this._pin; + } + set pin(val: boolean) { + this._pin = isTrueProperty(val); + } + + /** + * @input {boolean} Show two knobs. Defaults to `false`. + */ + @Input() + get dualKnobs(): boolean { + return this._dual; + } + set dualKnobs(val: boolean) { + this._dual = isTrueProperty(val); + } + + /** + * @output {Range} Expression to evaluate when the range value changes. + */ + @Output() rangeChange: EventEmitter = new EventEmitter(); + + + constructor( + private _form: Form, + @Optional() private _item: Item, + private _renderer: Renderer + ) { + _form.register(this); + + if (_item) { + this.id = 'rng-' + _item.registerInput('range'); + this._labelId = 'lbl-' + _item.id; + _item.setCssClass('item-range', true); + } + } + + /** + * @private + */ + ngAfterViewInit() { + let barL = ''; + let barR = ''; + + let firstRatio = this._knobs.first.ratio; + + if (this._dual) { + let lastRatio = this._knobs.last.ratio; + barL = `${(Math.min(firstRatio, lastRatio) * 100)}%`; + barR = `${100 - (Math.max(firstRatio, lastRatio) * 100)}%`; + + } else { + barR = `${100 - (firstRatio * 100)}%`; + } + + this._renderer.setElementStyle(this._bar.nativeElement, 'left', barL); + this._renderer.setElementStyle(this._bar.nativeElement, 'right', barR); + + this.createTicks(); + + // add touchstart/mousedown listeners + this._renderer.listen(this._slider.nativeElement, 'touchstart', this.pointerDown.bind(this)); + this._mouseRemove = this._renderer.listen(this._slider.nativeElement, 'mousedown', this.pointerDown.bind(this)); + } + + /** + * @private + */ + pointerDown(ev: UIEvent) { + console.debug(`range, ${ev.type}`); + + // prevent default so scrolling does not happen + ev.preventDefault(); + ev.stopPropagation(); + + if (ev.type === 'touchstart') { + // if this was a touchstart, then let's remove the mousedown + this._mouseRemove && this._mouseRemove(); + } + + // get the start coordinates + this._start = pointerCoord(ev); + + // get the full dimensions of the slider element + let rect: ClientRect = this._rect = this._slider.nativeElement.getBoundingClientRect(); + + // figure out the offset + // the start of the pointer could actually + // have been left or right of the slider bar + if (this._start.x < rect.left) { + rect.xOffset = (this._start.x - rect.left); + + } else if (this._start.x > rect.right) { + rect.xOffset = (this._start.x - rect.right); + + } else { + rect.xOffset = 0; + } + + // figure out which knob we're interacting with + this.setActiveKnob(this._start, rect); + + // update the ratio for the active knob + this.updateKnob(this._start, rect); + + // ensure past listeners have been removed + this.clearListeners(); + + // update the active knob's position + this._active.position(); + this._pressed = this._active.pressed = true; + + // add a move listener depending on touch/mouse + let renderer = this._renderer; + let removes = this._removes; + + if (ev.type === 'touchstart') { + removes.push(renderer.listen(this._slider.nativeElement, 'touchmove', this.pointerMove.bind(this))); + removes.push(renderer.listen(this._slider.nativeElement, 'touchend', this.pointerUp.bind(this))); + + } else { + removes.push(renderer.listenGlobal('body', 'mousemove', this.pointerMove.bind(this))); + removes.push(renderer.listenGlobal('body', 'mouseup', this.pointerUp.bind(this))); + } + } + + /** + * @private + */ + pointerMove(ev: UIEvent) { + console.debug(`range, ${ev.type}`); + + // prevent default so scrolling does not happen + ev.preventDefault(); + ev.stopPropagation(); + + if (this._start !== null && this._active !== null) { + // only use pointer move if it's a valid pointer + // and we already have start coordinates + + // update the ratio for the active knob + this.updateKnob(pointerCoord(ev), this._rect); + + // update the active knob's position + this._active.position(); + this._pressed = this._active.pressed = true; + + } else { + // ensure listeners have been removed + this.clearListeners(); + } + } + + /** + * @private + */ + pointerUp(ev: UIEvent) { + console.debug(`range, ${ev.type}`); + + // prevent default so scrolling does not happen + ev.preventDefault(); + ev.stopPropagation(); + + // update the ratio for the active knob + this.updateKnob(pointerCoord(ev), this._rect); + + // update the active knob's position + this._active.position(); + + // clear the start coordinates and active knob + this._start = this._active = null; + + // ensure listeners have been removed + this.clearListeners(); + } + + /** + * @private + */ + clearListeners() { + this._pressed = this._knobs.first.pressed = this._knobs.last.pressed = false; + + for (var i = 0; i < this._removes.length; i++) { + this._removes[i](); + } + this._removes.length = 0; + } + + /** + * @private + */ + setActiveKnob(current: Coordinates, rect: ClientRect) { + // figure out which knob is the closest one to the pointer + let ratio = (current.x - rect.left) / (rect.width); + + if (this._dual && Math.abs(ratio - this._knobs.first.ratio) > Math.abs(ratio - this._knobs.last.ratio)) { + this._active = this._knobs.last; + + } else { + this._active = this._knobs.first; + } + } + + /** + * @private + */ + updateKnob(current: Coordinates, rect: ClientRect) { + // figure out where the pointer is currently at + // update the knob being interacted with + if (this._active) { + let oldVal = this._active.value; + this._active.ratio = (current.x - rect.left) / (rect.width); + let newVal = this._active.value; + + if (oldVal !== newVal) { + // value has been updated + if (this._dual) { + this.value = { + lower: Math.min(this._knobs.first.value, this._knobs.last.value), + upper: Math.max(this._knobs.first.value, this._knobs.last.value), + }; + + } else { + this.value = newVal; + } + + this.onChange(this.value); + } + + this.updateBar(); + } + } + + /** + * @private + */ + updateBar() { + let firstRatio = this._knobs.first.ratio; + + if (this._dual) { + let lastRatio = this._knobs.last.ratio; + this._barL = `${(Math.min(firstRatio, lastRatio) * 100)}%`; + this._barR = `${100 - (Math.max(firstRatio, lastRatio) * 100)}%`; + + } else { + this._barL = ''; + this._barR = `${100 - (firstRatio * 100)}%`; + } + + this.updateTicks(); + } + + /** + * @private + */ + createTicks() { + if (this._snaps) { + this._ticks = []; + for (var value = this._min; value <= this._max; value += this._step) { + var ratio = this.valueToRatio(value); + this._ticks.push({ + ratio: ratio, + left: `${ratio * 100}%`, + }); + } + this.updateTicks(); + + } else { + this._ticks = null; + } + } + + /** + * @private + */ + updateTicks() { + if (this._snaps) { + let ratio = this.ratio; + if (this._dual) { + let upperRatio = this.ratioUpper; + + this._ticks.forEach(t => { + t.active = (t.ratio >= ratio && t.ratio <= upperRatio); + }); + + } else { + this._ticks.forEach(t => { + t.active = (t.ratio <= ratio); + }); + } + } + } + + /** + * @private + */ + ratioToValue(ratio: number) { + ratio = Math.round(((this._max - this._min) * ratio) + this._min); + return Math.round(ratio / this._step) * this._step; + } + + /** + * @private + */ + valueToRatio(value: number) { + value = Math.round(clamp(this._min, value, this._max) / this._step) * this._step; + return (value - this._min) / (this._max - this._min); + } + + /** + * @private + */ + writeValue(val: any) { + if (isPresent(val)) { + let knobs = this._knobs; + this.value = val; + + if (this._knobs) { + if (this._dual) { + knobs.first.value = val.lower; + knobs.last.value = val.upper; + knobs.last.position(); + + } else { + knobs.first.value = val; + } + knobs.first.position(); + this.updateBar(); + } + } + } + + /** + * @private + */ + registerOnChange(fn: Function): void { + this._fn = fn; + this.onChange = (val: any) => { + fn(val); + this.onTouched(); + }; + } + + /** + * @private + */ + registerOnTouched(fn) { this.onTouched = fn; } + + /** + * @input {boolean} whether or not the checkbox is disabled or not. + */ + @Input() + get disabled(): boolean { + return this._disabled; + } + set disabled(val: boolean) { + this._disabled = isTrueProperty(val); + this._item && this._item.setCssClass('item-range-disabled', this._disabled); + } + + /** + * Returns the ratio of the knob's is current location, which is a number between `0` and `1`. + * If two knobs are used, this property represents the lower value. + */ + get ratio(): number { + if (this._dual) { + return Math.min(this._knobs.first.ratio, this._knobs.last.ratio); + } + return this._knobs.first.ratio; + } + + /** + * Returns the ratio of the upper value's is current location, which is a number between `0` and `1`. + * If there is only one knob, then this will return `null`. + */ + get ratioUpper(): number { + if (this._dual) { + return Math.max(this._knobs.first.ratio, this._knobs.last.ratio); + } + return null; + } + + /** + * @private + */ + onChange(val: any) { + // used when this input does not have an ngModel or ngControl + this.onTouched(); + } + + /** + * @private + */ + onTouched() {} + + /** + * @private + */ + ngOnDestroy() { + this._form.deregister(this); + this.clearListeners(); + } +} + + +export interface ClientRect { + top?: number; + right?: number; + bottom?: number; + left?: number; + width?: number; + height?: number; + xOffset?: number; + yOffset?: number; +} + +export interface Coordinates { + x?: number; + y?: number; +} diff --git a/src/components/range/range.wp.scss b/src/components/range/range.wp.scss new file mode 100644 index 0000000000..edf79664cb --- /dev/null +++ b/src/components/range/range.wp.scss @@ -0,0 +1,4 @@ +@import "../../globals.wp"; + +// Windows Range +// -------------------------------------------------- diff --git a/src/components/range/test/basic/e2e.ts b/src/components/range/test/basic/e2e.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/components/range/test/basic/index.ts b/src/components/range/test/basic/index.ts new file mode 100644 index 0000000000..7cdfc8d9d6 --- /dev/null +++ b/src/components/range/test/basic/index.ts @@ -0,0 +1,22 @@ +import {App, Page} from '../../../../../src'; + + +@Page({ + templateUrl: 'page1.html' +}) +class Page1 { + singleValue: number; + singleValue2: number = 150; + singleValue3: number = 64; + singleValue4: number = 1300; + dualValue: any; + dualValue2 = {lower: 33, upper: 60}; +} + + +@App({ + templateUrl: 'main.html' +}) +class E2EApp { + rootPage = Page1; +} diff --git a/src/components/range/test/basic/main.html b/src/components/range/test/basic/main.html new file mode 100644 index 0000000000..4f1fff2076 --- /dev/null +++ b/src/components/range/test/basic/main.html @@ -0,0 +1,133 @@ + + + + Left Menu + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Right Menu + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/components/range/test/basic/page1.html b/src/components/range/test/basic/page1.html new file mode 100644 index 0000000000..bf0e15ebfb --- /dev/null +++ b/src/components/range/test/basic/page1.html @@ -0,0 +1,86 @@ + + + + + + Range + + + + + + + + + + + + + no init value, default min/max, {{singleValue}} + + + + +
{{singleValue2}}
+ init=150, min=-200, max=200 + +
{{singleValue2}}
+
+ + + step=2, {{singleValue3}} + + + + + step=100, snaps, {{singleValue4}} + + + + + dual, {{dualValue | json}} + + + + + dual, step=3, snaps, {{dualValue2 | json}} + + + + + pin, {{singleValue}} + + + + + init=150, min=-200, max=200, {{singleValue2}} + + + + + step=2, {{singleValue3}} + + + + + step=100, snaps, pin, {{singleValue4}} + + + + + dual, pin, {{dualValue | json}} + + + + + dual, step=3, snaps, {{dualValue2 | json}} + + + +
+ +
diff --git a/src/config/directives.ts b/src/config/directives.ts index b499145ff3..a50c130b8b 100644 --- a/src/config/directives.ts +++ b/src/config/directives.ts @@ -34,6 +34,7 @@ import {Label} from '../components/label/label'; import {Segment, SegmentButton} from '../components/segment/segment'; import {RadioButton} from '../components/radio/radio-button'; import {RadioGroup} from '../components/radio/radio-group'; +import {Range} from '../components/range/range'; import {Searchbar, SearchbarInput} from '../components/searchbar/searchbar'; import {Nav} from '../components/nav/nav'; import {NavPush, NavPop} from '../components/nav/nav-push'; @@ -197,6 +198,7 @@ export const IONIC_DIRECTIVES: any[] = [ Checkbox, RadioGroup, RadioButton, + Range, Select, Option, DateTime, From af6d5e4be4345984ac27568943e4b7dd65fcf2ad Mon Sep 17 00:00:00 2001 From: Brandy Carney Date: Fri, 27 May 2016 18:37:32 -0400 Subject: [PATCH 2/8] feat(range): add md and wp styling, tweak ios styling references #5422 --- src/components/range/range.ios.scss | 37 +++-- src/components/range/range.md.scss | 178 +++++++++++++++++++++ src/components/range/range.wp.scss | 175 ++++++++++++++++++++ src/components/range/test/basic/page1.html | 4 +- 4 files changed, 383 insertions(+), 11 deletions(-) diff --git a/src/components/range/range.ios.scss b/src/components/range/range.ios.scss index 5c729e782a..02e1994ea8 100644 --- a/src/components/range/range.ios.scss +++ b/src/components/range/range.ios.scss @@ -3,26 +3,30 @@ // iOS Range // -------------------------------------------------- +$range-ios-padding: 8px 16px !default; + $range-ios-slider-height: 42px !default; $range-ios-hit-width: 42px !default; $range-ios-hit-height: $range-ios-slider-height !default; -$range-ios-bar-height: 2px !default; +$range-ios-bar-height: 1px !default; $range-ios-bar-background-color: #bdbdbd !default; $range-ios-bar-active-background-color: color($colors-ios, primary) !default; -$range-ios-knob-width: 12px !default; +$range-ios-knob-width: 28px !default; $range-ios-knob-height: $range-ios-knob-width !default; -$range-ios-knob-background-color: $range-ios-bar-active-background-color !default; +$range-ios-knob-box-shadow: 0 3px 1px rgba(0, 0, 0, 0.1), 0 4px 8px rgba(0, 0, 0, 0.13), 0 0 0 1px rgba(0, 0, 0, 0.02) !default; +$range-ios-knob-border-radius: 50% !default; +$range-ios-knob-background-color: #fff !default; $range-ios-tick-width: 6px !default; $range-ios-tick-height: $range-ios-tick-width !default; $range-ios-tick-background-color: $range-ios-bar-background-color !default; $range-ios-tick-active-background-color: $range-ios-bar-active-background-color !default; -$range-ios-pin-background-color: $range-ios-bar-active-background-color !default; -$range-ios-pin-color: color-contrast($colors-ios, $range-ios-pin-background-color) !default; +$range-ios-pin-background-color: transparent !default; +$range-ios-pin-color: $text-ios-color !default; $range-ios-pin-font-size: 12px !default; @@ -44,8 +48,8 @@ ion-range { position: relative; display: block; - margin-top: -16px; - padding: 8px; + margin-top: -8px; + padding: $range-ios-padding; } .range-slider { @@ -101,16 +105,18 @@ ion-range { .range-knob { position: absolute; - top: ($range-ios-hit-height / 2) - ($range-ios-knob-height / 2) + ($range-ios-bar-height / 2); + top: ($range-ios-hit-height / 2) - ($range-ios-knob-height / 2) + ($range-ios-bar-height / 2) - .5px; left: ($range-ios-hit-width / 2) - ($range-ios-knob-width / 2); width: $range-ios-knob-width; height: $range-ios-knob-height; - border-radius: 50%; + border-radius: $range-ios-knob-border-radius; background: $range-ios-knob-background-color; + box-shadow: $range-ios-knob-box-shadow; + pointer-events: none; } @@ -160,3 +166,16 @@ ion-range { .range-knob-pressed .range-pin { transform: translate3d(0, 0, 0) scale(1); } + +// Generate iOS Range Colors +// -------------------------------------------------- + +@each $color-name, $color-base, $color-contrast in get-colors($colors-ios) { + + ion-range[#{$color-name}] { + .range-bar-active { + background: $color-base; + } + } + +} diff --git a/src/components/range/range.md.scss b/src/components/range/range.md.scss index 7fb37a2c58..d06cb7f17b 100644 --- a/src/components/range/range.md.scss +++ b/src/components/range/range.md.scss @@ -2,3 +2,181 @@ // Material Design Range // -------------------------------------------------- + +$range-md-padding: 8px !default; + +$range-md-slider-height: 42px !default; + +$range-md-hit-width: 42px !default; +$range-md-hit-height: $range-md-slider-height !default; + +$range-md-bar-height: 2px !default; +$range-md-bar-background-color: #bdbdbd !default; +$range-md-bar-active-background-color: color($colors-md, primary) !default; + +$range-md-knob-width: 12px !default; +$range-md-knob-height: $range-md-knob-width !default; +$range-md-knob-background-color: $range-md-bar-active-background-color !default; + +$range-md-tick-width: 0 !default; +$range-md-tick-height: $range-md-tick-width !default; +$range-md-tick-background-color: $range-md-bar-background-color !default; +$range-md-tick-active-background-color: $range-md-bar-active-background-color !default; + +$range-md-pin-background-color: $range-md-bar-active-background-color !default; +$range-md-pin-color: color-contrast($colors-md, $range-md-bar-active-background-color) !default; +$range-md-pin-font-size: 12px !default; + + +.item-range .item-inner { + overflow: visible; +} + +.item-range .input-wrapper { + overflow: visible; + + flex-direction: column; +} + +.item-range ion-range { + width: 100%; +} + +ion-range { + position: relative; + display: block; + + margin-top: -16px; + padding: $range-md-padding; +} + +.range-slider { + position: relative; + + height: $range-md-slider-height; + + cursor: pointer; +} + +.range-bar { + position: absolute; + top: ($range-md-slider-height / 2); + left: 0; + + width: 100%; + height: $range-md-bar-height; + + background: $range-md-bar-background-color; + + pointer-events: none; +} + +.range-pressed .range-bar-active { + will-change: left, right; +} + +.range-pressed .range-knob-handle { + will-change: left; +} + +.range-bar-active { + bottom: 0; + + width: auto; + + background: $range-md-bar-active-background-color; +} + +.range-knob-handle { + position: absolute; + top: ($range-md-slider-height / 2); + left: 0%; + + margin-top: -($range-md-hit-height / 2); + margin-left: -($range-md-hit-width / 2); + + width: $range-md-hit-width; + height: $range-md-hit-height; + + text-align: center; +} + +.range-knob { + position: absolute; + top: ($range-md-hit-height / 2) - ($range-md-knob-height / 2) + ($range-md-bar-height / 2); + left: ($range-md-hit-width / 2) - ($range-md-knob-width / 2); + + width: $range-md-knob-width; + height: $range-md-knob-height; + + border-radius: 50%; + + background: $range-md-knob-background-color; + + pointer-events: none; +} + +.range-tick { + position: absolute; + top: ($range-md-hit-height / 2) - ($range-md-tick-height / 2) + ($range-md-bar-height / 2); + + margin-left: ($range-md-tick-width / 2) * -1; + + width: $range-md-tick-width; + height: $range-md-tick-height; + + border-radius: 50%; + + background: $range-md-tick-background-color; + + pointer-events: none; +} + +.range-tick-active { + background: $range-md-tick-active-background-color; +} + +.range-pin { + position: relative; + top: -20px; + display: inline-block; + + padding: 8px; + + min-width: 28px; + + border-radius: 50px; + + font-size: $range-md-pin-font-size; + + text-align: center; + + color: $range-md-pin-color; + + background: $range-md-pin-background-color; + + transform: translate3d(0, 28px, 0) scale(.01); + transition: transform 120ms ease; +} + +.range-knob-pressed .range-pin { + transform: translate3d(0, 0, 0) scale(1); +} + +.range-knob-pressed .range-knob { + transform: scale(1.5); +} + +// Generate iOS Range Colors +// -------------------------------------------------- + +@each $color-name, $color-base, $color-contrast in get-colors($colors-md) { + + ion-range[#{$color-name}] { + .range-bar-active, + .range-knob { + background: $color-base; + } + } + +} diff --git a/src/components/range/range.wp.scss b/src/components/range/range.wp.scss index edf79664cb..0c78e6a1ad 100644 --- a/src/components/range/range.wp.scss +++ b/src/components/range/range.wp.scss @@ -2,3 +2,178 @@ // Windows Range // -------------------------------------------------- + +$range-wp-padding: 8px !default; + +$range-wp-slider-height: 42px !default; + +$range-wp-hit-width: 42px !default; +$range-wp-hit-height: $range-wp-slider-height !default; + +$range-wp-bar-height: 2px !default; +$range-wp-bar-background-color: #bdbdbd !default; +$range-wp-bar-active-background-color: color($colors-wp, primary) !default; + +$range-wp-knob-width: 8px !default; +$range-wp-knob-height: $range-wp-knob-width * 3 !default; +$range-wp-knob-background-color: $range-wp-bar-active-background-color !default; +$range-wp-knob-border-radius: $range-wp-knob-width / 2 !default; + +$range-wp-tick-width: 0 !default; +$range-wp-tick-height: $range-wp-tick-width !default; +$range-wp-tick-background-color: $range-wp-bar-background-color !default; +$range-wp-tick-active-background-color: $range-wp-bar-active-background-color !default; + +$range-wp-pin-background-color: $range-wp-bar-active-background-color !default; +$range-wp-pin-color: color-contrast($colors-wp, $range-wp-bar-active-background-color) !default; +$range-wp-pin-font-size: 12px !default; + + +.item-range .item-inner { + overflow: visible; +} + +.item-range .input-wrapper { + overflow: visible; + + flex-direction: column; +} + +.item-range ion-range { + width: 100%; +} + +ion-range { + position: relative; + display: block; + + margin-top: -16px; + padding: $range-wp-padding; +} + +.range-slider { + position: relative; + + height: $range-wp-slider-height; + + cursor: pointer; +} + +.range-bar { + position: absolute; + top: ($range-wp-slider-height / 2); + left: 0; + + width: 100%; + height: $range-wp-bar-height; + + background: $range-wp-bar-background-color; + + pointer-events: none; +} + +.range-pressed .range-bar-active { + will-change: left, right; +} + +.range-pressed .range-knob-handle { + will-change: left; +} + +.range-bar-active { + bottom: 0; + + width: auto; + + background: $range-wp-bar-active-background-color; +} + +.range-knob-handle { + position: absolute; + top: ($range-wp-slider-height / 2); + left: 0%; + + margin-top: -($range-wp-hit-height / 2); + margin-left: -($range-wp-hit-width / 2); + + width: $range-wp-hit-width; + height: $range-wp-hit-height; + + text-align: center; +} + +.range-knob { + position: absolute; + top: ($range-wp-hit-height / 2) - ($range-wp-knob-height / 2) + ($range-wp-bar-height / 2); + left: ($range-wp-hit-width / 2) - ($range-wp-knob-width / 2); + + width: $range-wp-knob-width; + height: $range-wp-knob-height; + + border-radius: $range-wp-knob-border-radius; + + background: $range-wp-knob-background-color; + + pointer-events: none; +} + +.range-tick { + position: absolute; + top: ($range-wp-hit-height / 2) - ($range-wp-tick-height / 2) + ($range-wp-bar-height / 2); + + margin-left: ($range-wp-tick-width / 2) * -1; + + width: $range-wp-tick-width; + height: $range-wp-tick-height; + + border-radius: 50%; + + background: $range-wp-tick-background-color; + + pointer-events: none; +} + +.range-tick-active { + background: $range-wp-tick-active-background-color; +} + +.range-pin { + position: relative; + top: -20px; + display: inline-block; + + padding: 8px; + + min-width: 28px; + + border-radius: 50px; + + font-size: $range-wp-pin-font-size; + + text-align: center; + + color: $range-wp-pin-color; + + background: $range-wp-pin-background-color; + + transform: translate3d(0, 28px, 0) scale(.01); + transition: transform 120ms ease; +} + +.range-knob-pressed .range-pin { + transform: translate3d(0, 0, 0) scale(1); +} + +// Generate iOS Range Colors +// -------------------------------------------------- + +@each $color-name, $color-base, $color-contrast in get-colors($colors-wp) { + + ion-range[#{$color-name}] { + .range-bar-active, + .range-knob { + background: $color-base; + } + } + +} diff --git a/src/components/range/test/basic/page1.html b/src/components/range/test/basic/page1.html index bf0e15ebfb..0b4a250fcd 100644 --- a/src/components/range/test/basic/page1.html +++ b/src/components/range/test/basic/page1.html @@ -27,7 +27,7 @@
{{singleValue2}}
init=150, min=-200, max=200 - +
{{singleValue2}}
@@ -43,7 +43,7 @@ dual, {{dualValue | json}} - + From d24b080cb749308933e2576659a3a542724d380a Mon Sep 17 00:00:00 2001 From: Brandy Carney Date: Tue, 31 May 2016 14:37:43 -0400 Subject: [PATCH 3/8] fix(range): fix styling on range, add demo references #5422 --- demos/range/index.ts | 15 ++++++++ demos/range/main.html | 54 +++++++++++++++++++++++++++++ src/components/range/range.ios.scss | 4 +-- src/components/range/range.md.scss | 1 - src/components/range/range.ts | 16 +++++++-- src/components/range/range.wp.scss | 1 - 6 files changed, 85 insertions(+), 6 deletions(-) create mode 100644 demos/range/index.ts create mode 100644 demos/range/main.html diff --git a/demos/range/index.ts b/demos/range/index.ts new file mode 100644 index 0000000000..96de5bbeb4 --- /dev/null +++ b/demos/range/index.ts @@ -0,0 +1,15 @@ +import {App} from 'ionic-angular'; + +@App({ + templateUrl: 'main.html' +}) +class ApiDemoApp { + brightness: number = 20; + saturation: number = 0; + warmth: number = 1300; + structure: any = {lower: 33, upper: 60}; + + onChange(ev) { + console.log("Changed", ev); + } +} diff --git a/demos/range/main.html b/demos/range/main.html new file mode 100644 index 0000000000..41d961c304 --- /dev/null +++ b/demos/range/main.html @@ -0,0 +1,54 @@ + + Range + + + + + + Brightness + {{brightness}} + + + + + + + + + + + Saturation + {{saturation}} + + + -200 + + 200 + + + + + + Warmth + {{warmth}} + + + + + + + + + + + Structure + {{structure.lower}} + {{structure.upper}} + + + 0 + + 100 + + + diff --git a/src/components/range/range.ios.scss b/src/components/range/range.ios.scss index 02e1994ea8..7b383663f1 100644 --- a/src/components/range/range.ios.scss +++ b/src/components/range/range.ios.scss @@ -48,7 +48,6 @@ ion-range { position: relative; display: block; - margin-top: -8px; padding: $range-ios-padding; } @@ -173,7 +172,8 @@ ion-range { @each $color-name, $color-base, $color-contrast in get-colors($colors-ios) { ion-range[#{$color-name}] { - .range-bar-active { + .range-bar-active, + .range-tick-active { background: $color-base; } } diff --git a/src/components/range/range.md.scss b/src/components/range/range.md.scss index d06cb7f17b..4d0849edd5 100644 --- a/src/components/range/range.md.scss +++ b/src/components/range/range.md.scss @@ -46,7 +46,6 @@ ion-range { position: relative; display: block; - margin-top: -16px; padding: $range-md-padding; } diff --git a/src/components/range/range.ts b/src/components/range/range.ts index 3249b02777..16cca5ae1c 100644 --- a/src/components/range/range.ts +++ b/src/components/range/range.ts @@ -10,7 +10,19 @@ import {pointerCoord} from '../../util/dom'; const RANGE_VALUE_ACCESSOR = new Provider( NG_VALUE_ACCESSOR, {useExisting: forwardRef(() => Range), multi: true}); - +/** + * @name Range + * @description + * The Range slider lets users select from a range of values by moving + * the slider knob. + * + * + * + * + * + * + * @demo /docs/v2/demos/range/ + */ @Component({ selector: '.range-knob-handle', template: @@ -231,7 +243,7 @@ export class Range { /** * @output {Range} Expression to evaluate when the range value changes. */ - @Output() rangeChange: EventEmitter = new EventEmitter(); + @Output() ionChange: EventEmitter = new EventEmitter(); constructor( diff --git a/src/components/range/range.wp.scss b/src/components/range/range.wp.scss index 0c78e6a1ad..7f5d1dcf19 100644 --- a/src/components/range/range.wp.scss +++ b/src/components/range/range.wp.scss @@ -47,7 +47,6 @@ ion-range { position: relative; display: block; - margin-top: -16px; padding: $range-wp-padding; } From 061af93b3ca19d15ccb22bae080ec4d160ea1ffa Mon Sep 17 00:00:00 2001 From: Brandy Carney Date: Tue, 31 May 2016 16:11:47 -0400 Subject: [PATCH 4/8] fix(range): update the styling for all modes references #5422 --- src/components/range/range.ios.scss | 20 +++++++++----- src/components/range/range.md.scss | 32 +++++++++++++++------- src/components/range/range.ts | 1 + src/components/range/range.wp.scss | 27 ++++++++++++------ src/components/range/test/basic/page1.html | 7 +++-- 5 files changed, 59 insertions(+), 28 deletions(-) diff --git a/src/components/range/range.ios.scss b/src/components/range/range.ios.scss index 7b383663f1..7669f4d0de 100644 --- a/src/components/range/range.ios.scss +++ b/src/components/range/range.ios.scss @@ -3,7 +3,8 @@ // iOS Range // -------------------------------------------------- -$range-ios-padding: 8px 16px !default; +$range-ios-padding-vertical: 8px !default; +$range-ios-padding-horizontal: 16px !default; $range-ios-slider-height: 42px !default; @@ -20,15 +21,16 @@ $range-ios-knob-box-shadow: 0 3px 1px rgba(0, 0, 0, 0.1), 0 4p $range-ios-knob-border-radius: 50% !default; $range-ios-knob-background-color: #fff !default; -$range-ios-tick-width: 6px !default; -$range-ios-tick-height: $range-ios-tick-width !default; +$range-ios-tick-width: $range-ios-bar-height !default; +$range-ios-tick-height: 8px !default; +$range-ios-tick-border-radius: 0 !default; $range-ios-tick-background-color: $range-ios-bar-background-color !default; $range-ios-tick-active-background-color: $range-ios-bar-active-background-color !default; $range-ios-pin-background-color: transparent !default; $range-ios-pin-color: $text-ios-color !default; $range-ios-pin-font-size: 12px !default; - +$range-ios-pin-padding: 8px !default; .item-range .item-inner { overflow: visible; @@ -48,7 +50,11 @@ ion-range { position: relative; display: block; - padding: $range-ios-padding; + padding: $range-ios-padding-vertical $range-ios-padding-horizontal; +} + +.range-has-pin { + padding-top: $range-ios-padding-vertical + $range-ios-pin-font-size; } .range-slider { @@ -128,7 +134,7 @@ ion-range { width: $range-ios-tick-width; height: $range-ios-tick-height; - border-radius: 50%; + border-radius: $range-ios-tick-border-radius; background: $range-ios-tick-background-color; @@ -144,7 +150,7 @@ ion-range { top: -20px; display: inline-block; - padding: 8px; + padding: $range-ios-pin-padding; min-width: 28px; diff --git a/src/components/range/range.md.scss b/src/components/range/range.md.scss index 4d0849edd5..87283d5853 100644 --- a/src/components/range/range.md.scss +++ b/src/components/range/range.md.scss @@ -3,7 +3,8 @@ // Material Design Range // -------------------------------------------------- -$range-md-padding: 8px !default; +$range-md-padding-vertical: 8px !default; +$range-md-padding-horizontal: 8px !default; $range-md-slider-height: 42px !default; @@ -18,14 +19,16 @@ $range-md-knob-width: 12px !default; $range-md-knob-height: $range-md-knob-width !default; $range-md-knob-background-color: $range-md-bar-active-background-color !default; -$range-md-tick-width: 0 !default; +$range-md-tick-width: 2px !default; $range-md-tick-height: $range-md-tick-width !default; -$range-md-tick-background-color: $range-md-bar-background-color !default; -$range-md-tick-active-background-color: $range-md-bar-active-background-color !default; +$range-md-tick-border-radius: 50% !default; +$range-md-tick-background-color: #000 !default; +$range-md-tick-active-background-color: $range-md-tick-background-color !default; $range-md-pin-background-color: $range-md-bar-active-background-color !default; $range-md-pin-color: color-contrast($colors-md, $range-md-bar-active-background-color) !default; $range-md-pin-font-size: 12px !default; +$range-md-pin-padding: 8px !default; .item-range .item-inner { @@ -46,7 +49,11 @@ ion-range { position: relative; display: block; - padding: $range-md-padding; + padding: $range-md-padding-vertical $range-md-padding-horizontal; +} + +.range-has-pin { + padding-top: $range-md-padding-vertical + $range-md-pin-font-size + $range-md-pin-padding; } .range-slider { @@ -102,6 +109,8 @@ ion-range { .range-knob { position: absolute; + z-index: 2; + top: ($range-md-hit-height / 2) - ($range-md-knob-height / 2) + ($range-md-bar-height / 2); left: ($range-md-hit-width / 2) - ($range-md-knob-width / 2); @@ -117,6 +126,8 @@ ion-range { .range-tick { position: absolute; + z-index: 1; + top: ($range-md-hit-height / 2) - ($range-md-tick-height / 2) + ($range-md-bar-height / 2); margin-left: ($range-md-tick-width / 2) * -1; @@ -124,7 +135,7 @@ ion-range { width: $range-md-tick-width; height: $range-md-tick-height; - border-radius: 50%; + border-radius: $range-md-tick-border-radius; background: $range-md-tick-background-color; @@ -140,11 +151,11 @@ ion-range { top: -20px; display: inline-block; - padding: 8px; + padding: $range-md-pin-padding; min-width: 28px; - border-radius: 50px; + border-radius: 50%; font-size: $range-md-pin-font-size; @@ -166,14 +177,15 @@ ion-range { transform: scale(1.5); } -// Generate iOS Range Colors +// Generate Material Design Range Colors // -------------------------------------------------- @each $color-name, $color-base, $color-contrast in get-colors($colors-md) { ion-range[#{$color-name}] { .range-bar-active, - .range-knob { + .range-knob, + .range-pin { background: $color-base; } } diff --git a/src/components/range/range.ts b/src/components/range/range.ts index 16cca5ae1c..845ec8a066 100644 --- a/src/components/range/range.ts +++ b/src/components/range/range.ts @@ -127,6 +127,7 @@ export class RangeKnob { host: { '[class.range-disabled]': '_disabled', '[class.range-pressed]': '_pressed', + '[class.range-has-pin]': '_pin', }, directives: [RangeKnob], providers: [RANGE_VALUE_ACCESSOR], diff --git a/src/components/range/range.wp.scss b/src/components/range/range.wp.scss index 7f5d1dcf19..edc8a3b689 100644 --- a/src/components/range/range.wp.scss +++ b/src/components/range/range.wp.scss @@ -3,7 +3,8 @@ // Windows Range // -------------------------------------------------- -$range-wp-padding: 8px !default; +$range-wp-padding-vertical: 8px !default; +$range-wp-padding-horizontal: 8px !default; $range-wp-slider-height: 42px !default; @@ -19,14 +20,16 @@ $range-wp-knob-height: $range-wp-knob-width * 3 !default; $range-wp-knob-background-color: $range-wp-bar-active-background-color !default; $range-wp-knob-border-radius: $range-wp-knob-width / 2 !default; -$range-wp-tick-width: 0 !default; -$range-wp-tick-height: $range-wp-tick-width !default; +$range-wp-tick-width: $range-wp-bar-height !default; +$range-wp-tick-height: $range-wp-tick-width * 3 !default; +$range-wp-tick-border-radius: $range-wp-knob-width / 2 !default; $range-wp-tick-background-color: $range-wp-bar-background-color !default; $range-wp-tick-active-background-color: $range-wp-bar-active-background-color !default; $range-wp-pin-background-color: $range-wp-bar-active-background-color !default; $range-wp-pin-color: color-contrast($colors-wp, $range-wp-bar-active-background-color) !default; $range-wp-pin-font-size: 12px !default; +$range-wp-pin-padding: 8px !default; .item-range .item-inner { @@ -47,7 +50,11 @@ ion-range { position: relative; display: block; - padding: $range-wp-padding; + padding: $range-wp-padding-vertical $range-wp-padding-horizontal; +} + +.range-has-pin { + padding-top: $range-wp-padding-vertical + $range-wp-pin-font-size + $range-wp-pin-padding; } .range-slider { @@ -125,7 +132,7 @@ ion-range { width: $range-wp-tick-width; height: $range-wp-tick-height; - border-radius: 50%; + border-radius: $range-wp-tick-border-radius; background: $range-wp-tick-background-color; @@ -138,10 +145,10 @@ ion-range { .range-pin { position: relative; - top: -20px; + top: -24px; display: inline-block; - padding: 8px; + padding: $range-wp-pin-padding; min-width: 28px; @@ -163,14 +170,16 @@ ion-range { transform: translate3d(0, 0, 0) scale(1); } -// Generate iOS Range Colors +// Generate Windows Range Colors // -------------------------------------------------- @each $color-name, $color-base, $color-contrast in get-colors($colors-wp) { ion-range[#{$color-name}] { .range-bar-active, - .range-knob { + .range-tick-active, + .range-knob, + .range-pin { background: $color-base; } } diff --git a/src/components/range/test/basic/page1.html b/src/components/range/test/basic/page1.html index 0b4a250fcd..9e211ca9f3 100644 --- a/src/components/range/test/basic/page1.html +++ b/src/components/range/test/basic/page1.html @@ -18,6 +18,9 @@ + + + no init value, default min/max, {{singleValue}} @@ -38,7 +41,7 @@ step=100, snaps, {{singleValue4}} - + @@ -53,7 +56,7 @@ pin, {{singleValue}} - + From f7cf3780425179c8a15fd9306621ad58dc7933e7 Mon Sep 17 00:00:00 2001 From: Brandy Carney Date: Tue, 31 May 2016 17:09:07 -0400 Subject: [PATCH 5/8] docs(range): add API docs for range references #5422 --- demos/range/main.html | 6 +++ src/components.ts | 1 + src/components/range/range.ts | 77 ++++++++++++++++++++++++++++------- 3 files changed, 70 insertions(+), 14 deletions(-) diff --git a/demos/range/main.html b/demos/range/main.html index 41d961c304..306f4f4112 100644 --- a/demos/range/main.html +++ b/demos/range/main.html @@ -52,3 +52,9 @@ + + diff --git a/src/components.ts b/src/components.ts index 8e7738428b..d7468ad2d9 100644 --- a/src/components.ts +++ b/src/components.ts @@ -34,6 +34,7 @@ export * from './components/picker/picker'; export * from './components/popover/popover'; export * from './components/radio/radio-button'; export * from './components/radio/radio-group'; +export * from './components/range/range'; export * from './components/refresher/refresher'; export * from './components/refresher/refresher-content'; export * from './components/scroll/scroll'; diff --git a/src/components/range/range.ts b/src/components/range/range.ts index 845ec8a066..2c80178d31 100644 --- a/src/components/range/range.ts +++ b/src/components/range/range.ts @@ -11,17 +11,7 @@ const RANGE_VALUE_ACCESSOR = new Provider( NG_VALUE_ACCESSOR, {useExisting: forwardRef(() => Range), multi: true}); /** - * @name Range - * @description - * The Range slider lets users select from a range of values by moving - * the slider knob. - * - * - * - * - * - * - * @demo /docs/v2/demos/range/ + * @private */ @Component({ selector: '.range-knob-handle', @@ -111,8 +101,64 @@ export class RangeKnob { /** * @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. + * + * + * ### 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 `dualKnobs` 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`. + * + * + * @usage + * ```html + * + * + * + * + * + * + * -200 + * + * 200 + * + * + * + * step=2, {{singleValue3}} + * + * + * + * + * step=100, snaps, {{singleValue4}} + * + * + * + * + * dual, step=3, snaps, {{dualValue2 | json}} + * + * + * + * ``` + * + * + * @demo /docs/v2/demos/range/ */ @Component({ selector: 'ion-range', @@ -127,7 +173,7 @@ export class RangeKnob { host: { '[class.range-disabled]': '_disabled', '[class.range-pressed]': '_pressed', - '[class.range-has-pin]': '_pin', + '[class.range-has-pin]': '_pin' }, directives: [RangeKnob], providers: [RANGE_VALUE_ACCESSOR], @@ -155,6 +201,9 @@ export class Range { private _removes: Function[] = []; private _mouseRemove: Function; + /** + * @private + */ value: any; @ViewChild('bar') private _bar: ElementRef; @@ -573,7 +622,7 @@ export class Range { registerOnTouched(fn) { this.onTouched = fn; } /** - * @input {boolean} whether or not the checkbox is disabled or not. + * @input {boolean} Whether or not the range is disabled. Defaults to `false`. */ @Input() get disabled(): boolean { From b59e5afe22f3521b74ff6611fea3b96c5a1c41df Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Tue, 31 May 2016 16:24:13 -0500 Subject: [PATCH 6/8] chore(range): emit ionChange on range --- src/components/range/range.ts | 2 ++ src/components/range/test/basic/index.ts | 5 +++++ src/components/range/test/basic/page1.html | 2 +- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/components/range/range.ts b/src/components/range/range.ts index 2c80178d31..c149abd34e 100644 --- a/src/components/range/range.ts +++ b/src/components/range/range.ts @@ -500,6 +500,8 @@ export class Range { } this.onChange(this.value); + + this.ionChange.emit(this); } this.updateBar(); diff --git a/src/components/range/test/basic/index.ts b/src/components/range/test/basic/index.ts index 7cdfc8d9d6..bb1fa45247 100644 --- a/src/components/range/test/basic/index.ts +++ b/src/components/range/test/basic/index.ts @@ -11,6 +11,11 @@ class Page1 { singleValue4: number = 1300; dualValue: any; dualValue2 = {lower: 33, upper: 60}; + + rangeChange(ev) { + console.log(`range, change, ratio: ${ev.ratio}, value: ${ev.value}`); + } + } diff --git a/src/components/range/test/basic/page1.html b/src/components/range/test/basic/page1.html index 9e211ca9f3..5943831148 100644 --- a/src/components/range/test/basic/page1.html +++ b/src/components/range/test/basic/page1.html @@ -19,7 +19,7 @@ - + From d7a69801128228cd4514bb30910003cee149f1ce Mon Sep 17 00:00:00 2001 From: Brandy Carney Date: Tue, 31 May 2016 18:51:21 -0400 Subject: [PATCH 7/8] chore: fix linter errororororors --- src/components/range/range.ios.scss | 2 +- src/components/range/range.md.scss | 6 ++++-- src/config/bootstrap.ts | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/components/range/range.ios.scss b/src/components/range/range.ios.scss index 7669f4d0de..818d9fa280 100644 --- a/src/components/range/range.ios.scss +++ b/src/components/range/range.ios.scss @@ -17,7 +17,7 @@ $range-ios-bar-active-background-color: color($colors-ios, primary) !defau $range-ios-knob-width: 28px !default; $range-ios-knob-height: $range-ios-knob-width !default; -$range-ios-knob-box-shadow: 0 3px 1px rgba(0, 0, 0, 0.1), 0 4px 8px rgba(0, 0, 0, 0.13), 0 0 0 1px rgba(0, 0, 0, 0.02) !default; +$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; $range-ios-knob-border-radius: 50% !default; $range-ios-knob-background-color: #fff !default; diff --git a/src/components/range/range.md.scss b/src/components/range/range.md.scss index 87283d5853..03dae9e502 100644 --- a/src/components/range/range.md.scss +++ b/src/components/range/range.md.scss @@ -109,11 +109,12 @@ ion-range { .range-knob { position: absolute; - z-index: 2; top: ($range-md-hit-height / 2) - ($range-md-knob-height / 2) + ($range-md-bar-height / 2); left: ($range-md-hit-width / 2) - ($range-md-knob-width / 2); + z-index: 2; + width: $range-md-knob-width; height: $range-md-knob-height; @@ -126,10 +127,11 @@ ion-range { .range-tick { position: absolute; - z-index: 1; top: ($range-md-hit-height / 2) - ($range-md-tick-height / 2) + ($range-md-bar-height / 2); + z-index: 1; + margin-left: ($range-md-tick-width / 2) * -1; width: $range-md-tick-width; diff --git a/src/config/bootstrap.ts b/src/config/bootstrap.ts index 375fe61206..61465b1fec 100644 --- a/src/config/bootstrap.ts +++ b/src/config/bootstrap.ts @@ -39,7 +39,7 @@ export function ionicBootstrap(appRootComponent: any, customProviders?: Array): ComponentRef { - //ngComponentRef.injector.get(TapClick); + // ngComponentRef.injector.get(TapClick); let app: App = ngComponentRef.injector.get(App); app.setAppInjector(ngComponentRef.injector); From 3b02ffbcdcba67df91360a3683ec13d722b955c6 Mon Sep 17 00:00:00 2001 From: Mike Hartington Date: Tue, 31 May 2016 18:56:19 -0400 Subject: [PATCH 8/8] chore(tsconfig): set noImplicitAny --- tsconfig.json | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/tsconfig.json b/tsconfig.json index f4ab03bd64..7b2ff9383a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,14 +5,22 @@ "target": "es5", "module": "commonjs", "declaration": true, - "outDir": "dist" + "outDir": "dist", + "noImplicitAny": true }, + "filesGlob": [ + "src/**/*.ts", + "!node_modules/**/*" + ], "exclude": [ "node_modules", "scripts", "typings/main", "typings/main.d.ts" ], - "compileOnSave" : false, - "buildOnSave": false + "compileOnSave": false, + "buildOnSave": false, + "atom": { + "rewriteTsconfig": false + } }