8ebc1da9e4
- Astro statique, EN par defaut + /fr/ + /nl/, detection langue navigateur - i18n par fichiers JSON, ajouter une langue = ajouter des traductions - Contenu original porte a l'identique (diff d'inventaire par langue) - Chronologie legislative corrigee sur sources primaires (PE, Conseil, votes) - Timeline 23 precedents + observatoire 35 items + annuaire 66 outils FOSS, chaque affirmation verifiee et sourcee - Zero requete tierce (teste), lisible JS coupe, axe WCAG AA x2 themes - Version offline monofichier par langue a chaque build - Docker multi-stage vers nginx + CSP stricte auto-generee - CI GitHub Actions (SHA-pinnees) + verification hebdo des liens - CONTRIBUTING : divulgation d'affiliation obligatoire, allowlist de domaines testee en CI
85 lines
2.6 KiB
Plaintext
85 lines
2.6 KiB
Plaintext
---
|
|
// Share / copy-link / print row at the end of the guide. Pure enhancement:
|
|
// the whole row is server-rendered hidden and revealed by script (none of
|
|
// the three actions can work without JS), and print.css hides .share-row on
|
|
// paper. The share button additionally stays hidden when the Web Share API
|
|
// is unsupported.
|
|
import { useT, type Locale } from '../i18n/config'
|
|
|
|
interface Props {
|
|
locale: Locale
|
|
}
|
|
|
|
const { locale } = Astro.props
|
|
const t = useT(locale)
|
|
|
|
const btn =
|
|
'cursor-pointer rounded-dossier border border-rule bg-surface-2 px-3 py-1.5 font-mono text-xs text-ink hover:border-accent'
|
|
---
|
|
|
|
<div
|
|
class="share-row mt-12 border-t border-rule pt-5"
|
|
data-share-row
|
|
aria-label={t('share.label')}
|
|
hidden
|
|
>
|
|
<p class="eyebrow mt-0 mb-3">{t('share.label')}</p>
|
|
<div class="flex flex-wrap gap-2">
|
|
<button type="button" data-share-action="share" class={btn} hidden>
|
|
{t('share.share')}
|
|
</button>
|
|
<button type="button" data-share-action="copy" data-copied={t('share.copied')} class={btn}>
|
|
{t('share.copy')}
|
|
</button>
|
|
<button type="button" data-share-action="print" class={btn}>
|
|
{t('share.print')}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
// Reveal on load (and after every ClientRouter swap); actions delegated
|
|
// once on document.
|
|
const reveal = () => {
|
|
for (const row of document.querySelectorAll<HTMLElement>('[data-share-row]')) {
|
|
row.hidden = false
|
|
const share = row.querySelector<HTMLElement>('[data-share-action="share"]')
|
|
if (share) share.hidden = typeof navigator.share !== 'function'
|
|
}
|
|
}
|
|
|
|
document.addEventListener('click', async (e) => {
|
|
const btn = (e.target as HTMLElement).closest<HTMLButtonElement>('[data-share-action]')
|
|
if (!btn) return
|
|
const url = location.href.replace(/#.*$/, '')
|
|
const action = btn.dataset.shareAction
|
|
if (action === 'print') {
|
|
window.print()
|
|
return
|
|
}
|
|
if (action === 'share') {
|
|
try {
|
|
await navigator.share({ title: document.title, url })
|
|
} catch {
|
|
/* dismissed or unsupported: nothing to clean up */
|
|
}
|
|
return
|
|
}
|
|
if (action === 'copy') {
|
|
try {
|
|
await navigator.clipboard.writeText(url)
|
|
if (!btn.dataset.label) btn.dataset.label = btn.textContent ?? ''
|
|
btn.textContent = btn.dataset.copied ?? btn.dataset.label
|
|
window.setTimeout(() => {
|
|
btn.textContent = btn.dataset.label ?? ''
|
|
}, 2000)
|
|
} catch {
|
|
/* clipboard unavailable (permissions, http): leave the label as-is */
|
|
}
|
|
}
|
|
})
|
|
|
|
reveal()
|
|
document.addEventListener('astro:page-load', reveal)
|
|
</script>
|