Skip to content

Commit

Permalink
Create a Size Selector Component (devhatt#265)
Browse files Browse the repository at this point in the history
* feat: create SizeSelector component

feat: add responsive

chore: changed imgs to template sting

fix: translation bug mobile

fix: add aria-checked event

feat: create new sizeSelector

feat: create methods and add navigation with keyboard

feat: finish component

fix: remove css noPet

fix: remove justify-content: center

* chore: reduce method handleKeyDown

* chore: change name methods activeCard and remove width and height fixed for padding

* chore: remove next declaration

* chore: change component for scroll-snap and scrollIntoView

* feat: finish component 2.0

* chore: event for 'size:change'

* chore: remove max width card and add min-width card-size
  • Loading branch information
JonasGz committed Aug 2, 2024
1 parent 93bfb66 commit b6bdf23
Show file tree
Hide file tree
Showing 7 changed files with 373 additions and 0 deletions.
5 changes: 5 additions & 0 deletions src/components/SizeSelector/images/large.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/components/SizeSelector/images/medium.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/components/SizeSelector/images/small.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

147 changes: 147 additions & 0 deletions src/components/SizeSelector/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
import { Component } from 'pet-dex-utilities';
import small from './images/small';
import medium from './images/medium';
import large from './images/large';

import './index.scss';

const events = ['size:change'];

const html = `
<div class="container-size-selector" role="radiogroup">
<ul class="container-size-selector__size-list" data-select="size-list">
<li class="container-size-selector__card" role="radio" aria-checked="false" tabindex="0">
<div class="container-size-selector__card-size">
<div class="container-size-selector__container-img">
${small}
</div>
<div class="container-size-selector__container-text">
<h3 class="container-size-selector__title">Small</h3>
<span class="container-size-selector__text">under 14kg</span>
</div>
</div>
</li>
<li class="container-size-selector__card container-size-selector__card--active" data-select="initial-card" role="radio" aria-checked="true" tabindex="0">
<div class="container-size-selector__card-size">
<div class="container-size-selector__container-img">
${medium}
</div>
<div class="container-size-selector__container-text">
<h3 class="container-size-selector__title">Medium</h3>
<span class="container-size-selector__text">14-25kg</span>
</div>
</div>
</li>
<li class="container-size-selector__card" role="radio" aria-checked="false" tabindex="0">
<div class="container-size-selector__card-size">
<div class="container-size-selector__container-img">
${large}
</div>
<div class="container-size-selector__container-text">
<h3 class="container-size-selector__title">Large</h3>
<span class="container-size-selector__text">under 25kg</span>
</div>
</div>
</li>
</ul>
</div>
`;

export default function SizeSelector() {
Component.call(this, { html, events });

this.$sizeList = this.selected.get('size-list');
this.$cards = this.$sizeList.querySelectorAll(
'.container-size-selector__card',
);
this.$initialCard = this.selected.get('initial-card');

this.listen('mount', () => {
requestAnimationFrame(() => {
this.$initialCard.scrollIntoView({
inline: 'center',
behavior: 'instant',
});
});
});

this.$cards.forEach((item, index) => {
item.addEventListener('click', () => {
this.setScroll(item);
this.scrollEnd(item);
this.emitCardEvent('click', item, index);
});

item.addEventListener('keydown', (event) => {
this.handleKeyDown(event, item);
});
});
}

SizeSelector.prototype = Object.assign(
SizeSelector.prototype,
Component.prototype,
{
nextElement(next) {
let nextIndex;
if (next) {
nextIndex = Array.from(this.$cards).indexOf(next);
this.scrollEnd(next);
this.emitCardEvent('keydown', next, nextIndex);
}
},

handleKeyDown(event, card) {
if (event.key === 'ArrowRight') {
this.nextElement(card.nextElementSibling);
}
if (event.key === 'ArrowLeft') {
this.nextElement(card.previousElementSibling);
}
},

setScroll(card) {
card.scrollIntoView({ inline: 'center' });
},

scrollEnd(card) {
this.$sizeList.addEventListener('scrollend', () => {
this.setActiveCard(card);
card.focus();
});
},

setActiveCard(element) {
this.$cards.forEach((card) => {
card.setAttribute('aria-checked', 'false');
card.classList.remove('container-size-selector__card--active');
});
element.classList.add('container-size-selector__card--active');
element.setAttribute('aria-checked', 'true');
},

emitCardEvent(eventName, card, index) {
if (eventName === 'click') {
this.emit('size:change', card, index);
}
if (eventName === 'keydown') {
this.emit('size:change', card, index);
}
},

activeCardInit() {
const $activeCard = Array.from(this.$cards).find((element) =>
element.classList.contains('container-size-selector__card--active'),
);
const indexCard = Array.from(this.$cards).findIndex((element) =>
element.classList.contains('container-size-selector__card--active'),
);
return {
card: $activeCard,
index: indexCard,
};
},
},
);
192 changes: 192 additions & 0 deletions src/components/SizeSelector/index.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
@use '~styles/colors.scss' as colors;
@use '~styles/breakpoints.scss' as breakpoints;
@use '~styles/fonts.scss' as fonts;

.container-size-selector {
width: 100%;
max-width: 15rem;

display: flex;

justify-content: center;

&__size-list {
max-width: 100%;
overflow-x: hidden;

display: flex;
flex-direction: row;
flex-shrink: 0;
gap: 2.5rem;

padding: 5rem 10rem;

scroll-behavior: smooth;
scroll-snap-type: x mandatory;
}

&__card:focus {
outline: none;
}

&__card {
display: flex;

align-items: center;
justify-content: center;

padding: 1.5rem 0.3rem;

box-sizing: border-box;

background-color: colors.$secondary100;
box-shadow: 0 0 0.3rem 0.3rem colors.$gray150;
outline: 0.3rem solid transparent;
border-radius: 1.8rem;

transition: 0.3s ease;

cursor: pointer;
scroll-snap-align: center;

&:focus {
outline: 0.3rem solid colors.$primary200;
}

&:hover {
outline: 0.3rem solid colors.$primary200;

.container-size-selector__svg path {
fill: colors.$primary200;
}

.container-size-selector__title {
color: colors.$primary200;
}

.container-size-selector__text {
color: colors.$primary200;
}

.container-size-selector__container-img {
background-color: rgb(209, 230, 255);
}
}

&--active {
transform: scale(1.3);
outline: 0.3rem solid colors.$primary200;

.container-size-selector__svg path {
fill: colors.$primary200;
}

.container-size-selector__title {
color: colors.$primary200;
font-size: fonts.$xs;
}

.container-size-selector__text {
color: colors.$primary200;
font-size: fonts.$xxs;
}

.container-size-selector__container-img {
background-color: rgb(209, 230, 255);
transform: scale(1.1);
}
}
}

&__card-size {
min-width: 9rem;

display: flex;
flex-direction: column;
gap: 1.5rem;

align-items: center;
justify-content: center;
}

&__container-img {
width: 4rem;
height: 4rem;

display: flex;

align-items: center;

justify-content: center;

padding: 0.4rem;

background-color: colors.$gray150;
border-radius: 100%;

transition: 0.3s ease;
}

&__container-text {
display: flex;
flex-direction: column;
gap: 0.3em;

align-items: center;
justify-content: center;

text-align: center;
}

&__svg path {
transition: 0.3s ease;

fill: rgb(128, 139, 154);
}

&__title {
font-family: fonts.$primaryFont;
color: colors.$gray800;
font-size: fonts.$xs;
font-weight: fonts.$semiBold;

transition: 0.3s ease;
}

&__text {
font-family: fonts.$primaryFont;
color: colors.$gray600;
font-size: fonts.$xxs;
font-weight: fonts.$regular;

transition: 0.3s ease;
}
}

@include breakpoints.from667 {
.container-size-selector {
&__size-list {
gap: 4rem;

padding: 5rem 20rem;
}

&__card {
padding: 4rem 2rem;

&--active {
.container-size-selector__title {
font-size: fonts.$sm;
}

.container-size-selector__text {
font-size: fonts.$xs;
}
}
}

&__text {
font-size: fonts.$xs;
}
}
}
18 changes: 18 additions & 0 deletions src/stories/SizeSelector.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import SizeSelector from '../components/SizeSelector';

export default {
title: 'Components/SizeSelector',
render: () => {
const sizeselector = new SizeSelector();

const $container = document.createElement('div');
$container.style.width = '100%';
$container.style.display = 'flex';
$container.style.justifyContent = 'center';
sizeselector.mount($container);

return $container;
},
};

export const Default = {};
1 change: 1 addition & 0 deletions src/styles/fonts.scss
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ $fourthFont: 'Noto Sans', sans-serif;
$fifthFont: 'Poppins', sans-serif;
$sixthFont: 'Catamaran', sans-serif;

$xxs: 1.2rem;
$xs: 1.4rem;
$sm: 1.6rem;
$md: 2rem;
Expand Down

0 comments on commit b6bdf23

Please sign in to comment.