Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(slider): Add the ticks variant #1104

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
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
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, CollapseTriggerDirective} 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 @@
} 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 @@
/**
* 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 @@
*/
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 @@
}
</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 @@
*/
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 @@
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 @@
defaultConfig: {
structure: sliderDefaultSlotStructure,
handle: sliderDefaultSlotHandle,
tick: sliderDefaultSlotTick,
},
events: {
onValuesChange: (event) => {
Expand All @@ -354,6 +452,7 @@
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
Loading