Skip to content

Commit

Permalink
feat(slider): Add the ticks variant
Browse files Browse the repository at this point in the history
  • Loading branch information
MarkoOleksiyenko committed Feb 18, 2025
1 parent d0cfae6 commit 28b235f
Show file tree
Hide file tree
Showing 34 changed files with 1,931 additions and 40 deletions.
9 changes: 8 additions & 1 deletion angular/bootstrap/src/agnos-ui-angular.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,13 @@ import {
AccordionBodyDirective,
AccordionItemStructureDirective,
} from './components/accordion/accordion.component';
import {SliderComponent, SliderHandleDirective, SliderLabelDirective, SliderStructureDirective} from './components/slider/slider.component';
import {
SliderComponent,
SliderHandleDirective,
SliderLabelDirective,
SliderStructureDirective,
SliderTickDirective,
} from './components/slider/slider.component';
import {ProgressbarComponent, ProgressbarBodyDirective, ProgressbarStructureDirective} from './components/progressbar/progressbar.component';
import {ToastBodyDirective, ToastComponent, ToastHeaderDirective, ToastStructureDirective} from './components/toast/toast.component';
import {CollapseDirective} from './components/collapse';
Expand Down Expand Up @@ -77,6 +83,7 @@ const components = [
SliderHandleDirective,
SliderLabelDirective,
SliderStructureDirective,
SliderTickDirective,
ProgressbarComponent,
ProgressbarStructureDirective,
ProgressbarBodyDirective,
Expand Down
105 changes: 102 additions & 3 deletions angular/bootstrap/src/components/slider/slider.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import {
} from '@angular/core';
import {NG_VALUE_ACCESSOR} from '@angular/forms';
import {callWidgetFactory} from '../../config';
import type {SliderContext, SliderSlotHandleContext, SliderSlotLabelContext, SliderWidget} from './slider.gen';
import type {SliderContext, SliderSlotHandleContext, SliderSlotLabelContext, SliderSlotTickContext, SliderWidget} from './slider.gen';
import {createSlider} from './slider.gen';

/**
Expand All @@ -43,11 +43,11 @@ export class SliderLabelDirective {
/**
* Directive representing a handle for a slider component.
*
* This directive uses a template reference to render the {@link SliderSlotLabelContext}.
* This directive uses a template reference to render the {@link SliderSlotHandleContext}.
*/
@Directive({selector: 'ng-template[auSliderHandle]'})
export class SliderHandleDirective {
public templateRef = inject(TemplateRef<SliderSlotLabelContext>);
public templateRef = inject(TemplateRef<SliderSlotHandleContext>);
static ngTemplateContextGuard(_dir: SliderHandleDirective, context: unknown): context is SliderSlotHandleContext {
return true;
}
Expand Down Expand Up @@ -107,6 +107,73 @@ class SliderDefaultHandleSlotComponent {
*/
export const sliderDefaultSlotHandle: SlotContent<SliderSlotHandleContext> = new ComponentTemplate(SliderDefaultHandleSlotComponent, 'handle');

/**
* Directive representing a tick for a slider component.
*
* This directive uses a template reference to render the {@link SliderSlotTickContext}.
*/
@Directive({selector: 'ng-template[auSliderTick]'})
export class SliderTickDirective {
public templateRef = inject(TemplateRef<SliderSlotTickContext>);

Check warning on line 117 in angular/bootstrap/src/components/slider/slider.component.ts

View check run for this annotation

Codecov / codecov/patch

angular/bootstrap/src/components/slider/slider.component.ts#L117

Added line #L117 was not covered by tests
static ngTemplateContextGuard(_dir: SliderTickDirective, context: unknown): context is SliderSlotTickContext {
return true;

Check warning on line 119 in angular/bootstrap/src/components/slider/slider.component.ts

View check run for this annotation

Codecov / codecov/patch

angular/bootstrap/src/components/slider/slider.component.ts#L119

Added line #L119 was not covered by tests
}
}

@Component({
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [UseDirective],
template: `
<ng-template auSliderTick #tick let-state="state" let-directives="directives" let-item="tick">
@if (item.displayLabel) {
<span [auUse]="[directives.tickLabelDirective, {item}]">
{{ item.value }}
</span>
}
<span [auUse]="[directives.tickDirective, {item}]">
@if (!item.selected) {
<svg
xmlns="http://www.w3.org/2000/svg"
style="width: var(--bs-slider-tick-secondary-size); height: var(--bs-slider-tick-primary-size);"
fill="none"
>
<circle
cx="50%"
cy="50%"
r="45%"
fill="white"
[attr.stroke]="state.disabled() ? 'var(--bs-slider-tick-disabled-color)' : 'var(--bs-slider-tick-neutral-color)'"
stroke-width="1.5"
/>
</svg>
} @else {
<svg
xmlns="http://www.w3.org/2000/svg"
style="width: var(--bs-slider-tick-secondary-size); height: var(--bs-slider-tick-primary-size);"
fill="none"
>
<circle
cx="50%"
cy="50%"
r="50%"
[attr.fill]="state.disabled() ? 'var(--bs-slider-tick-disabled-color)' : 'var(--bs-slider-tick-selected-color)'"
/>
<circle cx="50%" cy="50%" r="25%" fill="white" />
</svg>
}
</span>
</ng-template>
`,
})
class SliderDefaultTickSlotComponent {
readonly tick = viewChild.required<TemplateRef<SliderSlotTickContext>>('tick');
}

/**
* A constant representing the default slot tick for the slider component.
*/
export const sliderDefaultSlotTick: SlotContent<SliderSlotTickContext> = new ComponentTemplate(SliderDefaultTickSlotComponent, 'tick');

/**
* Directive that provides structure for the slider component.
*
Expand Down Expand Up @@ -148,6 +215,9 @@ export class SliderStructureDirective {
}
</div>
}
@for (tick of state.ticks(); track tick.position) {
<ng-template [auSlot]="state.tick()" [auSlotProps]="{state, api, directives, tick}" />
}
@for (item of state.sortedHandles(); track item.id; let i = $index) {
<ng-template [auSlot]="state.handle()" [auSlotProps]="{state, api, directives, item}" />
@if (state.showValueLabels() && !state.combinedLabelDisplay()) {
Expand Down Expand Up @@ -296,6 +366,27 @@ export class SliderComponent extends BaseWidgetDirective<SliderWidget> {
*/
readonly vertical = input(undefined, {alias: 'auVertical', transform: auBooleanAttribute});

/**
* If `true` the ticks are displayed on the slider
*
* @defaultValue `false`
*/
readonly showTicks = input(undefined, {alias: 'auShowTicks', transform: auBooleanAttribute});

/**
* Unit value between the ticks
*
* @defaultValue `0`
*/
readonly tickStep = input(undefined, {alias: 'auTickStep', transform: auNumberAttribute});

/**
* If `true` the tick values are displayed on the slider
*
* @defaultValue `true`
*/
readonly showTickValues = input(undefined, {alias: 'auShowTickValues', transform: auBooleanAttribute});

/**
* An event emitted when slider values are changed
*
Expand Down Expand Up @@ -331,6 +422,12 @@ export class SliderComponent extends BaseWidgetDirective<SliderWidget> {
readonly handle = input<SlotContent<SliderSlotHandleContext>>(undefined, {alias: 'auHandle'});
readonly slotHandleFromContent = contentChild(SliderHandleDirective);

/**
* Slot to change the ticks
*/
readonly tick = input<SlotContent<SliderSlotTickContext>>(undefined, {alias: 'auTick'});
readonly slotTickFromContent = contentChild(SliderTickDirective);

constructor() {
super(
callWidgetFactory({
Expand All @@ -339,6 +436,7 @@ export class SliderComponent extends BaseWidgetDirective<SliderWidget> {
defaultConfig: {
structure: sliderDefaultSlotStructure,
handle: sliderDefaultSlotHandle,
tick: sliderDefaultSlotTick,
},
events: {
onValuesChange: (event) => {
Expand All @@ -354,6 +452,7 @@ export class SliderComponent extends BaseWidgetDirective<SliderWidget> {
structure: this.slotStructureFromContent()?.templateRef,
handle: this.slotHandleFromContent()?.templateRef,
label: this.slotLabelFromContent()?.templateRef,
tick: this.slotTickFromContent()?.templateRef,
}),
}),
);
Expand Down
1 change: 1 addition & 0 deletions angular/bootstrap/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ export type {
SliderState,
SliderWidget,
SliderHandle,
SliderTick,
HandleDisplayOptions,
ProgressDisplayOptions,
SliderDirectives,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {FormControl, FormsModule, ReactiveFormsModule} from '@angular/forms';
imports: [SliderComponent, ReactiveFormsModule, FormsModule],
template: `
<h2>Horizontal slider</h2>
<div auSlider auMin="0" auMax="100" auStepSize="1" [formControl]="sliderControl" [auRtl]="true"></div>
<div auSlider auMin="0" auMax="100" auStepSize="1" [formControl]="sliderControl" [auRtl]="true" auShowTicks="true" auTickStep="25"></div>
<br />
<div auSlider auMin="0" auMax="100" auStepSize="1" [formControl]="sliderRangeControl" [auRtl]="true"></div>
<h2>Vertical slider</h2>
Expand Down
39 changes: 39 additions & 0 deletions angular/demo/bootstrap/src/app/samples/slider/ticks.route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import {SliderComponent} from '@agnos-ui/angular-bootstrap';
import {Component, signal} from '@angular/core';
import {FormControl, FormsModule, ReactiveFormsModule} from '@angular/forms';

@Component({
imports: [SliderComponent, ReactiveFormsModule, FormsModule],
template: `
<h2>With intermediate steps</h2>
<div auSlider auMin="0" auMax="100" auStepSize="1" [formControl]="sliderControl" auShowTicks="true" auTickStep="25"></div>
<br />
<h2>Ticks as steps</h2>
<div auSlider auMin="0" auMax="100" auStepSize="25" [formControl]="sliderRangeControl" auShowTicks="true"></div>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="disabled" [(ngModel)]="disabledToggle" (change)="handleDisabled()" />
<label class="form-check-label" for="disabled">Disabled</label>
</div>
<h2>Without tick labels</h2>
<div auSlider auMin="0" auMax="100" auStepSize="25" [formControl]="sliderRangeControlLabels" auShowTicks="true" auShowTickValues="false"></div>
<p>If <i>showTickValues</i> is set to <i>false</i> automatically put the min/max/current label display to <i>true</i></p>
`,
})
export default class TicksSliderComponent {
readonly sliderControl = new FormControl([30]);

readonly sliderRangeControl = new FormControl([30, 70]);
readonly disabledToggle = signal(false);

readonly sliderRangeControlLabels = new FormControl([30, 70]);

handleDisabled() {
if (this.disabledToggle()) {
this.sliderRangeControl.disable();
} else {
this.sliderRangeControl.enable();
}
}
}
12 changes: 11 additions & 1 deletion angular/demo/bootstrap/src/app/samples/slider/vertical.route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,17 @@ import {FormControl, FormsModule, ReactiveFormsModule} from '@angular/forms';
<div class="mt-3">Form control values: {{ sliderControlRangeValues()?.join(', ') }}</div>
</div>
<div class="col-6" style="height: 300px">
<div auSlider auMin="0" auMax="100" auStepSize="1" auVertical [formControl]="sliderControl" auClassName="my-0"></div>
<div
auSlider
auMin="0"
auMax="100"
auStepSize="1"
auVertical
[formControl]="sliderControl"
auClassName="my-0"
auShowTicks="true"
auTickStep="25"
></div>
<div class="mt-3">From control value: {{ sliderControlValues() }}</div>
</div>
</div>
Expand Down
18 changes: 17 additions & 1 deletion core-bootstrap/src/components/slider/slider.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type {SliderDirectives, SliderHandle, SliderProps as CoreProps, SliderState as CoreState} from '@agnos-ui/core/components/slider';
import type {SliderDirectives, SliderHandle, SliderProps as CoreProps, SliderState as CoreState, SliderTick} from '@agnos-ui/core/components/slider';
import {createSlider as createCoreSlider, getSliderDefaultConfig as getCoreDefaultConfig} from '@agnos-ui/core/components/slider';
import {extendWidgetProps} from '@agnos-ui/core/services/extendWidget';
import type {SlotContent, Widget, WidgetFactory, WidgetSlotContext} from '@agnos-ui/core/types';
Expand Down Expand Up @@ -31,6 +31,16 @@ export interface SliderSlotHandleContext extends SliderContext {
item: SliderHandle;
}

/**
* Represents the context for a slider tick slot
*/
export interface SliderSlotTickContext extends SliderContext {
/**
* tick context
*/
tick: SliderTick;
}

interface SliderExtraProps {
/**
* Slot to change the default display of the slider
Expand All @@ -51,6 +61,11 @@ interface SliderExtraProps {
* Slot to change the handlers
*/
handle: SlotContent<SliderSlotHandleContext>;

/**
* Slot to change the ticks
*/
tick: SlotContent<SliderSlotTickContext>;
}

/**
Expand All @@ -71,6 +86,7 @@ const defaultConfigExtraProps: SliderExtraProps = {
structure: undefined,
label: ({value}: SliderSlotLabelContext) => '' + value,
handle: undefined,
tick: undefined,
};

/**
Expand Down
10 changes: 10 additions & 0 deletions core-bootstrap/src/scss/_variables.scss
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ $au-slider-handle-border-radius: 50% !default;
$au-slider-handle-outline: none !default;
$au-slider-handle-focus-box-shadow: 0 0 0 $au-focus-ring-width $au-focus-ring-color !default;
$au-slider-handle-focus-hover-box-shadow: 0 0 0 $au-focus-ring-width $au-focus-ring-color !default;
$au-slider-tick-primary-size: 1rem !default;
$au-slider-tick-secondary-size: 1rem !default;
$au-slider-tick-neutral-color: var(--#{$prefix}light-emphasis, #666666) !default;
$au-slider-tick-disabled-color: var(--#{$prefix}dark-bg-subtle, #ced4da) !default;
$au-slider-tick-selected-color: var(--#{$prefix}primary, #0d6efd) !default;
$au-slider-tick-label-translate-vertical: translateY(75%) !default;
$au-slider-progress-color: var(--#{$prefix}primary, #0d6efd) !default;
$au-slider-progress-height: 0.25rem !default;
$au-slider-progress-vertical-transform: rotate(90deg) !default;
Expand Down Expand Up @@ -59,11 +65,15 @@ $au-slider-disabled-cursor: not-allowed !default;

$au-slider-bar-size-sm: 0.2rem !default;
$au-slider-handle-size-sm: 1rem !default;
$au-slider-tick-primary-size-sm: 0.75rem !default;
$au-slider-tick-secondary-size-sm: 0.75rem !default;
$au-slider-font-size-sm: 0.875rem !default;
$au-slider-offset-sm: 0rem !default;

$au-slider-bar-size-lg: 0.3125rem !default;
$au-slider-handle-size-lg: 1.5rem !default;
$au-slider-tick-primary-size-lg: 1.25rem !default;
$au-slider-tick-secondary-size-lg: 1.25rem !default;
$au-slider-font-size-lg: 1.125rem !default;
$au-slider-offset-lg: 0rem !default;
// scss-docs-end slider-vars
Expand Down
Loading

0 comments on commit 28b235f

Please sign in to comment.