Skip to content

Commit 304c1d4

Browse files
committed
chore: implements scroll selectors
1 parent 3486e62 commit 304c1d4

File tree

13 files changed

+332
-182
lines changed

13 files changed

+332
-182
lines changed
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { Component } from 'pet-dex-utilities';
2+
import './index.scss';
3+
4+
const events = ['item:click'];
5+
6+
const html = `
7+
<span class="date-selected" data-select="date-selected"></span>
8+
`;
9+
10+
export default function DateSelected(date) {
11+
Component.call(this, { html, events });
12+
13+
this.$dateSelected = this.selected.get('date-selected');
14+
this.$dateSelected.innerText = date;
15+
16+
this.emitClickevent = () => {
17+
this.emit('item:click');
18+
};
19+
20+
this.$dateSelected.addEventListener('click', this.emitClickevent);
21+
}
22+
23+
DateSelected.prototype = Object.assign(
24+
DateSelected.prototype,
25+
Component.prototype,
26+
{},
27+
);
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
@use '~styles/base.scss';
2+
@use '~styles/colors.scss' as colors;
3+
@use '~styles/fonts.scss' as fonts;
4+
@use '~styles/breakpoints.scss' as breakpoints;
5+
6+
.date-selected {
7+
font-family: fonts.$primaryFont;
8+
color: colors.$gray800;
9+
font-size: fonts.$lg;
10+
font-weight: fonts.$regular;
11+
12+
&:hover {
13+
color: colors.$primary200;
14+
font-weight: fonts.$semiBold;
15+
}
16+
}

src/components/Calendar/components/DateSelectorComposer/components/MonthSelector/index.js

Lines changed: 128 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,142 @@
11
import { Component } from 'pet-dex-utilities';
2-
import './index.scss';
2+
import { makeSwipable } from '../../../../../../utils/swiper';
33
import SelectorItem from '../SelectorItem';
4+
import { listenBreakpoint } from '../../../../../../utils/breakpoints/breakpoints';
5+
import { MONTHS } from '../../../../utils/months';
6+
import './index.scss';
47

5-
const events = ['selector:click'];
8+
const events = ['month:change', 'selector:click'];
69

710
const html = `
8-
<div class="month-selector" data-select="month-selector">
9-
<ul class="month-selector__previous-months" data-select="previous-months"></ul>
10-
<span class="month-selector__current-month" data-select="current-month"></span>
11-
<ul class="month-selector__next-months" data-select="next-months"></ul>
11+
<div class="month-selector" data-select="month-selector">
12+
<div>
13+
<div data-select="month-list">
14+
<ul data-select="list-content">
15+
</ul>
16+
</div>
1217
</div>
18+
</div>
1319
`;
1420

