diff --git a/angular/src/directives/proxies.ts b/angular/src/directives/proxies.ts
index e7f23a40025..06f9019ccda 100644
--- a/angular/src/directives/proxies.ts
+++ b/angular/src/directives/proxies.ts
@@ -1304,13 +1304,13 @@ mouse drag, touch gesture, or keyboard interaction.
@ProxyCmp({
defineCustomElementFn: undefined,
- inputs: ['color', 'debounce', 'disabled', 'dualKnobs', 'max', 'min', 'mode', 'name', 'pin', 'pinFormatter', 'snaps', 'step', 'ticks', 'value']
+ inputs: ['activeBarStart', 'color', 'debounce', 'disabled', 'dualKnobs', 'max', 'min', 'mode', 'name', 'pin', 'pinFormatter', 'snaps', 'step', 'ticks', 'value']
})
@Component({
selector: 'ion-range',
changeDetection: ChangeDetectionStrategy.OnPush,
template: '',
- inputs: ['color', 'debounce', 'disabled', 'dualKnobs', 'max', 'min', 'mode', 'name', 'pin', 'pinFormatter', 'snaps', 'step', 'ticks', 'value']
+ inputs: ['activeBarStart', 'color', 'debounce', 'disabled', 'dualKnobs', 'max', 'min', 'mode', 'name', 'pin', 'pinFormatter', 'snaps', 'step', 'ticks', 'value']
})
export class IonRange {
protected el: HTMLElement;
diff --git a/core/api.txt b/core/api.txt
index 4ba332dafff..a844e4c7432 100644
--- a/core/api.txt
+++ b/core/api.txt
@@ -971,6 +971,7 @@ ion-radio-group,prop,value,any,undefined,false,false
ion-radio-group,event,ionChange,RadioGroupChangeEventDetail,true
ion-range,shadow
+ion-range,prop,activeBarStart,number | undefined,undefined,false,false
ion-range,prop,color,"danger" | "dark" | "light" | "medium" | "primary" | "secondary" | "success" | "tertiary" | "warning" | string & Record | undefined,undefined,false,true
ion-range,prop,debounce,number,0,false,false
ion-range,prop,disabled,boolean,false,false,false
diff --git a/core/src/components.d.ts b/core/src/components.d.ts
index ef595095875..66c0c6e50da 100644
--- a/core/src/components.d.ts
+++ b/core/src/components.d.ts
@@ -2076,6 +2076,10 @@ export namespace Components {
"value"?: any | null;
}
interface IonRange {
+ /**
+ * The start position of the range active bar. This feature is only available with a single knob (dualKnobs="false"). Valid values are greater than or equal to the min value and less than or equal to the max value.
+ */
+ "activeBarStart"?: number;
/**
* The color to use from your application's color palette. Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`. For more information on colors, see [theming](/docs/theming/basics).
*/
@@ -5961,6 +5965,10 @@ declare namespace LocalJSX {
"value"?: any | null;
}
interface IonRange {
+ /**
+ * The start position of the range active bar. This feature is only available with a single knob (dualKnobs="false"). Valid values are greater than or equal to the min value and less than or equal to the max value.
+ */
+ "activeBarStart"?: number;
/**
* The color to use from your application's color palette. Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`. For more information on colors, see [theming](/docs/theming/basics).
*/
diff --git a/core/src/components/range/range.tsx b/core/src/components/range/range.tsx
index 5bf61d19c87..f97ce9c5d1d 100644
--- a/core/src/components/range/range.tsx
+++ b/core/src/components/range/range.tsx
@@ -16,6 +16,7 @@ import type {
import { findClosestIonContent, disableContentScrollY, resetContentScrollY } from '../../utils/content';
import type { Attributes } from '../../utils/helpers';
import { inheritAriaAttributes, clamp, debounceEvent, getAriaLabel, renderHiddenInput } from '../../utils/helpers';
+import { printIonWarning } from '../../utils/logging';
import { isRTL } from '../../utils/rtl';
import { createColorClasses, hostContext } from '../../utils/theme';
@@ -142,6 +143,31 @@ export class Range implements ComponentInterface {
*/
@Prop() ticks = true;
+ /**
+ * The start position of the range active bar. This feature is only available with a single knob (dualKnobs="false").
+ * Valid values are greater than or equal to the min value and less than or equal to the max value.
+ */
+ @Prop({ mutable: true }) activeBarStart?: number;
+ @Watch('activeBarStart')
+ protected activeBarStartChanged() {
+ const { activeBarStart } = this;
+ if (activeBarStart !== undefined) {
+ if (activeBarStart > this.max) {
+ printIonWarning(
+ `Range: The value of activeBarStart (${activeBarStart}) is greater than the max (${this.max}). Valid values are greater than or equal to the min value and less than or equal to the max value.`,
+ this.el
+ );
+ this.activeBarStart = this.max;
+ } else if (activeBarStart < this.min) {
+ printIonWarning(
+ `Range: The value of activeBarStart (${activeBarStart}) is less than the min (${this.min}). Valid values are greater than or equal to the min value and less than or equal to the max value.`,
+ this.el
+ );
+ this.activeBarStart = this.min;
+ }
+ }
+ }
+
/**
* If `true`, the user cannot interact with the range.
*/
@@ -252,6 +278,7 @@ export class Range implements ComponentInterface {
this.updateRatio();
this.debounceChanged();
this.disabledChanged();
+ this.activeBarStartChanged();
/**
* If we have not yet rendered
@@ -395,7 +422,11 @@ export class Range implements ComponentInterface {
if (this.dualKnobs) {
return Math.min(this.ratioA, this.ratioB);
}
- return 0;
+ const { activeBarStart } = this;
+ if (activeBarStart == null) {
+ return 0;
+ }
+ return valueToRatio(activeBarStart, this.min, this.max);
}
private get ratioUpper() {
@@ -484,8 +515,8 @@ export class Range implements ComponentInterface {
labelText = inheritedAttributes['aria-label'];
}
const mode = getIonMode(this);
- const barStart = `${ratioLower * 100}%`;
- const barEnd = `${100 - ratioUpper * 100}%`;
+ let barStart = `${ratioLower * 100}%`;
+ let barEnd = `${100 - ratioUpper * 100}%`;
const rtl = isRTL(this.el);
@@ -498,6 +529,33 @@ export class Range implements ComponentInterface {
};
};
+ if (this.dualKnobs === false) {
+ /**
+ * When the value is less than the activeBarStart or the min value,
+ * the knob will display at the start of the active bar.
+ */
+ if (this.valA < (this.activeBarStart ?? this.min)) {
+ /**
+ * Sets the bar positions relative to the upper and lower limits.
+ * Converts the ratio values into percentages, used as offsets for left/right styles.
+ *
+ * The ratioUpper refers to the knob position on the bar.
+ * The ratioLower refers to the end position of the active bar (the value).
+ */
+ barStart = `${ratioUpper * 100}%`;
+ barEnd = `${100 - ratioLower * 100}%`;
+ } else {
+ /**
+ * Otherwise, the knob will display at the end of the active bar.
+ *
+ * The ratioLower refers to the start position of the active bar (the value).
+ * The ratioUpper refers to the knob position on the bar.
+ */
+ barStart = `${ratioLower * 100}%`;
+ barEnd = `${100 - ratioUpper * 100}%`;
+ }
+ }
+
const barStyle = {
[start]: barStart,
[end]: barEnd,
@@ -508,9 +566,16 @@ export class Range implements ComponentInterface {
for (let value = min; value <= max; value += step) {
const ratio = valueToRatio(value, min, max);
+ const ratioMin = Math.min(ratioLower, ratioUpper);
+ const ratioMax = Math.max(ratioLower, ratioUpper);
+
const tick: any = {
ratio,
- active: ratio >= ratioLower && ratio <= ratioUpper,
+ /**
+ * Sets the tick mark as active when the tick is between the min bounds and the knob.
+ * When using activeBarStart, the tick mark will be active between the knob and activeBarStart.
+ */
+ active: ratio >= ratioMin && ratio <= ratioMax,
};
tick[start] = `${ratio * 100}%`;
diff --git a/core/src/components/range/test/activeBarStart/index.html b/core/src/components/range/test/activeBarStart/index.html
new file mode 100644
index 00000000000..a34e159cae6
--- /dev/null
+++ b/core/src/components/range/test/activeBarStart/index.html
@@ -0,0 +1,148 @@
+
+
+
+
+ Range - activeBarStart
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Range - activeBarStart
+
+
+
+
+
+ activeBarStart is 0 and value is 0.
+
+
+
+
+ activeBarStart is 0 and value is 70.
+
+
+
+
+ activeBarStart is 0 and value is -70.
+
+
+
+
+ activeBarStart is -30 and value is 0.
+
+
+
+
+ activeBarStart is 30 and value is 0.
+
+
+
+
+ activeBarStart is between steps
+
+
+
+ invalid activeBarStart value (less than min)
+
+
+
+
+ invalid activeBarStart value (greater than max)
+
+
+
+
+ activeBarStart is ignored with dual knobs enabled.
+
+
+
+
+
+
+
diff --git a/core/src/components/range/test/activeBarStart/range.e2e.ts b/core/src/components/range/test/activeBarStart/range.e2e.ts
new file mode 100644
index 00000000000..b3eb72e0be1
--- /dev/null
+++ b/core/src/components/range/test/activeBarStart/range.e2e.ts
@@ -0,0 +1,12 @@
+import { expect } from '@playwright/test';
+import { test } from '@utils/test/playwright';
+
+test.describe('range: activeBarStart', () => {
+ test('should not have visual regressions', async ({ page }) => {
+ await page.goto(`/src/components/range/test/activeBarStart`);
+
+ await page.setIonViewport();
+
+ expect(await page.screenshot()).toMatchSnapshot(`range-activeBarStart-diff-${page.getSnapshotSettings()}.png`);
+ });
+});
diff --git a/core/src/components/range/test/activeBarStart/range.e2e.ts-snapshots/range-activeBarStart-diff-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/range/test/activeBarStart/range.e2e.ts-snapshots/range-activeBarStart-diff-ios-ltr-Mobile-Chrome-linux.png
new file mode 100644
index 00000000000..e61f2f80b83
Binary files /dev/null and b/core/src/components/range/test/activeBarStart/range.e2e.ts-snapshots/range-activeBarStart-diff-ios-ltr-Mobile-Chrome-linux.png differ
diff --git a/core/src/components/range/test/activeBarStart/range.e2e.ts-snapshots/range-activeBarStart-diff-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/range/test/activeBarStart/range.e2e.ts-snapshots/range-activeBarStart-diff-ios-ltr-Mobile-Firefox-linux.png
new file mode 100644
index 00000000000..060bac98cdc
Binary files /dev/null and b/core/src/components/range/test/activeBarStart/range.e2e.ts-snapshots/range-activeBarStart-diff-ios-ltr-Mobile-Firefox-linux.png differ
diff --git a/core/src/components/range/test/activeBarStart/range.e2e.ts-snapshots/range-activeBarStart-diff-ios-ltr-Mobile-Safari-linux.png b/core/src/components/range/test/activeBarStart/range.e2e.ts-snapshots/range-activeBarStart-diff-ios-ltr-Mobile-Safari-linux.png
new file mode 100644
index 00000000000..b3fdbdb3ef8
Binary files /dev/null and b/core/src/components/range/test/activeBarStart/range.e2e.ts-snapshots/range-activeBarStart-diff-ios-ltr-Mobile-Safari-linux.png differ
diff --git a/core/src/components/range/test/activeBarStart/range.e2e.ts-snapshots/range-activeBarStart-diff-ios-rtl-Mobile-Chrome-linux.png b/core/src/components/range/test/activeBarStart/range.e2e.ts-snapshots/range-activeBarStart-diff-ios-rtl-Mobile-Chrome-linux.png
new file mode 100644
index 00000000000..062ab1bc903
Binary files /dev/null and b/core/src/components/range/test/activeBarStart/range.e2e.ts-snapshots/range-activeBarStart-diff-ios-rtl-Mobile-Chrome-linux.png differ
diff --git a/core/src/components/range/test/activeBarStart/range.e2e.ts-snapshots/range-activeBarStart-diff-ios-rtl-Mobile-Firefox-linux.png b/core/src/components/range/test/activeBarStart/range.e2e.ts-snapshots/range-activeBarStart-diff-ios-rtl-Mobile-Firefox-linux.png
new file mode 100644
index 00000000000..ec3e42f8ad9
Binary files /dev/null and b/core/src/components/range/test/activeBarStart/range.e2e.ts-snapshots/range-activeBarStart-diff-ios-rtl-Mobile-Firefox-linux.png differ
diff --git a/core/src/components/range/test/activeBarStart/range.e2e.ts-snapshots/range-activeBarStart-diff-ios-rtl-Mobile-Safari-linux.png b/core/src/components/range/test/activeBarStart/range.e2e.ts-snapshots/range-activeBarStart-diff-ios-rtl-Mobile-Safari-linux.png
new file mode 100644
index 00000000000..19b99ad6fbb
Binary files /dev/null and b/core/src/components/range/test/activeBarStart/range.e2e.ts-snapshots/range-activeBarStart-diff-ios-rtl-Mobile-Safari-linux.png differ
diff --git a/core/src/components/range/test/activeBarStart/range.e2e.ts-snapshots/range-activeBarStart-diff-md-ltr-Mobile-Chrome-linux.png b/core/src/components/range/test/activeBarStart/range.e2e.ts-snapshots/range-activeBarStart-diff-md-ltr-Mobile-Chrome-linux.png
new file mode 100644
index 00000000000..defe7434e9f
Binary files /dev/null and b/core/src/components/range/test/activeBarStart/range.e2e.ts-snapshots/range-activeBarStart-diff-md-ltr-Mobile-Chrome-linux.png differ
diff --git a/core/src/components/range/test/activeBarStart/range.e2e.ts-snapshots/range-activeBarStart-diff-md-ltr-Mobile-Firefox-linux.png b/core/src/components/range/test/activeBarStart/range.e2e.ts-snapshots/range-activeBarStart-diff-md-ltr-Mobile-Firefox-linux.png
new file mode 100644
index 00000000000..b565d59dd96
Binary files /dev/null and b/core/src/components/range/test/activeBarStart/range.e2e.ts-snapshots/range-activeBarStart-diff-md-ltr-Mobile-Firefox-linux.png differ
diff --git a/core/src/components/range/test/activeBarStart/range.e2e.ts-snapshots/range-activeBarStart-diff-md-ltr-Mobile-Safari-linux.png b/core/src/components/range/test/activeBarStart/range.e2e.ts-snapshots/range-activeBarStart-diff-md-ltr-Mobile-Safari-linux.png
new file mode 100644
index 00000000000..560e9a3e355
Binary files /dev/null and b/core/src/components/range/test/activeBarStart/range.e2e.ts-snapshots/range-activeBarStart-diff-md-ltr-Mobile-Safari-linux.png differ
diff --git a/core/src/components/range/test/activeBarStart/range.e2e.ts-snapshots/range-activeBarStart-diff-md-rtl-Mobile-Chrome-linux.png b/core/src/components/range/test/activeBarStart/range.e2e.ts-snapshots/range-activeBarStart-diff-md-rtl-Mobile-Chrome-linux.png
new file mode 100644
index 00000000000..e0fca449b6e
Binary files /dev/null and b/core/src/components/range/test/activeBarStart/range.e2e.ts-snapshots/range-activeBarStart-diff-md-rtl-Mobile-Chrome-linux.png differ
diff --git a/core/src/components/range/test/activeBarStart/range.e2e.ts-snapshots/range-activeBarStart-diff-md-rtl-Mobile-Firefox-linux.png b/core/src/components/range/test/activeBarStart/range.e2e.ts-snapshots/range-activeBarStart-diff-md-rtl-Mobile-Firefox-linux.png
new file mode 100644
index 00000000000..7ea1ed52564
Binary files /dev/null and b/core/src/components/range/test/activeBarStart/range.e2e.ts-snapshots/range-activeBarStart-diff-md-rtl-Mobile-Firefox-linux.png differ
diff --git a/core/src/components/range/test/activeBarStart/range.e2e.ts-snapshots/range-activeBarStart-diff-md-rtl-Mobile-Safari-linux.png b/core/src/components/range/test/activeBarStart/range.e2e.ts-snapshots/range-activeBarStart-diff-md-rtl-Mobile-Safari-linux.png
new file mode 100644
index 00000000000..17fbd22d5c1
Binary files /dev/null and b/core/src/components/range/test/activeBarStart/range.e2e.ts-snapshots/range-activeBarStart-diff-md-rtl-Mobile-Safari-linux.png differ
diff --git a/core/src/utils/logging/index.ts b/core/src/utils/logging/index.ts
index 45c03cbdcf7..ee4234cb6a5 100644
--- a/core/src/utils/logging/index.ts
+++ b/core/src/utils/logging/index.ts
@@ -4,8 +4,8 @@
*
* @param message - The string message to be logged to the console.
*/
-export const printIonWarning = (message: string) => {
- return console.warn(`[Ionic Warning]: ${message}`);
+export const printIonWarning = (message: string, ...params: any[]) => {
+ return console.warn(`[Ionic Warning]: ${message}`, ...params);
};
/*
diff --git a/packages/vue/src/proxies.ts b/packages/vue/src/proxies.ts
index 9371abe3d46..1169996ade4 100644
--- a/packages/vue/src/proxies.ts
+++ b/packages/vue/src/proxies.ts
@@ -587,6 +587,7 @@ export const IonRange = /*@__PURE__*/ defineContainer('ion-range',
'snaps',
'step',
'ticks',
+ 'activeBarStart',
'disabled',
'value',
'ionChange',