Added marker add modal
This commit is contained in:
@@ -14,7 +14,7 @@
|
|||||||
"@astrojs/vue": "^3.0.1",
|
"@astrojs/vue": "^3.0.1",
|
||||||
"@nanostores/vue": "^0.10.0",
|
"@nanostores/vue": "^0.10.0",
|
||||||
"@types/leaflet": "^1.9.6",
|
"@types/leaflet": "^1.9.6",
|
||||||
"@vueuse/core": "^10.5.0",
|
"@vueuse/core": "^10.7.1",
|
||||||
"astro": "^3.2.0",
|
"astro": "^3.2.0",
|
||||||
"nanostores": "^0.9.3",
|
"nanostores": "^0.9.3",
|
||||||
"vue": "^3.3.4"
|
"vue": "^3.3.4"
|
||||||
|
|||||||
47
src/assets/scss/_buttons.scss
Normal file
47
src/assets/scss/_buttons.scss
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
.btn {
|
||||||
|
padding: .3rem .7rem;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: .5rem;
|
||||||
|
font-size: .9em;
|
||||||
|
font-weight: 500;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
border-radius: .2rem;
|
||||||
|
cursor: pointer;
|
||||||
|
transition-property: color, background-color, border-color;
|
||||||
|
transition-duration: .2s;
|
||||||
|
transition-timing-function: cubic-bezier(0.785, 0.135, 0.15, 0.86);
|
||||||
|
|
||||||
|
&-secondary {
|
||||||
|
&:not(:disabled) {
|
||||||
|
background-color: var(--slate-200);
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:focus-visible {
|
||||||
|
color: var(--white);
|
||||||
|
background-color: var(--blue-500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-danger {
|
||||||
|
&:not(:disabled) {
|
||||||
|
background-color: var(--slate-200);
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:focus-visible {
|
||||||
|
color: var(--white);
|
||||||
|
background-color: var(--red-500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-icon {
|
||||||
|
width: 1.75rem;
|
||||||
|
padding: 0;
|
||||||
|
display: grid;
|
||||||
|
place-items: center;
|
||||||
|
aspect-ratio: 1;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
29
src/assets/scss/_forms.scss
Normal file
29
src/assets/scss/_forms.scss
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
.form-group {
|
||||||
|
label {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: .35rem;
|
||||||
|
font-size: .9em;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-input {
|
||||||
|
input[type=text],
|
||||||
|
input[type=tel],
|
||||||
|
input[type=email],
|
||||||
|
textarea {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
padding: .4rem .5rem;
|
||||||
|
border: 1px solid var(--slate-300);
|
||||||
|
font-size: .8em;
|
||||||
|
border-radius: .2rem;
|
||||||
|
transition-property: box-shadow;
|
||||||
|
transition-duration: .2s;
|
||||||
|
transition-timing-function: cubic-bezier(0.785, 0.135, 0.15, 0.86);
|
||||||
|
|
||||||
|
&:focus-visible {
|
||||||
|
box-shadow: 0 0 0 .2rem color-mix(in srgb, var(--slate-300) 20%, transparent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,6 +13,8 @@
|
|||||||
border-radius: .25rem;
|
border-radius: .25rem;
|
||||||
|
|
||||||
.leaflet-popup-content {
|
.leaflet-popup-content {
|
||||||
|
margin: .65rem 1.3rem .65rem .9rem;
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
display: block;
|
display: block;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
@@ -38,6 +40,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.leaflet-popup-tip {
|
||||||
|
width: .8rem;
|
||||||
|
height: .8rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.leaflet-tooltip-pane {
|
.leaflet-tooltip-pane {
|
||||||
.leaflet-tooltip {
|
.leaflet-tooltip {
|
||||||
|
|||||||
@@ -3,6 +3,9 @@
|
|||||||
@use 'reset';
|
@use 'reset';
|
||||||
@use 'leaflet';
|
@use 'leaflet';
|
||||||
|
|
||||||
|
@use 'forms';
|
||||||
|
@use 'buttons';
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--white: #fff;
|
--white: #fff;
|
||||||
--black: #111;
|
--black: #111;
|
||||||
@@ -44,4 +47,4 @@ a[href^='http']::after {
|
|||||||
width: 1em;
|
width: 1em;
|
||||||
height: 1em;
|
height: 1em;
|
||||||
margin-left: .25em;
|
margin-left: .25em;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ const players = playersData as PlayerMarker
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script lang="ts" define:vars={{ markers, players }} defer>
|
<script lang="ts" define:vars={{ markers, players }} defer>
|
||||||
|
|
||||||
const mapHeight = 21896; //
|
const mapHeight = 21896; //
|
||||||
const mapWidth = 30000; // Find these in the zoomify / original image data
|
const mapWidth = 30000; // Find these in the zoomify / original image data
|
||||||
|
|
||||||
@@ -308,8 +309,9 @@ function storeLastHeldPositions(e) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Function that creates custom markers and places them on the map
|
* Function that creates custom markers and places them on the map
|
||||||
|
* @param {string?} markerTitle
|
||||||
*/
|
*/
|
||||||
function addCustomMarker() {
|
function addCustomMarker(markerTitle) {
|
||||||
const lon = Number(localStorage.getItem('lastHeldXPosition'))
|
const lon = Number(localStorage.getItem('lastHeldXPosition'))
|
||||||
const lat = Number(localStorage.getItem('lastHeldYPosition'))
|
const lat = Number(localStorage.getItem('lastHeldYPosition'))
|
||||||
|
|
||||||
@@ -326,9 +328,18 @@ function addCustomMarker() {
|
|||||||
|
|
||||||
// Sticks the custom marker on the map
|
// Sticks the custom marker on the map
|
||||||
const customMarker = L.marker([lat, lon], { icon: customMarkerIcon }).addTo(map)
|
const customMarker = L.marker([lat, lon], { icon: customMarkerIcon }).addTo(map)
|
||||||
|
|
||||||
|
if (markerTitle) {
|
||||||
|
const popupContent = `
|
||||||
|
${markerTitle}
|
||||||
|
`
|
||||||
|
customMarker.bindPopup(popupContent, { offset: [5, -5] }).openPopup()
|
||||||
|
}
|
||||||
|
|
||||||
customMarkersList.push({
|
customMarkersList.push({
|
||||||
lat,
|
lat,
|
||||||
lon,
|
lon,
|
||||||
|
title: markerTitle,
|
||||||
icon: customMarkerIconOptions
|
icon: customMarkerIconOptions
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -337,7 +348,9 @@ function addCustomMarker() {
|
|||||||
|
|
||||||
// Listeners for the custom add marker event
|
// Listeners for the custom add marker event
|
||||||
// See MarkerCreator.vue component for more details
|
// See MarkerCreator.vue component for more details
|
||||||
document.addEventListener('add-custom-marker', addCustomMarker)
|
document.addEventListener('add-custom-marker', (e) => {
|
||||||
|
addCustomMarker(e.detail.title)
|
||||||
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load all custom markers onto the map
|
* Load all custom markers onto the map
|
||||||
@@ -352,7 +365,14 @@ function loadCustomMarkersFromStorage() {
|
|||||||
for (let i = 0; i < customMarkersData.length; i++) {
|
for (let i = 0; i < customMarkersData.length; i++) {
|
||||||
const m = customMarkersData[i];
|
const m = customMarkersData[i];
|
||||||
|
|
||||||
L.marker([m.lat, m.lon], { icon: L.icon(m.icon) }).addTo(map)
|
const savedMarker = L.marker([m.lat, m.lon], { icon: L.icon(m.icon) }).addTo(map)
|
||||||
|
|
||||||
|
if (m.title) {
|
||||||
|
const popupContent = `
|
||||||
|
${m.title}
|
||||||
|
`
|
||||||
|
savedMarker.bindPopup(popupContent, { offset: [5, -5] })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
loadCustomMarkersFromStorage()
|
loadCustomMarkersFromStorage()
|
||||||
|
|||||||
@@ -1,13 +1,21 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onClickOutside, onLongPress, useCssVar, useLocalStorage, useMouse, useTimeout, useTimeoutFn } from '@vueuse/core';
|
import { onClickOutside, onLongPress, useCssVar, useFocus, useLocalStorage, useMouse, useTimeout, useTimeoutFn } from '@vueuse/core';
|
||||||
import { onMounted, onUnmounted, ref, watch } from 'vue';
|
import { computed, onMounted, onUnmounted, ref, watch } from 'vue';
|
||||||
|
|
||||||
const markerMenu = ref<HTMLMenuElement>();
|
const markerMenu = ref<HTMLMenuElement>();
|
||||||
|
|
||||||
|
type MenuMode = "editing-marker" | "default";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Appearing behaviour
|
* Appearing behaviour
|
||||||
*/
|
*/
|
||||||
const isActive = ref<Boolean>(false)
|
const isActive = ref<Boolean>(false)
|
||||||
|
const shouldBeShown = computed(() => isActive.value)
|
||||||
|
const currentMode = ref<MenuMode>("default");
|
||||||
|
|
||||||
|
function switchMenuMode(newMode: MenuMode) {
|
||||||
|
currentMode.value = newMode
|
||||||
|
}
|
||||||
|
|
||||||
// const hasBeenLongPressed = ref<Boolean>(false)
|
// const hasBeenLongPressed = ref<Boolean>(false)
|
||||||
// const longPressDelay = 500
|
// const longPressDelay = 500
|
||||||
@@ -18,9 +26,9 @@ const isActive = ref<Boolean>(false)
|
|||||||
// }, longPressOver)
|
// }, longPressOver)
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
window.addEventListener('contextmenu', handleContextMenu)
|
const mapRef = document.getElementById('world')
|
||||||
|
mapRef?.addEventListener('contextmenu', handleContextMenu)
|
||||||
// onLongPress(document.documentElement, handleLongPress, { delay: longPressDelay })
|
// onLongPress(document.documentElement, handleLongPress, { delay: longPressDelay })
|
||||||
onClickOutside(markerMenu, handleClickOutside)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// function handleLongPress(e: Event) {
|
// function handleLongPress(e: Event) {
|
||||||
@@ -77,30 +85,111 @@ function updateCSSVars() {
|
|||||||
/**
|
/**
|
||||||
* Adding custom marker
|
* Adding custom marker
|
||||||
*/
|
*/
|
||||||
const lastHeldXPosition = useLocalStorage('lastHeldXPosition', 0);
|
const markerTitle = ref<string>()
|
||||||
const lastHeldYPosition = useLocalStorage('lastHeldYPosition', 0);
|
const markerTitleInput = ref<HTMLInputElement>()
|
||||||
|
|
||||||
const addCustomMarker = ref<HTMLButtonElement>();
|
const addCustomMarker = ref<HTMLButtonElement>();
|
||||||
|
|
||||||
onMounted(() => {
|
const markerModal = ref<HTMLDialogElement>()
|
||||||
const addCustomMarkerEvent = new CustomEvent(`add-custom-marker`, { bubbles: true })
|
const markerModalOpen = ref<boolean>(false)
|
||||||
|
|
||||||
addCustomMarker.value?.addEventListener('click', () => {
|
// Shows modal if mode changes to editing-marker
|
||||||
addCustomMarker.value?.dispatchEvent(addCustomMarkerEvent)
|
watch(currentMode, (n, o) => {
|
||||||
})
|
if (n === 'editing-marker') {
|
||||||
|
showMarkerModal()
|
||||||
|
} else {
|
||||||
|
hideMarkerModal()
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Closes the menu if clicked outside
|
||||||
|
onClickOutside(markerMenu, handleClickOutside, { ignore: [markerModal] })
|
||||||
|
|
||||||
|
function handleAddCustomMarker() {
|
||||||
|
const addCustomMarkerEvent = new CustomEvent(`add-custom-marker`, { bubbles: true, detail: { title: markerTitle.value }})
|
||||||
|
|
||||||
|
addCustomMarker.value?.dispatchEvent(addCustomMarkerEvent)
|
||||||
|
hideMenu()
|
||||||
|
hideMarkerModal()
|
||||||
|
switchMenuMode('default')
|
||||||
|
|
||||||
|
markerTitle.value = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleMarkerModalCancel() {
|
||||||
|
showMenu()
|
||||||
|
hideMarkerModal()
|
||||||
|
switchMenuMode('default')
|
||||||
|
}
|
||||||
|
|
||||||
|
function showMarkerModal() {
|
||||||
|
markerModal.value?.showModal()
|
||||||
|
markerModalOpen.value = true
|
||||||
|
switchMenuMode('editing-marker')
|
||||||
|
}
|
||||||
|
|
||||||
|
function hideMarkerModal() {
|
||||||
|
markerModal.value?.close()
|
||||||
|
markerModalOpen.value = false
|
||||||
|
switchMenuMode('default')
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Transition>
|
<div>
|
||||||
<menu v-show="isActive" ref="markerMenu">
|
<Transition>
|
||||||
<li>
|
<menu v-show="shouldBeShown" ref="markerMenu">
|
||||||
<button ref="addCustomMarker">
|
<li>
|
||||||
Ajouter un marqueur personnel
|
<button @click="switchMenuMode('editing-marker')">
|
||||||
|
Nouveau marqueur
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
</menu>
|
||||||
|
</Transition>
|
||||||
|
|
||||||
|
<dialog ref="markerModal" :class="{ 'open': markerModalOpen }" @cancel.prevent="handleMarkerModalCancel" @click="console.log">
|
||||||
|
<!-- <button autofocus @click="switchMenuMode('default')">Close</button> -->
|
||||||
|
<form @submit.prevent="handleAddCustomMarker">
|
||||||
|
<div class="modal-head">
|
||||||
|
<h2>
|
||||||
|
Ajouter un marqueur personnel
|
||||||
|
</h2>
|
||||||
|
<p class="modal-notice">
|
||||||
|
<i class="icon ph-light ph-warning"></i>
|
||||||
|
<span>Le marqueur sera sauvegardé mais n'apparaîtra que sur votre carte !</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="marker-name">
|
||||||
|
Titre
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<div class="form-input">
|
||||||
|
<input ref="markerTitleInput" type="text" name="marker-name" id="marker-name" v-model="markerTitle">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button
|
||||||
|
ref="addCustomMarker"
|
||||||
|
type="submit"
|
||||||
|
class="btn btn-secondary"
|
||||||
|
>
|
||||||
|
<span>Créer</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div class="modal-actions">
|
||||||
|
<button class="btn btn-danger btn-icon" @click="hideMarkerModal">
|
||||||
|
<i class="ph-light ph-x"></i>
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</div>
|
||||||
</menu>
|
</dialog>
|
||||||
</Transition>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@@ -109,6 +198,7 @@ menu {
|
|||||||
--mouse-x: 0px;
|
--mouse-x: 0px;
|
||||||
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
padding-block: .25rem;
|
||||||
top: calc(var(--mouse-y) - .5rem);
|
top: calc(var(--mouse-y) - .5rem);
|
||||||
left: calc(var(--mouse-x) + .75rem);
|
left: calc(var(--mouse-x) + .75rem);
|
||||||
background-color: var(--white);
|
background-color: var(--white);
|
||||||
@@ -123,18 +213,96 @@ menu {
|
|||||||
li {
|
li {
|
||||||
a, button {
|
a, button {
|
||||||
display: block;
|
display: block;
|
||||||
padding: .5rem 1.2rem;
|
padding: .33rem 1rem;
|
||||||
font-size: .8em;
|
font-size: .77em;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
&:hover,
|
&:hover,
|
||||||
&:focus-within {
|
&:focus-within {
|
||||||
|
background-color: var(--slate-100);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dialog {
|
||||||
|
position: fixed;
|
||||||
|
width: 30%;
|
||||||
|
padding: 1.5rem 2rem;
|
||||||
|
z-index: 9999;
|
||||||
|
background-color: var(--white);
|
||||||
|
border: 1px solid var(--slate-400);
|
||||||
|
border-radius: .75rem;
|
||||||
|
overflow: visible;
|
||||||
|
transform: translateY(1rem);
|
||||||
|
transition: visibility 0s ease-out .3s, opacity .3s ease-out, transform .3s ease-out;
|
||||||
|
|
||||||
|
&,
|
||||||
|
&::backdrop {
|
||||||
|
visibility: hidden;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::backdrop {
|
||||||
|
background-color: rgba(0, 0, 0, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.open {
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.open,
|
||||||
|
&.open::backdrop {
|
||||||
|
transition-delay: 0s;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.open,
|
||||||
|
&.open::backdrop {
|
||||||
|
visibility: visible;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-head {
|
||||||
|
margin-bottom: .75rem;
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: 1.4em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-notice {
|
||||||
|
margin-top: .2rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: .15rem;
|
||||||
|
opacity: .85;
|
||||||
|
font-size: .8em;
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
font-size: 1.3em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-body {
|
||||||
|
> * + * {
|
||||||
|
margin-top: .5em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-footer {
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 1rem;
|
||||||
|
position: absolute;
|
||||||
|
top: -0.8rem;
|
||||||
|
right: 0;
|
||||||
|
padding-right: .75rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ANIMATIONS
|
* ANIMATIONS
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -173,7 +173,7 @@ function resetAllFields(actionAfter?: "focusAfter") {
|
|||||||
<div ref="searchBar" class="search-w" :data-focused="shouldBeActive">
|
<div ref="searchBar" class="search-w" :data-focused="shouldBeActive">
|
||||||
<div class="input-w">
|
<div class="input-w">
|
||||||
<i class="search-icon ph-fill ph-magnifying-glass"></i>
|
<i class="search-icon ph-fill ph-magnifying-glass"></i>
|
||||||
<input ref="qInput" name="recherche" type="text" v-model="q" title="Rechercher le monde" placeholder="Ville, point d'intérêt…">
|
<input ref="qInput" class="" name="recherche" type="text" v-model="q" title="Rechercher le monde" placeholder="Ville, point d'intérêt…">
|
||||||
|
|
||||||
<button v-if="hasGroupFilter || hasQuery" @click="onCloseQuery" class="close-btn" title="Enlever le filtre">
|
<button v-if="hasGroupFilter || hasQuery" @click="onCloseQuery" class="close-btn" title="Enlever le filtre">
|
||||||
<i class="ph-light ph-x"></i>
|
<i class="ph-light ph-x"></i>
|
||||||
|
|||||||
Reference in New Issue
Block a user