Added marker add modal
This commit is contained in:
@@ -14,7 +14,7 @@
|
||||
"@astrojs/vue": "^3.0.1",
|
||||
"@nanostores/vue": "^0.10.0",
|
||||
"@types/leaflet": "^1.9.6",
|
||||
"@vueuse/core": "^10.5.0",
|
||||
"@vueuse/core": "^10.7.1",
|
||||
"astro": "^3.2.0",
|
||||
"nanostores": "^0.9.3",
|
||||
"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;
|
||||
|
||||
.leaflet-popup-content {
|
||||
margin: .65rem 1.3rem .65rem .9rem;
|
||||
|
||||
.title {
|
||||
display: block;
|
||||
font-weight: 600;
|
||||
@@ -38,6 +40,11 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.leaflet-popup-tip {
|
||||
width: .8rem;
|
||||
height: .8rem;
|
||||
}
|
||||
}
|
||||
.leaflet-tooltip-pane {
|
||||
.leaflet-tooltip {
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
@use 'reset';
|
||||
@use 'leaflet';
|
||||
|
||||
@use 'forms';
|
||||
@use 'buttons';
|
||||
|
||||
:root {
|
||||
--white: #fff;
|
||||
--black: #111;
|
||||
@@ -44,4 +47,4 @@ a[href^='http']::after {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
margin-left: .25em;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ const players = playersData as PlayerMarker
|
||||
</div>
|
||||
|
||||
<script lang="ts" define:vars={{ markers, players }} defer>
|
||||
|
||||
const mapHeight = 21896; //
|
||||
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
|
||||
* @param {string?} markerTitle
|
||||
*/
|
||||
function addCustomMarker() {
|
||||
function addCustomMarker(markerTitle) {
|
||||
const lon = Number(localStorage.getItem('lastHeldXPosition'))
|
||||
const lat = Number(localStorage.getItem('lastHeldYPosition'))
|
||||
|
||||
@@ -326,9 +328,18 @@ function addCustomMarker() {
|
||||
|
||||
// Sticks the custom marker on the 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({
|
||||
lat,
|
||||
lon,
|
||||
title: markerTitle,
|
||||
icon: customMarkerIconOptions
|
||||
})
|
||||
|
||||
@@ -337,7 +348,9 @@ function addCustomMarker() {
|
||||
|
||||
// Listeners for the custom add marker event
|
||||
// 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
|
||||
@@ -352,7 +365,14 @@ function loadCustomMarkersFromStorage() {
|
||||
for (let i = 0; i < customMarkersData.length; 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()
|
||||
|
||||
@@ -1,13 +1,21 @@
|
||||
<script lang="ts" setup>
|
||||
import { onClickOutside, onLongPress, useCssVar, useLocalStorage, useMouse, useTimeout, useTimeoutFn } from '@vueuse/core';
|
||||
import { onMounted, onUnmounted, ref, watch } from 'vue';
|
||||
import { onClickOutside, onLongPress, useCssVar, useFocus, useLocalStorage, useMouse, useTimeout, useTimeoutFn } from '@vueuse/core';
|
||||
import { computed, onMounted, onUnmounted, ref, watch } from 'vue';
|
||||
|
||||
const markerMenu = ref<HTMLMenuElement>();
|
||||
|
||||
type MenuMode = "editing-marker" | "default";
|
||||
|
||||
/**
|
||||
* Appearing behaviour
|
||||
*/
|
||||
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 longPressDelay = 500
|
||||
@@ -18,9 +26,9 @@ const isActive = ref<Boolean>(false)
|
||||
// }, longPressOver)
|
||||
|
||||
onMounted(() => {
|
||||
window.addEventListener('contextmenu', handleContextMenu)
|
||||
const mapRef = document.getElementById('world')
|
||||
mapRef?.addEventListener('contextmenu', handleContextMenu)
|
||||
// onLongPress(document.documentElement, handleLongPress, { delay: longPressDelay })
|
||||
onClickOutside(markerMenu, handleClickOutside)
|
||||
})
|
||||
|
||||
// function handleLongPress(e: Event) {
|
||||
@@ -77,30 +85,111 @@ function updateCSSVars() {
|
||||
/**
|
||||
* Adding custom marker
|
||||
*/
|
||||
const lastHeldXPosition = useLocalStorage('lastHeldXPosition', 0);
|
||||
const lastHeldYPosition = useLocalStorage('lastHeldYPosition', 0);
|
||||
const markerTitle = ref<string>()
|
||||
const markerTitleInput = ref<HTMLInputElement>()
|
||||
|
||||
const addCustomMarker = ref<HTMLButtonElement>();
|
||||
|
||||
onMounted(() => {
|
||||
const addCustomMarkerEvent = new CustomEvent(`add-custom-marker`, { bubbles: true })
|
||||
const markerModal = ref<HTMLDialogElement>()
|
||||
const markerModalOpen = ref<boolean>(false)
|
||||
|
||||
addCustomMarker.value?.addEventListener('click', () => {
|
||||
addCustomMarker.value?.dispatchEvent(addCustomMarkerEvent)
|
||||
})
|
||||
// Shows modal if mode changes to editing-marker
|
||||
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>
|
||||
|
||||
<template>
|
||||
<Transition>
|
||||
<menu v-show="isActive" ref="markerMenu">
|
||||
<li>
|
||||
<button ref="addCustomMarker">
|
||||
Ajouter un marqueur personnel
|
||||
<div>
|
||||
<Transition>
|
||||
<menu v-show="shouldBeShown" ref="markerMenu">
|
||||
<li>
|
||||
<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>
|
||||
</li>
|
||||
</menu>
|
||||
</Transition>
|
||||
</div>
|
||||
</dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@@ -109,6 +198,7 @@ menu {
|
||||
--mouse-x: 0px;
|
||||
|
||||
position: absolute;
|
||||
padding-block: .25rem;
|
||||
top: calc(var(--mouse-y) - .5rem);
|
||||
left: calc(var(--mouse-x) + .75rem);
|
||||
background-color: var(--white);
|
||||
@@ -123,18 +213,96 @@ menu {
|
||||
li {
|
||||
a, button {
|
||||
display: block;
|
||||
padding: .5rem 1.2rem;
|
||||
font-size: .8em;
|
||||
padding: .33rem 1rem;
|
||||
font-size: .77em;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover,
|
||||
&: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
|
||||
*/
|
||||
|
||||
@@ -173,7 +173,7 @@ function resetAllFields(actionAfter?: "focusAfter") {
|
||||
<div ref="searchBar" class="search-w" :data-focused="shouldBeActive">
|
||||
<div class="input-w">
|
||||
<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">
|
||||
<i class="ph-light ph-x"></i>
|
||||
|
||||
Reference in New Issue
Block a user