From 7f963b6dcb5e271687d874c03f5418eab5cda20b Mon Sep 17 00:00:00 2001 From: Jonas Chagas Date: Mon, 3 Jun 2024 20:04:19 -0300 Subject: [PATCH] feat: create SizeSelector component feat: add responsive --- src/components/SizeSelector/index.js | 165 +++++++++++++++++++ src/components/SizeSelector/index.scss | 216 +++++++++++++++++++++++++ src/stories/SizeSelector.stories.js | 48 ++++++ 3 files changed, 429 insertions(+) create mode 100644 src/components/SizeSelector/index.js create mode 100644 src/components/SizeSelector/index.scss create mode 100644 src/stories/SizeSelector.stories.js diff --git a/src/components/SizeSelector/index.js b/src/components/SizeSelector/index.js new file mode 100644 index 00000000..1b948152 --- /dev/null +++ b/src/components/SizeSelector/index.js @@ -0,0 +1,165 @@ +import { Component } from 'pet-dex-utilities'; +import './index.scss'; + +const events = ['click', 'keydown']; + +const html = ` +
+ +
+`; + +export default function SizeSelector() { + Component.call(this, { html, events }); + + this.$sizeComponent = this.selected.get('card'); + + this.$cards = Array.from( + this.selected.get('card').querySelectorAll('[role="radio"]'), + ); + + const addEventListeners = (card, index) => { + card.addEventListener('click', () => { + this.selectCard(card, index); + this.getCardActiveEvent('click', card, index); + }); + card.addEventListener('keydown', (event) => { + this.handleKeyDown(event, card); + }); + }; + + this.$cards.forEach((card, index) => addEventListeners(card, index)); +} + +SizeSelector.prototype = Object.assign( + SizeSelector.prototype, + Component.prototype, + { + setText(index, title, subtitle) { + const h3 = this.$cards[index].querySelector('h3'); + const span = this.$cards[index].querySelector('span'); + h3.textContent = title; + span.textContent = subtitle; + }, + + centerCard(index) { + this.$cards.forEach((card, i) => { + let orderCard; + const cardItem = card; + if (index === 2) { + if (i === 0) orderCard = 2; + if (i === 1) orderCard = 0; + if (i === 2) orderCard = 1; + } + + if (index === 0) { + if (i === 0) orderCard = 1; + if (i === 1) orderCard = 2; + if (i === 2) orderCard = 0; + } + + if (index === 1) { + if (i === 0) orderCard = 0; + if (i === 1) orderCard = 1; + if (i === 2) orderCard = 2; + } + + if (orderCard !== undefined) cardItem.style.order = orderCard; + }); + }, + + addActive(element, className) { + this.$cards.forEach((card) => { + card.setAttribute('aria-checked', 'false'); + card.classList.remove(className); + }); + element.classList.add(className); + }, + + selectCard(selected, index) { + this.addActive(selected, 'container-size-selector__card--active'); + this.centerCard(index); + }, + + handleKeyDown(event, card) { + let next; + let prev; + let nextIndex; + let prevIndex; + if (event.key === 'ArrowRight' || event.key === 'ArrowDown') { + next = card.nextElementSibling; + if (!next) [next] = this.$cards; + nextIndex = this.$cards.indexOf(next); + next.focus(); + this.selectCard(next, nextIndex); + this.getCardActiveEvent('keydown', next, nextIndex); + } + if (event.key === 'ArrowLeft' || event.key === 'ArrowUp') { + prev = card.previousElementSibling; + if (!prev) prev = this.$cards[this.$cards.length - 1]; + prevIndex = this.$cards.indexOf(prev); + prev.focus(); + this.selectCard(prev, prevIndex); + this.getCardActiveEvent('keydown', prev, prevIndex); + } + }, + + getCardActiveEvent(eventName, card, index) { + if (eventName === 'click') this.emit('click', card, index); + if (eventName === 'keydown') this.emit('keydown', card, index); + }, + + getCardActive() { + const $activeCard = this.$cards.find((element) => + element.classList.contains('container-size-selector__card--active'), + ); + const indexCard = this.$cards.findIndex((element) => + element.classList.contains('container-size-selector__card--active'), + ); + return { + card: $activeCard, + index: indexCard, + }; + }, + }, +); diff --git a/src/components/SizeSelector/index.scss b/src/components/SizeSelector/index.scss new file mode 100644 index 00000000..0371b0e6 --- /dev/null +++ b/src/components/SizeSelector/index.scss @@ -0,0 +1,216 @@ +@use '~styles/colors.scss' as colors; +@use '~styles/breakpoints.scss' as breakpoints; +@use '~styles/fonts.scss' as fonts; + +.container-size-selector { + min-width: 30rem; + height: 13rem; + + display: flex; + + align-items: center; + justify-content: center; + + &__size-list { + display: flex; + flex-direction: row; + gap: 1rem; + + align-items: center; + justify-content: center; + } + + &__card:focus { + outline: none; + } + + &__card { + width: 8.5rem; + height: 10.5rem; + + display: flex; + + align-items: center; + justify-content: center; + + border: 0.3rem solid transparent; + box-sizing: border-box; + + background-color: colors.$secondary100; + box-shadow: 0 0 0.3rem 0.3rem colors.$gray150; + border-radius: 1.8rem; + + transition: + width 0.3s ease-in-out, + height 0.3s ease-in-out; + + &:hover { + border: 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 { + width: 10.5rem; + height: 13.5rem; + + border: 0.3rem solid colors.$primary200; + + border-radius: 1.8rem; + + transition: + width 0.3s, + height 0.3s; + + .container-size-selector__svg path { + fill: colors.$primary200; + } + + .container-size-selector__title { + color: colors.$primary200; + font-size: fonts.$sm; + } + + .container-size-selector__text { + color: colors.$primary200; + font-size: fonts.$xs; + } + + .container-size-selector__container-img { + width: 4.5rem; + height: 4.5rem; + + background-color: rgb(209, 230, 255); + } + } + } + + &__card-size { + 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; + + background-color: colors.$gray150; + border-radius: 100%; + } + + &__container-text { + display: flex; + flex-direction: column; + gap: 0.3em; + + align-items: center; + + justify-content: center; + } + + &__title { + font-family: fonts.$primaryFont; + color: colors.$gray800; + font-size: fonts.$xs; + font-weight: fonts.$semiBold; + } + + &__text { + font-family: fonts.$primaryFont; + color: colors.$gray600; + font-size: fonts.$xs; + font-weight: fonts.$regular; + } +} + +@include breakpoints.from360() { + .container-size-selector { + &__card { + width: 9rem; + height: 12rem; + + &--active { + width: 11.5rem; + height: 15rem; + + .container-size-selector__container-img { + width: 6rem; + height: 6rem; + } + } + + &__container-img { + width: 4.5rem; + height: 4.5rem; + } + } + } +} + +@include breakpoints.from667() { + .container-size-selector { + height: 26rem; + + align-items: center; + justify-content: center; + + &__size-list { + gap: 5rem; + } + + &__card { + width: 15.6rem; + height: 20rem; + + transition: + width 0.3s, + height 0.3s; + + &--active { + width: 20rem; + height: 26rem; + + .container-size-selector__title { + font-size: fonts.$lg; + } + + .container-size-selector__text { + font-size: fonts.$md; + } + } + } + + &__title { + font-size: fonts.$md; + } + + &__text { + font-size: fonts.$sm; + font-weight: fonts.$regular; + } + } +} diff --git a/src/stories/SizeSelector.stories.js b/src/stories/SizeSelector.stories.js new file mode 100644 index 00000000..f57305de --- /dev/null +++ b/src/stories/SizeSelector.stories.js @@ -0,0 +1,48 @@ +import SizeSelector from '../components/SizeSelector'; + +export default { + title: 'Components/SizeSelector', + render: (args) => { + const sizeselector = new SizeSelector(); + sizeselector.setText(args.card, args.titleCard, args.subtitleCard); + sizeselector.selectCard( + sizeselector.$cards[args.selectCard], + args.selectCard, + ); + const $container = document.createElement('div'); + sizeselector.mount($container); + + return $container; + }, + argTypes: { + card: { control: 'number', default: 1 }, + titleCard: { control: 'text', default: 'Medium' }, + subtitleCard: { control: 'text', default: 'under 14kg' }, + selectCard: { control: 'number', default: 1 }, + }, +}; + +export const Default = { + args: { + card: 1, + titleCard: 'Medium', + subtitleCard: 'under 14kg', + selectCard: 1, + }, +}; + +export const CardText = { + args: { + ...Default.args, + card: 1, + titleCard: 'Medium', + subtitleCard: 'under 14kg', + }, +}; + +export const CardSelect = { + args: { + ...Default.args, + selectCard: 1, + }, +};