15-
export default function MonthSelector(monthArray) {
21+
export default function MonthSelector(dateArray) {
1622
Component.call(this, { html, events });
1723

18-
this.monthArray = monthArray;
24+
this.dateArray = dateArray;
1925
this.$monthSelector = this.selected.get('month-selector');
20-
this.$previousMonths = this.selected.get('previous-months');
21-
this.$currentMonth = this.selected.get('current-month');
22-
this.$nextMonths = this.selected.get('next-months');
23-
24-
for (let i = 0; i < this.monthArray.length; i += 1) {
25-
if (i < 3) {
26-
const selectorItem = new SelectorItem(this.monthArray[i]);
27-
selectorItem.mount(this.$previousMonths);
28-
}
29-
if (i === 3) {
30-
this.$currentMonth.innerText = this.monthArray[i];
31-
}
32-
if (i > 3) {
33-
const selectorItem = new SelectorItem(this.monthArray[i]);
34-
selectorItem.mount(this.$nextMonths);
35-
}
36-
}
37-
38-
this.$currentMonth.addEventListener('click', () =>
39-
this.emit('selector:click'),
40-
);
26+
this.$dateList = this.selected.get('month-list');
27+
this.$listContent = this.selected.get('list-content');
28+
29+
this.itemCount = this.dateArray.length;
30+
this.columnWidth = 150;
31+
this.nodePadding = 5;
32+
this.scrollLeft = this.$monthSelector.scrollLeft;
33+
34+
const swiper = makeSwipable(this.$monthSelector);
35+
36+
const handleItemClick = (index) => {
37+
const itemScroll =
38+
index * this.columnWidth -
39+
(this.viewportWidth / 2 - this.columnWidth / 2);
40+
this.$monthSelector.scrollLeft = itemScroll;
41+
};
42+
43+
const emitMonthChangeEvent = (month) => {
44+
const newMonth = MONTHS.indexOf(month);
45+
this.emit('month:change', newMonth);
46+
};
47+
48+
setTimeout(() => {
49+
this.viewportWidth = this.$monthSelector.offsetWidth;
50+
51+
const renderWindow = () => {
52+
this.totalContentWidth = this.itemCount * this.columnWidth;
53+
54+
this.startNode =
55+
Math.floor(this.scrollLeft / this.columnWidth) - this.nodePadding;
56+
this.startNode = Math.max(0, this.startNode);
57+
58+
this.visibleNodesCount =
59+
Math.ceil(this.viewportWidth / this.columnWidth) + 2 * this.nodePadding;
60+
this.visibleNodesCount = Math.min(
61+
this.itemCount - this.startNode,
62+
this.visibleNodesCount,
63+
);
64+
65+
this.offsetX = this.startNode * this.columnWidth;
66+
67+
this.$dateList.style.width = `${this.totalContentWidth}px`;
68+
this.$listContent.style.transform = `translateX(${this.offsetX}px)`;
69+
70+
this.$listContent.innerHTML = '';
71+
this.visibleChildren = new Array(this.visibleNodesCount)
72+
.fill(null)
73+
.map(
74+
(_, index) =>
75+
new SelectorItem(this.dateArray[index + this.startNode]),
76+
);
77+
78+
this.visibleChildren.forEach((selectorItem, index) => {
79+
selectorItem.mount(this.$listContent);
80+
selectorItem.listen('item:click', () =>
81+
handleItemClick(index + this.startNode),
82+
);
83+
selectorItem.listen('item:change', (item) =>
84+
emitMonthChangeEvent(item),
85+
);
86+
87+
if (index === 6) {
88+
selectorItem.active();
89+
}
90+
});
91+
};
92+
93+
this.$monthSelector.addEventListener('scroll', (e) => {
94+
if (this.animationFrame) {
95+
cancelAnimationFrame(this.animationFrame);
96+
}
97+
this.animationFrame = requestAnimationFrame(() => {
98+
this.scrollLeft = e.target.scrollLeft;
99+
renderWindow();
100+
101+
const activeItem = this.$dateList.querySelector(
102+
'selector-item--active',
103+
);
104+
if (activeItem) {
105+
activeItem.scrollIntoView({
106+
inline: 'center',
107+
behavior: 'smooth',
108+
});
109+
}
110+
});
111+
});
112+
113+
swiper.left(() => {
114+
this.scrollLeft = Math.max(this.scrollLeft - this.columnWidth, 0);
115+
this.$monthSelector.scrollLeft = this.scrollLeft;
116+
renderWindow();
117+
});
118+
119+
swiper.right(() => {
120+
this.scrollLeft = Math.min(
121+
this.scrollLeft + this.columnWidth,
122+
this.totalContentWidth - this.viewportWidth,
123+
);
124+
this.$monthSelector.scrollLeft = this.scrollLeft;
125+
renderWindow();
126+
});
127+
128+
const scrollToMiddle = () => {
129+
this.scrollLeft = this.totalContentWidth / 2 - this.viewportWidth / 2;
130+
this.$monthSelector.scrollLeft = this.scrollLeft;
131+
};
132+
133+
renderWindow();
134+
scrollToMiddle();
135+
136+
listenBreakpoint('from667', (matches) => {
137+
if (matches) scrollToMiddle();
138+
});
139+
}, 0);
41140
}
42141

43142
MonthSelector.prototype = Object.assign(

src/components/Calendar/components/DateSelectorComposer/components/MonthSelector/index.scss

Lines changed: 9 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -4,71 +4,27 @@
44
@use '~styles/breakpoints.scss' as breakpoints;
55

66
.month-selector {
7-
width: 100%;
7+
overflow: auto;
88

99
display: flex;
10-
gap: 2.4rem;
11-
12-
align-items: center;
13-
justify-content: center;
10+
flex-direction: row;
1411

1512
margin-bottom: 2.4rem;
1613

1714
padding-bottom: 2.4rem;
1815

1916
border-bottom: 0.1rem solid colors.$gray150;
2017

21-
&__previous-months,
22-
&__next-months {
23-
display: flex;
24-
gap: 1.6rem;
25-
26-
align-items: center;
27-
justify-content: center;
28-
}
29-
30-
&__current-month {
31-
align-self: center;
32-
33-
font-family: fonts.$primaryFont;
34-
color: colors.$primary200;
35-
font-size: fonts.$lg;
36-
font-weight: fonts.$semiBold;
37-
38-
padding: 0.6rem 1.2rem;
39-
border: 0.1rem solid colors.$blue100;
18+
box-sizing: border-box;
4019

41-
background-color: colors.$blue150;
42-
border-radius: 1.4rem;
43-
}
44-
}
45-
46-
@include breakpoints.from667 {
47-
.month-selector {
48-
width: max-content;
49-
50-
margin-bottom: 0;
51-
padding-bottom: 0;
52-
border-bottom: 0;
20+
box-sizing: border-box;
21+
scroll-snap-type: x proximity;
5322

54-
&__previous-months,
55-
&__next-months {
56-
display: none;
57-
}
23+
[data-select='list-content'] {
24+
width: fit-content;
5825

59-
&__current-month {
60-
color: colors.$gray800;
61-
font-weight: fonts.$regular;
62-
63-
padding: 0;
64-
border: 0;
65-
66-
background-color: colors.$secondary100;
26+
display: flex;
6727

68-
&:hover {
69-
color: colors.$primary200;
70-
font-weight: fonts.$semiBold;
71-
}
72-
}
28+
align-items: center;
7329
}
7430
}
Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,27 @@
11
import { Component } from 'pet-dex-utilities';
22
import './index.scss';
33

4-
const events = [];
4+
const events = ['item:change', 'item:click'];
55

66
const html = `
7-
<li class="selector-item" data-select="selector-item" tabindex=""></li>
7+
<li class="selector-item" data-select="selector-item"></li>
88
`;
99

1010
export default function SelectorItem(item) {
1111
Component.call(this, { html, events });
1212
this.item = item;
1313
this.$selectorItem = this.selected.get('selector-item');
1414
this.$selectorItem.innerText = this.item;
15+
16+
if (typeof this.item === 'string') this.$selectorItem.style.width = '150px';
17+
18+
if (typeof this.item === 'number') this.$selectorItem.style.width = '70px';
19+
20+
this.emitClickEvent = () => {
21+
this.emit('item:click');
22+
};
23+
24+
this.$selectorItem.addEventListener('click', this.emitClickEvent);
1525
}
1626

1727
SelectorItem.prototype = Object.assign(
@@ -20,6 +30,7 @@ SelectorItem.prototype = Object.assign(
2030
{
2131
active() {
2232
this.$selectorItem.classList.add('selector-item--active');
33+
this.emit('item:change', this.item);
2334
},
2435
},
2536
);

src/components/Calendar/components/DateSelectorComposer/components/SelectorItem/index.scss

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44
@use '~styles/breakpoints.scss' as breakpoints;
55

66
.selector-item {
7-
width: 75px;
8-
97
font-family: fonts.$primaryFont;
108
color: colors.$gray700;
119

src/components/Calendar/components/DateSelectorComposer/components/SelectorModal/components/ModalItem/index.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Component } from 'pet-dex-utilities';
22
import './index.scss';
33

4-
const events = ['item:change'];
4+
const events = ['item:change', 'item:click'];
55

66
const html = `
77
<li class="selector-modal__item" data-select="modal-item"></li>
@@ -13,6 +13,12 @@ export default function ModalItem(item) {
1313
this.item = item;
1414
this.$modalItem = this.selected.get('modal-item');
1515
this.$modalItem.innerText = this.item;
16+
17+
const emitEventClick = () => {
18+
this.emit('item:click', this.item);
19+
};
20+
21+
this.$modalItem.addEventListener('click', () => emitEventClick());
1622
}
1723

1824
ModalItem.prototype = Object.assign(ModalItem.prototype, Component.prototype, {

src/components/Calendar/components/DateSelectorComposer/components/SelectorModal/utils/scrollModal.js

Lines changed: 0 additions & 24 deletions
This file was deleted.

0 commit comments

Comments
 (0)