Files
f3-simple-blog/public/assets/app.js
2026-03-30 15:05:13 +02:00

171 lines
5.1 KiB
JavaScript

(() => {
const each = (selector, handler) => document.querySelectorAll(selector).forEach(handler);
const siteHeader = document.querySelector('.site-header');
const mobileMenu = document.querySelector('[data-mobile-menu]');
const mobileMenuOpen = document.querySelector('[data-mobile-menu-open]');
if (siteHeader && mobileMenu && mobileMenuOpen) {
const setMobileMenuState = (isOpen) => {
mobileMenu.classList.toggle('is-open', isOpen);
siteHeader.classList.toggle('is-mobile-menu-open', isOpen);
mobileMenuOpen.setAttribute('aria-expanded', isOpen ? 'true' : 'false');
};
mobileMenuOpen.addEventListener('click', () => {
setMobileMenuState(true);
});
each('[data-mobile-menu-close]', (button) => {
button.addEventListener('click', () => {
setMobileMenuState(false);
});
});
document.addEventListener('keydown', (event) => {
if (event.key === 'Escape' && mobileMenu.classList.contains('is-open')) {
setMobileMenuState(false);
mobileMenuOpen.focus();
}
});
const mediaQuery = window.matchMedia('(min-width: 721px)');
const handleViewportChange = (event) => {
if (event.matches) {
setMobileMenuState(false);
}
};
if (typeof mediaQuery.addEventListener === 'function') {
mediaQuery.addEventListener('change', handleViewportChange);
} else if (typeof mediaQuery.addListener === 'function') {
mediaQuery.addListener(handleViewportChange);
}
}
each('[data-copy-text]', (button) => {
button.addEventListener('click', async () => {
const text = button.getAttribute('data-copy-text') || '';
if (!text) {
return;
}
try {
await navigator.clipboard.writeText(text);
const previous = button.textContent;
button.textContent = 'Copié';
window.setTimeout(() => {
button.textContent = previous;
}, 1200);
} catch {
window.prompt('Copie ce texte :', text);
}
});
});
each('[data-confirm]', (form) => {
form.addEventListener('submit', (event) => {
const message = form.getAttribute('data-confirm') || 'Confirmer cette action ?';
if (!window.confirm(message)) {
event.preventDefault();
}
});
});
each('[data-char-count]', (field) => {
const counter = field.parentElement?.querySelector('[data-char-count-value]');
if (!counter) {
return;
}
const update = () => {
counter.textContent = String(field.value.length);
};
field.addEventListener('input', update);
update();
});
const editor = document.querySelector('[data-markdown-editor]');
if (editor) {
const focusEditor = () => editor.focus();
const replaceSelection = (before, after = '', placeholder = '') => {
const start = editor.selectionStart;
const end = editor.selectionEnd;
const selection = editor.value.slice(start, end);
const content = selection || placeholder;
const insertion = before + content + after;
editor.setRangeText(insertion, start, end, 'end');
if (!selection && placeholder) {
const cursorStart = start + before.length;
editor.setSelectionRange(cursorStart, cursorStart + placeholder.length);
}
focusEditor();
};
const prefixLines = (prefix, placeholder) => {
const start = editor.selectionStart;
const end = editor.selectionEnd;
const selection = editor.value.slice(start, end) || placeholder;
const value = selection
.split('\n')
.map((line) => (line ? prefix + line : prefix.trimEnd()))
.join('\n');
editor.setRangeText(value, start, end, 'end');
focusEditor();
};
each('[data-md-action]', (button) => {
button.addEventListener('click', () => {
switch (button.getAttribute('data-md-action')) {
case 'bold':
replaceSelection('**', '**', 'texte');
break;
case 'italic':
replaceSelection('*', '*', 'texte');
break;
case 'heading':
prefixLines('## ', 'Titre');
break;
case 'list':
prefixLines('- ', 'Élément');
break;
case 'quote':
prefixLines('> ', 'Citation');
break;
case 'link':
replaceSelection('[', '](https://)', 'texte');
break;
case 'code': {
const selection = editor.value.slice(editor.selectionStart, editor.selectionEnd);
replaceSelection(selection.includes('\n') ? '```\n' : '`', selection.includes('\n') ? '\n```' : '`', 'code');
break;
}
}
});
});
}
each('[data-alt-input]', (input) => {
const card = input.closest('.card');
const button = card?.querySelector('[data-markdown-template]');
if (!button) {
return;
}
const update = () => {
const template = button.getAttribute('data-markdown-template') || '';
button.setAttribute('data-copy-text', template.replace('![](', '![' + input.value + ']('));
};
input.addEventListener('input', update);
update();
});
})();