Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions packages/bits/demo/src/components/app/app-routing.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,11 @@ const appRoutes: Routes = [
loadChildren: async () =>
import("../demo/radio-group/radio-group.module"),
},
{
path: "range-filter",
loadChildren: async () =>
import("../demo/range-filter/range-filter.module"),
},
{
path: "repeat",
loadChildren: async () => import("../demo/repeat/repeat.module"),
Expand Down
13 changes: 13 additions & 0 deletions packages/bits/demo/src/components/demo/demo.files.ts
Original file line number Diff line number Diff line change
Expand Up @@ -962,6 +962,19 @@ export const DEMO_PATHS = [
"radio-group/radio-group.module.ts",
"radio-group/value-change-radio-group/value-change-radio-group.example.component.html",
"radio-group/value-change-radio-group/value-change-radio-group.example.component.ts",
"range-filter/index.ts",
"range-filter/range-filter-basic/range-filter-basic.example.component.html",
"range-filter/range-filter-basic/range-filter-basic.example.component.less",
"range-filter/range-filter-basic/range-filter-basic.example.component.ts",
"range-filter/range-filter-docs/range-filter-docs.example.component.html",
"range-filter/range-filter-docs/range-filter-docs.example.component.ts",
"range-filter/range-filter-guides/range-filter-guides.example.component.html",
"range-filter/range-filter-guides/range-filter-guides.example.component.less",
"range-filter/range-filter-guides/range-filter-guides.example.component.ts",
"range-filter/range-filter-vertical/range-filter-vertical.example.component.html",
"range-filter/range-filter-vertical/range-filter-vertical.example.component.less",
"range-filter/range-filter-vertical/range-filter-vertical.example.component.ts",
"range-filter/range-filter.module.ts",
"repeat/index.ts",
"repeat/repeat-disabled-multi-selection/repeat-disabled-multi-selection.example.component.html",
"repeat/repeat-disabled-multi-selection/repeat-disabled-multi-selection.example.component.ts",
Expand Down
4 changes: 4 additions & 0 deletions packages/bits/demo/src/components/demo/range-filter/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export * from "./range-filter-docs/range-filter-docs.example.component";
export * from "./range-filter-basic/range-filter-basic.example.component";
export * from "./range-filter-guides/range-filter-guides.example.component";
export * from "./range-filter-vertical/range-filter-vertical.example.component";
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<div class="demo-card" [attr.data-testid]="'test-range-filter-basic'">
<div class="demo-card__header">
<div>
<div class="demo-card__eyebrow">Alert Tuning</div>
<h3 class="demo-card__title">CPU usage focus band</h3>
<p class="demo-card__subtitle">
Pick the range you want the dashboard highlight to emphasize.
</p>
</div>
<div class="demo-card__pill">
{{ selectedRange.low }}% - {{ selectedRange.high }}%
</div>
</div>

<nui-range-filter
label="CPU usage"
unit="%"
[min]="0"
[max]="100"
[step]="1"
[valueLow]="selectedRange.low"
[valueHigh]="selectedRange.high"
(rangeChange)="onRangeChange($event)"
></nui-range-filter>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
.demo-card {
padding: 20px 24px;
border: 1px solid var(--nui-color-line-default);
border-radius: 12px;
background: linear-gradient(
180deg,
var(--nui-color-background-default) 0%,
var(--nui-color-background-strong) 100%
);

&__header {
display: flex;
justify-content: space-between;
gap: 16px;
margin-bottom: 20px;
}

&__eyebrow {
margin-bottom: 4px;
color: var(--nui-color-text-secondary);
font-size: 12px;
font-weight: 600;
letter-spacing: 0.04em;
text-transform: uppercase;
}

&__title {
margin: 0;
font-size: 20px;
}

&__subtitle {
margin: 8px 0 0;
max-width: 420px;
color: var(--nui-color-text-secondary);
}

&__pill {
align-self: flex-start;
padding: 8px 12px;
border-radius: 999px;
background: var(--nui-color-background-accented);
color: var(--nui-color-text-default);
font-size: 14px;
font-weight: 700;
white-space: nowrap;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Component } from "@angular/core";

import { RangeValue } from "@nova-ui/bits";

@Component({
selector: "nui-range-filter-basic-example",
templateUrl: "./range-filter-basic.example.component.html",
styleUrls: ["./range-filter-basic.example.component.less"],
standalone: false,
})
export class RangeFilterBasicExampleComponent {
public selectedRange: RangeValue = {
low: 18,
high: 72,
};

public onRangeChange(value: RangeValue): void {
this.selectedRange = value;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<h2>Required Modules</h2>
<ul>
<li>
<code>NuiRangeFilterModule</code>
</li>
</ul>

<h2>Basic Usage</h2>
<p>
The <code>&lt;nui-range-filter&gt;</code> component provides a draggable
range or single-value selector with keyboard support. Use the
<code>rangeChange</code> output to react to user input and feed the updated
value back through <code>valueLow</code> and <code>valueHigh</code>.
</p>
<nui-example-wrapper
filenamePrefix="range-filter-basic"
exampleTitle="Horizontal range selection"
>
<nui-range-filter-basic-example></nui-range-filter-basic-example>
</nui-example-wrapper>

<h2>Guided Stepped Range</h2>
<p>
Enable <code>guides</code> to show step markers along the track. This works
well for bounded metric ranges where users should stay aligned to discrete
increments.
</p>
<nui-message type="info" [allowDismiss]="false">
When <code>guides</code> is enabled, the component shows compact value
labels instead of number inputs.
</nui-message>
<nui-example-wrapper
filenamePrefix="range-filter-guides"
exampleTitle="Stepped range with guide markers"
>
<nui-range-filter-guides-example></nui-range-filter-guides-example>
</nui-example-wrapper>

<h2>Vertical Single Value</h2>
<p>
Use <code>orientation="vertical"</code> together with
<code>mode="single"</code> when the primary interaction is setting one
threshold rather than selecting a window.
</p>
<nui-example-wrapper
filenamePrefix="range-filter-vertical"
exampleTitle="Vertical threshold selector"
>
<nui-range-filter-vertical-example></nui-range-filter-vertical-example>
</nui-example-wrapper>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { Component } from "@angular/core";

@Component({
selector: "nui-range-filter-docs-example",
templateUrl: "./range-filter-docs.example.component.html",
standalone: false,
})
export class RangeFilterDocsExampleComponent {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<div class="metric-card" [attr.data-testid]="'test-range-filter-guides'">
<div class="metric-card__header">
<div>
<h3 class="metric-card__title">Response-time budget</h3>
<p class="metric-card__subtitle">
Discrete 25 ms steps make it easier to align the selected band
with alert thresholds.
</p>
</div>
<div class="metric-card__legend">
{{ selectedRange.low }} ms to {{ selectedRange.high }} ms
</div>
</div>

<nui-range-filter
label="Latency"
unit="ms"
[min]="0"
[max]="500"
[step]="25"
[guides]="true"
[valueLow]="selectedRange.low"
[valueHigh]="selectedRange.high"
(rangeChange)="onRangeChange($event)"
></nui-range-filter>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
.metric-card {
padding: 20px 24px;
border-left: 4px solid var(--nui-color-active);
border-radius: 12px;
background: var(--nui-color-background-default);
box-shadow: inset 0 0 0 1px var(--nui-color-line-default);

&__header {
display: flex;
justify-content: space-between;
gap: 16px;
margin-bottom: 16px;
}

&__title {
margin: 0;
font-size: 18px;
}

&__subtitle {
margin: 8px 0 0;
max-width: 420px;
color: var(--nui-color-text-secondary);
}

&__legend {
align-self: flex-start;
font-weight: 700;
color: var(--nui-color-active);
white-space: nowrap;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Component } from "@angular/core";

import { RangeValue } from "@nova-ui/bits";

@Component({
selector: "nui-range-filter-guides-example",
templateUrl: "./range-filter-guides.example.component.html",
styleUrls: ["./range-filter-guides.example.component.less"],
standalone: false,
})
export class RangeFilterGuidesExampleComponent {
public selectedRange: RangeValue = {
low: 125,
high: 350,
};

public onRangeChange(value: RangeValue): void {
this.selectedRange = value;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<div class="threshold-demo" [attr.data-testid]="'test-range-filter-vertical'">
<div class="threshold-demo__details">
<div class="threshold-demo__eyebrow">Single Value Mode</div>
<h3 class="threshold-demo__title">Priority threshold</h3>
<p class="threshold-demo__subtitle">
Use a vertical slider when the range itself is less important than a
single cut-off point.
</p>
<div class="threshold-demo__status">
Level {{ threshold.high }} / 10
</div>
<div class="threshold-demo__badge">{{ thresholdLabel }}</div>
</div>

<nui-range-filter
label="Priority"
orientation="vertical"
mode="single"
[min]="0"
[max]="10"
[step]="1"
[valueHigh]="threshold.high"
(rangeChange)="onRangeChange($event)"
></nui-range-filter>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
.threshold-demo {
display: flex;
align-items: center;
gap: 32px;
padding: 20px 24px;
border-radius: 12px;
background:
radial-gradient(circle at top left, rgba(15, 126, 209, 0.12), transparent 45%),
var(--nui-color-background-default);
box-shadow: inset 0 0 0 1px var(--nui-color-line-default);

&__details {
max-width: 320px;
}

&__eyebrow {
margin-bottom: 4px;
color: var(--nui-color-text-secondary);
font-size: 12px;
font-weight: 600;
letter-spacing: 0.04em;
text-transform: uppercase;
}

&__title {
margin: 0;
font-size: 18px;
}

&__subtitle {
margin: 8px 0 16px;
color: var(--nui-color-text-secondary);
}

&__status {
margin-bottom: 8px;
font-size: 24px;
font-weight: 700;
}

&__badge {
display: inline-block;
padding: 6px 10px;
border-radius: 999px;
background: var(--nui-color-background-accented);
font-weight: 700;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { Component } from "@angular/core";

import { RangeValue } from "@nova-ui/bits";

@Component({
selector: "nui-range-filter-vertical-example",
templateUrl: "./range-filter-vertical.example.component.html",
styleUrls: ["./range-filter-vertical.example.component.less"],
standalone: false,
})
export class RangeFilterVerticalExampleComponent {
public threshold: RangeValue = {
low: 0,
high: 7,
};

public get thresholdLabel(): string {
if (this.threshold.high >= 8) {
return "Critical";
}
if (this.threshold.high >= 5) {
return "Warning";
}
return "Healthy";
}

public onRangeChange(value: RangeValue): void {
this.threshold = value;
}
}
Loading