mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-23 05:58:26 +08:00
feat(range): add neutral point (#17400)
* feat(Range): add neutral point * feat(Range): generate proxies and api * fix(): check positive case in neutralPointChanged * fix(Range): neutralPoint to min if neutralPoint < min * fix(Range): active bar style * fix(Range): tick styling
This commit is contained in:

committed by
Liam DeBeasi

parent
39fbc32348
commit
15acb4be37
@ -44,6 +44,25 @@ export class Range implements ComponentInterface {
|
||||
*/
|
||||
@Prop() mode!: Mode;
|
||||
|
||||
/**
|
||||
* The neutral point of the range slider.
|
||||
* Default: value is `0` or the `min` when `neutralPoint < min` or `max` when `max < neutralPoint`.
|
||||
*/
|
||||
@Prop() neutralPoint = 0;
|
||||
protected neutralPointChanged() {
|
||||
if (this.noUpdate) {
|
||||
return;
|
||||
}
|
||||
const { min, max, neutralPoint } = this;
|
||||
|
||||
if (max < neutralPoint) {
|
||||
this.neutralPoint = max;
|
||||
}
|
||||
if (neutralPoint < min) {
|
||||
this.neutralPoint = min;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* How long, in milliseconds, to wait to trigger the
|
||||
* `ionChange` event after each change in the range value.
|
||||
@ -119,7 +138,7 @@ export class Range implements ComponentInterface {
|
||||
/**
|
||||
* the value of the range.
|
||||
*/
|
||||
@Prop({ mutable: true }) value: RangeValue = 0;
|
||||
@Prop({ mutable: true }) value: RangeValue | null = null;
|
||||
@Watch('value')
|
||||
protected valueChanged(value: RangeValue) {
|
||||
if (!this.noUpdate) {
|
||||
@ -210,14 +229,14 @@ export class Range implements ComponentInterface {
|
||||
}
|
||||
|
||||
private getValue(): RangeValue {
|
||||
const value = this.value || 0;
|
||||
const value = this.value || this.neutralPoint || 0;
|
||||
if (this.dualKnobs) {
|
||||
if (typeof value === 'object') {
|
||||
return value;
|
||||
}
|
||||
return {
|
||||
lower: 0,
|
||||
upper: value
|
||||
lower: this.value === null ? this.neutralPoint : 0,
|
||||
upper: this.value === null ? this.neutralPoint : value
|
||||
};
|
||||
} else {
|
||||
if (typeof value === 'object') {
|
||||
@ -361,25 +380,67 @@ export class Range implements ComponentInterface {
|
||||
}
|
||||
};
|
||||
}
|
||||
protected getActiveBarPosition() {
|
||||
const { min, max, neutralPoint, ratioLower, ratioUpper } = this;
|
||||
const neutralPointRatio = valueToRatio(neutralPoint, min, max);
|
||||
|
||||
// dual knob handling
|
||||
let left = `${ratioLower * 100}%`;
|
||||
let right = `${100 - ratioUpper * 100}%`;
|
||||
|
||||
// single knob handling
|
||||
if (!this.dualKnobs) {
|
||||
if (this.ratioA < neutralPointRatio) {
|
||||
right = `${neutralPointRatio * 100}%`;
|
||||
left = `${this.ratioA * 100}%`;
|
||||
} else {
|
||||
right = `${100 - this.ratioA * 100}%`;
|
||||
left = `${neutralPointRatio * 100}%`;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
left,
|
||||
right
|
||||
};
|
||||
}
|
||||
|
||||
protected isTickActive(stepRatio: number) {
|
||||
const { min, max, neutralPoint, ratioLower, ratioUpper } = this;
|
||||
const neutralPointRatio = valueToRatio(neutralPoint, min, max);
|
||||
|
||||
if (this.dualKnobs) {
|
||||
return (stepRatio >= ratioLower && stepRatio <= ratioUpper);
|
||||
}
|
||||
|
||||
if (this.ratioA <= neutralPointRatio && stepRatio >= this.ratioA && stepRatio <= neutralPointRatio) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this.ratioA >= neutralPointRatio && stepRatio <= this.ratioA && stepRatio >= neutralPointRatio) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { min, max, step, ratioLower, ratioUpper } = this;
|
||||
|
||||
const barStart = `${ratioLower * 100}%`;
|
||||
const barEnd = `${100 - ratioUpper * 100}%`;
|
||||
const { min, max, neutralPoint, step } = this;
|
||||
const barPosition = this.getActiveBarPosition();
|
||||
|
||||
const isRTL = document.dir === 'rtl';
|
||||
const start = isRTL ? 'right' : 'left';
|
||||
const end = isRTL ? 'left' : 'right';
|
||||
|
||||
const ticks = [];
|
||||
const ticks: any[] = [];
|
||||
|
||||
if (this.snaps) {
|
||||
for (let value = min; value <= max; value += step) {
|
||||
const ratio = valueToRatio(value, min, max);
|
||||
|
||||
const tick: any = {
|
||||
ratio,
|
||||
active: ratio >= ratioLower && ratio <= ratioUpper,
|
||||
active: this.isTickActive(ratio),
|
||||
};
|
||||
|
||||
tick[start] = `${ratio * 100}%`;
|
||||
@ -390,7 +451,6 @@ export class Range implements ComponentInterface {
|
||||
|
||||
const tickStyle = (tick: any) => {
|
||||
const style: any = {};
|
||||
|
||||
style[start] = tick[start];
|
||||
|
||||
return style;
|
||||
@ -399,8 +459,8 @@ export class Range implements ComponentInterface {
|
||||
const barStyle = () => {
|
||||
const style: any = {};
|
||||
|
||||
style[start] = barStart;
|
||||
style[end] = barEnd;
|
||||
style[start] = barPosition[start];
|
||||
style[end] = barPosition[end];
|
||||
|
||||
return style;
|
||||
};
|
||||
@ -435,7 +495,8 @@ export class Range implements ComponentInterface {
|
||||
disabled: this.disabled,
|
||||
handleKeyboard: this.handleKeyboard,
|
||||
min,
|
||||
max
|
||||
max,
|
||||
neutralPoint
|
||||
})}
|
||||
|
||||
{ this.dualKnobs && renderKnob({
|
||||
@ -447,7 +508,8 @@ export class Range implements ComponentInterface {
|
||||
disabled: this.disabled,
|
||||
handleKeyboard: this.handleKeyboard,
|
||||
min,
|
||||
max
|
||||
max,
|
||||
neutralPoint
|
||||
})}
|
||||
</div>,
|
||||
<slot name="end"></slot>
|
||||
@ -461,6 +523,7 @@ interface RangeKnob {
|
||||
ratio: number;
|
||||
min: number;
|
||||
max: number;
|
||||
neutralPoint: number;
|
||||
disabled: boolean;
|
||||
pressed: boolean;
|
||||
pin: boolean;
|
||||
@ -468,7 +531,7 @@ interface RangeKnob {
|
||||
handleKeyboard: (name: KnobName, isIncrease: boolean) => void;
|
||||
}
|
||||
|
||||
function renderKnob({ knob, value, ratio, min, max, disabled, pressed, pin, handleKeyboard }: RangeKnob) {
|
||||
function renderKnob({ knob, value, ratio, min, max, neutralPoint, disabled, pressed, pin, handleKeyboard }: RangeKnob) {
|
||||
const isRTL = document.dir === 'rtl';
|
||||
const start = isRTL ? 'right' : 'left';
|
||||
|
||||
@ -501,7 +564,8 @@ function renderKnob({ knob, value, ratio, min, max, disabled, pressed, pin, hand
|
||||
'range-knob-b': knob === 'B',
|
||||
'range-knob-pressed': pressed,
|
||||
'range-knob-min': value === min,
|
||||
'range-knob-max': value === max
|
||||
'range-knob-max': value === max,
|
||||
'range-knob-neutral': value === neutralPoint
|
||||
}}
|
||||
style={knobStyle()}
|
||||
role="slider"
|
||||
@ -510,6 +574,7 @@ function renderKnob({ knob, value, ratio, min, max, disabled, pressed, pin, hand
|
||||
aria-valuemax={max}
|
||||
aria-disabled={disabled ? 'true' : null}
|
||||
aria-valuenow={value}
|
||||
aria-valueneutral={neutralPoint}
|
||||
>
|
||||
{pin && <div class="range-pin" role="presentation">{Math.round(value)}</div>}
|
||||
<div class="range-knob" role="presentation" />
|
||||
|
Reference in New Issue
Block a user