Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
99e66f6feb | ||
|
|
cfe3fe2dcc | ||
|
|
42b3cfd725 | ||
|
|
d3c0a87770 | ||
|
|
3e1d7c5200 | ||
|
|
e893e133cc | ||
|
|
4cf2f66d42 | ||
|
|
f003df7d58 | ||
|
|
355e9ae009 | ||
|
|
ca689c23af | ||
|
|
c282c7ec7a | ||
|
|
0bc65e96c8 | ||
|
|
c6bf77462e | ||
|
|
fb26813932 | ||
|
|
6152b707d1 | ||
|
|
a40bf13d98 | ||
|
|
c68b1baf8c | ||
|
|
199bb98412 | ||
|
|
82aa2f4d35 | ||
|
|
86dbe7ba2b | ||
|
|
8b976e909a | ||
|
|
47f5df936e | ||
|
|
6d281e14ea | ||
|
|
cd99ed2444 | ||
|
|
3e1382c404 | ||
|
|
c84745b35a | ||
|
|
f5a76f9c6e |
@@ -1,21 +1,21 @@
|
|||||||
import { defineConfig } from 'astro/config';
|
import { defineConfig, envField } from 'astro/config';
|
||||||
|
|
||||||
import { availableLangs, defaultLang } from './src/i18n/ui';
|
|
||||||
|
|
||||||
import vue from "@astrojs/vue";
|
import vue from "@astrojs/vue";
|
||||||
import sitemap from "@astrojs/sitemap";
|
import sitemap from "@astrojs/sitemap";
|
||||||
|
|
||||||
|
|
||||||
// https://astro.build/config
|
// https://astro.build/config
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
integrations: [vue(), sitemap()],
|
integrations: [vue(), sitemap()],
|
||||||
output: 'static',
|
output: 'static',
|
||||||
site: 'https://maps.alexcreates.fr',
|
site: 'https://maps.alexcreates.fr',
|
||||||
// i18n: {
|
prefetch: true,
|
||||||
// locales: availableLangs,
|
env: {
|
||||||
// defaultLocale: defaultLang,
|
schema: {
|
||||||
// routing: {
|
ENABLE_TOOLBAR: envField.boolean({ context: "server", access: "secret", default: true }),
|
||||||
// prefixDefaultLocale: true,
|
ENABLE_TOOLBAR_TAGS: envField.boolean({ context: "server", access: "secret", default: true }),
|
||||||
// }
|
ENABLE_CUSTOM_MARKERS: envField.boolean({ context: "server", access: "secret", default: true }),
|
||||||
// }
|
ENABLE_BREADCRUMB: envField.boolean({ context: "server", access: "secret", default: true }),
|
||||||
|
ENABLE_I18N: envField.boolean({ context: "server", access: "secret", default: true }),
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
22
package.json
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "leim-maps",
|
"name": "leim-maps",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "1.4.6",
|
"version": "1.4.9",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "astro dev",
|
"dev": "astro dev",
|
||||||
"start": "astro dev",
|
"start": "astro dev",
|
||||||
@@ -10,18 +10,18 @@
|
|||||||
"astro": "astro"
|
"astro": "astro"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@astrojs/sitemap": "^3.3.0",
|
"@astrojs/sitemap": "^3.4.0",
|
||||||
"@astrojs/vue": "^5.0.7",
|
"@astrojs/vue": "^5.1.0",
|
||||||
"@nanostores/persistent": "^0.10.2",
|
"@nanostores/persistent": "1.0.0",
|
||||||
"@nanostores/vue": "^0.11.0",
|
"@nanostores/vue": "^1.0.0",
|
||||||
"@types/leaflet": "^1.9.17",
|
"@types/leaflet": "^1.9.18",
|
||||||
"@vueuse/core": "^13.0.0",
|
"@vueuse/core": "^13.2.0",
|
||||||
"astro": "^5.5.4",
|
"astro": "^5.8.0",
|
||||||
"nanostores": "^0.11.4",
|
"nanostores": "^1.0.1",
|
||||||
"radix-vue": "^1.9.17",
|
"radix-vue": "^1.9.17",
|
||||||
"vue": "^3.5.13"
|
"vue": "^3.5.14"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"sass": "^1.86.0"
|
"sass": "^1.89.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
1858
pnpm-lock.yaml
generated
5
pnpm-workspace.yaml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
onlyBuiltDependencies:
|
||||||
|
- '@parcel/watcher'
|
||||||
|
- esbuild
|
||||||
|
- sharp
|
||||||
|
- vue-demi
|
||||||
|
Before Width: | Height: | Size: 29 KiB |
BIN
public/images/cover/Académie_Centrale_Kaldélienne.webp
Normal file
|
After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 25 KiB |
BIN
public/images/cover/Ambrose.webp
Normal file
|
After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 34 KiB |
BIN
public/images/cover/Cantane.webp
Normal file
|
After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 44 KiB |
BIN
public/images/cover/Cordouac.webp
Normal file
|
After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 82 KiB |
BIN
public/images/cover/Faille_de_Kiam.webp
Normal file
|
After Width: | Height: | Size: 65 KiB |
|
Before Width: | Height: | Size: 23 KiB |
BIN
public/images/cover/Faranne.webp
Normal file
|
After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 20 KiB |
BIN
public/images/cover/Fasàch.webp
Normal file
|
After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 26 KiB |
BIN
public/images/cover/Garràn.webp
Normal file
|
After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 65 KiB |
BIN
public/images/cover/Horfer.webp
Normal file
|
After Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 80 KiB |
BIN
public/images/cover/Les Mines Blanches.webp
Normal file
|
After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 17 KiB |
BIN
public/images/cover/Miverne.webp
Normal file
|
After Width: | Height: | Size: 9.8 KiB |
|
Before Width: | Height: | Size: 641 KiB |
BIN
public/images/cover/Plaines_de_poussière.webp
Normal file
|
After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 30 KiB |
BIN
public/images/cover/Port-brisé.webp
Normal file
|
After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 21 KiB |
BIN
public/images/cover/Port_de_Borélis.webp
Normal file
|
After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 35 KiB |
BIN
public/images/cover/Rocheverre.webp
Normal file
|
After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 36 KiB |
BIN
public/images/cover/Soubreciel.webp
Normal file
|
After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 154 KiB |
BIN
public/images/cover/Tourgrise.webp
Normal file
|
After Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 52 KiB |
BIN
public/images/cover/Tours_des_Cages.webp
Normal file
|
After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 36 KiB |
BIN
public/images/cover/Willeau.webp
Normal file
|
After Width: | Height: | Size: 25 KiB |
134
public/js/leaflet-zoomify.min.js
vendored
@@ -1,130 +1,4 @@
|
|||||||
/*
|
L.TileLayer.Zoomify=L.TileLayer.extend({options:{width:-1,height:-1,tileGroupPrefix:'TileGroup',tilesPerTileGroup:256},initialize:function(url,options){L.TileLayer.prototype.initialize.call(this,url,options);if(this.options.width<0||this.options.height<0){throw new Error('The user must set the Width and Height of the Zoomify image')}},beforeAdd:function(map){var imageSize=L.point(this.options.width,this.options.height);this._imageSize=[imageSize];this._gridSize=[this._getGridSize(imageSize)];while(imageSize.x>this.options.tileSize||imageSize.y>this.options.tileSize){imageSize=imageSize.divideBy(2).ceil();this._imageSize.push(imageSize);this._gridSize.push(this._getGridSize(imageSize))}
|
||||||
* L.TileLayer.Zoomify display Zoomify tiles with Leaflet
|
this._imageSize.reverse();this._gridSize.reverse();var maxNativeZoom=this._gridSize.length-1;this.options.maxNativeZoom=maxNativeZoom;var maxZoomGrid=this._gridSize[maxNativeZoom],maxX=maxZoomGrid.x*this.options.tileSize,maxY=maxZoomGrid.y*this.options.tileSize,southEast=map.unproject([maxX,maxY],maxNativeZoom);this.options.bounds=new L.LatLngBounds([[0,0],southEast]);L.TileLayer.prototype.beforeAdd.call(this,map)},_getGridSize:function(imageSize){var tileSize=this.options.tileSize;return L.point(Math.ceil(imageSize.x/tileSize),Math.ceil(imageSize.y/tileSize))},_addTile:function(coords,container){L.TileLayer.prototype._addTile.call(this,coords,container);var imageSize=this._imageSize[this._getZoomForUrl()],gridSize=this._gridSize[this._getZoomForUrl()];var realTileSize=L.GridLayer.prototype.getTileSize.call(this),displayTileSize=L.TileLayer.prototype.getTileSize.call(this);var key=this._tileCoordsToKey(coords),tile=this._tiles[key].el;var scaleFactor=L.point((imageSize.x%realTileSize.x),(imageSize.y%realTileSize.y)).unscaleBy(realTileSize);if((imageSize.x%realTileSize.x)>0&&coords.x===gridSize.x-1){tile.style.width=displayTileSize.scaleBy(scaleFactor).x+'px'}
|
||||||
*
|
if((imageSize.y%realTileSize.y)>0&&coords.y===gridSize.y-1){tile.style.height=displayTileSize.scaleBy(scaleFactor).y+'px'}},getTileUrl:function(coords){this.options.g=this.options.tileGroupPrefix+this._getTileGroup(coords);return L.TileLayer.prototype.getTileUrl.call(this,coords)},_getTileGroup:function(coords){var zoom=this._getZoomForUrl(),num=0,gridSize;for(var z=0;z<zoom;z++){gridSize=this._gridSize[z];num+=gridSize.x*gridSize.y}
|
||||||
* Based on the Leaflet.Zoomify (https://github.com/turban/Leaflet.Zoomify)
|
num+=coords.y*this._gridSize[zoom].x+coords.x;return Math.floor(num/this.options.tilesPerTileGroup)},getBounds:function(){return this.options.bounds}});L.tileLayer.zoomify=function(url,options){return new L.TileLayer.Zoomify(url,options)}
|
||||||
* from turban (https://github.com/turban)
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
L.TileLayer.Zoomify = L.TileLayer.extend({
|
|
||||||
options: {
|
|
||||||
width: -1, // Must be set by user, max zoom image width
|
|
||||||
height: -1, // Must be set by user, max zoom image height
|
|
||||||
tileGroupPrefix: 'TileGroup',
|
|
||||||
tilesPerTileGroup: 256
|
|
||||||
},
|
|
||||||
|
|
||||||
initialize: function (url, options) {
|
|
||||||
L.TileLayer.prototype.initialize.call(this, url, options);
|
|
||||||
|
|
||||||
// Replace with automatic loading from ImageProperties.xml
|
|
||||||
if (this.options.width < 0 || this.options.height < 0) {
|
|
||||||
throw new Error('The user must set the Width and Height of the Zoomify image');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
beforeAdd: function (map) {
|
|
||||||
var imageSize = L.point(this.options.width, this.options.height);
|
|
||||||
|
|
||||||
// Build the zoom sizes of the pyramid and cache them in an array
|
|
||||||
this._imageSize = [imageSize];
|
|
||||||
this._gridSize = [this._getGridSize(imageSize)];
|
|
||||||
|
|
||||||
// Register the image size in pixels and the grid size in # of tiles for each zoom level
|
|
||||||
while (imageSize.x > this.options.tileSize || imageSize.y > this.options.tileSize) {
|
|
||||||
imageSize = imageSize.divideBy(2).ceil();
|
|
||||||
this._imageSize.push(imageSize);
|
|
||||||
this._gridSize.push(this._getGridSize(imageSize));
|
|
||||||
}
|
|
||||||
|
|
||||||
// We built the cache from bottom to top, but leaflet uses a top to bottom index for the zoomlevel,
|
|
||||||
// so reverse it for easy indexing by current zoomlevel
|
|
||||||
this._imageSize.reverse();
|
|
||||||
this._gridSize.reverse();
|
|
||||||
|
|
||||||
// Register our max supported zoom level
|
|
||||||
var maxNativeZoom = this._gridSize.length - 1;
|
|
||||||
this.options.maxNativeZoom = maxNativeZoom;
|
|
||||||
|
|
||||||
// Register our bounds for this zoomify layer based on the maximum zoom
|
|
||||||
var maxZoomGrid = this._gridSize[maxNativeZoom],
|
|
||||||
maxX = maxZoomGrid.x * this.options.tileSize,
|
|
||||||
maxY = maxZoomGrid.y * this.options.tileSize,
|
|
||||||
southEast = map.unproject([maxX, maxY], maxNativeZoom);
|
|
||||||
this.options.bounds = new L.LatLngBounds([[0, 0], southEast]);
|
|
||||||
|
|
||||||
L.TileLayer.prototype.beforeAdd.call(this, map);
|
|
||||||
},
|
|
||||||
|
|
||||||
// Calculate the grid size for a given image size (based on tile size)
|
|
||||||
_getGridSize: function (imageSize) {
|
|
||||||
var tileSize = this.options.tileSize;
|
|
||||||
return L.point(Math.ceil(imageSize.x / tileSize), Math.ceil(imageSize.y / tileSize));
|
|
||||||
},
|
|
||||||
|
|
||||||
// Extend the add tile function to update our arbitrary sized border tiles
|
|
||||||
_addTile: function (coords, container) {
|
|
||||||
// Load the tile via the original leaflet code
|
|
||||||
L.TileLayer.prototype._addTile.call(this, coords, container);
|
|
||||||
|
|
||||||
// Get out imagesize in pixels for this zoom level and our grid size
|
|
||||||
var imageSize = this._imageSize[this._getZoomForUrl()],
|
|
||||||
gridSize = this._gridSize[this._getZoomForUrl()];
|
|
||||||
|
|
||||||
// The real tile size (default:256) and the display tile size (if zoom > maxNativeZoom)
|
|
||||||
var realTileSize = L.GridLayer.prototype.getTileSize.call(this),
|
|
||||||
displayTileSize = L.TileLayer.prototype.getTileSize.call(this);
|
|
||||||
|
|
||||||
// Get the current tile to adjust
|
|
||||||
var key = this._tileCoordsToKey(coords),
|
|
||||||
tile = this._tiles[key].el;
|
|
||||||
|
|
||||||
// Calculate the required size of the border tiles
|
|
||||||
var scaleFactor = L.point( (imageSize.x % realTileSize.x),
|
|
||||||
(imageSize.y % realTileSize.y)).unscaleBy(realTileSize);
|
|
||||||
|
|
||||||
// Update tile dimensions if we are on a border
|
|
||||||
if ((imageSize.x % realTileSize.x) > 0 && coords.x === gridSize.x - 1) {
|
|
||||||
tile.style.width = displayTileSize.scaleBy(scaleFactor).x + 'px';
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((imageSize.y % realTileSize.y) > 0 && coords.y === gridSize.y - 1) {
|
|
||||||
tile.style.height = displayTileSize.scaleBy(scaleFactor).y + 'px';
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// Construct the tile url, by inserting our tilegroup before we template the url
|
|
||||||
getTileUrl: function (coords) {
|
|
||||||
// Set our TileGroup variable, and then let the original tile templater do the work
|
|
||||||
this.options.g = this.options.tileGroupPrefix + this._getTileGroup(coords);
|
|
||||||
|
|
||||||
// Call the original templater
|
|
||||||
return L.TileLayer.prototype.getTileUrl.call(this, coords);
|
|
||||||
},
|
|
||||||
|
|
||||||
// Calculates the TileGroup number, each group contains 256 tiles. The tiles are stored from topleft to bottomright
|
|
||||||
_getTileGroup: function (coords) {
|
|
||||||
var zoom = this._getZoomForUrl(),
|
|
||||||
num = 0,
|
|
||||||
gridSize;
|
|
||||||
|
|
||||||
// Get the total number of tiles from the lowest zoom level to our zoomlevel
|
|
||||||
for (var z = 0; z < zoom; z++) {
|
|
||||||
gridSize = this._gridSize[z];
|
|
||||||
num += gridSize.x * gridSize.y;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add the remaining tiles from this zoom layer to the running total of tiles
|
|
||||||
num += coords.y * this._gridSize[zoom].x + coords.x;
|
|
||||||
return Math.floor(num / this.options.tilesPerTileGroup);
|
|
||||||
},
|
|
||||||
|
|
||||||
getBounds: function () {
|
|
||||||
return this.options.bounds;
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
L.tileLayer.zoomify = function (url, options) {
|
|
||||||
return new L.TileLayer.Zoomify(url, options);
|
|
||||||
};
|
|
||||||
49
public/robots.txt
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
User-agent: GPTBot
|
||||||
|
Disallow: /
|
||||||
|
User-agent: ChatGPT-User
|
||||||
|
Disallow: /
|
||||||
|
User-agent: Google-Extended
|
||||||
|
Disallow: /
|
||||||
|
User-agent: PerplexityBot
|
||||||
|
Disallow: /
|
||||||
|
User-agent: Amazonbot
|
||||||
|
Disallow: /
|
||||||
|
User-agent: ClaudeBot
|
||||||
|
Disallow: /
|
||||||
|
User-agent: Omgilibot
|
||||||
|
Disallow: /
|
||||||
|
User-Agent: FacebookBot
|
||||||
|
Disallow: /
|
||||||
|
User-Agent: Applebot
|
||||||
|
Disallow: /
|
||||||
|
User-agent: anthropic-ai
|
||||||
|
Disallow: /
|
||||||
|
User-agent: Bytespider
|
||||||
|
Disallow: /
|
||||||
|
User-agent: Claude-Web
|
||||||
|
Disallow: /
|
||||||
|
User-agent: Diffbot
|
||||||
|
Disallow: /
|
||||||
|
User-agent: ImagesiftBot
|
||||||
|
Disallow: /
|
||||||
|
User-agent: Omgilibot
|
||||||
|
Disallow: /
|
||||||
|
User-agent: Omgili
|
||||||
|
Disallow: /
|
||||||
|
User-agent: YouBot
|
||||||
|
Disallow: /
|
||||||
|
|
||||||
|
User-agent: AhrefsBot
|
||||||
|
Disallow: /
|
||||||
|
User-agent: AhrefsSiteAudit
|
||||||
|
Disallow: /
|
||||||
|
User-agent: SemrushBot
|
||||||
|
Disallow: /
|
||||||
|
User-agent: YandexMarket
|
||||||
|
Disallow: /
|
||||||
|
User-agent: YandexBot
|
||||||
|
Disallow: /
|
||||||
|
User-agent: DataForSeoBot
|
||||||
|
Disallow: /
|
||||||
|
|
||||||
|
Sitemap: https://maps.alexcreates.fr/sitemap-index.xml
|
||||||
@@ -16,121 +16,6 @@
|
|||||||
.leaflet-popup-content {
|
.leaflet-popup-content {
|
||||||
min-width: 20rem;
|
min-width: 20rem;
|
||||||
margin: 1rem;
|
margin: 1rem;
|
||||||
|
|
||||||
figure {
|
|
||||||
position: relative;
|
|
||||||
margin-inline: -1.1rem;
|
|
||||||
margin-top: -1.1rem;
|
|
||||||
margin-bottom: .5rem;
|
|
||||||
|
|
||||||
&.landscape {
|
|
||||||
aspect-ratio: 16 / 9;
|
|
||||||
min-width: 20rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.portrait {
|
|
||||||
aspect-ratio: 15 / 16;
|
|
||||||
min-width: 10rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::before {
|
|
||||||
content: '';
|
|
||||||
display: block;
|
|
||||||
pointer-events: none;
|
|
||||||
user-select: none;
|
|
||||||
position: absolute;
|
|
||||||
inset: 0;
|
|
||||||
z-index: 10;
|
|
||||||
// Make a linear gradient from top to bottom, from transparent to black
|
|
||||||
background: linear-gradient(to bottom, transparent 70%, var(--black) 100%);
|
|
||||||
}
|
|
||||||
|
|
||||||
img {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
object-fit: cover;
|
|
||||||
object-position: top;
|
|
||||||
}
|
|
||||||
|
|
||||||
figcaption cite {
|
|
||||||
position: absolute;
|
|
||||||
font-size: 85%;
|
|
||||||
font-style: italic;
|
|
||||||
font-weight: 600;
|
|
||||||
bottom: .5rem;
|
|
||||||
right: 1rem;
|
|
||||||
color: var(--white);
|
|
||||||
z-index: 20;
|
|
||||||
opacity: .6;
|
|
||||||
transition-property: opacity;
|
|
||||||
transition-duration: .2s;
|
|
||||||
transition-timing-function: cubic-bezier(0.23, 1, 0.320, 1);
|
|
||||||
|
|
||||||
a {
|
|
||||||
padding-right: 0;
|
|
||||||
color: var(--white);
|
|
||||||
text-decoration: underline;
|
|
||||||
text-underline-offset: .15rem;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: var(--blue-500);
|
|
||||||
}
|
|
||||||
|
|
||||||
&::after {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
figcaption cite {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.title {
|
|
||||||
display: block;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
strong.title {
|
|
||||||
color: var(--green-500);
|
|
||||||
}
|
|
||||||
|
|
||||||
a.title,
|
|
||||||
a.map-link {
|
|
||||||
text-underline-offset: 2px;
|
|
||||||
text-decoration: underline;
|
|
||||||
|
|
||||||
&:hover,
|
|
||||||
&:focus-visible {
|
|
||||||
text-decoration: none;
|
|
||||||
color: var(--green-500);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.map-link {
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: .5ch;
|
|
||||||
|
|
||||||
svg {
|
|
||||||
width: 1rem;
|
|
||||||
height: 1rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.tooltip-content > * + * {
|
|
||||||
margin: 0;
|
|
||||||
margin-top: .66em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tooltip-footer {
|
|
||||||
display: grid;
|
|
||||||
gap: .15em;
|
|
||||||
font-size: .95em;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,6 +24,7 @@
|
|||||||
height: .8rem;
|
height: .8rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.leaflet-tooltip-pane {
|
.leaflet-tooltip-pane {
|
||||||
.leaflet-tooltip {
|
.leaflet-tooltip {
|
||||||
&.capital-name {
|
&.capital-name {
|
||||||
|
|||||||
115
src/assets/scss/radix/_toast.scss
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
.toast-viewport {
|
||||||
|
--viewport-padding: 25px;
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
right: 2rem;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: var(--viewport-padding);
|
||||||
|
gap: 10px;
|
||||||
|
width: 480px;
|
||||||
|
max-width: 100vw;
|
||||||
|
margin: 0;
|
||||||
|
list-style: none;
|
||||||
|
z-index: 2147483647;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast-root {
|
||||||
|
position: relative;
|
||||||
|
padding: 15px;
|
||||||
|
display: grid;
|
||||||
|
grid-template-areas: 'title action' 'description action';
|
||||||
|
grid-template-columns: auto max-content;
|
||||||
|
column-gap: 15px;
|
||||||
|
align-items: center;
|
||||||
|
color: var(--slate-800);
|
||||||
|
border: 1px solid var(--slate-400);
|
||||||
|
background-color: var(--white);
|
||||||
|
border-radius: 6px;
|
||||||
|
box-shadow: hsl(206 22% 7% / 35%) 0px 10px 38px -10px, hsl(206 22% 7% / 20%) 0px 10px 20px -15px;
|
||||||
|
|
||||||
|
&.toast-success {
|
||||||
|
color: var(--green-700);
|
||||||
|
border: 1px solid var(--green-700);
|
||||||
|
background-color: color-mix(in srgb, var(--green-500) 17.5%, var(--white));
|
||||||
|
|
||||||
|
.toast-icon svg {
|
||||||
|
fill: var(--green-700);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:has(.toast-icon) {
|
||||||
|
padding-left: 2.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast-icon {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
left: .66rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.toast-root[data-state='open'] {
|
||||||
|
animation: slideIn 150ms cubic-bezier(0.16, 1, 0.3, 1);
|
||||||
|
}
|
||||||
|
.toast-root[data-state='closed'] {
|
||||||
|
animation: hide 100ms ease-in;
|
||||||
|
}
|
||||||
|
.toast-root[data-swipe='move'] {
|
||||||
|
transform: translateX(var(--radix-toast-swipe-move-x));
|
||||||
|
}
|
||||||
|
.toast-root[data-swipe='cancel'] {
|
||||||
|
transform: translateX(0);
|
||||||
|
transition: transform 200ms ease-out;
|
||||||
|
}
|
||||||
|
.toast-root[data-swipe='end'] {
|
||||||
|
animation: swipeOut 100ms ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes hide {
|
||||||
|
from {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slideIn {
|
||||||
|
from {
|
||||||
|
transform: translateX(calc(100% + var(--viewport-padding)));
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes swipeOut {
|
||||||
|
from {
|
||||||
|
transform: translateX(var(--radix-toast-swipe-end-x));
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
transform: translateX(calc(100% + var(--viewport-padding)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast-title {
|
||||||
|
grid-area: title;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--slate-12);
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast-description {
|
||||||
|
grid-area: description;
|
||||||
|
margin: 0;
|
||||||
|
color: var(--slate-11);
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast-action {
|
||||||
|
grid-area: action;
|
||||||
|
}
|
||||||
@@ -9,6 +9,8 @@
|
|||||||
@use 'card';
|
@use 'card';
|
||||||
@use 'map';
|
@use 'map';
|
||||||
|
|
||||||
|
@use 'radix/toast';
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--white: #fff;
|
--white: #fff;
|
||||||
--black: #111;
|
--black: #111;
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
---
|
---
|
||||||
|
import { ENABLE_CUSTOM_MARKERS } from "astro:env/server";
|
||||||
|
|
||||||
import type { MapMarker, PlayerMarker } from '@/types/Leaflet';
|
import type { MapMarker, PlayerMarker } from '@/types/Leaflet';
|
||||||
import type { MapProps } from '@/types/Map';
|
import type { MapProps } from '@/types/Map';
|
||||||
import MarkerCreator from './overlay/MarkerCreator.vue';
|
import MarkerCreator from './overlay/MarkerCreator.vue';
|
||||||
import { t } from '@/i18n/store';
|
import MarkerContent from './MarkerContent.astro';
|
||||||
import { getLangFromUrl } from '@/i18n/utils';
|
|
||||||
import { normalizeString } from '@/utils/Strings';
|
|
||||||
import CopyText from '../global/CopyText.astro';
|
|
||||||
|
|
||||||
interface Props extends MapProps {}
|
interface Props extends MapProps {}
|
||||||
|
|
||||||
@@ -21,55 +20,21 @@ const {
|
|||||||
rulerHideWalkDistance = false,
|
rulerHideWalkDistance = false,
|
||||||
backgroundColor = 'transparent',
|
backgroundColor = 'transparent',
|
||||||
} = Astro.props
|
} = Astro.props
|
||||||
|
|
||||||
const lang = getLangFromUrl(Astro.url);
|
|
||||||
|
|
||||||
const byText = t('common.by', lang)
|
|
||||||
const seeMapText = t('maps.go-to-map', lang)
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<div class="map-wrapper">
|
<div class="map-wrapper">
|
||||||
<div class="world-wrapper">
|
<main class="world-wrapper">
|
||||||
<div id="world"></div>
|
<div id="world"></div>
|
||||||
|
|
||||||
|
{ENABLE_CUSTOM_MARKERS &&
|
||||||
<MarkerCreator client:only="vue" mapKey={mapKey} />
|
<MarkerCreator client:only="vue" mapKey={mapKey} />
|
||||||
</div>
|
}
|
||||||
|
</main>
|
||||||
|
|
||||||
<div style="height: 0; width: 0; overflow: hidden; display: none;">
|
<div style="height: 0; width: 0; overflow: hidden; display: none;">
|
||||||
{markers.map((m, index) => (
|
{markers.map((m, index) => (
|
||||||
<div id="popup-template" data-id={`marker-${index}`} class="tooltip-content">
|
<div id="popup-template" data-id={`marker-${index}`}>
|
||||||
{m.cover && m.coverAuthor && m.coverLink && (
|
<MarkerContent marker={m} />
|
||||||
<figure class={m.coverPortrait ? 'portrait' : 'landscape'}>
|
|
||||||
<img src={`/images/cover/${m.cover}`} alt={m.title} width={m.coverPortrait ? 210 : 420} />
|
|
||||||
<figcaption>
|
|
||||||
<cite>{byText} <a href={m.coverLink} target="_blank">{m.coverAuthor}</a></cite>
|
|
||||||
</figcaption>
|
|
||||||
</figure>
|
|
||||||
)}
|
|
||||||
{m.link ? (
|
|
||||||
<a href={m.link} target="_blank" class="title">{m.title}</a>
|
|
||||||
) : (
|
|
||||||
<strong class="title">{m.title}</strong>
|
|
||||||
)}
|
|
||||||
{m.description && <p>{m.description}</p>}
|
|
||||||
|
|
||||||
<div class="tooltip-footer">
|
|
||||||
{m.mapId && (
|
|
||||||
<div>
|
|
||||||
<a href={m.mapId} class="map-link">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"><rect width="256" height="256" fill="none"/><line x1="80" y1="112" x2="144" y2="112" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><circle cx="112" cy="112" r="80" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><line x1="168.57" y1="168.57" x2="224" y2="224" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><line x1="112" y1="80" x2="112" y2="144" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/></svg>
|
|
||||||
|
|
||||||
<span>
|
|
||||||
{ seeMapText }
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<CopyText text={`${Astro.url}?p=${normalizeString(m.title)}`} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
@@ -85,9 +50,7 @@ const seeMapText = t('maps.go-to-map', lang)
|
|||||||
mapWidth,
|
mapWidth,
|
||||||
rulerDistanceRatio,
|
rulerDistanceRatio,
|
||||||
rulerMainUnit,
|
rulerMainUnit,
|
||||||
rulerHideWalkDistance,
|
rulerHideWalkDistance
|
||||||
byText,
|
|
||||||
seeMapText
|
|
||||||
}}
|
}}
|
||||||
defer
|
defer
|
||||||
>
|
>
|
||||||
@@ -639,6 +602,19 @@ map.on('moveend', () => {
|
|||||||
setupToURL()
|
setupToURL()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listener for querying a copy of the current coordinates
|
||||||
|
*/
|
||||||
|
window.addEventListener('on-copy-coords', () => {
|
||||||
|
const lon = convertScaleToX(Number(localStorage.getItem('lastHeldXPosition')))
|
||||||
|
const lat = convertScaleToY(Number(localStorage.getItem('lastHeldYPosition')))
|
||||||
|
const coordsText = JSON.stringify({ x: lon, y: lat })
|
||||||
|
|
||||||
|
navigator.clipboard.writeText(coordsText)
|
||||||
|
|
||||||
|
window.dispatchEvent(new CustomEvent('on-toast-push', { bubbles: true, detail: { preset: 'copied-coords' }}))
|
||||||
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* UTILITIES
|
* UTILITIES
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
---
|
---
|
||||||
|
import { ENABLE_BREADCRUMB, ENABLE_I18N, ENABLE_TOOLBAR, ENABLE_TOOLBAR_TAGS } from "astro:env/server";
|
||||||
|
|
||||||
import type { MapMarker, PlayerMarker } from "@/types/Leaflet";
|
import type { MapMarker, PlayerMarker } from "@/types/Leaflet";
|
||||||
import type { MapOverlayProps } from "@/types/Map";
|
import type { MapOverlayProps } from "@/types/Map";
|
||||||
|
|
||||||
import SearchMarkers from "./overlay/SearchMarkers.vue";
|
import SearchMarkers from "./overlay/SearchMarkers.vue";
|
||||||
import LangSwitcher from "./overlay/LangSwitcher.vue";
|
import LangSwitcher from "./overlay/LangSwitcher.vue";
|
||||||
import MapOverlayBreadcrumbs from "./MapOverlayBreadcrumbs.astro";
|
import MapOverlayBreadcrumbs from "./MapOverlayBreadcrumbs.astro";
|
||||||
|
import ToastService from "./overlay/ToastService.vue";
|
||||||
|
|
||||||
interface Props extends MapOverlayProps {}
|
interface Props extends MapOverlayProps {}
|
||||||
|
|
||||||
@@ -25,16 +28,31 @@ const currentUrl = Astro.url
|
|||||||
|
|
||||||
<div class="world-overlay">
|
<div class="world-overlay">
|
||||||
<div class="top-bar">
|
<div class="top-bar">
|
||||||
<SearchMarkers client:only="vue" mapKey={mapKey} markers={markers} players={players} searchConfig={searchConfig} >
|
{ENABLE_TOOLBAR &&
|
||||||
|
<SearchMarkers
|
||||||
|
client:only="vue"
|
||||||
|
mapKey={mapKey}
|
||||||
|
markers={markers}
|
||||||
|
players={players}
|
||||||
|
searchConfig={searchConfig}
|
||||||
|
disableTags={!ENABLE_TOOLBAR_TAGS}
|
||||||
|
>
|
||||||
<div slot="fallback" style="height: 45px;"></div>
|
<div slot="fallback" style="height: 45px;"></div>
|
||||||
</SearchMarkers>
|
</SearchMarkers>
|
||||||
|
}
|
||||||
|
|
||||||
|
{ENABLE_I18N &&
|
||||||
<LangSwitcher currentUrl={currentUrl} client:only="vue" />
|
<LangSwitcher currentUrl={currentUrl} client:only="vue" />
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{ENABLE_BREADCRUMB &&
|
||||||
<MapOverlayBreadcrumbs breadcrumbs={breadcrumbs} />
|
<MapOverlayBreadcrumbs breadcrumbs={breadcrumbs} />
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<ToastService client:only="vue" />
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.world-overlay {
|
.world-overlay {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|||||||
@@ -17,15 +17,15 @@ const { breadcrumbs = [] } = Astro.props
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="#0f172a" viewBox="0 0 256 256" class="folder"><path d="M245,110.64A16,16,0,0,0,232,104H216V88a16,16,0,0,0-16-16H130.67L102.94,51.2a16.14,16.14,0,0,0-9.6-3.2H40A16,16,0,0,0,24,64V208h0a8,8,0,0,0,8,8H211.1a8,8,0,0,0,7.59-5.47l28.49-85.47A16.05,16.05,0,0,0,245,110.64ZM93.34,64,123.2,86.4A8,8,0,0,0,128,88h72v16H69.77a16,16,0,0,0-15.18,10.94L40,158.7V64Z"></path></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="#0f172a" viewBox="0 0 256 256" class="folder"><path d="M245,110.64A16,16,0,0,0,232,104H216V88a16,16,0,0,0-16-16H130.67L102.94,51.2a16.14,16.14,0,0,0-9.6-3.2H40A16,16,0,0,0,24,64V208h0a8,8,0,0,0,8,8H211.1a8,8,0,0,0,7.59-5.47l28.49-85.47A16.05,16.05,0,0,0,245,110.64ZM93.34,64,123.2,86.4A8,8,0,0,0,128,88h72v16H69.77a16,16,0,0,0-15.18,10.94L40,158.7V64Z"></path></svg>
|
||||||
|
|
||||||
<span class="breadcrumb" aria-current="page">
|
<span class="breadcrumb" aria-current="page">
|
||||||
<span class="label">
|
<h1 class="label">
|
||||||
{breadcrumb.name}
|
{breadcrumb.name}
|
||||||
</span>
|
</h1>
|
||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
{!(index === breadcrumbs.length - 1) &&
|
{!(index === breadcrumbs.length - 1) &&
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="#0f172a" viewBox="0 0 256 256" class="folder"><path d="M216,72H131.31L104,44.69A15.86,15.86,0,0,0,92.69,40H40A16,16,0,0,0,24,56V200.62A15.4,15.4,0,0,0,39.38,216H216.89A15.13,15.13,0,0,0,232,200.89V88A16,16,0,0,0,216,72ZM40,56H92.69l16,16H40ZM216,200H40V88H216Z"></path></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="#0f172a" viewBox="0 0 256 256" class="folder"><path d="M216,72H131.31L104,44.69A15.86,15.86,0,0,0,92.69,40H40A16,16,0,0,0,24,56V200.62A15.4,15.4,0,0,0,39.38,216H216.89A15.13,15.13,0,0,0,232,200.89V88A16,16,0,0,0,216,72ZM40,56H92.69l16,16H40ZM216,200H40V88H216Z"></path></svg>
|
||||||
|
|
||||||
<a href={breadcrumb.url} class="breadcrumb" class:list={{ active: index === breadcrumbs.length - 1 }}>
|
<a href={breadcrumb.url} class="breadcrumb" class:list={{ active: index === breadcrumbs.length - 1 }} data-astro-prefetch="hover">
|
||||||
<span class="label">
|
<span class="label">
|
||||||
{breadcrumb.name}
|
{breadcrumb.name}
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
176
src/components/maps/MarkerContent.astro
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
---
|
||||||
|
import type { MapMarker } from '@/types/Leaflet';
|
||||||
|
import { normalizeString } from '@/utils/Strings';
|
||||||
|
import { getLangFromUrl } from '@/i18n/utils';
|
||||||
|
import { t } from '@/i18n/store';
|
||||||
|
|
||||||
|
import CopyText from '@/components/global/CopyText.astro';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
marker: MapMarker
|
||||||
|
}
|
||||||
|
|
||||||
|
const { marker: m } = Astro.props as Props;
|
||||||
|
|
||||||
|
const lang = getLangFromUrl(Astro.url);
|
||||||
|
|
||||||
|
const byText = t('common.by', lang)
|
||||||
|
const seeMapText = t('maps.go-to-map', lang)
|
||||||
|
---
|
||||||
|
|
||||||
|
<div class="tooltip-content">
|
||||||
|
{m.cover && m.coverAuthor && m.coverLink && (
|
||||||
|
<figure class={m.coverPortrait ? 'portrait' : 'landscape'}>
|
||||||
|
<img src={`/images/cover/${m.cover}`} alt={m.title} width={m.coverPortrait ? 210 : 420} />
|
||||||
|
<figcaption>
|
||||||
|
<span class="author">
|
||||||
|
<span>{byText}</span>
|
||||||
|
<cite>
|
||||||
|
<a href={m.coverLink} target="_blank">{m.coverAuthor}</a>
|
||||||
|
</cite>
|
||||||
|
</span>
|
||||||
|
</figcaption>
|
||||||
|
</figure>
|
||||||
|
)}
|
||||||
|
{m.link ? (
|
||||||
|
<a href={m.link} target="_blank" class="title">{m.title}</a>
|
||||||
|
) : (
|
||||||
|
<strong class="title">{m.title}</strong>
|
||||||
|
)}
|
||||||
|
{m.description && <p set:html={m.description} />}
|
||||||
|
|
||||||
|
<div class="tooltip-footer">
|
||||||
|
{m.mapId && (
|
||||||
|
<div>
|
||||||
|
<a href={m.mapId} class="map-link">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"><rect width="256" height="256" fill="none"/><line x1="80" y1="112" x2="144" y2="112" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><circle cx="112" cy="112" r="80" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><line x1="168.57" y1="168.57" x2="224" y2="224" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><line x1="112" y1="80" x2="112" y2="144" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/></svg>
|
||||||
|
|
||||||
|
<span>
|
||||||
|
{ seeMapText }
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<CopyText text={`${Astro.url}?p=${normalizeString(m.title)}`} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
figure {
|
||||||
|
position: relative;
|
||||||
|
margin-inline: -1.1rem;
|
||||||
|
margin-top: -1.1rem;
|
||||||
|
margin-bottom: .5rem;
|
||||||
|
|
||||||
|
&.landscape {
|
||||||
|
aspect-ratio: 16 / 9;
|
||||||
|
min-width: 20rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.portrait {
|
||||||
|
aspect-ratio: 15 / 16;
|
||||||
|
min-width: 10rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
display: block;
|
||||||
|
pointer-events: none;
|
||||||
|
user-select: none;
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
z-index: 10;
|
||||||
|
// Make a linear gradient from top to bottom, from transparent to black
|
||||||
|
background: linear-gradient(to bottom, transparent 70%, var(--black) 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
object-position: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
figcaption .author {
|
||||||
|
position: absolute;
|
||||||
|
font-size: 85%;
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 600;
|
||||||
|
bottom: .5rem;
|
||||||
|
right: 1rem;
|
||||||
|
color: var(--white);
|
||||||
|
z-index: 20;
|
||||||
|
opacity: .6;
|
||||||
|
transition-property: opacity;
|
||||||
|
transition-duration: .2s;
|
||||||
|
transition-timing-function: cubic-bezier(0.23, 1, 0.320, 1);
|
||||||
|
|
||||||
|
a {
|
||||||
|
padding-right: 0;
|
||||||
|
color: var(--white);
|
||||||
|
text-decoration: underline;
|
||||||
|
text-underline-offset: .15rem;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: var(--blue-500);
|
||||||
|
}
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
figcaption .author {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
display: block;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
strong.title {
|
||||||
|
color: var(--green-500);
|
||||||
|
}
|
||||||
|
|
||||||
|
a.title,
|
||||||
|
a.map-link {
|
||||||
|
text-underline-offset: 2px;
|
||||||
|
text-decoration: underline;
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:focus-visible {
|
||||||
|
text-decoration: none;
|
||||||
|
color: var(--green-500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.map-link {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: .5ch;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
width: 1rem;
|
||||||
|
height: 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip-content > * + * {
|
||||||
|
margin: 0;
|
||||||
|
margin-top: .66em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip-footer {
|
||||||
|
display: grid;
|
||||||
|
gap: .15em;
|
||||||
|
font-size: .95em;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
"x": 83.6,
|
"x": 83.6,
|
||||||
"y": -185.2
|
"y": -185.2
|
||||||
},
|
},
|
||||||
"cover": "Ambrose.jpg",
|
"cover": "Ambrose.webp",
|
||||||
"coverAuthor": "Claudio Pilia",
|
"coverAuthor": "Claudio Pilia",
|
||||||
"coverLink": "https://pilia-art.com/portfolio-item/game-of-thrones-the-city-of-tyrosh/"
|
"coverLink": "https://pilia-art.com/portfolio-item/game-of-thrones-the-city-of-tyrosh/"
|
||||||
},
|
},
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
"x": 133.46875,
|
"x": 133.46875,
|
||||||
"y": -146.0625
|
"y": -146.0625
|
||||||
},
|
},
|
||||||
"cover": "Cordouac.jpg",
|
"cover": "Cordouac.webp",
|
||||||
"coverAuthor": "Vincent Joyal",
|
"coverAuthor": "Vincent Joyal",
|
||||||
"coverLink": "https://www.artstation.com/artwork/6B0b0"
|
"coverLink": "https://www.artstation.com/artwork/6B0b0"
|
||||||
},
|
},
|
||||||
@@ -45,7 +45,7 @@
|
|||||||
"x": 132.26953,
|
"x": 132.26953,
|
||||||
"y": -98.19141
|
"y": -98.19141
|
||||||
},
|
},
|
||||||
"cover": "Tourgrise.png",
|
"cover": "Tourgrise.webp",
|
||||||
"coverAuthor": "saya kuroha",
|
"coverAuthor": "saya kuroha",
|
||||||
"coverLink": "https://www.pixiv.net/en/artworks/83793821",
|
"coverLink": "https://www.pixiv.net/en/artworks/83793821",
|
||||||
"coverPortrait": true
|
"coverPortrait": true
|
||||||
@@ -79,7 +79,7 @@
|
|||||||
"x": 168.39491,
|
"x": 168.39491,
|
||||||
"y": -109.13376
|
"y": -109.13376
|
||||||
},
|
},
|
||||||
"cover": "Port-brisé.jpg",
|
"cover": "Port-brisé.webp",
|
||||||
"coverAuthor": "JC Jongwon Park",
|
"coverAuthor": "JC Jongwon Park",
|
||||||
"coverLink": "https://www.artstation.com/artwork/okmQL"
|
"coverLink": "https://www.artstation.com/artwork/okmQL"
|
||||||
},
|
},
|
||||||
@@ -122,7 +122,7 @@
|
|||||||
"x": 204.42515,
|
"x": 204.42515,
|
||||||
"y": -226.85883
|
"y": -226.85883
|
||||||
},
|
},
|
||||||
"cover": "Soubreciel.jpg",
|
"cover": "Soubreciel.webp",
|
||||||
"coverAuthor": "Tony Andreas Rudolph",
|
"coverAuthor": "Tony Andreas Rudolph",
|
||||||
"coverLink": "https://www.artstation.com/artwork/EB84"
|
"coverLink": "https://www.artstation.com/artwork/EB84"
|
||||||
},
|
},
|
||||||
@@ -166,7 +166,7 @@
|
|||||||
"y": -51.54687
|
"y": -51.54687
|
||||||
},
|
},
|
||||||
"mapId": "/en/aldys/borelis",
|
"mapId": "/en/aldys/borelis",
|
||||||
"cover": "Port_de_Borélis.jpeg",
|
"cover": "Port_de_Borélis.webp",
|
||||||
"coverAuthor": "Anton Bezrukov",
|
"coverAuthor": "Anton Bezrukov",
|
||||||
"coverLink": "https://www.artstation.com/artwork/oODL5B"
|
"coverLink": "https://www.artstation.com/artwork/oODL5B"
|
||||||
},
|
},
|
||||||
@@ -210,7 +210,7 @@
|
|||||||
"y": -210.87224307207197
|
"y": -210.87224307207197
|
||||||
},
|
},
|
||||||
"mapId": "/en/aldys/cantane",
|
"mapId": "/en/aldys/cantane",
|
||||||
"cover": "Cantane.jpg",
|
"cover": "Cantane.webp",
|
||||||
"coverAuthor": "Andreas Roller",
|
"coverAuthor": "Andreas Roller",
|
||||||
"coverLink": "https://en.wikipedia.org/wiki/Andreas_Roller"
|
"coverLink": "https://en.wikipedia.org/wiki/Andreas_Roller"
|
||||||
},
|
},
|
||||||
@@ -223,7 +223,7 @@
|
|||||||
"y": -210.9293951755887
|
"y": -210.9293951755887
|
||||||
},
|
},
|
||||||
"mapId": "/en/aldys/cantane/mines-blanches",
|
"mapId": "/en/aldys/cantane/mines-blanches",
|
||||||
"cover": "Les Mines Blanches.png",
|
"cover": "Les Mines Blanches.webp",
|
||||||
"coverAuthor": "MeckanicalMind",
|
"coverAuthor": "MeckanicalMind",
|
||||||
"coverLink": "https://www.deviantart.com/meckanicalmind/art/R3-Mines-258469134"
|
"coverLink": "https://www.deviantart.com/meckanicalmind/art/R3-Mines-258469134"
|
||||||
},
|
},
|
||||||
@@ -236,7 +236,7 @@
|
|||||||
"x": 185.57431,
|
"x": 185.57431,
|
||||||
"y": -229.08588
|
"y": -229.08588
|
||||||
},
|
},
|
||||||
"cover": "Horfer.jpg",
|
"cover": "Horfer.webp",
|
||||||
"coverAuthor": "Nele-Diel",
|
"coverAuthor": "Nele-Diel",
|
||||||
"coverLink": "https://www.deviantart.com/nele-diel/art/City-in-the-Lowlands-693529688"
|
"coverLink": "https://www.deviantart.com/nele-diel/art/City-in-the-Lowlands-693529688"
|
||||||
},
|
},
|
||||||
@@ -519,7 +519,7 @@
|
|||||||
"x": 121.47116769539711,
|
"x": 121.47116769539711,
|
||||||
"y": -94.66997667550434
|
"y": -94.66997667550434
|
||||||
},
|
},
|
||||||
"cover": "Faille_de_Kiam.jpg",
|
"cover": "Faille_de_Kiam.webp",
|
||||||
"coverAuthor": "Laura Sava",
|
"coverAuthor": "Laura Sava",
|
||||||
"coverLink": "https://www.behance.net/gallery/16130223/Zariels-Doom",
|
"coverLink": "https://www.behance.net/gallery/16130223/Zariels-Doom",
|
||||||
"coverPortrait": true
|
"coverPortrait": true
|
||||||
@@ -573,7 +573,7 @@
|
|||||||
"x": 107.50352,
|
"x": 107.50352,
|
||||||
"y": -67.36631
|
"y": -67.36631
|
||||||
},
|
},
|
||||||
"cover": "Willeau.jpg",
|
"cover": "Willeau.webp",
|
||||||
"coverAuthor": "Inconnu",
|
"coverAuthor": "Inconnu",
|
||||||
"coverLink": "https://www.hebus.com/image-339033.html"
|
"coverLink": "https://www.hebus.com/image-339033.html"
|
||||||
},
|
},
|
||||||
@@ -635,7 +635,7 @@
|
|||||||
"x": 168.31641,
|
"x": 168.31641,
|
||||||
"y": -60.54297
|
"y": -60.54297
|
||||||
},
|
},
|
||||||
"cover": "Tours_des_Cages.jpg",
|
"cover": "Tours_des_Cages.webp",
|
||||||
"coverAuthor": "25kartinok",
|
"coverAuthor": "25kartinok",
|
||||||
"coverLink": "https://www.deviantart.com/25kartinok/art/the-dark-tower-700231527",
|
"coverLink": "https://www.deviantart.com/25kartinok/art/the-dark-tower-700231527",
|
||||||
"coverPortrait": true
|
"coverPortrait": true
|
||||||
@@ -677,7 +677,7 @@
|
|||||||
"x": 159.57688930332125,
|
"x": 159.57688930332125,
|
||||||
"y": -157.4739168466964
|
"y": -157.4739168466964
|
||||||
},
|
},
|
||||||
"cover": "Plaines_de_poussière.png",
|
"cover": "Plaines_de_poussière.webp",
|
||||||
"coverAuthor": "Inconnu",
|
"coverAuthor": "Inconnu",
|
||||||
"coverLink": "https://www.pinterest.com/pin/pin-de-soup-em-inspiration--279223245638830461"
|
"coverLink": "https://www.pinterest.com/pin/pin-de-soup-em-inspiration--279223245638830461"
|
||||||
},
|
},
|
||||||
@@ -710,7 +710,7 @@
|
|||||||
"x": 285.0990986858211,
|
"x": 285.0990986858211,
|
||||||
"y": -146.08986069534564
|
"y": -146.08986069534564
|
||||||
},
|
},
|
||||||
"cover": "Garràn.jpg",
|
"cover": "Garràn.webp",
|
||||||
"coverAuthor": "Dmitry Dubinsky",
|
"coverAuthor": "Dmitry Dubinsky",
|
||||||
"coverLink": "https://elhour.artstation.com/projects/RYynYm?album_id=47640"
|
"coverLink": "https://elhour.artstation.com/projects/RYynYm?album_id=47640"
|
||||||
},
|
},
|
||||||
@@ -773,7 +773,7 @@
|
|||||||
"x": 326.7497076703842,
|
"x": 326.7497076703842,
|
||||||
"y": -206.67114645854178
|
"y": -206.67114645854178
|
||||||
},
|
},
|
||||||
"cover": "Fasàch.jpg",
|
"cover": "Fasàch.webp",
|
||||||
"coverAuthor": "Liang Mark",
|
"coverAuthor": "Liang Mark",
|
||||||
"coverLink": "https://www.artstation.com/artwork/Jll2Jd"
|
"coverLink": "https://www.artstation.com/artwork/Jll2Jd"
|
||||||
},
|
},
|
||||||
@@ -886,7 +886,7 @@
|
|||||||
"x": 83.80737652386473,
|
"x": 83.80737652386473,
|
||||||
"y": -184.52963682119173
|
"y": -184.52963682119173
|
||||||
},
|
},
|
||||||
"cover": "Académie_Centrale_Kaldélienne.jpg",
|
"cover": "Académie_Centrale_Kaldélienne.webp",
|
||||||
"coverAuthor": "Pengzhen Zhang",
|
"coverAuthor": "Pengzhen Zhang",
|
||||||
"coverLink": "https://www.artstation.com/artwork/zAOWNd",
|
"coverLink": "https://www.artstation.com/artwork/zAOWNd",
|
||||||
"icon": "graduation-cap"
|
"icon": "graduation-cap"
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
"x": 119.6667886200924,
|
"x": 119.6667886200924,
|
||||||
"y": -17.945469721492717
|
"y": -17.945469721492717
|
||||||
},
|
},
|
||||||
"cover": "Faranne.jpg",
|
"cover": "Faranne.webp",
|
||||||
"coverAuthor": "Pablo Mendoza Peces",
|
"coverAuthor": "Pablo Mendoza Peces",
|
||||||
"coverLink": "https://www.artstation.com/artwork/dAJr1"
|
"coverLink": "https://www.artstation.com/artwork/dAJr1"
|
||||||
},
|
},
|
||||||
@@ -32,7 +32,7 @@
|
|||||||
"x": 113.52851847725508,
|
"x": 113.52851847725508,
|
||||||
"y": -19.882939646724424
|
"y": -19.882939646724424
|
||||||
},
|
},
|
||||||
"cover": "Miverne.jpg",
|
"cover": "Miverne.webp",
|
||||||
"coverAuthor": "Andrei-Pervukhin",
|
"coverAuthor": "Andrei-Pervukhin",
|
||||||
"coverLink": "https://www.deviantart.com/andrei-pervukhin/art/Medieval-Village-383779337"
|
"coverLink": "https://www.deviantart.com/andrei-pervukhin/art/Medieval-Village-383779337"
|
||||||
},
|
},
|
||||||
@@ -75,7 +75,7 @@
|
|||||||
"x": 122.87040892704968,
|
"x": 122.87040892704968,
|
||||||
"y": -58.7910005354329
|
"y": -58.7910005354329
|
||||||
},
|
},
|
||||||
"cover": "Rocheverre.jpg",
|
"cover": "Rocheverre.webp",
|
||||||
"coverAuthor": "Creative Intelligence Arts",
|
"coverAuthor": "Creative Intelligence Arts",
|
||||||
"coverLink": "https://www.kickstarter.com/projects/1300298569/project-phoenix-japans-indie-rpg-feat-aaa-talent"
|
"coverLink": "https://www.kickstarter.com/projects/1300298569/project-phoenix-japans-indie-rpg-feat-aaa-talent"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
"x": 83.6,
|
"x": 83.6,
|
||||||
"y": -185.2
|
"y": -185.2
|
||||||
},
|
},
|
||||||
"cover": "Ambrose.jpg",
|
"cover": "Ambrose.webp",
|
||||||
"coverAuthor": "Claudio Pilia",
|
"coverAuthor": "Claudio Pilia",
|
||||||
"coverLink": "https://pilia-art.com/portfolio-item/game-of-thrones-the-city-of-tyrosh/"
|
"coverLink": "https://pilia-art.com/portfolio-item/game-of-thrones-the-city-of-tyrosh/"
|
||||||
},
|
},
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
"x": 133.46875,
|
"x": 133.46875,
|
||||||
"y": -146.0625
|
"y": -146.0625
|
||||||
},
|
},
|
||||||
"cover": "Cordouac.jpg",
|
"cover": "Cordouac.webp",
|
||||||
"coverAuthor": "Vincent Joyal",
|
"coverAuthor": "Vincent Joyal",
|
||||||
"coverLink": "https://www.artstation.com/artwork/6B0b0"
|
"coverLink": "https://www.artstation.com/artwork/6B0b0"
|
||||||
},
|
},
|
||||||
@@ -45,7 +45,7 @@
|
|||||||
"x": 132.26953,
|
"x": 132.26953,
|
||||||
"y": -98.19141
|
"y": -98.19141
|
||||||
},
|
},
|
||||||
"cover": "Tourgrise.png",
|
"cover": "Tourgrise.webp",
|
||||||
"coverAuthor": "saya kuroha",
|
"coverAuthor": "saya kuroha",
|
||||||
"coverLink": "https://www.pixiv.net/en/artworks/83793821",
|
"coverLink": "https://www.pixiv.net/en/artworks/83793821",
|
||||||
"coverPortrait": true
|
"coverPortrait": true
|
||||||
@@ -79,7 +79,7 @@
|
|||||||
"x": 168.39491,
|
"x": 168.39491,
|
||||||
"y": -109.13376
|
"y": -109.13376
|
||||||
},
|
},
|
||||||
"cover": "Port-brisé.jpg",
|
"cover": "Port-brisé.webp",
|
||||||
"coverAuthor": "JC Jongwon Park",
|
"coverAuthor": "JC Jongwon Park",
|
||||||
"coverLink": "https://www.artstation.com/artwork/okmQL"
|
"coverLink": "https://www.artstation.com/artwork/okmQL"
|
||||||
},
|
},
|
||||||
@@ -122,7 +122,7 @@
|
|||||||
"x": 204.42515,
|
"x": 204.42515,
|
||||||
"y": -226.85883
|
"y": -226.85883
|
||||||
},
|
},
|
||||||
"cover": "Soubreciel.jpg",
|
"cover": "Soubreciel.webp",
|
||||||
"coverAuthor": "Tony Andreas Rudolph",
|
"coverAuthor": "Tony Andreas Rudolph",
|
||||||
"coverLink": "https://www.artstation.com/artwork/EB84"
|
"coverLink": "https://www.artstation.com/artwork/EB84"
|
||||||
},
|
},
|
||||||
@@ -166,7 +166,7 @@
|
|||||||
"y": -51.54687
|
"y": -51.54687
|
||||||
},
|
},
|
||||||
"mapId": "/fr/aldys/borelis",
|
"mapId": "/fr/aldys/borelis",
|
||||||
"cover": "Port_de_Borélis.jpeg",
|
"cover": "Port_de_Borélis.webp",
|
||||||
"coverAuthor": "Anton Bezrukov",
|
"coverAuthor": "Anton Bezrukov",
|
||||||
"coverLink": "https://www.artstation.com/artwork/oODL5B"
|
"coverLink": "https://www.artstation.com/artwork/oODL5B"
|
||||||
},
|
},
|
||||||
@@ -210,7 +210,7 @@
|
|||||||
"y": -210.87224307207197
|
"y": -210.87224307207197
|
||||||
},
|
},
|
||||||
"mapId": "/fr/aldys/cantane",
|
"mapId": "/fr/aldys/cantane",
|
||||||
"cover": "Cantane.jpg",
|
"cover": "Cantane.webp",
|
||||||
"coverAuthor": "Andreas Roller",
|
"coverAuthor": "Andreas Roller",
|
||||||
"coverLink": "https://en.wikipedia.org/wiki/Andreas_Roller"
|
"coverLink": "https://en.wikipedia.org/wiki/Andreas_Roller"
|
||||||
},
|
},
|
||||||
@@ -223,7 +223,7 @@
|
|||||||
"y": -210.9293951755887
|
"y": -210.9293951755887
|
||||||
},
|
},
|
||||||
"mapId": "/fr/aldys/cantane/mines-blanches",
|
"mapId": "/fr/aldys/cantane/mines-blanches",
|
||||||
"cover": "Les Mines Blanches.png",
|
"cover": "Les Mines Blanches.webp",
|
||||||
"coverAuthor": "MeckanicalMind",
|
"coverAuthor": "MeckanicalMind",
|
||||||
"coverLink": "https://www.deviantart.com/meckanicalmind/art/R3-Mines-258469134"
|
"coverLink": "https://www.deviantart.com/meckanicalmind/art/R3-Mines-258469134"
|
||||||
},
|
},
|
||||||
@@ -236,7 +236,7 @@
|
|||||||
"x": 185.57431,
|
"x": 185.57431,
|
||||||
"y": -229.08588
|
"y": -229.08588
|
||||||
},
|
},
|
||||||
"cover": "Horfer.jpg",
|
"cover": "Horfer.webp",
|
||||||
"coverAuthor": "Nele-Diel",
|
"coverAuthor": "Nele-Diel",
|
||||||
"coverLink": "https://www.deviantart.com/nele-diel/art/City-in-the-Lowlands-693529688"
|
"coverLink": "https://www.deviantart.com/nele-diel/art/City-in-the-Lowlands-693529688"
|
||||||
},
|
},
|
||||||
@@ -519,7 +519,7 @@
|
|||||||
"x": 121.47116769539711,
|
"x": 121.47116769539711,
|
||||||
"y": -94.66997667550434
|
"y": -94.66997667550434
|
||||||
},
|
},
|
||||||
"cover": "Faille_de_Kiam.jpg",
|
"cover": "Faille_de_Kiam.webp",
|
||||||
"coverAuthor": "Laura Sava",
|
"coverAuthor": "Laura Sava",
|
||||||
"coverLink": "https://www.behance.net/gallery/16130223/Zariels-Doom",
|
"coverLink": "https://www.behance.net/gallery/16130223/Zariels-Doom",
|
||||||
"coverPortrait": true
|
"coverPortrait": true
|
||||||
@@ -573,7 +573,7 @@
|
|||||||
"x": 107.50352,
|
"x": 107.50352,
|
||||||
"y": -67.36631
|
"y": -67.36631
|
||||||
},
|
},
|
||||||
"cover": "Willeau.jpg",
|
"cover": "Willeau.webp",
|
||||||
"coverAuthor": "Inconnu",
|
"coverAuthor": "Inconnu",
|
||||||
"coverLink": "https://www.hebus.com/image-339033.html"
|
"coverLink": "https://www.hebus.com/image-339033.html"
|
||||||
},
|
},
|
||||||
@@ -635,7 +635,7 @@
|
|||||||
"x": 168.31641,
|
"x": 168.31641,
|
||||||
"y": -60.54297
|
"y": -60.54297
|
||||||
},
|
},
|
||||||
"cover": "Tours_des_Cages.jpg",
|
"cover": "Tours_des_Cages.webp",
|
||||||
"coverAuthor": "25kartinok",
|
"coverAuthor": "25kartinok",
|
||||||
"coverLink": "https://www.deviantart.com/25kartinok/art/the-dark-tower-700231527",
|
"coverLink": "https://www.deviantart.com/25kartinok/art/the-dark-tower-700231527",
|
||||||
"coverPortrait": true
|
"coverPortrait": true
|
||||||
@@ -677,7 +677,7 @@
|
|||||||
"x": 159.57688930332125,
|
"x": 159.57688930332125,
|
||||||
"y": -157.4739168466964
|
"y": -157.4739168466964
|
||||||
},
|
},
|
||||||
"cover": "Plaines_de_poussière.png",
|
"cover": "Plaines_de_poussière.webp",
|
||||||
"coverAuthor": "Inconnu",
|
"coverAuthor": "Inconnu",
|
||||||
"coverLink": "https://www.pinterest.com/pin/pin-de-soup-em-inspiration--279223245638830461"
|
"coverLink": "https://www.pinterest.com/pin/pin-de-soup-em-inspiration--279223245638830461"
|
||||||
},
|
},
|
||||||
@@ -710,7 +710,7 @@
|
|||||||
"x": 285.0990986858211,
|
"x": 285.0990986858211,
|
||||||
"y": -146.08986069534564
|
"y": -146.08986069534564
|
||||||
},
|
},
|
||||||
"cover": "Garràn.jpg",
|
"cover": "Garràn.webp",
|
||||||
"coverAuthor": "Dmitry Dubinsky",
|
"coverAuthor": "Dmitry Dubinsky",
|
||||||
"coverLink": "https://elhour.artstation.com/projects/RYynYm?album_id=47640"
|
"coverLink": "https://elhour.artstation.com/projects/RYynYm?album_id=47640"
|
||||||
},
|
},
|
||||||
@@ -773,7 +773,7 @@
|
|||||||
"x": 326.7497076703842,
|
"x": 326.7497076703842,
|
||||||
"y": -206.67114645854178
|
"y": -206.67114645854178
|
||||||
},
|
},
|
||||||
"cover": "Fasàch.jpg",
|
"cover": "Fasàch.webp",
|
||||||
"coverAuthor": "Liang Mark",
|
"coverAuthor": "Liang Mark",
|
||||||
"coverLink": "https://www.artstation.com/artwork/Jll2Jd"
|
"coverLink": "https://www.artstation.com/artwork/Jll2Jd"
|
||||||
},
|
},
|
||||||
@@ -886,7 +886,7 @@
|
|||||||
"x": 83.80737652386473,
|
"x": 83.80737652386473,
|
||||||
"y": -184.52963682119173
|
"y": -184.52963682119173
|
||||||
},
|
},
|
||||||
"cover": "Académie_Centrale_Kaldélienne.jpg",
|
"cover": "Académie_Centrale_Kaldélienne.webp",
|
||||||
"coverAuthor": "Pengzhen Zhang",
|
"coverAuthor": "Pengzhen Zhang",
|
||||||
"coverLink": "https://www.artstation.com/artwork/zAOWNd",
|
"coverLink": "https://www.artstation.com/artwork/zAOWNd",
|
||||||
"icon": "graduation-cap"
|
"icon": "graduation-cap"
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
"x": 119.6667886200924,
|
"x": 119.6667886200924,
|
||||||
"y": -17.945469721492717
|
"y": -17.945469721492717
|
||||||
},
|
},
|
||||||
"cover": "Faranne.jpg",
|
"cover": "Faranne.webp",
|
||||||
"coverAuthor": "Pablo Mendoza Peces",
|
"coverAuthor": "Pablo Mendoza Peces",
|
||||||
"coverLink": "https://www.artstation.com/artwork/dAJr1"
|
"coverLink": "https://www.artstation.com/artwork/dAJr1"
|
||||||
},
|
},
|
||||||
@@ -32,7 +32,7 @@
|
|||||||
"x": 113.52851847725508,
|
"x": 113.52851847725508,
|
||||||
"y": -19.882939646724424
|
"y": -19.882939646724424
|
||||||
},
|
},
|
||||||
"cover": "Miverne.jpg",
|
"cover": "Miverne.webp",
|
||||||
"coverAuthor": "Andrei-Pervukhin",
|
"coverAuthor": "Andrei-Pervukhin",
|
||||||
"coverLink": "https://www.deviantart.com/andrei-pervukhin/art/Medieval-Village-383779337"
|
"coverLink": "https://www.deviantart.com/andrei-pervukhin/art/Medieval-Village-383779337"
|
||||||
},
|
},
|
||||||
@@ -75,7 +75,7 @@
|
|||||||
"x": 122.87040892704968,
|
"x": 122.87040892704968,
|
||||||
"y": -58.7910005354329
|
"y": -58.7910005354329
|
||||||
},
|
},
|
||||||
"cover": "Rocheverre.jpg",
|
"cover": "Rocheverre.webp",
|
||||||
"coverAuthor": "Creative Intelligence Arts",
|
"coverAuthor": "Creative Intelligence Arts",
|
||||||
"coverLink": "https://www.kickstarter.com/projects/1300298569/project-phoenix-japans-indie-rpg-feat-aaa-talent"
|
"coverLink": "https://www.kickstarter.com/projects/1300298569/project-phoenix-japans-indie-rpg-feat-aaa-talent"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { PopoverArrow, PopoverContent, PopoverRoot, PopoverTrigger } from 'radix
|
|||||||
import LangSwitcherButton from './LangSwitcherButton.vue';
|
import LangSwitcherButton from './LangSwitcherButton.vue';
|
||||||
|
|
||||||
import { useStore } from '@nanostores/vue';
|
import { useStore } from '@nanostores/vue';
|
||||||
import { currentLang } from '@/i18n/store';
|
import { currentLang, t } from '@/i18n/store';
|
||||||
import { availableLangs } from '@/i18n/ui';
|
import { availableLangs } from '@/i18n/ui';
|
||||||
|
|
||||||
const $currentLang = useStore(currentLang);
|
const $currentLang = useStore(currentLang);
|
||||||
@@ -22,7 +22,7 @@ const menuModel = ref(false);
|
|||||||
|
|
||||||
const wrapper = ref<HTMLDivElement | null>(null)
|
const wrapper = ref<HTMLDivElement | null>(null)
|
||||||
const buttonRef = ref<HTMLButtonElement | null>(null)
|
const buttonRef = ref<HTMLButtonElement | null>(null)
|
||||||
const wrapperHovered = useElementHover(wrapper, { delayEnter: 500, delayLeave: 500 })
|
const wrapperHovered = useElementHover(wrapper, { delayEnter: 100, delayLeave: 500 })
|
||||||
const { focused: buttonFocused } = useFocus(buttonRef)
|
const { focused: buttonFocused } = useFocus(buttonRef)
|
||||||
|
|
||||||
const isMenuOpen = computed(() => menuModel.value || wrapperHovered.value)
|
const isMenuOpen = computed(() => menuModel.value || wrapperHovered.value)
|
||||||
@@ -39,7 +39,7 @@ watch([wrapperHovered, buttonFocused], (value) => {
|
|||||||
<div ref="wrapper" class="wrapper appear-from-top">
|
<div ref="wrapper" class="wrapper appear-from-top">
|
||||||
<PopoverRoot :open="isMenuOpen" @update:open="() => menuModel = true">
|
<PopoverRoot :open="isMenuOpen" @update:open="() => menuModel = true">
|
||||||
<PopoverTrigger as-child>
|
<PopoverTrigger as-child>
|
||||||
<button ref="buttonRef" class="btn-round" :class="{ 'active': isMenuOpen }">
|
<button ref="buttonRef" class="btn-round" :title="t('nav.lang.aria-label')" :class="{ 'active': isMenuOpen }">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M247.15,212.42l-56-112a8,8,0,0,0-14.31,0l-21.71,43.43A88,88,0,0,1,108,126.93,103.65,103.65,0,0,0,135.69,64H160a8,8,0,0,0,0-16H104V32a8,8,0,0,0-16,0V48H32a8,8,0,0,0,0,16h87.63A87.76,87.76,0,0,1,96,116.35a87.74,87.74,0,0,1-19-31,8,8,0,1,0-15.08,5.34A103.63,103.63,0,0,0,84,127a87.55,87.55,0,0,1-52,17,8,8,0,0,0,0,16,103.46,103.46,0,0,0,64-22.08,104.18,104.18,0,0,0,51.44,21.31l-26.6,53.19a8,8,0,0,0,14.31,7.16L148.94,192h70.11l13.79,27.58A8,8,0,0,0,240,224a8,8,0,0,0,7.15-11.58ZM156.94,176,184,121.89,211.05,176Z"></path></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M247.15,212.42l-56-112a8,8,0,0,0-14.31,0l-21.71,43.43A88,88,0,0,1,108,126.93,103.65,103.65,0,0,0,135.69,64H160a8,8,0,0,0,0-16H104V32a8,8,0,0,0-16,0V48H32a8,8,0,0,0,0,16h87.63A87.76,87.76,0,0,1,96,116.35a87.74,87.74,0,0,1-19-31,8,8,0,1,0-15.08,5.34A103.63,103.63,0,0,0,84,127a87.55,87.55,0,0,1-52,17,8,8,0,0,0,0,16,103.46,103.46,0,0,0,64-22.08,104.18,104.18,0,0,0,51.44,21.31l-26.6,53.19a8,8,0,0,0,14.31,7.16L148.94,192h70.11l13.79,27.58A8,8,0,0,0,240,224a8,8,0,0,0,7.15-11.58ZM156.94,176,184,121.89,211.05,176Z"></path></svg>
|
||||||
</button>
|
</button>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { Language } from '@/i18n/ui';
|
import { useUrlSearchParams } from '@vueuse/core';
|
||||||
|
|
||||||
|
import type { Language } from '@/i18n/ui';
|
||||||
import { t } from '@/i18n/store';
|
import { t } from '@/i18n/store';
|
||||||
import { switchLang } from '@/i18n/utils';
|
import { switchLang } from '@/i18n/utils';
|
||||||
|
|
||||||
@@ -12,10 +13,12 @@ defineProps<{
|
|||||||
|
|
||||||
const flagWidth = 18;
|
const flagWidth = 18;
|
||||||
const flagHeight = 18;
|
const flagHeight = 18;
|
||||||
|
|
||||||
|
const { lat, lon, zoom } = useUrlSearchParams<{ lat: number; lon: number; zoom: number }>();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<a :href="switchLang(currentUrl, lang).pathname" :aria-disabled="disabled" :aria-current="disabled ? 'page' : 'false'" :tabindex="disabled ? -1 : 0">
|
<a :href="`${switchLang(currentUrl, lang).pathname}?lat=${lat}&lon=${lon}&zoom=${zoom}`" :aria-disabled="disabled" :aria-current="disabled ? 'page' : 'false'" :tabindex="disabled ? -1 : 0">
|
||||||
<div class="flag">
|
<div class="flag">
|
||||||
<svg v-if="disabled" xmlns="http://www.w3.org/2000/svg" :height="flagHeight" :width="flagWidth" class="valid" viewBox="0 0 256 256"><path d="M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm45.66,85.66-56,56a8,8,0,0,1-11.32,0l-24-24a8,8,0,0,1,11.32-11.32L112,148.69l50.34-50.35a8,8,0,0,1,11.32,11.32Z"></path></svg>
|
<svg v-if="disabled" xmlns="http://www.w3.org/2000/svg" :height="flagHeight" :width="flagWidth" class="valid" viewBox="0 0 256 256"><path d="M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm45.66,85.66-56,56a8,8,0,0,1-11.32,0l-24-24a8,8,0,0,1,11.32-11.32L112,148.69l50.34-50.35a8,8,0,0,1,11.32,11.32Z"></path></svg>
|
||||||
|
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ function switchMenuMode(newMode: MenuMode) {
|
|||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
const mapRef = document.getElementById('world')
|
const mapRef = document.getElementById('world')
|
||||||
|
|
||||||
if (mapRef) {
|
if (mapRef) {
|
||||||
mapRef.addEventListener('contextmenu', handleContextMenu)
|
mapRef.addEventListener('contextmenu', handleContextMenu)
|
||||||
mapRef.addEventListener('mousedown', hideMenu)
|
mapRef.addEventListener('mousedown', hideMenu)
|
||||||
@@ -188,6 +189,11 @@ function hideMarkerModal() {
|
|||||||
function setTitleError(error: Error | null) {
|
function setTitleError(error: Error | null) {
|
||||||
markerTitleInputError.value = error
|
markerTitleInputError.value = error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleCoordsCopy() {
|
||||||
|
markerMenu.value?.dispatchEvent(new CustomEvent('on-copy-coords', { bubbles: true }))
|
||||||
|
hideMenu()
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -196,7 +202,16 @@ function setTitleError(error: Error | null) {
|
|||||||
<menu v-show="shouldBeShown" ref="markerMenu">
|
<menu v-show="shouldBeShown" ref="markerMenu">
|
||||||
<li>
|
<li>
|
||||||
<button @click="switchMenuMode('editing-marker')">
|
<button @click="switchMenuMode('editing-marker')">
|
||||||
{{ t('maps.markers.new') }}
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="#000000" viewBox="0 0 256 256"><path d="M128,16a88.1,88.1,0,0,0-88,88c0,31.4,14.51,64.68,42,96.25a254.19,254.19,0,0,0,41.45,38.3,8,8,0,0,0,9.18,0A254.19,254.19,0,0,0,174,200.25c27.45-31.57,42-64.85,42-96.25A88.1,88.1,0,0,0,128,16Zm32,96H136v24a8,8,0,0,1-16,0V112H96a8,8,0,0,1,0-16h24V72a8,8,0,0,1,16,0V96h24a8,8,0,0,1,0,16Z"></path></svg>
|
||||||
|
|
||||||
|
<span>{{ t('maps.markers.new') }}</span>
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<button @click="handleCoordsCopy">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="#000000" viewBox="0 0 256 256"><path d="M216,32H88a8,8,0,0,0-8,8V80H40a8,8,0,0,0-8,8V216a8,8,0,0,0,8,8H168a8,8,0,0,0,8-8V176h40a8,8,0,0,0,8-8V40A8,8,0,0,0,216,32Zm-8,128H176V88a8,8,0,0,0-8-8H96V48H208Z"></path></svg>
|
||||||
|
|
||||||
|
<span>{{ t('maps.copyCoords') }}</span>
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
</menu>
|
</menu>
|
||||||
@@ -271,14 +286,23 @@ menu {
|
|||||||
|
|
||||||
li {
|
li {
|
||||||
a, button {
|
a, button {
|
||||||
display: block;
|
display: flex;
|
||||||
|
gap: .75ch;
|
||||||
|
align-items: center;
|
||||||
padding: .33rem 1rem;
|
padding: .33rem 1rem;
|
||||||
|
padding-left: .66rem;
|
||||||
font-size: .77em;
|
font-size: .77em;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
&:hover,
|
&:hover,
|
||||||
&:focus-within {
|
&:focus-within {
|
||||||
|
color: var(--blue-700);
|
||||||
background-color: var(--slate-100);
|
background-color: var(--slate-100);
|
||||||
|
|
||||||
|
svg {
|
||||||
|
fill: var(--blue-700);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ const navModel = ref(false);
|
|||||||
|
|
||||||
const wrapper = ref<HTMLDivElement | null>(null)
|
const wrapper = ref<HTMLDivElement | null>(null)
|
||||||
const buttonRef = ref<HTMLButtonElement | null>(null)
|
const buttonRef = ref<HTMLButtonElement | null>(null)
|
||||||
const wrapperHovered = useElementHover(wrapper, { delayEnter: 500, delayLeave: 500 })
|
const wrapperHovered = useElementHover(wrapper, { delayEnter: 100, delayLeave: 500 })
|
||||||
const { focused: buttonFocused } = useFocus(buttonRef)
|
const { focused: buttonFocused } = useFocus(buttonRef)
|
||||||
|
|
||||||
const isNavOpened = computed(() => navModel.value || wrapperHovered.value)
|
const isNavOpened = computed(() => navModel.value || wrapperHovered.value)
|
||||||
@@ -92,7 +92,7 @@ const menus: Menu[] = [
|
|||||||
<div class="wrapper" ref="wrapper">
|
<div class="wrapper" ref="wrapper">
|
||||||
<PopoverRoot :open="isNavOpened" @update:open="() => navModel = true">
|
<PopoverRoot :open="isNavOpened" @update:open="() => navModel = true">
|
||||||
<PopoverTrigger as-child>
|
<PopoverTrigger as-child>
|
||||||
<button ref="buttonRef" class="btn-round" :class="{ 'active': isNavOpened }">
|
<button ref="buttonRef" class="btn-round" :title="t('nav.map.aria-label')" :class="{ 'active': isNavOpened }">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"><rect width="256" height="256" fill="none"/><path d="M228.92,49.69a8,8,0,0,0-6.86-1.45L160.93,63.52,99.58,32.84a8,8,0,0,0-5.52-.6l-64,16A8,8,0,0,0,24,56V200a8,8,0,0,0,9.94,7.76l61.13-15.28,61.35,30.68A8.15,8.15,0,0,0,160,224a8,8,0,0,0,1.94-.24l64-16A8,8,0,0,0,232,200V56A8,8,0,0,0,228.92,49.69ZM96,176a8,8,0,0,0-1.94.24L40,189.75V62.25L95.07,48.48l.93.46Zm120,17.75-55.07,13.77-.93-.46V80a8,8,0,0,0,1.94-.23L216,66.25Z"/></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"><rect width="256" height="256" fill="none"/><path d="M228.92,49.69a8,8,0,0,0-6.86-1.45L160.93,63.52,99.58,32.84a8,8,0,0,0-5.52-.6l-64,16A8,8,0,0,0,24,56V200a8,8,0,0,0,9.94,7.76l61.13-15.28,61.35,30.68A8.15,8.15,0,0,0,160,224a8,8,0,0,0,1.94-.24l64-16A8,8,0,0,0,232,200V56A8,8,0,0,0,228.92,49.69ZM96,176a8,8,0,0,0-1.94.24L40,189.75V62.25L95.07,48.48l.93.46Zm120,17.75-55.07,13.77-.93-.46V80a8,8,0,0,0,1.94-.23L216,66.25Z"/></svg>
|
||||||
</button>
|
</button>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ const props = defineProps<{
|
|||||||
players: PlayerMarker,
|
players: PlayerMarker,
|
||||||
mapKey?: string,
|
mapKey?: string,
|
||||||
searchConfig?: SearchConfig
|
searchConfig?: SearchConfig
|
||||||
|
disableTags?: boolean
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const customMarkersKey = props.mapKey ? `custom-markers-${props.mapKey}` : 'custom-markers'
|
const customMarkersKey = props.mapKey ? `custom-markers-${props.mapKey}` : 'custom-markers'
|
||||||
@@ -209,7 +210,7 @@ function resetAllFields(actionAfter?: "focusAfter") {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<nav ref="searchBarWrapper" class="toolbar appear-from-top" :key="navKey">
|
<nav ref="searchBarWrapper" :aria-label="t('nav.aria-label')" class="toolbar appear-from-top" :key="navKey">
|
||||||
<SearchMapSwitch />
|
<SearchMapSwitch />
|
||||||
|
|
||||||
<div ref="searchBar" class="search-w" :data-focused="shouldBeActive">
|
<div ref="searchBar" class="search-w" :data-focused="shouldBeActive">
|
||||||
@@ -286,6 +287,7 @@ function resetAllFields(actionAfter?: "focusAfter") {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<SearchMarkersTags
|
<SearchMarkersTags
|
||||||
|
v-if="!props.disableTags"
|
||||||
:current-search-mode="currentSearchMode"
|
:current-search-mode="currentSearchMode"
|
||||||
:custom-markers="customMarkersData"
|
:custom-markers="customMarkersData"
|
||||||
:search-config="props.searchConfig"
|
:search-config="props.searchConfig"
|
||||||
|
|||||||
72
src/components/maps/overlay/ToastService.vue
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { isVNode, onMounted } from "vue"
|
||||||
|
import { ToastLifetime, useToast, type Toast } from './useToast.ts'
|
||||||
|
import { ToastDescription, ToastProvider, ToastRoot, ToastTitle, ToastViewport } from 'radix-vue'
|
||||||
|
|
||||||
|
const { toast, toasts } = useToast()
|
||||||
|
import { t } from '@/i18n/store';
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
window.addEventListener('on-toast-push', (e) => handleInserted(e as CustomEvent))
|
||||||
|
})
|
||||||
|
|
||||||
|
function handleInserted(e: CustomEvent) {
|
||||||
|
if ( e.detail.preset) {
|
||||||
|
switch (e.detail.preset) {
|
||||||
|
case "copied-coords":
|
||||||
|
default:
|
||||||
|
toast({
|
||||||
|
description: t('toast.copyCoords.description'),
|
||||||
|
duration: ToastLifetime.SHORT,
|
||||||
|
variant: 'success'
|
||||||
|
})
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const toastData = e.detail.toast as Toast
|
||||||
|
|
||||||
|
toast(toastData)
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleUpdate(id: string) {
|
||||||
|
toasts.value.forEach((t, i) => {
|
||||||
|
if (t.id === id) {
|
||||||
|
t.open = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ToastProvider>
|
||||||
|
<ToastRoot
|
||||||
|
class="toast-root"
|
||||||
|
:class="toast.variant ? `toast-${toast.variant}` : ''"
|
||||||
|
v-for="toast in toasts"
|
||||||
|
:key="toast.id"
|
||||||
|
v-bind="toast"
|
||||||
|
@update:open="handleUpdate(toast.id)"
|
||||||
|
>
|
||||||
|
<div v-if="toast.variant === 'success'" class="toast-icon">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="#000000" viewBox="0 0 256 256"><path d="M232.49,80.49l-128,128a12,12,0,0,1-17,0l-56-56a12,12,0,1,1,17-17L96,183,215.51,63.51a12,12,0,0,1,17,17Z"></path></svg>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ToastTitle class="toast-title" v-if="toast.title">
|
||||||
|
{{ toast.title }}
|
||||||
|
</ToastTitle>
|
||||||
|
<template v-if="toast.description">
|
||||||
|
<ToastDescription class="toast-description" v-if="isVNode(toast.description)">
|
||||||
|
<component :is="toast.description" />
|
||||||
|
</ToastDescription>
|
||||||
|
<ToastDescription v-else class="toast-description">
|
||||||
|
{{ toast.description }}
|
||||||
|
</ToastDescription>
|
||||||
|
</template>
|
||||||
|
</ToastRoot>
|
||||||
|
|
||||||
|
<ToastViewport class="toast-viewport"/>
|
||||||
|
</ToastProvider>
|
||||||
|
</template>
|
||||||
176
src/components/maps/overlay/useToast.ts
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
import { computed, ref } from "vue"
|
||||||
|
import type { Component, VNode } from "vue"
|
||||||
|
import type { ToastRootProps } from "radix-vue"
|
||||||
|
|
||||||
|
const TOAST_LIMIT = 3
|
||||||
|
const TOAST_REMOVE_DELAY = 1000000
|
||||||
|
|
||||||
|
export interface ToastProps extends ToastRootProps {
|
||||||
|
onOpenChange?: ((value: boolean) => void) | undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum ToastLifetime {
|
||||||
|
SHORT = 2000,
|
||||||
|
MEDIUM = 3500,
|
||||||
|
LONG = 6000,
|
||||||
|
}
|
||||||
|
|
||||||
|
export type StringOrVNode =
|
||||||
|
| string
|
||||||
|
| VNode
|
||||||
|
| (() => VNode)
|
||||||
|
|
||||||
|
type ToasterToast = ToastProps & {
|
||||||
|
id: string
|
||||||
|
title?: string
|
||||||
|
description?: StringOrVNode
|
||||||
|
action?: Component,
|
||||||
|
variant?: "default" | "success" | "destructive"
|
||||||
|
}
|
||||||
|
|
||||||
|
const actionTypes = {
|
||||||
|
ADD_TOAST: "ADD_TOAST",
|
||||||
|
UPDATE_TOAST: "UPDATE_TOAST",
|
||||||
|
DISMISS_TOAST: "DISMISS_TOAST",
|
||||||
|
REMOVE_TOAST: "REMOVE_TOAST",
|
||||||
|
} as const
|
||||||
|
|
||||||
|
let count = 0
|
||||||
|
|
||||||
|
function genId() {
|
||||||
|
count = (count + 1) % Number.MAX_VALUE
|
||||||
|
return count.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
type ActionType = typeof actionTypes
|
||||||
|
|
||||||
|
type Action =
|
||||||
|
| {
|
||||||
|
type: ActionType["ADD_TOAST"]
|
||||||
|
toast: ToasterToast
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: ActionType["UPDATE_TOAST"]
|
||||||
|
toast: Partial<ToasterToast>
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: ActionType["DISMISS_TOAST"]
|
||||||
|
toastId?: ToasterToast["id"]
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: ActionType["REMOVE_TOAST"]
|
||||||
|
toastId?: ToasterToast["id"]
|
||||||
|
}
|
||||||
|
|
||||||
|
interface State {
|
||||||
|
toasts: ToasterToast[]
|
||||||
|
}
|
||||||
|
|
||||||
|
const toastTimeouts = new Map<string, ReturnType<typeof setTimeout>>()
|
||||||
|
|
||||||
|
function addToRemoveQueue(toastId: string) {
|
||||||
|
if (toastTimeouts.has(toastId))
|
||||||
|
return
|
||||||
|
|
||||||
|
const timeout = setTimeout(() => {
|
||||||
|
toastTimeouts.delete(toastId)
|
||||||
|
dispatch({
|
||||||
|
type: actionTypes.REMOVE_TOAST,
|
||||||
|
toastId,
|
||||||
|
})
|
||||||
|
}, TOAST_REMOVE_DELAY)
|
||||||
|
|
||||||
|
toastTimeouts.set(toastId, timeout)
|
||||||
|
}
|
||||||
|
|
||||||
|
const state = ref<State>({
|
||||||
|
toasts: [],
|
||||||
|
})
|
||||||
|
|
||||||
|
function dispatch(action: Action) {
|
||||||
|
switch (action.type) {
|
||||||
|
case actionTypes.ADD_TOAST:
|
||||||
|
state.value.toasts = [action.toast, ...state.value.toasts].slice(0, TOAST_LIMIT)
|
||||||
|
break
|
||||||
|
|
||||||
|
case actionTypes.UPDATE_TOAST:
|
||||||
|
state.value.toasts = state.value.toasts.map(t =>
|
||||||
|
t.id === action.toast.id ? { ...t, ...action.toast } : t,
|
||||||
|
)
|
||||||
|
break
|
||||||
|
|
||||||
|
case actionTypes.DISMISS_TOAST: {
|
||||||
|
const { toastId } = action
|
||||||
|
|
||||||
|
if (toastId) {
|
||||||
|
addToRemoveQueue(toastId)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
state.value.toasts.forEach((toast) => {
|
||||||
|
addToRemoveQueue(toast.id)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
state.value.toasts = state.value.toasts.map(t =>
|
||||||
|
t.id === toastId || toastId === undefined
|
||||||
|
? {
|
||||||
|
...t,
|
||||||
|
open: false,
|
||||||
|
}
|
||||||
|
: t,
|
||||||
|
)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
case actionTypes.REMOVE_TOAST:
|
||||||
|
if (action.toastId === undefined)
|
||||||
|
state.value.toasts = []
|
||||||
|
else
|
||||||
|
state.value.toasts = state.value.toasts.filter(t => t.id !== action.toastId)
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function useToast() {
|
||||||
|
return {
|
||||||
|
toasts: computed(() => state.value.toasts),
|
||||||
|
toast,
|
||||||
|
dismiss: (toastId?: string) => dispatch({ type: actionTypes.DISMISS_TOAST, toastId }),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Toast = Omit<ToasterToast, "id">
|
||||||
|
|
||||||
|
function toast(props: Toast) {
|
||||||
|
const id = genId()
|
||||||
|
|
||||||
|
const update = (props: ToasterToast) =>
|
||||||
|
dispatch({
|
||||||
|
type: actionTypes.UPDATE_TOAST,
|
||||||
|
toast: { ...props, id },
|
||||||
|
})
|
||||||
|
|
||||||
|
const dismiss = () => dispatch({ type: actionTypes.DISMISS_TOAST, toastId: id })
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: actionTypes.ADD_TOAST,
|
||||||
|
toast: {
|
||||||
|
...props,
|
||||||
|
id,
|
||||||
|
open: true,
|
||||||
|
onOpenChange: (open: boolean) => {
|
||||||
|
if (!open)
|
||||||
|
dismiss()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
dismiss,
|
||||||
|
update,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { toast, useToast }
|
||||||
12
src/env.d.ts
vendored
@@ -1,2 +1,14 @@
|
|||||||
/// <reference path="../.astro/types.d.ts" />
|
/// <reference path="../.astro/types.d.ts" />
|
||||||
/// <reference types="astro/client" />
|
/// <reference types="astro/client" />
|
||||||
|
|
||||||
|
interface ImportMetaEnv {
|
||||||
|
readonly ENABLE_TOOLBAR: boolean
|
||||||
|
readonly ENABLE_TOOLBAR_TAGS: boolean
|
||||||
|
readonly ENABLE_CUSTOM_MARKERS: boolean
|
||||||
|
readonly ENABLE_BREADCRUMB: boolean
|
||||||
|
readonly ENABLE_I18N: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ImportMeta {
|
||||||
|
readonly env: ImportMetaEnv;
|
||||||
|
}
|
||||||
@@ -21,6 +21,9 @@ export const translations: LanguageDict = {
|
|||||||
'capitals': 'Capitales',
|
'capitals': 'Capitales',
|
||||||
'points-of-interest': 'Points d\'intérêt',
|
'points-of-interest': 'Points d\'intérêt',
|
||||||
'personals': 'Personnels',
|
'personals': 'Personnels',
|
||||||
|
'nav.aria-label': 'Navigation principale',
|
||||||
|
'nav.map.aria-label': 'Changer de carte',
|
||||||
|
'nav.lang.aria-label': 'Changer de langue',
|
||||||
'maps.go-to-map': 'Voir la carte détaillée',
|
'maps.go-to-map': 'Voir la carte détaillée',
|
||||||
'maps.go-to-player': 'Aller à la position actuelle des joueurs',
|
'maps.go-to-player': 'Aller à la position actuelle des joueurs',
|
||||||
'maps.seeQuest': 'Voir la quête',
|
'maps.seeQuest': 'Voir la quête',
|
||||||
@@ -28,9 +31,11 @@ export const translations: LanguageDict = {
|
|||||||
'maps.search': 'Rechercher la carte',
|
'maps.search': 'Rechercher la carte',
|
||||||
'maps.searchPlaceholder': "Ville, point d'intérêt…",
|
'maps.searchPlaceholder': "Ville, point d'intérêt…",
|
||||||
'maps.closeSearch': 'Enlever le filtre',
|
'maps.closeSearch': 'Enlever le filtre',
|
||||||
|
'maps.copyCoords': 'Copier les coordonnées',
|
||||||
'maps.markers.new': 'Nouveau marqueur',
|
'maps.markers.new': 'Nouveau marqueur',
|
||||||
'maps.markers.addPersonal': 'Ajouter un marqueur personnel',
|
'maps.markers.addPersonal': 'Ajouter un marqueur personnel',
|
||||||
'maps.markers.newNotice': "Le marqueur sera sauvegardé mais n'apparaîtra que sur votre carte !"
|
'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 !'
|
||||||
},
|
},
|
||||||
'en': {
|
'en': {
|
||||||
'lang.fr': 'Français',
|
'lang.fr': 'Français',
|
||||||
@@ -48,6 +53,9 @@ export const translations: LanguageDict = {
|
|||||||
'capitals': 'Capitals',
|
'capitals': 'Capitals',
|
||||||
'points-of-interest': 'Points of interest',
|
'points-of-interest': 'Points of interest',
|
||||||
'personals': 'Personals',
|
'personals': 'Personals',
|
||||||
|
'nav.aria-label': 'Main navigation',
|
||||||
|
'nav.map.aria-label': 'Switch map',
|
||||||
|
'nav.lang.aria-label': 'Switch language',
|
||||||
'maps.go-to-map': 'See detailed map',
|
'maps.go-to-map': 'See detailed map',
|
||||||
'maps.go-to-player': 'Go to current players position',
|
'maps.go-to-player': 'Go to current players position',
|
||||||
'maps.seeQuest': 'See the quest',
|
'maps.seeQuest': 'See the quest',
|
||||||
@@ -55,8 +63,10 @@ export const translations: LanguageDict = {
|
|||||||
'maps.search': 'Search the map',
|
'maps.search': 'Search the map',
|
||||||
'maps.searchPlaceholder': "City, point of interest…",
|
'maps.searchPlaceholder': "City, point of interest…",
|
||||||
'maps.closeSearch': 'Remove filter',
|
'maps.closeSearch': 'Remove filter',
|
||||||
|
'maps.copyCoords': 'Copy coordinates',
|
||||||
'maps.markers.new': 'New marker',
|
'maps.markers.new': 'New marker',
|
||||||
'maps.markers.addPersonal': 'Add a personal marker',
|
'maps.markers.addPersonal': 'Add a personal marker',
|
||||||
'maps.markers.newNotice': "The marker will be saved but will only appear on your map!"
|
'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 !'
|
||||||
}
|
}
|
||||||
} as const;
|
} as const;
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ const lang = getLangFromUrl(Astro.url) || defaultLang;
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="author" content="Alexis Pelé" />
|
<meta name="author" content="Alexis Pelé" />
|
||||||
{head?.description && <meta name="description" content={head.description} /> }
|
{head?.description && <meta name="description" content={head.description} /> }
|
||||||
<meta name="viewport" content="width=device-width; initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<meta name="generator" content={Astro.generator} />
|
<meta name="generator" content={Astro.generator} />
|
||||||
<title>{title} — Leim Wiki</title>
|
<title>{title} — Leim Wiki</title>
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import MapOverlay from '@/components/maps/MapOverlay.astro'
|
|||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
<Map
|
<Map
|
||||||
|
title='Borélis'
|
||||||
mapKey='aldys-borelis'
|
mapKey='aldys-borelis'
|
||||||
zoomifyKey='fr/borelis'
|
zoomifyKey='fr/borelis'
|
||||||
mapHeight={3710}
|
mapHeight={3710}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ const markers = markersData.data as MapMarker[]
|
|||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
<Map
|
<Map
|
||||||
|
title='Cantane'
|
||||||
mapKey='aldys-cantane'
|
mapKey='aldys-cantane'
|
||||||
zoomifyKey='fr/cantane'
|
zoomifyKey='fr/cantane'
|
||||||
mapHeight={4124}
|
mapHeight={4124}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ const markers = markersData.data as MapMarker[]
|
|||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
<Map
|
<Map
|
||||||
|
title='White Hollow'
|
||||||
mapKey='aldys-cantane-mines'
|
mapKey='aldys-cantane-mines'
|
||||||
zoomifyKey='fr/mines-blanches'
|
zoomifyKey='fr/mines-blanches'
|
||||||
mapHeight={6020}
|
mapHeight={6020}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ const players = playersData as PlayerMarker
|
|||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
<Map
|
<Map
|
||||||
|
title='Bamast'
|
||||||
mapKey='bamast'
|
mapKey='bamast'
|
||||||
zoomifyKey='fr/bamast'
|
zoomifyKey='fr/bamast'
|
||||||
mapHeight={18342}
|
mapHeight={18342}
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ const players = playersData as PlayerMarker
|
|||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
<Map
|
<Map
|
||||||
|
title='Aldys'
|
||||||
mapKey='aldys'
|
mapKey='aldys'
|
||||||
zoomifyKey='en/aldys-nord'
|
zoomifyKey='en/aldys-nord'
|
||||||
mapHeight={21896}
|
mapHeight={21896}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import MapOverlay from '@/components/maps/MapOverlay.astro'
|
|||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
<Map
|
<Map
|
||||||
|
title='Borélis'
|
||||||
mapKey='aldys-borelis'
|
mapKey='aldys-borelis'
|
||||||
zoomifyKey='fr/borelis'
|
zoomifyKey='fr/borelis'
|
||||||
mapHeight={3710}
|
mapHeight={3710}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ const markers = markersData.data as MapMarker[]
|
|||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
<Map
|
<Map
|
||||||
|
title='Cantane'
|
||||||
mapKey='aldys-cantane'
|
mapKey='aldys-cantane'
|
||||||
zoomifyKey='fr/cantane'
|
zoomifyKey='fr/cantane'
|
||||||
mapHeight={4124}
|
mapHeight={4124}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ const markers = markersData.data as MapMarker[]
|
|||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
<Map
|
<Map
|
||||||
|
title='Les Mines Blanches'
|
||||||
mapKey='aldys-cantane-mines'
|
mapKey='aldys-cantane-mines'
|
||||||
zoomifyKey='fr/mines-blanches'
|
zoomifyKey='fr/mines-blanches'
|
||||||
mapHeight={6020}
|
mapHeight={6020}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ const players = playersData as PlayerMarker
|
|||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
<Map
|
<Map
|
||||||
|
title='Bamast'
|
||||||
mapKey='bamast'
|
mapKey='bamast'
|
||||||
zoomifyKey='fr/bamast'
|
zoomifyKey='fr/bamast'
|
||||||
mapHeight={18342}
|
mapHeight={18342}
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ const players = playersData as PlayerMarker
|
|||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
<Map
|
<Map
|
||||||
|
title='Aldys'
|
||||||
mapKey='aldys'
|
mapKey='aldys'
|
||||||
zoomifyKey='fr/aldys-nord'
|
zoomifyKey='fr/aldys-nord'
|
||||||
mapHeight={21896}
|
mapHeight={21896}
|
||||||
|
|||||||
@@ -7,26 +7,90 @@ export type MapCoords = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type MapMarker = {
|
export type MapMarker = {
|
||||||
|
/**
|
||||||
|
* The title of the marker (must be unique).
|
||||||
|
*/
|
||||||
title: string,
|
title: string,
|
||||||
|
/**
|
||||||
|
* The coordinates of the marker.
|
||||||
|
* @example { x: 123, y: 456 }
|
||||||
|
*/
|
||||||
markerCoords: MapCoords,
|
markerCoords: MapCoords,
|
||||||
|
/**
|
||||||
|
* The description of the marker.
|
||||||
|
*/
|
||||||
description?: string,
|
description?: string,
|
||||||
|
/**
|
||||||
|
* The link that can appear on the marker title.
|
||||||
|
* @example https://example.com
|
||||||
|
*/
|
||||||
link?: string,
|
link?: string,
|
||||||
|
/**
|
||||||
|
* The ID of the map the marker is supposed to point to.
|
||||||
|
*
|
||||||
|
* This should be a valid path of the `maps/data` folder.
|
||||||
|
* @example "fr/aldys/borelis"
|
||||||
|
*/
|
||||||
mapId?: string,
|
mapId?: string,
|
||||||
|
/**
|
||||||
|
* The group that the marker belongs to.
|
||||||
|
* @example "capitals"
|
||||||
|
*/
|
||||||
group?: MapMarkerGroup,
|
group?: MapMarkerGroup,
|
||||||
|
/**
|
||||||
|
* The icon that the marker should use.
|
||||||
|
* @example "castle"
|
||||||
|
*/
|
||||||
icon?: MapMarkerIcon,
|
icon?: MapMarkerIcon,
|
||||||
|
/**
|
||||||
|
* Cover image for the marker.
|
||||||
|
* This should be a valid image in the public folder.
|
||||||
|
*
|
||||||
|
* @example "Ambrose.webp"
|
||||||
|
*/
|
||||||
cover?: string,
|
cover?: string,
|
||||||
|
/**
|
||||||
|
* The link to the cover image's source.
|
||||||
|
*/
|
||||||
coverLink?: string,
|
coverLink?: string,
|
||||||
|
/**
|
||||||
|
* The author of the cover image.
|
||||||
|
*/
|
||||||
coverAuthor?: string,
|
coverAuthor?: string,
|
||||||
|
/**
|
||||||
|
* Whether the cover image should be displayed in portrait mode.
|
||||||
|
*/
|
||||||
coverPortrait?: boolean
|
coverPortrait?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A marker that is used to display players' location on the map.
|
||||||
|
*/
|
||||||
export type PlayerMarker = {
|
export type PlayerMarker = {
|
||||||
|
/**
|
||||||
|
* The title of the marker (must be unique).
|
||||||
|
*/
|
||||||
title: string,
|
title: string,
|
||||||
|
/**
|
||||||
|
* The coordinates of the marker.
|
||||||
|
* @example { x: 123, y: 456 }
|
||||||
|
*/
|
||||||
markerCoords: MapCoords,
|
markerCoords: MapCoords,
|
||||||
|
/**
|
||||||
|
* The description of the marker.
|
||||||
|
*/
|
||||||
description?: string,
|
description?: string,
|
||||||
|
/**
|
||||||
|
* The link that can appear on the marker title.
|
||||||
|
* @example https://example.com
|
||||||
|
*/
|
||||||
link?: string,
|
link?: string,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A marker that is used to display a custom location on the map.
|
||||||
|
* These are only used on the client side and are not saved anywhere else.
|
||||||
|
*/
|
||||||
export type CustomMarker = {
|
export type CustomMarker = {
|
||||||
lat: number,
|
lat: number,
|
||||||
lon: number,
|
lon: number,
|
||||||
|
|||||||
@@ -1,33 +1,105 @@
|
|||||||
import type { MapMarker, PlayerMarker } from "./Leaflet"
|
import type { MapMarker, PlayerMarker } from "./Leaflet"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A breadcrumb item.
|
||||||
|
*/
|
||||||
export interface BreadcrumbItem {
|
export interface BreadcrumbItem {
|
||||||
name: string;
|
/**
|
||||||
url: string;
|
* The label of the breadcrumb.
|
||||||
|
*/
|
||||||
|
name: string
|
||||||
|
/**
|
||||||
|
* The URL the breadcrumb points to.
|
||||||
|
*/
|
||||||
|
url: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search configuration options for the map overlay.
|
||||||
|
* This is used to filter the markers on the map.
|
||||||
|
*/
|
||||||
export interface SearchConfig {
|
export interface SearchConfig {
|
||||||
|
/** Whether to disable the quests in the search bar. */
|
||||||
disableQuests?: boolean
|
disableQuests?: boolean
|
||||||
|
/** Whether to disable the capitals in the search bar. */
|
||||||
disableCapitals?: boolean
|
disableCapitals?: boolean
|
||||||
|
/** Whether to disable the landmarks in the search bar. */
|
||||||
disableLandmarks?: boolean
|
disableLandmarks?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MapProps {
|
export interface MapProps {
|
||||||
|
/**
|
||||||
|
* The ID of the map.
|
||||||
|
* This is used to identify the map and store custom markers in the local storage.
|
||||||
|
* @example "fr/aldys/borelis"
|
||||||
|
*/
|
||||||
mapKey: string
|
mapKey: string
|
||||||
|
/**
|
||||||
|
* The ID of the map in the zoomify folder.
|
||||||
|
*/
|
||||||
zoomifyKey: string
|
zoomifyKey: string
|
||||||
|
/**
|
||||||
|
* Height of the map in pixels.
|
||||||
|
* This information is generated by the zoomify tool.
|
||||||
|
* @example 30000
|
||||||
|
*/
|
||||||
mapHeight: number
|
mapHeight: number
|
||||||
|
/**
|
||||||
|
* Width of the map in pixels.
|
||||||
|
* This information is generated by the zoomify tool.
|
||||||
|
* @example 30000
|
||||||
|
*/
|
||||||
mapWidth: number
|
mapWidth: number
|
||||||
|
/**
|
||||||
|
* List of markers to display on the map.
|
||||||
|
*/
|
||||||
markers?: MapMarker[]
|
markers?: MapMarker[]
|
||||||
|
/**
|
||||||
|
* Current player position on the map.
|
||||||
|
*/
|
||||||
players?: PlayerMarker
|
players?: PlayerMarker
|
||||||
|
/**
|
||||||
|
* Main unit of the map.
|
||||||
|
*/
|
||||||
rulerMainUnit?: string
|
rulerMainUnit?: string
|
||||||
|
/**
|
||||||
|
* Ratio used to convert the map units to their relevant unit.
|
||||||
|
*/
|
||||||
rulerDistanceRatio?: number
|
rulerDistanceRatio?: number
|
||||||
|
/**
|
||||||
|
* Whether to show the walking distance in the ruler.
|
||||||
|
*/
|
||||||
rulerHideWalkDistance?: boolean
|
rulerHideWalkDistance?: boolean
|
||||||
|
/**
|
||||||
|
* Default background color of the map.
|
||||||
|
* @exemple "#FFF"
|
||||||
|
*/
|
||||||
backgroundColor?: string
|
backgroundColor?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MapOverlayProps {
|
export interface MapOverlayProps {
|
||||||
|
/**
|
||||||
|
* The ID of the map.
|
||||||
|
* This is used to identify the map and store custom markers in the local storage.
|
||||||
|
* @example "fr/aldys/borelis"
|
||||||
|
*/
|
||||||
mapKey: string
|
mapKey: string
|
||||||
|
/**
|
||||||
|
* List of markers to display on the map.
|
||||||
|
*/
|
||||||
markers?: MapMarker[]
|
markers?: MapMarker[]
|
||||||
|
/**
|
||||||
|
* Current player position on the map.
|
||||||
|
*/
|
||||||
players?: PlayerMarker
|
players?: PlayerMarker
|
||||||
|
/**
|
||||||
|
* Configuration for the search bar.
|
||||||
|
* This is used to filter the markers on the map.
|
||||||
|
* @example { disableQuests: true, disableCapitals: false, disableLandmarks: false }
|
||||||
|
*/
|
||||||
searchConfig?: SearchConfig
|
searchConfig?: SearchConfig
|
||||||
|
/**
|
||||||
|
* List of breadcrumbs to display.
|
||||||
|
*/
|
||||||
breadcrumbs?: BreadcrumbItem[]
|
breadcrumbs?: BreadcrumbItem[]
|
||||||
}
|
}
|
||||||
|
|||||||