Skip to content

Commit f88259d

Browse files
authored
Create Image Switcher block (#158)
1 parent 5e598e9 commit f88259d

File tree

2 files changed

+311
-0
lines changed

2 files changed

+311
-0
lines changed
+224
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
.image-switcher .nav {
2+
display: flex;
3+
justify-content: center;
4+
align-items: center;
5+
margin: auto;
6+
7+
.nav-list {
8+
text-decoration: none;
9+
list-style-type: none;
10+
display: flex;
11+
flex-wrap: wrap;
12+
justify-content: center;
13+
padding: 0;
14+
15+
.nav-item {
16+
block-size: 64px;
17+
border: 1px solid var(--calcite-ui-border-1);
18+
display: flex;
19+
justify-content: center;
20+
align-items: center;
21+
transition: background-color 180ms, box-shadow 180ms;
22+
}
23+
24+
.nav-item.active, .nav-item:hover {
25+
box-shadow: 0 0 0 .5px var(--calcite-ui-brand), inset 0 0 0 .5px var(--calcite-ui-brand), inset 0 3px 0 0 var(--calcite-ui-brand);
26+
}
27+
28+
p {
29+
display: none;
30+
}
31+
32+
picture {
33+
inline-size: 48px;
34+
block-size: 48px;
35+
36+
img {
37+
inline-size: 48px;
38+
block-size: 48px;
39+
object-fit: cover;
40+
object-position: center;
41+
border-radius: 50%;
42+
}
43+
}
44+
}
45+
}
46+
47+
.image-switcher .content {
48+
padding: var(--space-5);
49+
50+
div {
51+
max-inline-size: 600px;
52+
}
53+
54+
h2 {
55+
font-size: var(--font-4);
56+
line-height: 1.375;
57+
font-weight: 400;
58+
margin-block: 0 var(--space-2);
59+
}
60+
61+
p {
62+
margin-block: 0 var(--space-6);
63+
}
64+
}
65+
66+
.image-switcher .images {
67+
block-size: 75vh;
68+
69+
h3 {
70+
font-size: 16px;
71+
line-height: 20px;
72+
font-weight: 300;
73+
margin-block-start: 0;
74+
}
75+
76+
p {
77+
font-size: 15px;
78+
line-height: 20px;
79+
color: var(--calcite-ui-text-2)
80+
}
81+
82+
calcite-link {
83+
font-size: 15px;
84+
line-height: 20px;
85+
}
86+
87+
& > div {
88+
position: relative;
89+
block-size: 100%;
90+
}
91+
92+
& > div[aria-hidden="true"] {
93+
display: none;
94+
}
95+
96+
.image-div {
97+
position: absolute;
98+
inset-block-start: 0;
99+
inset-inline-start: 0;
100+
block-size: 100%;
101+
inline-size: 100%;
102+
}
103+
104+
.image-content {
105+
position: absolute;
106+
inset-block-start: 0;
107+
inset-inline-start: 0;
108+
z-index: 3;
109+
background-color: var(--esri-ui-opacity97-inverse);
110+
margin: var(--space-10);
111+
padding: var(--space-8);
112+
}
113+
114+
img {
115+
object-fit: cover;
116+
block-size: 100%;
117+
inline-size: 100%;
118+
}
119+
}
120+
121+
@media (width >= 736px) {
122+
.image-switcher .images {
123+
h3 {
124+
font-size: 24px;
125+
line-height: 30px;
126+
font-weight: 300;
127+
}
128+
129+
p {
130+
font-size: 16px;
131+
line-height: 25px;
132+
}
133+
134+
calcite-link {
135+
font-size: 16px;
136+
line-height: 25px;
137+
}
138+
}
139+
}
140+
141+
@media (width >= 1096px) {
142+
.image-switcher {
143+
display: flex;
144+
flex-direction: row-reverse;
145+
block-size: 950px;
146+
}
147+
148+
.image-switcher.flip {
149+
flex-direction: row;
150+
}
151+
152+
.image-switcher .images, .image-switcher .content {
153+
block-size: 100%;
154+
inline-size: 50%;
155+
padding: 0;
156+
}
157+
158+
.image-switcher .images {
159+
.image-content {
160+
margin: auto auto var(--space-24);
161+
max-inline-size: 82%;
162+
padding: var(--space-8);
163+
inset-inline-end: 0;
164+
inset-block: auto 0;
165+
}
166+
}
167+
168+
.image-switcher .content {
169+
padding: var(--space-20);
170+
display: flex;
171+
flex-direction: column;
172+
justify-content: center;
173+
align-items: center;
174+
}
175+
176+
.image-switcher .nav {
177+
block-size: auto;
178+
inline-size: 100%;
179+
180+
.nav-list {
181+
flex-direction: column;
182+
inline-size: 100%;
183+
184+
.nav-item {
185+
block-size: auto;
186+
padding: var(--space-3) var(--space-3) var(--space-3) var(--space-6);
187+
justify-content: flex-start;
188+
}
189+
190+
.nav-item.active, .nav-item:hover {
191+
box-shadow: inset 3px 0 var(--calcite-ui-brand), inset -1px 0 var(--calcite-ui-brand), inset 0 1px var(--calcite-ui-brand), inset 0 -1px var(--calcite-ui-brand);
192+
}
193+
194+
picture {
195+
inline-size: 60px;
196+
block-size: 60px;
197+
margin-inline-end: var(--space-5);
198+
199+
img {
200+
inline-size: 60px;
201+
block-size: 60px;
202+
}
203+
}
204+
205+
p {
206+
display: block;
207+
color: var(--calcite-ui-text-2);
208+
text-align: start;
209+
font-size: var(--font-0);
210+
cursor: pointer;
211+
margin-block: auto;
212+
padding-inline: 0;
213+
}
214+
}
215+
}
216+
}
217+
218+
@media (width >= 1455px) {
219+
.image-switcher .nav {
220+
.nav-list {
221+
max-block-size: calc(86px * 4);
222+
}
223+
}
224+
}
+87
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import { createOptimizedPicture } from '../../scripts/aem.js';
2+
import {
3+
ul,
4+
li,
5+
div,
6+
p,
7+
domEl,
8+
} from '../../scripts/dom-helpers.js';
9+
10+
export default function decorate(block) {
11+
block.querySelectorAll('img').forEach((image) => image
12+
.closest('picture')
13+
.replaceWith(
14+
createOptimizedPicture(image.src, image.alt, false, [{ width: '750' }]),
15+
));
16+
17+
const tabs = [...block.children].slice(1);
18+
const nav = div({ class: 'nav' }, ul({ class: 'nav-list' }));
19+
const navList = nav.children[0];
20+
21+
let selectedIndex = 0;
22+
const changeSelectedTab = (index) => {
23+
tabs[selectedIndex].setAttribute('aria-hidden', 'true');
24+
navList.children[selectedIndex].classList.remove('active');
25+
navList.children[index].classList.add('active');
26+
tabs[index].setAttribute('aria-hidden', 'false');
27+
selectedIndex = index;
28+
};
29+
30+
tabs.forEach((tab, index) => {
31+
const tabTitle = tab.children[0].children[0].textContent;
32+
const thumbnail = tab.querySelector('picture');
33+
thumbnail.parentElement.parentElement.removeChild(thumbnail.parentElement);
34+
35+
const listItem = li({ class: 'nav-item' }, thumbnail, p(tabTitle));
36+
listItem.addEventListener('click', () => changeSelectedTab(index));
37+
38+
navList.appendChild(listItem);
39+
});
40+
41+
const navItems = nav.querySelectorAll('.nav-item');
42+
const mediaQuery1 = window.matchMedia('(width >= 1096px)');
43+
const mediaQuery2 = window.matchMedia('(width >= 1455px)');
44+
45+
const setNavItemsSize = () => {
46+
if (navItems.length > 4 && mediaQuery2.matches) {
47+
navItems.forEach((item) => {
48+
item.style.inlineSize = '50%';
49+
});
50+
} else if (mediaQuery1.matches) {
51+
navItems.forEach((item) => {
52+
item.style.inlineSize = '100%';
53+
});
54+
} else {
55+
navItems.forEach((item) => {
56+
item.style.inlineSize = '64px';
57+
});
58+
}
59+
};
60+
mediaQuery2.onchange = () => setNavItemsSize();
61+
mediaQuery1.onchange = () => setNavItemsSize();
62+
63+
setNavItemsSize();
64+
65+
const imageSwitcherContent = block.children[0];
66+
imageSwitcherContent.classList.add('content');
67+
imageSwitcherContent.appendChild(nav);
68+
69+
const imageSwitcherImages = div({ class: 'images' }, ...tabs);
70+
71+
tabs.forEach((tab) => {
72+
const buttonContainer = tab.querySelector('.button-container');
73+
const url = buttonContainer?.querySelector('a').href;
74+
const title = buttonContainer?.querySelector('a').textContent;
75+
tab.children[0].classList.add('image-content');
76+
tab.children[1].classList.add('image-div');
77+
78+
const link = domEl('calcite-link', { href: url, 'icon-end': 'arrow-right' }, title);
79+
buttonContainer?.parentElement.replaceChild(link, buttonContainer);
80+
81+
tab.setAttribute('aria-hidden', 'true');
82+
tab.setAttribute('role', 'tabpanel');
83+
});
84+
85+
block.appendChild(imageSwitcherImages);
86+
changeSelectedTab(0);
87+
}

0 commit comments

Comments
 (0)