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(range-slider): add component (VIV-1488) #1584

Merged
merged 17 commits into from
Feb 28, 2024
Merged
Show file tree
Hide file tree
Changes from 11 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 apps/docs/_data/components.json
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,11 @@
"title": "File Picker",
"markdown": "./libs/components/src/lib/file-picker/README.md"
},
{
"title": "Range Slider",
"status": "alpha",
"markdown": "./libs/components/src/lib/range-slider/README.md"
},
{
"title": "Split Button",
"markdown": "./libs/components/src/lib/split-button/README.md"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<template>
<VRangeSlider
:start="start"
:end="end"
@update:start="start = $event"
@update:end="end = $event"
/>
<!-- In Vue 3 you can use v-model shorthand instead of @update:start and @update:end
<VRangeSlider v-model:start="start" v-model:end="end" />
-->
</template>

<script setup lang="ts">
import { VRangeSlider } from '@vonage/vivid-vue';
import { ref } from 'vue';
const start = ref('0');
const end = ref('10');
</script>
21 changes: 21 additions & 0 deletions apps/vue-docs/docs/examples/range-slider.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Range Slider Examples

## Range Slider
<code-tab>
<template #example>
<RangeSliderExample />
</template>
<template #code>

```vue
<!--@include: ./components/range-slider/RangeSliderExample.vue -->
```
</template>
</code-tab>

<script setup lang="ts">
import CodeTab from '../custom/CodeTab.vue';
import { defineClientComponent } from 'vitepress';

const RangeSliderExample = defineClientComponent(() => import('./components/range-slider/RangeSliderExample.vue'));
</script>
1 change: 1 addition & 0 deletions libs/components/src/lib/components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export * from './progress-ring/definition';
export * from './progress/definition';
export * from './radio-group/definition';
export * from './radio/definition';
export * from './range-slider/definition';
export * from './select/definition';
export * from './selectable-box/definition';
export * from './side-drawer/definition';
Expand Down
212 changes: 212 additions & 0 deletions libs/components/src/lib/range-slider/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
# Range Slider

The range slider component allows users to select a range. It includes two thumb controls that can be dragged to set the start and end values.

```js
<script type="module">import '@vonage/vivid/range-slider';</script>
```

```html preview
<vwc-range-slider></vwc-range-slider>
```

## Members

### Min

The lowest value allowed for the range.

- Type: `number`
- Default: `0`

```html preview blocks
<div>
<vwc-range-slider id="slider" min="-10"></vwc-range-slider>
</div>
<div>Current range: <span id="start"></span> to <span id="end"></span></div>

<script>
const slider = document.querySelector('#slider');
const start = document.querySelector('#start');
const end = document.querySelector('#end');
const updateDescription = () => {
start.innerText = slider.start;
end.innerText = slider.end;
}
customElements.whenDefined('vwc-range-slider').then(updateDescription);
slider.addEventListener('change', updateDescription);
</script>
```

### Max

The highest value allowed for the range.

- Type: `number`
- Default: `0`

```html preview blocks
<div>
<vwc-range-slider id="slider" max="20"></vwc-range-slider>
</div>
<div>Current range: <span id="start"></span> to <span id="end"></span></div>

<script>
const slider = document.querySelector('#slider');
const start = document.querySelector('#start');
const end = document.querySelector('#end');
const updateDescription = () => {
start.innerText = slider.start;
end.innerText = slider.end;
}
customElements.whenDefined('vwc-range-slider').then(updateDescription);
slider.addEventListener('change', updateDescription);
</script>
```

### Step

Sets the granularity with which values can be incremented/decremented.

- Type: `number`
- Default: `1`

```html preview blocks
<div>
<vwc-range-slider id="slider" step="0.5"></vwc-range-slider>
</div>
<div>Current range: <span id="start"></span> to <span id="end"></span></div>

<script>
const slider = document.querySelector('#slider');
const start = document.querySelector('#start');
const end = document.querySelector('#end');
const updateDescription = () => {
start.innerText = slider.start;
end.innerText = slider.end;
}
customElements.whenDefined('vwc-range-slider').then(updateDescription);
slider.addEventListener('change', updateDescription);
</script>
```

### Orientation

RichardHelm marked this conversation as resolved.
Show resolved Hide resolved
When used vertically, the range slider fills the height of its container.

- Type: `'horizontal'` | `'vertical'`
- Default: `'horizontal'`

```html preview center 300px
<vwc-range-slider orientation="vertical"></vwc-range-slider>
```

### Markers

Toggles markers display.

- Type: `boolean`
- Default: `false`

```html preview blocks
<vwc-range-slider markers></vwc-range-slider>
```

### Connotation

- Type: `'accent'` | `'cta'`
- Default: `'accent'`

```html preview blocks
<vwc-range-slider connotation="cta"></vwc-range-slider>
```

### Disabled

Toggle the `disabled` member to disable/enable the slider.

- Type: `boolean`
- Default: `false`

```html preview blocks
<vwc-range-slider disabled></vwc-range-slider>
```

### Start

The lower value of the range.

- Type: `string`
- Default: `min`

```html preview blocks
<vwc-range-slider start="5"></vwc-range-slider>
```

### End

The upper value of the range.

- Type: `string`
- Default: `max`

```html preview blocks
<vwc-range-slider end="5"></vwc-range-slider>
```

## Events

<div class="table-wrapper">

| Name | Description |
|-------------|---------------------------------------------|
| input | When either the start or end value changes. |
| change | When either the start or end value changes. |
| input:start | When the start value changes |
| input:end | When the end value changes |

</div>

## Accessibility

Both thumbs have a `role` of `slider`, which needs an accessible label. By default, they use a localized version of "min" and "max".
You can change the labels by setting the `aria-start-label` and `aria-end-label` attributes.

You can set the `valueTextFormatter` member to customize how values will be formatted for the thumbs' `aria-valuetext` attribute.

## Use Cases

```html preview
<div>
<vwc-range-slider id="slider" min="0" max="7200" end="7200" step="15"></vwc-range-slider>
</div>
<div>Duration from <strong><span id="start"></span> to <span id="end"></span></strong></div>
<script>
const slider = document.querySelector('#slider');
const start = document.querySelector('#start');
const end = document.querySelector('#end');
const formatValue = (value) => {
const totalSeconds = Number.parseFloat(value);
const hours = Math.floor(totalSeconds / 3600);
const minutes = Math.floor((totalSeconds % 3600) / 60);
const seconds = Math.floor(totalSeconds % 60);
return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
};
const updateDescription = () => {
start.innerText = formatValue(slider.start);
end.innerText = formatValue(slider.end);
};
customElements.whenDefined('vwc-range-slider').then(updateDescription);
slider.valueTextFormatter = formatValue;
slider.addEventListener('change', updateDescription);
</script>
```
28 changes: 28 additions & 0 deletions libs/components/src/lib/range-slider/definition.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import type { FoundationElementDefinition } from '@microsoft/fast-foundation';
import { registerFactory } from '../../shared/design-system';
import styles from './range-slider.scss?inline';

import { RangeSlider } from './range-slider';
import { RangeSliderTemplate as template } from './range-slider.template';

export const rangeSliderDefinition =
RangeSlider.compose<FoundationElementDefinition>({
baseName: 'range-slider',
template: template as any,
styles,
shadowOptions: {
delegatesFocus: true,
},
});

/**
* @internal
*/
export const rangeSliderRegistries = [rangeSliderDefinition()];

/**
* Registers the range-slider element with the design system.
*
* @param prefix - the prefix to use for the component name
*/
export const registerRangeSlider = registerFactory(rangeSliderRegistries);
3 changes: 3 additions & 0 deletions libs/components/src/lib/range-slider/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { registerRangeSlider } from './definition';

registerRangeSlider();
4 changes: 4 additions & 0 deletions libs/components/src/lib/range-slider/locale.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface RangeSliderLocale {
startThumbLabel: string;
endThumbLabel: string;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { FormAssociated, FoundationElement } from '@microsoft/fast-foundation';

class _RangeSlider extends FoundationElement {}
// eslint-disable-next-line @typescript-eslint/naming-convention
interface _RangeSlider extends FormAssociated {}

export class FormAssociatedRangeSlider extends FormAssociated(_RangeSlider) {
proxy = document.createElement('input');
}
7 changes: 7 additions & 0 deletions libs/components/src/lib/range-slider/range-slider.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
@import "../slider/slider";

.track-start {
// Unlike slider, track-start should not be aligned to the start of the track
top: unset !important;
left: unset !important;
RichardHelm marked this conversation as resolved.
Show resolved Hide resolved
}
Loading
Loading