feat: add navigation to gallery / dialog
This commit is contained in:
parent
51dbd48e67
commit
8b542139b5
6 changed files with 90 additions and 14 deletions
|
|
@ -61,7 +61,9 @@ export const details = {
|
||||||
collapse: 'collapse all'
|
collapse: 'collapse all'
|
||||||
};
|
};
|
||||||
export const dialog = {
|
export const dialog = {
|
||||||
close: 'Close'
|
close: 'Close',
|
||||||
|
next: 'Next',
|
||||||
|
previous: 'Previous'
|
||||||
};
|
};
|
||||||
export const navigation = {
|
export const navigation = {
|
||||||
navLabel: 'Menu',
|
navLabel: 'Menu',
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,18 @@
|
||||||
<is-land on:idle>
|
<is-land on:idle>
|
||||||
<div class="gallery | grid mt-l-xl" role="list">
|
<div class="gallery | grid mt-l-xl gutter-xs" role="list">
|
||||||
{%- for item in gallery -%}
|
{%- for item in gallery -%}
|
||||||
<dialog class="flow modal{{ loop.index }}">
|
<dialog class="flow modal{{ loop.index }}">
|
||||||
<button class="button" autofocus>{{ meta.dialog.close }}</button>
|
<div class="cluster justify-center flow-space-m gutter-s">
|
||||||
|
<button data-nav="prev" class="button text-step-1" data-button-variant="primary">
|
||||||
|
<span class="visually-hidden">{{ meta.dialog.previous }}</span>{% svg "misc/arrow-left" %}
|
||||||
|
</button>
|
||||||
|
<button data-close class="button" autofocus data-button-variant="primary">
|
||||||
|
{{ meta.dialog.close }}
|
||||||
|
</button>
|
||||||
|
<button data-nav="next" class="button text-step-1" data-button-variant="primary">
|
||||||
|
<span class="visually-hidden">{{ meta.dialog.next }}</span>{% svg "misc/arrow-right" %}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
{%- image item.image, item.alt, item.caption -%}
|
{%- image item.image, item.alt, item.caption -%}
|
||||||
</dialog>
|
</dialog>
|
||||||
<button data-index="{{ loop.index }}">{%- image item.image, item.alt -%}</button>
|
<button data-index="{{ loop.index }}">{%- image item.image, item.alt -%}</button>
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,11 @@
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.gallery dialog + button:focus-visible {
|
||||||
|
outline: 3px solid var(--focus-color, currentColor);
|
||||||
|
outline-offset: var(--focus-offset, 0.3ch);
|
||||||
|
}
|
||||||
|
|
||||||
/* the close button */
|
/* the close button */
|
||||||
.gallery dialog > button {
|
.gallery dialog > button {
|
||||||
margin-inline: auto;
|
margin-inline: auto;
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,79 @@
|
||||||
// manages the behavior of modal several dialogs on a page: open / close buttons and light dismiss.
|
// manages the behavior of modal several dialogs on a page: open / close buttons and light dismiss.
|
||||||
|
|
||||||
const buttons = document.querySelectorAll('button[data-index]');
|
const modals = Array.from(document.querySelectorAll('dialog'));
|
||||||
const modals = document.querySelectorAll('dialog');
|
const openButtons = document.querySelectorAll('button[data-index]');
|
||||||
const closeButtons = document.querySelectorAll('dialog button');
|
|
||||||
buttons.forEach((button, index) => {
|
openButtons.forEach((button, index) => {
|
||||||
button.addEventListener('click', () => {
|
button.addEventListener('click', () => {
|
||||||
modals[index].showModal();
|
modals[index].showModal();
|
||||||
|
modals[index].focus();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
closeButtons.forEach((button, index) => {
|
|
||||||
button.addEventListener('click', () => {
|
modals.forEach((modal, index) => {
|
||||||
modals[index].close();
|
const closeBtn = modal.querySelector('button[data-close]');
|
||||||
|
const prevBtn = modal.querySelector('button[data-nav="prev"]');
|
||||||
|
const nextBtn = modal.querySelector('button[data-nav="next"]');
|
||||||
|
|
||||||
|
closeBtn?.addEventListener('click', () => modal.close());
|
||||||
|
|
||||||
|
if (!prevBtn && !nextBtn) return;
|
||||||
|
|
||||||
|
prevBtn?.addEventListener('click', () => {
|
||||||
|
modal.close();
|
||||||
|
const prev = modals[(index - 1 + modals.length) % modals.length];
|
||||||
|
prev.showModal();
|
||||||
});
|
});
|
||||||
});
|
|
||||||
window.addEventListener('click', event => {
|
nextBtn?.addEventListener('click', () => {
|
||||||
modals.forEach(modal => {
|
modal.close();
|
||||||
if (event.target === modal) {
|
const next = modals[(index + 1) % modals.length];
|
||||||
|
next.showModal();
|
||||||
|
});
|
||||||
|
|
||||||
|
// arrow navigation
|
||||||
|
modal.addEventListener('keydown', event => {
|
||||||
|
if (event.key === 'ArrowRight') {
|
||||||
|
event.preventDefault();
|
||||||
|
const next = modals[(index + 1) % modals.length];
|
||||||
modal.close();
|
modal.close();
|
||||||
|
next.showModal();
|
||||||
|
} else if (event.key === 'ArrowLeft') {
|
||||||
|
event.preventDefault();
|
||||||
|
const prev = modals[(index - 1 + modals.length) % modals.length];
|
||||||
|
modal.close();
|
||||||
|
prev.showModal();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// swipe navigation
|
||||||
|
let startX = 0;
|
||||||
|
let endX = 0;
|
||||||
|
modal.addEventListener('touchstart', event => {
|
||||||
|
if (event.touches.length > 1) return;
|
||||||
|
startX = event.touches[0].screenX;
|
||||||
|
});
|
||||||
|
modal.addEventListener('touchend', event => {
|
||||||
|
if (event.changedTouches.length > 1) return;
|
||||||
|
endX = event.changedTouches[0].screenX;
|
||||||
|
handleSwipe();
|
||||||
|
});
|
||||||
|
|
||||||
|
function handleSwipe() {
|
||||||
|
const diff = endX - startX;
|
||||||
|
if (Math.abs(diff) < 50) return;
|
||||||
|
modal.close();
|
||||||
|
const target =
|
||||||
|
diff > 0
|
||||||
|
? modals[(index - 1 + modals.length) % modals.length] // swipe right → prev
|
||||||
|
: modals[(index + 1) % modals.length]; // swipe left → next
|
||||||
|
target.showModal();
|
||||||
|
target.focus();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
window.addEventListener('click', event => {
|
||||||
|
modals.forEach(modal => {
|
||||||
|
if (event.target === modal) modal.close();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
1
src/assets/svg/misc/arrow-left.svg
Normal file
1
src/assets/svg/misc/arrow-left.svg
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m12 19-7-7 7-7"/><path d="M19 12H5"/></svg>
|
||||||
|
After Width: | Height: | Size: 235 B |
1
src/assets/svg/misc/arrow-right.svg
Normal file
1
src/assets/svg/misc/arrow-right.svg
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg>
|
||||||
|
After Width: | Height: | Size: 234 B |
Loading…
Reference in a new issue