Slider
The WAI-ARIA APG slider — and its multi-thumb sibling — as bare form editors on the suite's one control base: arrows move by step (RTL-aware), PageUp/PageDown by largeStep, Home/End to the ends, dragging commits live ([debounce] throttles it) and Escape cancels the gesture, restoring the start value — no reference slider offers that.
Works standalone via [(value)], with Signal Forms via [formField], and with reactive/template forms via formControl/ngModel — the same three modes as every other editor in the family, with no extra wiring.
Getting started
One thumb, the full APG key set, live commits while dragging. valueCommitted reports every change; slideEnded fires once per gesture at release — the DevExtreme onHandleRelease timing without a mode switch.
Value: 40
import { ChangeDetectionStrategy, Component, signal } from '@angular/core';
import { OgeSlider } from '@oge-ui/inputs';
@Component({
selector: 'demo-root',
imports: [OgeSlider],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<!-- The WAI-ARIA APG slider: a focusable role="slider" thumb —
arrows ±step (RTL-aware), PageUp/PageDown ±largeStep, Home/End to the
ends. Dragging commits live; [debounce] throttles it; Escape cancels the
gesture and restores the start value. -->
<oge-slider [(value)]="volume" [min]="0" [max]="100" ariaLabel="Volume" />
`,
})
export class Demo {
protected readonly volume = signal(40);
}Range slider
Two thumbs selecting a [start, end] pair. Each thumb's aria-valuemin/aria-valuemax is dynamically constrained by the other — the APG multi-thumb rule — and minRange keeps a minimum gap. Clicking the track moves the nearest thumb.
Range: 200 – 600
import { ChangeDetectionStrategy, Component, signal } from '@angular/core';
import { OgeRangeSlider } from '@oge-ui/inputs';
@Component({
selector: 'demo-root',
imports: [OgeRangeSlider],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<!-- APG multi-thumb: two focusable thumbs, each one's
aria-valuemin/max dynamically constrained by the other (plus minRange).
Clicking the track moves the NEAREST thumb. -->
<oge-range-slider
[(value)]="price"
[min]="0"
[max]="1000"
[step]="10"
[minRange]="50"
/>
`,
})
export class Demo {
protected readonly price = signal<readonly [number, number]>([200, 600]);
}Ticks and labels
Ticks sit on the tickStep grid (falling back to largeStep, then step). showTickLabels renders a formatted label under each tick — Kendo's tick title callback, fed by formatValue; showLabels renders just the min/max ends instead.
import { ChangeDetectionStrategy, Component, signal } from '@angular/core';
import { OgeSlider } from '@oge-ui/inputs';
@Component({
selector: 'demo-root',
imports: [OgeSlider],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<!-- Ticks sit on the tickStep grid (falling back to largeStep,
then step). showTickLabels renders a formatted label under each tick
(Kendo's tick title callback, fed by formatValue); showLabels renders
just the min/max ends instead. -->
<oge-slider
[(value)]="rating"
[min]="0"
[max]="10"
[largeStep]="2"
[showTicks]="true"
[showTickLabels]="true"
/>
`,
})
export class Demo {
protected readonly rating = signal(6);
}Value indicator
valueIndicator: 'active' shows the bubble while focused or dragging; 'always' keeps it up. formatValue feeds the bubble, the end labels and aria-valuetext — display and screen-reader announcement can never diverge.
import { ChangeDetectionStrategy, Component, signal } from '@angular/core';
import { OgeSlider } from '@oge-ui/inputs';
@Component({
selector: 'demo-root',
imports: [OgeSlider],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<!-- valueIndicator: 'active' shows the bubble while focused or
dragging (Material's discrete), 'always' keeps it up (DevExtreme's
tooltip.showMode). formatValue feeds the bubble, the end labels AND
aria-valuetext — one input, display and announcement never diverge. -->
<oge-slider
[(value)]="volume"
valueIndicator="active"
[formatValue]="asDecibels"
[showLabels]="true"
ariaLabel="Volume"
/>
`,
})
export class Demo {
protected readonly volume = signal(40);
protected readonly asDecibels = (value: number): string => `${value} dB`;
}Buttons and vertical
showButtons adds Kendo-style increment/decrement buttons with press-and-hold repeat (the number box's spin timing). A vertical slider keeps the same role with aria-orientation — Up still increases, per the APG.
import { ChangeDetectionStrategy, Component, signal } from '@angular/core';
import { OgeSlider } from '@oge-ui/inputs';
@Component({
selector: 'demo-root',
imports: [OgeSlider],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<!-- showButtons adds Kendo-style increment/decrement buttons
with press-and-hold repeat (the number box's spin timing config).
orientation="vertical" keeps role and keys per the APG — Up increases. -->
<oge-slider [(value)]="volume" [showButtons]="true" ariaLabel="Volume" />
<div style="height: 200px">
<oge-slider
[(value)]="volume"
orientation="vertical"
ariaLabel="Volume (vertical)"
/>
</div>
`,
})
export class Demo {
protected readonly volume = signal(40);
}Inside a form
A bare editor: <oge-form> renders the label/hint/error chrome around it. dataType: 'number' still defaults to the number box — the slider is an explicit editorType choice, with min/max/step from editorOptions.
Model: 70
import { ChangeDetectionStrategy, Component, signal } from '@angular/core';
import { OgeForm } from '@oge-ui/forms';
import type { OgeFormItemData } from '@oge-ui/forms';
@Component({
selector: 'demo-root',
imports: [OgeForm],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<!-- Inside <oge-form>: a bare editor — the form renders the
label/hint/error chrome around it. dataType 'number' still defaults to
the number box; the slider is an explicit editorType choice. -->
<oge-form [(formData)]="settings" [items]="items" />
`,
})
export class Demo {
protected readonly settings = signal({ brightness: 70 });
protected readonly items: OgeFormItemData[] = [
{
field: 'brightness',
label: 'Brightness',
editorType: 'slider',
editorOptions: { min: 0, max: 100, step: 5 },
},
];
}