Added legal notice modal

This commit is contained in:
Alexis
2025-06-14 18:57:13 +02:00
parent a2253a165f
commit dd8a506ef3
8 changed files with 152 additions and 16 deletions

View File

@@ -76,7 +76,7 @@
}
&.btn-shadow {
box-shadow: rgba(0, 0, 0, 0.15) 0 .2rem .3rem;
box-shadow: color-mix(in srgb, var(--black) 10%, transparent) 0 .2rem .3rem;
}
}
@@ -87,7 +87,7 @@
aspect-ratio: 1;
background-color: var(--white);
border: 1px solid var(--slate-400);
box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;
box-shadow: color-mix(in srgb, var(--black) 10%, transparent) 0px 4px 12px;
border-radius: 100vmax;
pointer-events: all;
cursor: pointer;

View File

@@ -4,7 +4,7 @@
border-radius: 8px;
background-color: var(--white);
border: 1px solid var(--slate-100);
box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;
box-shadow: color-mix(in srgb, var(--black) 10%, transparent) 0px 4px 12px;
min-width: 15rem;
.card-arrow {

View File

@@ -0,0 +1,80 @@
.dialog-overlay {
background-color: color-mix(in srgb, var(--black) 40%, transparent);
position: fixed;
inset: 0;
animation: show-overlay 150ms cubic-bezier(0.16, 1, 0.3, 1);
z-index: 100;
}
.dialog-content {
position: fixed;
top: 50%;
left: 50%;
z-index: 200;
transform: translate(-50%, -50%);
width: 50rem;
max-width: calc(100% - 4rem);
max-height: 85vh;
padding: 25px;
border-radius: 8px;
background-color: var(--white);
border: 1px solid var(--slate-100);
box-shadow: color-mix(in srgb, var(--black) 10%, transparent) 0px 4px 12px;
animation: show-content 150ms cubic-bezier(0.16, 1, 0.3, 1);
&:focus {
outline: none;
}
}
.dialog-title {
margin-bottom: .5em;
font-size: 1.5em;
font-weight: 700;
}
.dialog-description {
h3 {
font-size: 1.25em;
font-weight: 700;
}
font-size: 1em;
color: var(--slate-800);
line-height: 1.5;
> * + * {
margin-top: 1em;
}
a {
text-underline-offset: 2px;
text-decoration: underline;
&:hover,
&:focus-visible {
text-decoration: none;
color: var(--green-500);
}
}
}
@keyframes show-overlay {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@keyframes show-content {
from {
opacity: 0;
transform: translate(-50%, -48%) scale(0.96);
}
to {
opacity: 1;
transform: translate(-50%, -50%) scale(1);
}
}

View File

@@ -10,6 +10,7 @@
@use 'map';
@use 'radix/toast';
@use 'radix/dialog';
:root {
--white: #fff;
@@ -56,6 +57,10 @@ a[href^='http']::after {
margin-left: .25em;
}
strong {
font-weight: 600;
}
.icon {
width: 1em;
}

View File

@@ -1,22 +1,36 @@
<script lang="ts" setup>
import { computed, onUpdated, ref, watch } from 'vue';
import { computed, onUpdated, ref, watch } from 'vue'
import { useElementHover, useFocus } from '@vueuse/core'
import { PopoverArrow, PopoverContent, PopoverRoot, PopoverTrigger } from 'radix-vue'
import { useStore } from '@nanostores/vue';
import {
PopoverArrow,
PopoverContent,
PopoverRoot,
PopoverTrigger,
DialogContent,
DialogDescription,
DialogOverlay,
DialogPortal,
DialogRoot,
DialogTitle,
DialogTrigger
} from 'radix-vue'
import { currentLang } from '@/i18n/store';
import { t } from '@/i18n/store';
import { useStore } from '@nanostores/vue'
const $currentLang = useStore(currentLang);
import { currentLang } from '@/i18n/store'
import { t } from '@/i18n/store'
const navModel = ref(false);
const $currentLang = useStore(currentLang)
const navModel = ref(false)
const legalModelOpened = ref(false)
const wrapper = ref<HTMLDivElement | null>(null)
const buttonRef = ref<HTMLButtonElement | null>(null)
const wrapperHovered = useElementHover(wrapper, { delayEnter: 100, delayLeave: 500 })
const { focused: buttonFocused } = useFocus(buttonRef)
const isNavOpened = computed(() => navModel.value || wrapperHovered.value)
const isNavOpened = computed(() => navModel.value || wrapperHovered.value || legalModelOpened.value )
watch([wrapperHovered, buttonFocused], (value) => {
// Check if all values from array are false
@@ -25,7 +39,7 @@ watch([wrapperHovered, buttonFocused], (value) => {
}
})
let url: URL | null = null;
let url: URL | null = null
onUpdated(() => {
url = new URL(window.location.href)
@@ -132,9 +146,23 @@ const menus: Menu[] = [
</template>
<div class="legal">
<a :href="`/${$currentLang}/legal`">
{{ t('legal.cta') }}
</a>
<DialogRoot v-model:open="legalModelOpened">
<DialogTrigger as-child>
<button :href="`/${$currentLang}/legal`">
{{ t('legal.cta') }}
</button>
</DialogTrigger>
<DialogPortal>
<DialogOverlay class="dialog-overlay" />
<DialogContent class="dialog-content">
<DialogTitle class="dialog-title">
{{ t('legal.cta') }}
</DialogTitle>
<DialogDescription class="dialog-description" v-html="t('legal.text')" />
</DialogContent>
</DialogPortal>
</DialogRoot>
</div>
</PopoverContent>
</PopoverRoot>
@@ -167,6 +195,10 @@ const menus: Menu[] = [
text-decoration: underline;
gap: .5ch;
button {
cursor: pointer;
}
&:hover,
&:focus-visible {
text-decoration: none;

View File

@@ -37,6 +37,15 @@ export const translations: LanguageDict = {
'maps.markers.newNotice': "Le marqueur sera sauvegardé mais n'apparaîtra que sur votre carte !",
'toast.copyCoords.description': 'Les coordonnées ont été copiées dans le presse-papiers !',
'legal.cta': 'Mentions légales',
'legal.text': `
<p>Cette application est construite avec <a href="https://astro.build/" target="_blank">astro</a>, <a href="https://leafletjs.com/" target="_blank">leaflet</a> et <a href="https://vuejs.org/" target="_blank">vue</a>. J'utilise également des icônes de <a href="https://phosphoricons.com/" target="_blank">phosphor</a> et elle est actuellement hébergée sur <a href="https://www.ovhcloud.com" target="_blank">OVH</a>.</p>
<h3>Données personelles</h3>
<p>Je ne collecte aucune donnée sur votre activité dans cette application. Les marqueurs personnalisés que vous pouvez placer n'existent que sur votre navigateur en tant que données de <a href="https://developer.mozilla.org/fr/docs/Web/API/Window/localStorage" target="_blank">local storage</a>.</p>
<p>Mon hébergeur collecte des données de trafic pour des raisons de sécurité ; comme les adresses IP et leur emplacement approximatif, les types de navigateurs, les agents utilisateurs (si vous êtes un robot ou un humain) et l'état de la demande. </p>
<p>Ce site n'utilise pas de pixels de suivi ou d'analyse, et je n'utilise aucune autre application tierce qui pourrait collecter des données sur vous (comme Youtube ou autre)</p>
<p>Malgré cela, <strong>aucune de vos données n'est vendue ou transmise à un tiers quel qu'il soit</strong>.</p>
<p>Si vous avez des questions, vous pouvez me contacter à <a href="mailto:contact@alexcreates.fr">contact@alexcreates.fr</a>.</p>
`
},
'en': {
'lang.fr': 'Français',
@@ -70,5 +79,14 @@ export const translations: LanguageDict = {
'maps.markers.newNotice': "The marker will be saved but will only appear on your map!",
'toast.copyCoords.description': 'Coordinates have been copied to your clipboard !',
'legal.cta': 'Legal notice',
'legal.text': `
<p>This app is built with <a href="https://astro.build/" target="_blank">astro</a>, <a href="https://leafletjs.com/" target="_blank">leaflet</a> and <a href="https://vuejs.org/" target="_blank">vue</a>. I also use icons from <a href="https://phosphoricons.com/" target="_blank">phosphor</a> and it's currently hosted on <a href="https://www.ovhcloud.com" target="_blank">OVH</a>.</p>
<h3>Privacy</h3>
<p>I don't collect any data of your activity with this app. The custom markers that you may place only exist on your browser as <a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage" target="_blank">local storage</a> data.</p>
<p>My hosting provider collects traffic data for security reasons ; like IP addresses and their approximate location, browsers types, user agents (if you're a robot or a human) and the request status.</p>
<p>This website doesn't use any tracking pixels or analytics, and I don't use any other third-party app that might collect stuff from you (like Youtube or anything else)</p>
<p>Regardless, <strong>none of your data is sold or passed to any third-party whatsoever</strong>.</p>
<p>If you have any questions, you can contact me at <a href="mailto:contact@alexcreates.fr">contact@alexcreates.fr</a>.</p>
`
}
} as const;

View File

@@ -28,6 +28,7 @@ export interface SearchConfig {
}
export interface MapProps {
title?: string
/**
* The ID of the map.
* This is used to identify the map and store custom markers in the local storage.