Skip to content

Commit

Permalink
feat: input dinamic range slider (#88)
Browse files Browse the repository at this point in the history
* feat: input dinamic range slider

fix #26

* fix: reverting changes on nopetregisredpage, this page should not be modified

fix #26

* refactor: add breakpoint and scss vars in style file

fix #26

* refactor: change html to span for semantic reasons and added a unit variable on component build

fix #26

* style: hide unit span on mobile devices

fix #26

* refactor: add getters and setters for RangeSlider values. change component storybook to follow pattern

fix #26

* refactor: change variable name for better reading and understanding

fix #26

* refactor: add options to storybook and unmount to remove event listeners

fix #26

* refactor: sepated handlemove into handlemouse and handletouch move

fix #26

* feat: add listener do key 'esc', when pressed the value resets

fix #26
  • Loading branch information
RafaelLimaC authored Mar 2, 2024
1 parent 3d720ca commit 8a16de8
Show file tree
Hide file tree
Showing 6 changed files with 355 additions and 2 deletions.
162 changes: 162 additions & 0 deletions src/components/RangeSlider/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
import { Component } from 'pet-dex-utilities';
import './index.scss';

const events = [
'value:change',
'unit:change',
'interactionEnd',
'interactionStart',
];

const html = `
<div class="range-slider" data-select="range-slider">
<div>
<span class="range-slider__info" data-select="range-slider-value"></span>
<span class="range-slider__info" data-select="range-slider-unit"></span>
</div>
<div class="range-slider__content" data-select="range-slider-content">
<button class="range-slider__button" data-select="range-slider-button">I I I</button>
</div>
</div>
`;

export default function RangeSlider({
minimum = 0,
maximum = 100,
unit = 'kg',
value = 10,
stepSize = 0.05,
} = {}) {
Component.call(this, { html, events });

this.setMinimum(minimum);
this.setMaximum(maximum);
this.setUnit(unit);
this.setValue(value);
this.setStepSize(stepSize);

const unitElement = this.selected.get('range-slider-unit');
const containerElement = this.selected.get('range-slider-content');

let isMouseDown = false;
let startX = 0;
let currentValue = Math.min(Math.max(value, minimum), maximum);
let prevValue = value;

const handleStart = (clientX) => {
isMouseDown = true;
startX = clientX;
prevValue = currentValue;
this.emit('interactionStart', currentValue);
};

const moveBackground = (clientX) => {
if (!isMouseDown) return;

const mouseX = clientX;
const offsetX = mouseX - startX;

containerElement.style.backgroundPositionX = `${parseInt(containerElement.style.backgroundPositionX || 0, 10) - offsetX}px`;

currentValue = mouseX > startX
? Math.min(maximum, currentValue + this.getStepSize())
: Math.max(minimum, currentValue - this.getStepSize());

this.setValue(currentValue);
unitElement.textContent = unit;

startX = mouseX;
this.emit('value:change', currentValue);
};

const handleEnd = () => {
isMouseDown = false;
this.emit('interactionEnd', currentValue);
};

const handleMouseMove = (event) => {
event.preventDefault();
const { clientX } = event;
moveBackground(clientX);
};

const handleTouchMove = (event) => {
event.preventDefault();
const { clientX } = event.touches[0];
moveBackground(clientX);
};

const escKeyDown = (event) => {
if (event.key === 'Escape') {
isMouseDown = false;
currentValue = prevValue;
this.setValue(prevValue);
this.emit('interactionEnd', currentValue);
}
};

containerElement.addEventListener('mousedown', (event) => {
event.preventDefault();
handleStart(event.clientX);
});

containerElement.addEventListener('touchstart', (event) => {
event.preventDefault();
handleStart(event.touches[0].clientX);
});

this.listen('mount', () => {
document.addEventListener('mousemove', handleMouseMove);
document.addEventListener('mouseup', handleEnd);
document.addEventListener('touchmove', handleTouchMove);
document.addEventListener('touchend', handleEnd);
document.addEventListener('keydown', escKeyDown);
});

this.listen('unmount', () => {
document.removeEventListener('mousemove', handleMouseMove);
document.removeEventListener('mouseup', handleEnd);
document.removeEventListener('touchmove', handleTouchMove);
document.removeEventListener('touchend', handleEnd);
document.removeEventListener('keydown', escKeyDown);
});
}

RangeSlider.prototype = Object.assign(
RangeSlider.prototype,
Component.prototype,
{
getValue() {
return parseFloat(this.selected.get('range-slider-value').textContent);
},
setValue(value) {
this.selected.get('range-slider-value').textContent = value.toFixed(1);
this.emit('value:change', value);
},
getUnit() {
return this.selected.get('range-slider-unit').textContent;
},
setUnit(unit) {
this.selected.get('range-slider-unit').textContent = unit;
this.emit('unit:change', unit);
},
getMinimum() {
return this.minimum;
},
setMinimum(value) {
this.minimum = value;
},
getMaximum() {
return this.maximum;
},
setMaximum(value) {
this.maximum = value;
},
getStepSize() {
return this.stepSize;
},
setStepSize(value) {
this.stepSize = value;
},
},
);
113 changes: 113 additions & 0 deletions src/components/RangeSlider/index.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
@use '~styles/base.scss';
@use '~styles/colors.scss' as colors;
@use '~styles/breakpoints.scss' as breakpoints;

.range-slider {
width: 100%;

display: flex;
flex-direction: column;

align-items: center;

&__button {
color: rgb(209, 230, 255);

font-size: 2rem;

padding: 1rem;
border: 2px solid colors.$blue600;

position: absolute;
top: 50%;
left: 50%;

background-color: colors.$blue500;
transform: translate(-50%, -50%);

border-radius: 0.8rem;

cursor: grab;
letter-spacing: 1;
}

&__info {
display: inline-block;

font-family: 'Noto Sans', sans-serif;
color: colors.$blue500;

text-align: center;
font-size: 8rem;
font-weight: 700;

margin-bottom: 2rem;
}

&__info:nth-child(2) {
display: none;
}

&__content {
width: 100%;
height: 100px;
overflow: hidden;

position: relative;

background-image: url('../../home/images/ruler-slider.svg');
background-repeat: repeat-x;
background-position: 0 center;
}

&__content::before {
left: 0;

background: linear-gradient(
to right,
colors.$appContentColor,
rgba(255, 255, 255, 0)
);
}

&__content::after {
right: 0;

background: linear-gradient(
to left,
colors.$appContentColor,
rgba(255, 255, 255, 0)
);
}

&__content::before,
&__content::after {
width: 30px;
height: 100%;

position: absolute;
z-index: 2;

pointer-events: none;
content: '';
}
}

@include breakpoints.from667 {
.range-slider__content::before,
.range-slider__content::after {
width: 16%;
}

.range-slider__button {
padding: 1rem 2rem;
}

.range-slider__info:first-child {
padding-right: 2rem;
}

.range-slider__info:nth-child(2) {
display: inline-block;
}
}
42 changes: 42 additions & 0 deletions src/home/images/ruler-slider.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
37 changes: 37 additions & 0 deletions src/stories/RangeSlider.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import RangeSlider from '../components/RangeSlider';

export default {
title: 'Components/RangeSlider',
render: (args) => {
const rangeSlider = new RangeSlider(args);
const $container = document.createElement('div');
rangeSlider.mount($container);

return $container;
},
argTypes: {
minimum: { control: 'number', default: 0 },
maximum: { control: 'number', default: 100 },
unit: {
control: {
type: 'text',
options: ['kg', 'lb'],
},
default: 'kg',
},
value: { control: 'number', default: 10 },
stepSize: { control: 'number', default: 0.05 },
},
};

export const Default = {
args: {},
};

export const WithLbUnit = {
args: {
...Default.args,
unit: 'lb',
value: 20,
},
};
1 change: 1 addition & 0 deletions src/styles/colors.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ $grey150: rgb(236, 239, 242);
$gray800: rgb(57, 67, 79);
$gray600: rgb(128, 139, 154);
$blue500: rgb(27, 133, 243);
$blue600: rgb(18, 104, 204);
$yellow500: rgb(255, 197, 66);
2 changes: 0 additions & 2 deletions src/styles/var.scss

This file was deleted.

0 comments on commit 8a16de8

Please sign in to comment.