From 2bd6e392d44d6573b28fd91e6d95d05093eb5de1 Mon Sep 17 00:00:00 2001 From: KartoffelChips Date: Fri, 25 Oct 2024 18:39:38 +0200 Subject: [PATCH] react rewrite --- .gitignore | 25 ++- .idea/.gitignore | 5 - .idea/vcs.xml | 2 +- .idea/watcherTasks.xml | 23 +-- bun.lockb | Bin 0 -> 97827 bytes css/.gitignore | 5 - css/fonts/crimson_pro.css | 128 ------------- eslint.config.js | 28 +++ index.html | 23 +-- js/Map.js | 167 ----------------- js/SlimeMap.js | 109 ----------- js/locations/index.js | 4 - js/mapBuilder.js | 4 - js/tooltip.js | 171 ------------------ package.json | 32 ++++ .../img}/favicon/apple-touch-icon.png | Bin {img => public/img}/favicon/favicon-48x48.png | Bin {img => public/img}/favicon/favicon.ico | Bin {img => public/img}/favicon/favicon.svg | 0 {img => public/img}/favicon/site.webmanifest | 0 .../img}/favicon/web-app-manifest-192x192.png | Bin .../img}/favicon/web-app-manifest-512x512.png | Bin {img => public/img}/icons/book.svg | 0 {img => public/img}/icons/campground.svg | 0 {img => public/img}/icons/house.svg | 0 {img => public/img}/icons/star.svg | 0 {img => public/img}/icons/village.svg | 0 {img => public/img}/locations/amrita.webp | Bin .../img}/locations/charybdis_cave.webp | Bin .../city_of_the_forgotten_dragon.webp | Bin .../img}/locations/dwargon_gate.webp | Bin {img => public/img}/locations/el_dorado.webp | Bin {img => public/img}/locations/elmine.webp | Bin .../img}/locations/guratol_county.webp | Bin {img => public/img}/locations/ice_palace.webp | Bin {img => public/img}/locations/jia.webp | Bin .../img}/locations/khusha_mountains.webp | Bin {img => public/img}/locations/lake_siss.webp | Bin {img => public/img}/locations/laura.webp | Bin {img => public/img}/locations/londo.webp | Bin {img => public/img}/locations/lune.webp | Bin {img => public/img}/locations/marris.webp | Bin .../img}/locations/migam_domain.webp | Bin .../img}/locations/ogre_village.webp | Bin .../img}/locations/rimuru_city.webp | Bin .../img}/locations/sealed_cave.webp | Bin .../img}/locations/tengu_village.webp | Bin {img => public/img}/map.webp | Bin {img => public/img}/ogImage.png | Bin {img => public/img}/slime.svg | 0 {img => public/img}/twitterImage.png | Bin src/App.scss | 0 src/App.tsx | 15 ++ .../cormant_garamont.css => src/_fonts.css | 20 +- src/_variables.scss | 4 + .../cormorant-garamond-v16-latin-300.woff2 | Bin ...rmorant-garamond-v16-latin-300italic.woff2 | Bin .../cormorant-garamond-v16-latin-500.woff2 | Bin ...rmorant-garamond-v16-latin-500italic.woff2 | Bin .../cormorant-garamond-v16-latin-600.woff2 | Bin ...rmorant-garamond-v16-latin-600italic.woff2 | Bin .../cormorant-garamond-v16-latin-700.woff2 | Bin ...rmorant-garamond-v16-latin-700italic.woff2 | Bin .../cormorant-garamond-v16-latin-italic.woff2 | Bin ...cormorant-garamond-v16-latin-regular.woff2 | Bin .../crimson-pro-v24-latin-200.woff2 | Bin .../crimson-pro-v24-latin-200italic.woff2 | Bin .../crimson-pro-v24-latin-300.woff2 | Bin .../crimson-pro-v24-latin-300italic.woff2 | Bin .../crimson-pro-v24-latin-500.woff2 | Bin .../crimson-pro-v24-latin-500italic.woff2 | Bin .../crimson-pro-v24-latin-600.woff2 | Bin .../crimson-pro-v24-latin-600italic.woff2 | Bin .../crimson-pro-v24-latin-700.woff2 | Bin .../crimson-pro-v24-latin-700italic.woff2 | Bin .../crimson-pro-v24-latin-800.woff2 | Bin .../crimson-pro-v24-latin-800italic.woff2 | Bin .../crimson-pro-v24-latin-900.woff2 | Bin .../crimson-pro-v24-latin-900italic.woff2 | Bin .../crimson-pro-v24-latin-italic.woff2 | Bin .../crimson-pro-v24-latin-regular.woff2 | Bin src/components/icons/CapitalIcon.tsx | 13 ++ src/components/icons/DescriptionIcon.tsx | 12 ++ src/components/icons/PopulationIcon.tsx | 13 ++ src/components/icons/RulerIcon.tsx | 13 ++ src/components/layout/Legend.scss | 24 +++ src/components/layout/Legend.tsx | 13 ++ src/components/map/Map.tsx | 118 ++++++++++++ src/components/map/locations/Capital.tsx | 39 ++++ src/components/map/locations/City.tsx | 51 ++++++ src/components/map/locations/Poi.tsx | 50 +++++ .../map/tooltips/MapLocationTooltip.tsx | 38 ++++ .../map/tooltips/MapRegionTooltip.tsx | 55 ++++++ .../capitals.js => src/data/capitals.ts | 25 +-- js/locations/cities.js => src/data/cities.ts | 25 +-- .../nations.js => src/data/nations.ts | 20 +- js/locations/pois.js => src/data/pois.ts | 25 +-- css/styles.scss => src/index.scss | 55 +++--- src/main.tsx | 10 + src/pages/Main.tsx | 23 +++ src/types/MapLocation.ts | 19 ++ src/types/MapRegion.ts | 18 ++ src/vite-env.d.ts | 1 + tsconfig.app.json | 25 +++ tsconfig.json | 7 + tsconfig.node.json | 23 +++ vite.config.ts | 7 + 107 files changed, 736 insertions(+), 751 deletions(-) delete mode 100644 .idea/.gitignore create mode 100755 bun.lockb delete mode 100644 css/.gitignore delete mode 100644 css/fonts/crimson_pro.css create mode 100644 eslint.config.js delete mode 100644 js/Map.js delete mode 100644 js/SlimeMap.js delete mode 100644 js/locations/index.js delete mode 100644 js/mapBuilder.js delete mode 100644 js/tooltip.js create mode 100644 package.json rename {img => public/img}/favicon/apple-touch-icon.png (100%) rename {img => public/img}/favicon/favicon-48x48.png (100%) rename {img => public/img}/favicon/favicon.ico (100%) rename {img => public/img}/favicon/favicon.svg (100%) rename {img => public/img}/favicon/site.webmanifest (100%) rename {img => public/img}/favicon/web-app-manifest-192x192.png (100%) rename {img => public/img}/favicon/web-app-manifest-512x512.png (100%) rename {img => public/img}/icons/book.svg (100%) rename {img => public/img}/icons/campground.svg (100%) rename {img => public/img}/icons/house.svg (100%) rename {img => public/img}/icons/star.svg (100%) rename {img => public/img}/icons/village.svg (100%) rename {img => public/img}/locations/amrita.webp (100%) rename {img => public/img}/locations/charybdis_cave.webp (100%) rename {img => public/img}/locations/city_of_the_forgotten_dragon.webp (100%) rename {img => public/img}/locations/dwargon_gate.webp (100%) rename {img => public/img}/locations/el_dorado.webp (100%) rename {img => public/img}/locations/elmine.webp (100%) rename {img => public/img}/locations/guratol_county.webp (100%) rename {img => public/img}/locations/ice_palace.webp (100%) rename {img => public/img}/locations/jia.webp (100%) rename {img => public/img}/locations/khusha_mountains.webp (100%) rename {img => public/img}/locations/lake_siss.webp (100%) rename {img => public/img}/locations/laura.webp (100%) rename {img => public/img}/locations/londo.webp (100%) rename {img => public/img}/locations/lune.webp (100%) rename {img => public/img}/locations/marris.webp (100%) rename {img => public/img}/locations/migam_domain.webp (100%) rename {img => public/img}/locations/ogre_village.webp (100%) rename {img => public/img}/locations/rimuru_city.webp (100%) rename {img => public/img}/locations/sealed_cave.webp (100%) rename {img => public/img}/locations/tengu_village.webp (100%) rename {img => public/img}/map.webp (100%) rename {img => public/img}/ogImage.png (100%) rename {img => public/img}/slime.svg (100%) rename {img => public/img}/twitterImage.png (100%) create mode 100644 src/App.scss create mode 100644 src/App.tsx rename css/fonts/cormant_garamont.css => src/_fonts.css (63%) create mode 100644 src/_variables.scss rename {fonts => src/assets/fonts}/cormant_garamond/cormorant-garamond-v16-latin-300.woff2 (100%) rename {fonts => src/assets/fonts}/cormant_garamond/cormorant-garamond-v16-latin-300italic.woff2 (100%) rename {fonts => src/assets/fonts}/cormant_garamond/cormorant-garamond-v16-latin-500.woff2 (100%) rename {fonts => src/assets/fonts}/cormant_garamond/cormorant-garamond-v16-latin-500italic.woff2 (100%) rename {fonts => src/assets/fonts}/cormant_garamond/cormorant-garamond-v16-latin-600.woff2 (100%) rename {fonts => src/assets/fonts}/cormant_garamond/cormorant-garamond-v16-latin-600italic.woff2 (100%) rename {fonts => src/assets/fonts}/cormant_garamond/cormorant-garamond-v16-latin-700.woff2 (100%) rename {fonts => src/assets/fonts}/cormant_garamond/cormorant-garamond-v16-latin-700italic.woff2 (100%) rename {fonts => src/assets/fonts}/cormant_garamond/cormorant-garamond-v16-latin-italic.woff2 (100%) rename {fonts => src/assets/fonts}/cormant_garamond/cormorant-garamond-v16-latin-regular.woff2 (100%) rename {fonts => src/assets/fonts}/crimson_pro/crimson-pro-v24-latin-200.woff2 (100%) rename {fonts => src/assets/fonts}/crimson_pro/crimson-pro-v24-latin-200italic.woff2 (100%) rename {fonts => src/assets/fonts}/crimson_pro/crimson-pro-v24-latin-300.woff2 (100%) rename {fonts => src/assets/fonts}/crimson_pro/crimson-pro-v24-latin-300italic.woff2 (100%) rename {fonts => src/assets/fonts}/crimson_pro/crimson-pro-v24-latin-500.woff2 (100%) rename {fonts => src/assets/fonts}/crimson_pro/crimson-pro-v24-latin-500italic.woff2 (100%) rename {fonts => src/assets/fonts}/crimson_pro/crimson-pro-v24-latin-600.woff2 (100%) rename {fonts => src/assets/fonts}/crimson_pro/crimson-pro-v24-latin-600italic.woff2 (100%) rename {fonts => src/assets/fonts}/crimson_pro/crimson-pro-v24-latin-700.woff2 (100%) rename {fonts => src/assets/fonts}/crimson_pro/crimson-pro-v24-latin-700italic.woff2 (100%) rename {fonts => src/assets/fonts}/crimson_pro/crimson-pro-v24-latin-800.woff2 (100%) rename {fonts => src/assets/fonts}/crimson_pro/crimson-pro-v24-latin-800italic.woff2 (100%) rename {fonts => src/assets/fonts}/crimson_pro/crimson-pro-v24-latin-900.woff2 (100%) rename {fonts => src/assets/fonts}/crimson_pro/crimson-pro-v24-latin-900italic.woff2 (100%) rename {fonts => src/assets/fonts}/crimson_pro/crimson-pro-v24-latin-italic.woff2 (100%) rename {fonts => src/assets/fonts}/crimson_pro/crimson-pro-v24-latin-regular.woff2 (100%) create mode 100644 src/components/icons/CapitalIcon.tsx create mode 100644 src/components/icons/DescriptionIcon.tsx create mode 100644 src/components/icons/PopulationIcon.tsx create mode 100644 src/components/icons/RulerIcon.tsx create mode 100644 src/components/layout/Legend.scss create mode 100644 src/components/layout/Legend.tsx create mode 100644 src/components/map/Map.tsx create mode 100644 src/components/map/locations/Capital.tsx create mode 100644 src/components/map/locations/City.tsx create mode 100644 src/components/map/locations/Poi.tsx create mode 100644 src/components/map/tooltips/MapLocationTooltip.tsx create mode 100644 src/components/map/tooltips/MapRegionTooltip.tsx rename js/locations/capitals.js => src/data/capitals.ts (93%) rename js/locations/cities.js => src/data/cities.ts (86%) rename js/locations/nations.js => src/data/nations.ts (99%) rename js/locations/pois.js => src/data/pois.ts (90%) rename css/styles.scss => src/index.scss (77%) create mode 100644 src/main.tsx create mode 100644 src/pages/Main.tsx create mode 100644 src/types/MapLocation.ts create mode 100644 src/types/MapRegion.ts create mode 100644 src/vite-env.d.ts create mode 100644 tsconfig.app.json create mode 100644 tsconfig.json create mode 100644 tsconfig.node.json create mode 100644 vite.config.ts diff --git a/.gitignore b/.gitignore index ab477e2..a547bf3 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,24 @@ -htmlmap.html \ No newline at end of file +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index b58b603..0000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml -# Editor-based HTTP Client requests -/httpRequests/ diff --git a/.idea/vcs.xml b/.idea/vcs.xml index 94a25f7..35eb1dd 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -1,6 +1,6 @@ - + \ No newline at end of file diff --git a/.idea/watcherTasks.xml b/.idea/watcherTasks.xml index c70980f..fb0d65a 100644 --- a/.idea/watcherTasks.xml +++ b/.idea/watcherTasks.xml @@ -1,25 +1,4 @@ - - - - - + \ No newline at end of file diff --git a/bun.lockb b/bun.lockb new file mode 100755 index 0000000000000000000000000000000000000000..de964e2760ffd405f57fb334f30105aa4352ca06 GIT binary patch literal 97827 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 + - - + + + Interactive Tensei Shitara Slime Datta Ken Map @@ -29,21 +30,9 @@ - - - - - -
- -
-

Interactive Map of the Magic Continent

-
-
- - - +
+ \ No newline at end of file diff --git a/js/Map.js b/js/Map.js deleted file mode 100644 index ee802c3..0000000 --- a/js/Map.js +++ /dev/null @@ -1,167 +0,0 @@ -/** - * Map class for creating interactive maps with SVG - */ -export default class Map { - /** - * @param {string} svgId - The ID of the SVG element - * @param {string} mapImage - The URL of the map image - */ - constructor(svgId, mapImage) { - this.svg = document.getElementById(svgId); - this.svg.innerHTML = ""; - - this.g = document.createElementNS("http://www.w3.org/2000/svg", "g"); - this.svg.appendChild(this.g); - - this.g.innerHTML += ``; - - this.isDragging = false; - this.startX = 0; - this.startY = 0; - this.initialTranslateX = 0; - this.initialTranslateY = 0; - this.initialScale = 1; - - this.dragSensitivity = 2; - this.maxScale = 6; - this.minScale = 1; - - this.initializeEvents(); - } - - initializeEvents() { - this.svg.addEventListener("mousedown", (e) => this.onMouseDown(e)); - this.svg.addEventListener("mousemove", (e) => this.onMouseMove(e)); - this.svg.addEventListener("mouseup", () => this.onMouseUp()); - this.svg.addEventListener("mouseleave", () => this.onMouseUp()); - this.svg.addEventListener("wheel", (e) => this.onWheel(e)); - } - - /** - * Set the zoom level - * @param {number} zoomLevel - The zoom level - */ - setZoomLevel(zoomLevel) { - const svgRect = this.svg.getBoundingClientRect(); - const centerX = svgRect.width / 2; - const centerY = svgRect.height / 2; - - const [translate, scale] = this.parseTransform(this.g.getAttribute("transform") || "translate(0,0) scale(1)"); - const newScale = Math.min(Math.max(zoomLevel, this.minScale), this.maxScale); - - const svgPoint = this.svg.createSVGPoint(); - svgPoint.x = centerX; - svgPoint.y = centerY; - const centerPoint = svgPoint.matrixTransform(this.svg.getScreenCTM().inverse()); - - const newTranslateX = translate[0] - (centerPoint.x - translate[0]) * (newScale / scale - 1); - const newTranslateY = translate[1] - (centerPoint.y - translate[1]) * (newScale / scale - 1); - - this.g.setAttribute("transform", `translate(${newTranslateX}, ${newTranslateY}) scale(${newScale})`); - } - - /** - * Zoom in - * @param factor - The zoom factor - */ - zoomIn(factor = 1.1) { - const [translate, scale] = this.parseTransform(this.g.getAttribute("transform") || "translate(0,0) scale(1)"); - const newScale = Math.min(scale * factor, this.maxScale); - this.setZoomLevel(newScale); - } - - /** - * Zoom out - * @param {number} factor - The zoom factor - */ - zoomOut(factor = 0.9) { - const [translate, scale] = this.parseTransform(this.g.getAttribute("transform") || "translate(0,0) scale(1)"); - const newScale = Math.max(scale * factor, this.minScale); - this.setZoomLevel(newScale); - } - - onMouseDown(e) { - if (!this.g.getAttribute("transform") || this.g.getAttribute("transform").includes("scale(1)")) return; - this.isDragging = true; - this.startX = e.clientX; - this.startY = e.clientY; - - const [translate, scale] = this.parseTransform(this.g.getAttribute("transform") || "translate(0,0) scale(1)"); - this.initialTranslateX = translate[0]; - this.initialTranslateY = translate[1]; - this.initialScale = scale; - } - - onMouseMove(e) { - if (!this.isDragging) return; - - const svgRect = this.svg.getBoundingClientRect(); - const mapRect = this.g.getBBox(); - - const deltaX = (e.clientX - this.startX) * this.dragSensitivity; - const deltaY = (e.clientY - this.startY) * this.dragSensitivity; - - let newTranslateX = this.initialTranslateX + deltaX; - let newTranslateY = this.initialTranslateY + deltaY; - - const scale = this.initialScale; - const mapWidth = mapRect.width * scale; - const mapHeight = mapRect.height * scale; - const svgWidth = svgRect.width; - const svgHeight = svgRect.height; - - const minTranslateX = Math.min(0, svgWidth - mapWidth); - const minTranslateY = Math.min(0, svgHeight - mapHeight); - - const maxTranslateX = 0; - const maxTranslateY = 0; - - newTranslateX = Math.max(minTranslateX, Math.min(maxTranslateX, newTranslateX)); - newTranslateY = Math.max(minTranslateY, Math.min(maxTranslateY, newTranslateY)); - - this.g.setAttribute("transform", `translate(${newTranslateX}, ${newTranslateY}) scale(${scale})`); - } - - onMouseUp() { - this.isDragging = false; - } - - onWheel(e) { - e.preventDefault(); - - const [translate, scale] = this.parseTransform(this.g.getAttribute("transform") || "translate(0,0) scale(1)"); - const newScale = Math.min(Math.max(scale * (e.deltaY > 0 ? 0.9 : 1.1), 1), this.maxScale); - - if (newScale === 1) { - this.g.setAttribute("transform", `translate(0,0) scale(1)`); - } else { - const svgPoint = this.svg.createSVGPoint(); - svgPoint.x = e.clientX; - svgPoint.y = e.clientY; - const mousePoint = svgPoint.matrixTransform(this.svg.getScreenCTM().inverse()); - - const newTranslateX = translate[0] - (mousePoint.x - translate[0]) * (newScale / scale - 1); - const newTranslateY = translate[1] - (mousePoint.y - translate[1]) * (newScale / scale - 1); - this.g.setAttribute("transform", `translate(${newTranslateX}, ${newTranslateY}) scale(${newScale})`); - } - } - - parseTransform(transform) { - const translateMatch = transform.match(/translate\(([^)]+)\)/); - const scaleMatch = transform.match(/scale\(([^)]+)\)/); - const translate = translateMatch ? translateMatch[1].split(",").map(Number) : [0, 0]; - const scale = scaleMatch ? parseFloat(scaleMatch[1]) : 1; - return [translate, scale]; - } - - addPolygon(points, attributes = {}) { - const polygon = document.createElementNS("http://www.w3.org/2000/svg", "polygon"); - polygon.setAttribute("points", points); - Object.entries(attributes).forEach(([key, value]) => polygon.setAttribute(key, value)); - this.g.appendChild(polygon); - } - - addSVGElement(svgString) { - this.g.innerHTML += svgString; - } -} \ No newline at end of file diff --git a/js/SlimeMap.js b/js/SlimeMap.js deleted file mode 100644 index b15023d..0000000 --- a/js/SlimeMap.js +++ /dev/null @@ -1,109 +0,0 @@ -import Map from "./Map.js"; - -export default class SlimeMap extends Map { - /** - * @param {string} svgId - The ID of the SVG element - * @param {string} mapImage - The URL of the map image - * @param nations - * @param capitals - * @param pois - * @param cities - */ - constructor(svgId, mapImage, nations, capitals, pois, cities) { - super(svgId, mapImage); - this.nations = nations; - this.capitals = capitals; - this.pois = pois; - this.cities = cities; - this.addNations(); - this.addCapitals(); - this.addPOIs(); - this.addCities(); - } - - addNations() { - for (let nation of this.nations) { - this.addPolygon(nation.points, { - class: "region", - fill: "transparent", - stroke: "transparent", - "stroke-width": "4", - "data-name": nation.name, - "data-ruler": nation.ruler, - "data-capital": nation.capital, - "data-population": nation.population, - "data-description": nation.description, - "data-url": nation.url ?? "" - }); - } - } - - addCapitals() { - for (let capital of this.capitals) { - const svgString = ` - - - - - - - `; - this.addSVGElement(svgString); - } - } - - addCities() { - for (let city of this.cities) { - const svgString = ` - - - - - - - `; - this.addSVGElement(svgString); - } - } - - addPOIs() { - for (let poi of this.pois) { - const svgString = ` - - - - - - - `; - this.addSVGElement(svgString); - } - } -} \ No newline at end of file diff --git a/js/locations/index.js b/js/locations/index.js deleted file mode 100644 index 99ae579..0000000 --- a/js/locations/index.js +++ /dev/null @@ -1,4 +0,0 @@ -export * from "./pois.js"; -export * from "./nations.js"; -export * from "./capitals.js"; -export * from "./cities.js"; \ No newline at end of file diff --git a/js/mapBuilder.js b/js/mapBuilder.js deleted file mode 100644 index ba0eba9..0000000 --- a/js/mapBuilder.js +++ /dev/null @@ -1,4 +0,0 @@ -import {pois, capitals, nations, cities} from "./locations"; -import SlimeMap from "./SlimeMap.js"; - -const slimeMap = new SlimeMap("map", "img/map.webp", nations, capitals, pois, cities); \ No newline at end of file diff --git a/js/tooltip.js b/js/tooltip.js deleted file mode 100644 index 35708ba..0000000 --- a/js/tooltip.js +++ /dev/null @@ -1,171 +0,0 @@ -document.addEventListener("DOMContentLoaded", () => { - const tooltip = document.getElementById('tooltip'); - let isControlHeld = false; - let mouseOnTooltipableArea = false; - let currentTooltipContent = ''; - - setInterval(() => { - console.log("Control is held:", isControlHeld); - }, 500) - - const showTooltip = (content, e) => { - mouseOnTooltipableArea = true; - currentTooltipContent = content; - // console.log("Set currentTooltipContent to", currentTooltipContent); - if (isControlHeld) return; - tooltip.innerHTML = content; - tooltip.style.display = 'flex'; - positionTooltip(e); - }; - - const hideTooltip = () => { - mouseOnTooltipableArea = false; - currentTooltipContent = ''; - // console.log("Set currentTooltipContent to", currentTooltipContent); - if (isControlHeld) return; - tooltip.style.display = 'none'; - }; - - const positionTooltip = (e) => { - if (isControlHeld) return; - - tooltip.innerHTML = currentTooltipContent; - if (currentTooltipContent) tooltip.style.display = 'flex'; - - const tooltipWidth = tooltip.offsetWidth; - const tooltipHeight = tooltip.offsetHeight; - const screenWidth = window.innerWidth; - const screenHeight = window.innerHeight; - - // Default positioning (to the right and below the cursor) - let tooltipX = e.pageX + 10; - let tooltipY = e.pageY + 10; - - // Horizontal Overflow (Check if tooltip overflows on the right side) - if (tooltipX + tooltipWidth > screenWidth) { - tooltipX = e.pageX - tooltipWidth - 10; - } - - // Vertical Overflow (Check if tooltip overflows at the bottom) - if (tooltipY + tooltipHeight > screenHeight) { - tooltipY = e.pageY - tooltipHeight - 10; - } - - tooltip.style.left = `${tooltipX}px`; - tooltip.style.top = `${tooltipY}px`; - }; - - const attachTooltipHandlers = (elements, getContentCallback) => { - elements.forEach(element => { - element.addEventListener('mouseover', (e) => { - const content = getContentCallback(element); - showTooltip(content, e); - }); - - element.addEventListener('mousemove', positionTooltip); - - element.addEventListener('mouseout', hideTooltip); - }); - }; - - document.addEventListener('keydown', (e) => { - if (e.ctrlKey || e.metaKey) isControlHeld = true; - }); - - document.addEventListener('keyup', (e) => { - if (!e.ctrlKey && !e.metaKey) { - isControlHeld = false; - // if (!mouseOnTooltipableArea) hideTooltip(); - } - }); - - document.addEventListener('mousemove', (e) => { - // console.log("IsControlHeld", isControlHeld); - // console.log("MouseOnTooltipableArea", mouseOnTooltipableArea); - if (!isControlHeld && !mouseOnTooltipableArea) { - hideTooltip(); - } - }); - - const locations = document.querySelectorAll('.location'); - attachTooltipHandlers(locations, getLocationTooltip); - - const regions = document.querySelectorAll('.region'); - attachTooltipHandlers(regions, getRegionTooltip); -}); - -/** - * @typedef {Object} RegionData - * @property {string} name - * @property {string} capital - * @property {string} description - * @property {string} ruler - * @property {string} population - */ - -/** - * Get region tooltip - * @param {Element} element - * @return {string} - */ -function getRegionTooltip(element) { - const regionName = element.getAttribute('data-name'); - const regionPopulation = element.getAttribute('data-population'); - const regionCapital = element.getAttribute('data-capital'); - const regionDescription = element.getAttribute('data-description'); - const regionRuler = element.getAttribute('data-ruler'); - const url = element.getAttribute('data-url'); - - // let data = url ? `

${regionName}

` : `

${regionName}

`; - let data = `

${regionName}

`; - - data += `
`; - - if (regionCapital) data += ` -
- - ${regionCapital} -
- `; - if (regionPopulation) data += ` -
- - ${regionPopulation} -
- `; - if (regionRuler) data += ` -
- - ${regionRuler} -
- `; - if (regionDescription) data += ` -
- - ${regionDescription} -
- `; - - if (url) data += `Wiki`; - - return data; -} - -function getLocationTooltip(location) { - const locationName = location.getAttribute('data-name'); - const locationDescription = location.getAttribute('data-description'); - const imageUrl = location.getAttribute('data-image-url'); - const imageName = location.getAttribute('data-image-name'); - const imageCredit = location.getAttribute('data-image-credit'); - const url = location.getAttribute('data-url'); - - // let locationString = url ? `

${locationName}

` : `

${locationName}

`; - let locationString = `

${locationName}

`; - - if (locationDescription || imageUrl) locationString += `
`; - if (locationDescription) locationString += `${locationDescription}`; - if (imageUrl) locationString += `${imageName ?? locationName}`; - if (url) locationString += `wiki iconWiki`; - - return locationString; -} \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..8d97e04 --- /dev/null +++ b/package.json @@ -0,0 +1,32 @@ +{ + "name": "slime_map", + "private": true, + "version": "0.0.0", + "type": "module", + "license": "GNU General Public License v3.0", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "lint": "eslint .", + "preview": "vite preview" + }, + "dependencies": { + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-router-dom": "^6.27.0" + }, + "devDependencies": { + "@eslint/js": "^9.13.0", + "@types/react": "^18.3.11", + "@types/react-dom": "^18.3.1", + "@vitejs/plugin-react": "^4.3.3", + "eslint": "^9.13.0", + "eslint-plugin-react-hooks": "^5.0.0", + "eslint-plugin-react-refresh": "^0.4.13", + "globals": "^15.11.0", + "typescript": "~5.6.2", + "typescript-eslint": "^8.10.0", + "vite": "^5.4.9", + "sass": "^1.80.4" + } +} diff --git a/img/favicon/apple-touch-icon.png b/public/img/favicon/apple-touch-icon.png similarity index 100% rename from img/favicon/apple-touch-icon.png rename to public/img/favicon/apple-touch-icon.png diff --git a/img/favicon/favicon-48x48.png b/public/img/favicon/favicon-48x48.png similarity index 100% rename from img/favicon/favicon-48x48.png rename to public/img/favicon/favicon-48x48.png diff --git a/img/favicon/favicon.ico b/public/img/favicon/favicon.ico similarity index 100% rename from img/favicon/favicon.ico rename to public/img/favicon/favicon.ico diff --git a/img/favicon/favicon.svg b/public/img/favicon/favicon.svg similarity index 100% rename from img/favicon/favicon.svg rename to public/img/favicon/favicon.svg diff --git a/img/favicon/site.webmanifest b/public/img/favicon/site.webmanifest similarity index 100% rename from img/favicon/site.webmanifest rename to public/img/favicon/site.webmanifest diff --git a/img/favicon/web-app-manifest-192x192.png b/public/img/favicon/web-app-manifest-192x192.png similarity index 100% rename from img/favicon/web-app-manifest-192x192.png rename to public/img/favicon/web-app-manifest-192x192.png diff --git a/img/favicon/web-app-manifest-512x512.png b/public/img/favicon/web-app-manifest-512x512.png similarity index 100% rename from img/favicon/web-app-manifest-512x512.png rename to public/img/favicon/web-app-manifest-512x512.png diff --git a/img/icons/book.svg b/public/img/icons/book.svg similarity index 100% rename from img/icons/book.svg rename to public/img/icons/book.svg diff --git a/img/icons/campground.svg b/public/img/icons/campground.svg similarity index 100% rename from img/icons/campground.svg rename to public/img/icons/campground.svg diff --git a/img/icons/house.svg b/public/img/icons/house.svg similarity index 100% rename from img/icons/house.svg rename to public/img/icons/house.svg diff --git a/img/icons/star.svg b/public/img/icons/star.svg similarity index 100% rename from img/icons/star.svg rename to public/img/icons/star.svg diff --git a/img/icons/village.svg b/public/img/icons/village.svg similarity index 100% rename from img/icons/village.svg rename to public/img/icons/village.svg diff --git a/img/locations/amrita.webp b/public/img/locations/amrita.webp similarity index 100% rename from img/locations/amrita.webp rename to public/img/locations/amrita.webp diff --git a/img/locations/charybdis_cave.webp b/public/img/locations/charybdis_cave.webp similarity index 100% rename from img/locations/charybdis_cave.webp rename to public/img/locations/charybdis_cave.webp diff --git a/img/locations/city_of_the_forgotten_dragon.webp b/public/img/locations/city_of_the_forgotten_dragon.webp similarity index 100% rename from img/locations/city_of_the_forgotten_dragon.webp rename to public/img/locations/city_of_the_forgotten_dragon.webp diff --git a/img/locations/dwargon_gate.webp b/public/img/locations/dwargon_gate.webp similarity index 100% rename from img/locations/dwargon_gate.webp rename to public/img/locations/dwargon_gate.webp diff --git a/img/locations/el_dorado.webp b/public/img/locations/el_dorado.webp similarity index 100% rename from img/locations/el_dorado.webp rename to public/img/locations/el_dorado.webp diff --git a/img/locations/elmine.webp b/public/img/locations/elmine.webp similarity index 100% rename from img/locations/elmine.webp rename to public/img/locations/elmine.webp diff --git a/img/locations/guratol_county.webp b/public/img/locations/guratol_county.webp similarity index 100% rename from img/locations/guratol_county.webp rename to public/img/locations/guratol_county.webp diff --git a/img/locations/ice_palace.webp b/public/img/locations/ice_palace.webp similarity index 100% rename from img/locations/ice_palace.webp rename to public/img/locations/ice_palace.webp diff --git a/img/locations/jia.webp b/public/img/locations/jia.webp similarity index 100% rename from img/locations/jia.webp rename to public/img/locations/jia.webp diff --git a/img/locations/khusha_mountains.webp b/public/img/locations/khusha_mountains.webp similarity index 100% rename from img/locations/khusha_mountains.webp rename to public/img/locations/khusha_mountains.webp diff --git a/img/locations/lake_siss.webp b/public/img/locations/lake_siss.webp similarity index 100% rename from img/locations/lake_siss.webp rename to public/img/locations/lake_siss.webp diff --git a/img/locations/laura.webp b/public/img/locations/laura.webp similarity index 100% rename from img/locations/laura.webp rename to public/img/locations/laura.webp diff --git a/img/locations/londo.webp b/public/img/locations/londo.webp similarity index 100% rename from img/locations/londo.webp rename to public/img/locations/londo.webp diff --git a/img/locations/lune.webp b/public/img/locations/lune.webp similarity index 100% rename from img/locations/lune.webp rename to public/img/locations/lune.webp diff --git a/img/locations/marris.webp b/public/img/locations/marris.webp similarity index 100% rename from img/locations/marris.webp rename to public/img/locations/marris.webp diff --git a/img/locations/migam_domain.webp b/public/img/locations/migam_domain.webp similarity index 100% rename from img/locations/migam_domain.webp rename to public/img/locations/migam_domain.webp diff --git a/img/locations/ogre_village.webp b/public/img/locations/ogre_village.webp similarity index 100% rename from img/locations/ogre_village.webp rename to public/img/locations/ogre_village.webp diff --git a/img/locations/rimuru_city.webp b/public/img/locations/rimuru_city.webp similarity index 100% rename from img/locations/rimuru_city.webp rename to public/img/locations/rimuru_city.webp diff --git a/img/locations/sealed_cave.webp b/public/img/locations/sealed_cave.webp similarity index 100% rename from img/locations/sealed_cave.webp rename to public/img/locations/sealed_cave.webp diff --git a/img/locations/tengu_village.webp b/public/img/locations/tengu_village.webp similarity index 100% rename from img/locations/tengu_village.webp rename to public/img/locations/tengu_village.webp diff --git a/img/map.webp b/public/img/map.webp similarity index 100% rename from img/map.webp rename to public/img/map.webp diff --git a/img/ogImage.png b/public/img/ogImage.png similarity index 100% rename from img/ogImage.png rename to public/img/ogImage.png diff --git a/img/slime.svg b/public/img/slime.svg similarity index 100% rename from img/slime.svg rename to public/img/slime.svg diff --git a/img/twitterImage.png b/public/img/twitterImage.png similarity index 100% rename from img/twitterImage.png rename to public/img/twitterImage.png diff --git a/src/App.scss b/src/App.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/App.tsx b/src/App.tsx new file mode 100644 index 0000000..24b3467 --- /dev/null +++ b/src/App.tsx @@ -0,0 +1,15 @@ +import './App.scss' +import {Routes, BrowserRouter as Router, Route} from "react-router-dom"; +import Main from "./pages/Main.tsx"; + +function App() { + return ( + + + } /> + + + ) +} + +export default App diff --git a/css/fonts/cormant_garamont.css b/src/_fonts.css similarity index 63% rename from css/fonts/cormant_garamont.css rename to src/_fonts.css index aa457b9..9e90bce 100644 --- a/css/fonts/cormant_garamont.css +++ b/src/_fonts.css @@ -4,7 +4,7 @@ font-family: 'Cormorant Garamond'; font-style: normal; font-weight: 300; - src: url('../../fonts/cormant_garamond/cormorant-garamond-v16-latin-300.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ + src: url('./assets/fonts/cormant_garamond/cormorant-garamond-v16-latin-300.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ } /* cormorant-garamond-300italic - latin */ @font-face { @@ -12,7 +12,7 @@ font-family: 'Cormorant Garamond'; font-style: italic; font-weight: 300; - src: url('../../fonts/cormant_garamond/cormorant-garamond-v16-latin-300italic.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ + src: url('./assets/fonts/cormant_garamond/cormorant-garamond-v16-latin-300italic.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ } /* cormorant-garamond-regular - latin */ @font-face { @@ -20,7 +20,7 @@ font-family: 'Cormorant Garamond'; font-style: normal; font-weight: 400; - src: url('../../fonts/cormant_garamond/cormorant-garamond-v16-latin-regular.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ + src: url('./assets/fonts/cormant_garamond/cormorant-garamond-v16-latin-regular.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ } /* cormorant-garamond-italic - latin */ @font-face { @@ -28,7 +28,7 @@ font-family: 'Cormorant Garamond'; font-style: italic; font-weight: 400; - src: url('../../fonts/cormant_garamond/cormorant-garamond-v16-latin-italic.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ + src: url('./assets/fonts/cormant_garamond/cormorant-garamond-v16-latin-italic.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ } /* cormorant-garamond-500 - latin */ @font-face { @@ -36,7 +36,7 @@ font-family: 'Cormorant Garamond'; font-style: normal; font-weight: 500; - src: url('../../fonts/cormant_garamond/cormorant-garamond-v16-latin-500.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ + src: url('./assets/fonts/cormant_garamond/cormorant-garamond-v16-latin-500.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ } /* cormorant-garamond-500italic - latin */ @font-face { @@ -44,7 +44,7 @@ font-family: 'Cormorant Garamond'; font-style: italic; font-weight: 500; - src: url('../../fonts/cormant_garamond/cormorant-garamond-v16-latin-500italic.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ + src: url('./assets/fonts/cormant_garamond/cormorant-garamond-v16-latin-500italic.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ } /* cormorant-garamond-600 - latin */ @font-face { @@ -52,7 +52,7 @@ font-family: 'Cormorant Garamond'; font-style: normal; font-weight: 600; - src: url('../../fonts/cormant_garamond/cormorant-garamond-v16-latin-600.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ + src: url('./assets/fonts/cormant_garamond/cormorant-garamond-v16-latin-600.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ } /* cormorant-garamond-600italic - latin */ @font-face { @@ -60,7 +60,7 @@ font-family: 'Cormorant Garamond'; font-style: italic; font-weight: 600; - src: url('../../fonts/cormant_garamond/cormorant-garamond-v16-latin-600italic.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ + src: url('./assets/fonts/cormant_garamond/cormorant-garamond-v16-latin-600italic.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ } /* cormorant-garamond-700 - latin */ @font-face { @@ -68,7 +68,7 @@ font-family: 'Cormorant Garamond'; font-style: normal; font-weight: 700; - src: url('../../fonts/cormant_garamond/cormorant-garamond-v16-latin-700.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ + src: url('./assets/fonts/cormant_garamond/cormorant-garamond-v16-latin-700.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ } /* cormorant-garamond-700italic - latin */ @font-face { @@ -76,5 +76,5 @@ font-family: 'Cormorant Garamond'; font-style: italic; font-weight: 700; - src: url('../../fonts/cormant_garamond/cormorant-garamond-v16-latin-700italic.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ + src: url('./assets/fonts/cormant_garamond/cormorant-garamond-v16-latin-700italic.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ } \ No newline at end of file diff --git a/src/_variables.scss b/src/_variables.scss new file mode 100644 index 0000000..17d2616 --- /dev/null +++ b/src/_variables.scss @@ -0,0 +1,4 @@ +@import "_fonts.css"; + +$header-font: "Cormorant Garamond", sans-serif; +$font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; \ No newline at end of file diff --git a/fonts/cormant_garamond/cormorant-garamond-v16-latin-300.woff2 b/src/assets/fonts/cormant_garamond/cormorant-garamond-v16-latin-300.woff2 similarity index 100% rename from fonts/cormant_garamond/cormorant-garamond-v16-latin-300.woff2 rename to src/assets/fonts/cormant_garamond/cormorant-garamond-v16-latin-300.woff2 diff --git a/fonts/cormant_garamond/cormorant-garamond-v16-latin-300italic.woff2 b/src/assets/fonts/cormant_garamond/cormorant-garamond-v16-latin-300italic.woff2 similarity index 100% rename from fonts/cormant_garamond/cormorant-garamond-v16-latin-300italic.woff2 rename to src/assets/fonts/cormant_garamond/cormorant-garamond-v16-latin-300italic.woff2 diff --git a/fonts/cormant_garamond/cormorant-garamond-v16-latin-500.woff2 b/src/assets/fonts/cormant_garamond/cormorant-garamond-v16-latin-500.woff2 similarity index 100% rename from fonts/cormant_garamond/cormorant-garamond-v16-latin-500.woff2 rename to src/assets/fonts/cormant_garamond/cormorant-garamond-v16-latin-500.woff2 diff --git a/fonts/cormant_garamond/cormorant-garamond-v16-latin-500italic.woff2 b/src/assets/fonts/cormant_garamond/cormorant-garamond-v16-latin-500italic.woff2 similarity index 100% rename from fonts/cormant_garamond/cormorant-garamond-v16-latin-500italic.woff2 rename to src/assets/fonts/cormant_garamond/cormorant-garamond-v16-latin-500italic.woff2 diff --git a/fonts/cormant_garamond/cormorant-garamond-v16-latin-600.woff2 b/src/assets/fonts/cormant_garamond/cormorant-garamond-v16-latin-600.woff2 similarity index 100% rename from fonts/cormant_garamond/cormorant-garamond-v16-latin-600.woff2 rename to src/assets/fonts/cormant_garamond/cormorant-garamond-v16-latin-600.woff2 diff --git a/fonts/cormant_garamond/cormorant-garamond-v16-latin-600italic.woff2 b/src/assets/fonts/cormant_garamond/cormorant-garamond-v16-latin-600italic.woff2 similarity index 100% rename from fonts/cormant_garamond/cormorant-garamond-v16-latin-600italic.woff2 rename to src/assets/fonts/cormant_garamond/cormorant-garamond-v16-latin-600italic.woff2 diff --git a/fonts/cormant_garamond/cormorant-garamond-v16-latin-700.woff2 b/src/assets/fonts/cormant_garamond/cormorant-garamond-v16-latin-700.woff2 similarity index 100% rename from fonts/cormant_garamond/cormorant-garamond-v16-latin-700.woff2 rename to src/assets/fonts/cormant_garamond/cormorant-garamond-v16-latin-700.woff2 diff --git a/fonts/cormant_garamond/cormorant-garamond-v16-latin-700italic.woff2 b/src/assets/fonts/cormant_garamond/cormorant-garamond-v16-latin-700italic.woff2 similarity index 100% rename from fonts/cormant_garamond/cormorant-garamond-v16-latin-700italic.woff2 rename to src/assets/fonts/cormant_garamond/cormorant-garamond-v16-latin-700italic.woff2 diff --git a/fonts/cormant_garamond/cormorant-garamond-v16-latin-italic.woff2 b/src/assets/fonts/cormant_garamond/cormorant-garamond-v16-latin-italic.woff2 similarity index 100% rename from fonts/cormant_garamond/cormorant-garamond-v16-latin-italic.woff2 rename to src/assets/fonts/cormant_garamond/cormorant-garamond-v16-latin-italic.woff2 diff --git a/fonts/cormant_garamond/cormorant-garamond-v16-latin-regular.woff2 b/src/assets/fonts/cormant_garamond/cormorant-garamond-v16-latin-regular.woff2 similarity index 100% rename from fonts/cormant_garamond/cormorant-garamond-v16-latin-regular.woff2 rename to src/assets/fonts/cormant_garamond/cormorant-garamond-v16-latin-regular.woff2 diff --git a/fonts/crimson_pro/crimson-pro-v24-latin-200.woff2 b/src/assets/fonts/crimson_pro/crimson-pro-v24-latin-200.woff2 similarity index 100% rename from fonts/crimson_pro/crimson-pro-v24-latin-200.woff2 rename to src/assets/fonts/crimson_pro/crimson-pro-v24-latin-200.woff2 diff --git a/fonts/crimson_pro/crimson-pro-v24-latin-200italic.woff2 b/src/assets/fonts/crimson_pro/crimson-pro-v24-latin-200italic.woff2 similarity index 100% rename from fonts/crimson_pro/crimson-pro-v24-latin-200italic.woff2 rename to src/assets/fonts/crimson_pro/crimson-pro-v24-latin-200italic.woff2 diff --git a/fonts/crimson_pro/crimson-pro-v24-latin-300.woff2 b/src/assets/fonts/crimson_pro/crimson-pro-v24-latin-300.woff2 similarity index 100% rename from fonts/crimson_pro/crimson-pro-v24-latin-300.woff2 rename to src/assets/fonts/crimson_pro/crimson-pro-v24-latin-300.woff2 diff --git a/fonts/crimson_pro/crimson-pro-v24-latin-300italic.woff2 b/src/assets/fonts/crimson_pro/crimson-pro-v24-latin-300italic.woff2 similarity index 100% rename from fonts/crimson_pro/crimson-pro-v24-latin-300italic.woff2 rename to src/assets/fonts/crimson_pro/crimson-pro-v24-latin-300italic.woff2 diff --git a/fonts/crimson_pro/crimson-pro-v24-latin-500.woff2 b/src/assets/fonts/crimson_pro/crimson-pro-v24-latin-500.woff2 similarity index 100% rename from fonts/crimson_pro/crimson-pro-v24-latin-500.woff2 rename to src/assets/fonts/crimson_pro/crimson-pro-v24-latin-500.woff2 diff --git a/fonts/crimson_pro/crimson-pro-v24-latin-500italic.woff2 b/src/assets/fonts/crimson_pro/crimson-pro-v24-latin-500italic.woff2 similarity index 100% rename from fonts/crimson_pro/crimson-pro-v24-latin-500italic.woff2 rename to src/assets/fonts/crimson_pro/crimson-pro-v24-latin-500italic.woff2 diff --git a/fonts/crimson_pro/crimson-pro-v24-latin-600.woff2 b/src/assets/fonts/crimson_pro/crimson-pro-v24-latin-600.woff2 similarity index 100% rename from fonts/crimson_pro/crimson-pro-v24-latin-600.woff2 rename to src/assets/fonts/crimson_pro/crimson-pro-v24-latin-600.woff2 diff --git a/fonts/crimson_pro/crimson-pro-v24-latin-600italic.woff2 b/src/assets/fonts/crimson_pro/crimson-pro-v24-latin-600italic.woff2 similarity index 100% rename from fonts/crimson_pro/crimson-pro-v24-latin-600italic.woff2 rename to src/assets/fonts/crimson_pro/crimson-pro-v24-latin-600italic.woff2 diff --git a/fonts/crimson_pro/crimson-pro-v24-latin-700.woff2 b/src/assets/fonts/crimson_pro/crimson-pro-v24-latin-700.woff2 similarity index 100% rename from fonts/crimson_pro/crimson-pro-v24-latin-700.woff2 rename to src/assets/fonts/crimson_pro/crimson-pro-v24-latin-700.woff2 diff --git a/fonts/crimson_pro/crimson-pro-v24-latin-700italic.woff2 b/src/assets/fonts/crimson_pro/crimson-pro-v24-latin-700italic.woff2 similarity index 100% rename from fonts/crimson_pro/crimson-pro-v24-latin-700italic.woff2 rename to src/assets/fonts/crimson_pro/crimson-pro-v24-latin-700italic.woff2 diff --git a/fonts/crimson_pro/crimson-pro-v24-latin-800.woff2 b/src/assets/fonts/crimson_pro/crimson-pro-v24-latin-800.woff2 similarity index 100% rename from fonts/crimson_pro/crimson-pro-v24-latin-800.woff2 rename to src/assets/fonts/crimson_pro/crimson-pro-v24-latin-800.woff2 diff --git a/fonts/crimson_pro/crimson-pro-v24-latin-800italic.woff2 b/src/assets/fonts/crimson_pro/crimson-pro-v24-latin-800italic.woff2 similarity index 100% rename from fonts/crimson_pro/crimson-pro-v24-latin-800italic.woff2 rename to src/assets/fonts/crimson_pro/crimson-pro-v24-latin-800italic.woff2 diff --git a/fonts/crimson_pro/crimson-pro-v24-latin-900.woff2 b/src/assets/fonts/crimson_pro/crimson-pro-v24-latin-900.woff2 similarity index 100% rename from fonts/crimson_pro/crimson-pro-v24-latin-900.woff2 rename to src/assets/fonts/crimson_pro/crimson-pro-v24-latin-900.woff2 diff --git a/fonts/crimson_pro/crimson-pro-v24-latin-900italic.woff2 b/src/assets/fonts/crimson_pro/crimson-pro-v24-latin-900italic.woff2 similarity index 100% rename from fonts/crimson_pro/crimson-pro-v24-latin-900italic.woff2 rename to src/assets/fonts/crimson_pro/crimson-pro-v24-latin-900italic.woff2 diff --git a/fonts/crimson_pro/crimson-pro-v24-latin-italic.woff2 b/src/assets/fonts/crimson_pro/crimson-pro-v24-latin-italic.woff2 similarity index 100% rename from fonts/crimson_pro/crimson-pro-v24-latin-italic.woff2 rename to src/assets/fonts/crimson_pro/crimson-pro-v24-latin-italic.woff2 diff --git a/fonts/crimson_pro/crimson-pro-v24-latin-regular.woff2 b/src/assets/fonts/crimson_pro/crimson-pro-v24-latin-regular.woff2 similarity index 100% rename from fonts/crimson_pro/crimson-pro-v24-latin-regular.woff2 rename to src/assets/fonts/crimson_pro/crimson-pro-v24-latin-regular.woff2 diff --git a/src/components/icons/CapitalIcon.tsx b/src/components/icons/CapitalIcon.tsx new file mode 100644 index 0000000..9e7b42c --- /dev/null +++ b/src/components/icons/CapitalIcon.tsx @@ -0,0 +1,13 @@ +const CapitalIcon = () => ( + + + + + + + +); + +export default CapitalIcon; \ No newline at end of file diff --git a/src/components/icons/DescriptionIcon.tsx b/src/components/icons/DescriptionIcon.tsx new file mode 100644 index 0000000..ea164a3 --- /dev/null +++ b/src/components/icons/DescriptionIcon.tsx @@ -0,0 +1,12 @@ +const DescriptionIcon = () => ( + + + + + + + +); + +export default DescriptionIcon; \ No newline at end of file diff --git a/src/components/icons/PopulationIcon.tsx b/src/components/icons/PopulationIcon.tsx new file mode 100644 index 0000000..3228514 --- /dev/null +++ b/src/components/icons/PopulationIcon.tsx @@ -0,0 +1,13 @@ +const PopulationIcon = () => ( + + + + + + + +); + +export default PopulationIcon; \ No newline at end of file diff --git a/src/components/icons/RulerIcon.tsx b/src/components/icons/RulerIcon.tsx new file mode 100644 index 0000000..1c33ce9 --- /dev/null +++ b/src/components/icons/RulerIcon.tsx @@ -0,0 +1,13 @@ +const RulerIcon = () => ( + + + + + + + +); + +export default RulerIcon; \ No newline at end of file diff --git a/src/components/layout/Legend.scss b/src/components/layout/Legend.scss new file mode 100644 index 0000000..1407d21 --- /dev/null +++ b/src/components/layout/Legend.scss @@ -0,0 +1,24 @@ +.legend { + display: flex; + flex-direction: column; + padding: 0 15px; + flex-grow: 1; + + h1 { + font-size: 3rem; + font-weight: 700; + width: fit-content; + margin-bottom: 12px; + margin-top: 20px; + + @media (max-width: 768px) { + font-size: 1rem; + } + } + + .border { + width: 100%; + height: 1px; + background-color: #d78453; + } +} \ No newline at end of file diff --git a/src/components/layout/Legend.tsx b/src/components/layout/Legend.tsx new file mode 100644 index 0000000..777b0de --- /dev/null +++ b/src/components/layout/Legend.tsx @@ -0,0 +1,13 @@ +import "./Legend.scss"; + +const Legend = () => { + + return ( +
+

Interactive Map of the Magic Continent

+
+
+ ) +} + +export default Legend; \ No newline at end of file diff --git a/src/components/map/Map.tsx b/src/components/map/Map.tsx new file mode 100644 index 0000000..f198f8c --- /dev/null +++ b/src/components/map/Map.tsx @@ -0,0 +1,118 @@ +import {MapLocation} from "../../types/MapLocation.ts"; +import {MapRegion} from "../../types/MapRegion.ts"; +import Capital from "./locations/Capital.tsx"; +import City from "./locations/City.tsx"; +import Poi from "./locations/Poi.tsx"; +import {useState} from "react"; +import MapLocationTooltip from "./tooltips/MapLocationTooltip.tsx"; +import MapRegionTooltip from "./tooltips/MapRegionTooltip.tsx"; + +interface MapProps { + imageUrl: string; + regions?: MapRegion[]; + capitals?: MapLocation[]; + cities?: MapLocation[]; + pois?: MapLocation[]; +} + +const Map: React.FC = ({imageUrl, regions = [], cities = [], pois = [], capitals = []}) => { + const [tooltip, setTooltip] = useState<{ visible: boolean; x: number; y: number; content: JSX.Element | null }>({ + visible: false, + x: 0, + y: 0, + content: null, + }); + + const handleMouseEnterRegion = (event: React.MouseEvent, region: MapRegion) => { + setTooltip({ + visible: true, + x: event.clientX + 10, + y: event.clientY + 10, + content: , + }); + }; + + const handleMouseEnterLocation = (event: React.MouseEvent, location: MapLocation) => { + setTooltip({ + visible: true, + x: event.clientX + 10, + y: event.clientY + 10, + content: , + }); + }; + + const handleMouseMove = (event: React.MouseEvent) => { + setTooltip(prev => ({ + ...prev, + x: event.clientX + 10, + y: event.clientY + 10, + })); + }; + + const handleMouseLeave = () => { + setTooltip({ visible: false, x: 0, y: 0, content: null }); + }; + + return ( + <> + + + {regions.map((region, index) => ( + handleMouseEnterRegion(e, region)} + onMouseMove={handleMouseMove} + onMouseLeave={handleMouseLeave} + /> + ))} + {capitals.map((location, index) => ( + handleMouseEnterLocation(e, location)} + onMouseMove={handleMouseMove} + onMouseLeave={handleMouseLeave} + /> + ))} + {cities.map((location, index) => ( + handleMouseEnterLocation(e, location)} + onMouseMove={handleMouseMove} + onMouseLeave={handleMouseLeave} + /> + ))} + {pois.map((location, index) => ( + handleMouseEnterLocation(e, location)} + onMouseMove={handleMouseMove} + onMouseLeave={handleMouseLeave} + /> + ))} + + {tooltip.visible && ( +
+ {tooltip.content} +
+ )} + + ); +} + +export default Map; \ No newline at end of file diff --git a/src/components/map/locations/Capital.tsx b/src/components/map/locations/Capital.tsx new file mode 100644 index 0000000..552da01 --- /dev/null +++ b/src/components/map/locations/Capital.tsx @@ -0,0 +1,39 @@ +import {MapLocation} from "../../../types/MapLocation.ts"; + +interface CapitalProps { + key: number; + city: MapLocation; + onMouseEnter: (event: React.MouseEvent, city: MapLocation) => void; + onMouseMove: (event: React.MouseEvent) => void; + onMouseLeave: () => void; +} + +const Capital: React.FC = ({ key, city, onMouseEnter, onMouseMove, onMouseLeave }) => ( + onMouseEnter(e, city)} + onMouseMove={onMouseMove} + onMouseLeave={onMouseLeave} + > + + + + +) + +export default Capital; \ No newline at end of file diff --git a/src/components/map/locations/City.tsx b/src/components/map/locations/City.tsx new file mode 100644 index 0000000..a1535a3 --- /dev/null +++ b/src/components/map/locations/City.tsx @@ -0,0 +1,51 @@ +import {MapLocation} from "../../../types/MapLocation.ts"; + +interface CityProps { + key: number; + city: MapLocation; + onMouseEnter: (event: React.MouseEvent, city: MapLocation) => void; + onMouseMove: (event: React.MouseEvent) => void; + onMouseLeave: () => void; +} + +const City: React.FC = ({ key, city, onMouseEnter, onMouseMove, onMouseLeave }) => ( + onMouseEnter(e, city)} + onMouseMove={onMouseMove} + onMouseLeave={onMouseLeave} + > + + + {/* Black border */} + + + {/* White inner stroke */} + + +) + +export default City; \ No newline at end of file diff --git a/src/components/map/locations/Poi.tsx b/src/components/map/locations/Poi.tsx new file mode 100644 index 0000000..d4409c0 --- /dev/null +++ b/src/components/map/locations/Poi.tsx @@ -0,0 +1,50 @@ +import {MapLocation} from "../../../types/MapLocation.ts"; + +interface PoiProps { + key: number; + poi: MapLocation; + onMouseEnter: (event: React.MouseEvent, city: MapLocation) => void; + onMouseMove: (event: React.MouseEvent) => void; + onMouseLeave: () => void; +} + +const City: React.FC = ({ key, poi, onMouseMove, onMouseLeave, onMouseEnter }) => ( + onMouseEnter(e, poi)} + onMouseMove={onMouseMove} + onMouseLeave={onMouseLeave} + > + {/* Outer rectangle */} + + + {/* Black border path */} + + + {/* White inner stroke */} + + +) + +export default City; \ No newline at end of file diff --git a/src/components/map/tooltips/MapLocationTooltip.tsx b/src/components/map/tooltips/MapLocationTooltip.tsx new file mode 100644 index 0000000..82cc465 --- /dev/null +++ b/src/components/map/tooltips/MapLocationTooltip.tsx @@ -0,0 +1,38 @@ +import {MapLocation} from "../../../types/MapLocation.ts"; + +interface MapLocationTooltipProps { + location: MapLocation; +} + +const MapLocationTooltip: React.FC = ({ location }) => { + return ( + <> +

{location.name}

+
+ {location.description && ( + + )} + {location.image?.url && ( + {location.image.name + )} + {location.url && ( + + wiki icon + Wiki + + )} + + ) +}; + +export default MapLocationTooltip; \ No newline at end of file diff --git a/src/components/map/tooltips/MapRegionTooltip.tsx b/src/components/map/tooltips/MapRegionTooltip.tsx new file mode 100644 index 0000000..4d7dd66 --- /dev/null +++ b/src/components/map/tooltips/MapRegionTooltip.tsx @@ -0,0 +1,55 @@ +import {MapRegion} from "../../../types/MapRegion.ts"; +import CapitalIcon from "../../icons/CapitalIcon.tsx"; +import PopulationIcon from "../../icons/PopulationIcon.tsx"; +import RulerIcon from "../../icons/RulerIcon.tsx"; +import DescriptionIcon from "../../icons/DescriptionIcon.tsx"; + +interface MapLocationTooltipProps { + region: MapRegion; +} + +const MapLocationTooltip: React.FC = ({ region }) => { + return ( + <> +

{region.name}

+
+ {region.capital && ( +
+ + +
+ )} + {region.population && ( +
+ + +
+ )} + {region.ruler && ( +
+ + +
+ )} + {region.description && ( +
+ + +
+ )} + {region.url && ( + + wiki icon + Wiki + + )} + + ) +}; + +export default MapLocationTooltip; \ No newline at end of file diff --git a/js/locations/capitals.js b/src/data/capitals.ts similarity index 93% rename from js/locations/capitals.js rename to src/data/capitals.ts index 39d7b7b..05f49c1 100644 --- a/js/locations/capitals.js +++ b/src/data/capitals.ts @@ -1,23 +1,6 @@ -/** - * @typedef {Object} CapitalData - * @property {string} name - * @property {string?} description - * @property {CapitalImage?} image - * @property {string?} url - * @property {{x:string,y:string}} position - */ +import {MapLocation} from "../types/MapLocation.ts"; -/** - * @typedef {Object} CapitalImage - * @property {string} url - * @property {string} name - * @property {string} credit - */ - -/** - * @type {[CapitalData]} - */ -export const capitals = [ +const capitals: MapLocation[] = [ { name: "Lune", description: "The capital of the Holy Empire Lubelius, home of God Luminous.", @@ -149,4 +132,6 @@ export const capitals = [ url: "https://tensura.fandom.com/wiki/Golden_City_of_El_Dorado", position: { x: "800", y: "1565" } }, -] \ No newline at end of file +]; + +export default capitals; \ No newline at end of file diff --git a/js/locations/cities.js b/src/data/cities.ts similarity index 86% rename from js/locations/cities.js rename to src/data/cities.ts index db3a9c0..2adc0d8 100644 --- a/js/locations/cities.js +++ b/src/data/cities.ts @@ -1,23 +1,6 @@ -/** - * @typedef {Object} CityData - * @property {string} name - * @property {string?} description - * @property {CityImage?} image - * @property {string?} url - * @property {{x:string,y:string}} position - */ +import {MapLocation} from "../types/MapLocation.ts"; -/** - * @typedef {Object} CityImage - * @property {string} url - * @property {string} name - * @property {string} credit - */ - -/** - * @type {[CapitalData]} - */ -export const cities = [ +const cities: MapLocation[] = [ { name: "Tengu Village", description: "The village of the Tengu tribe, located in the Khusha Mountains.", @@ -73,4 +56,6 @@ export const cities = [ }, position: { x: "1224", y: "1010" } }, -]; \ No newline at end of file +]; + +export default cities; \ No newline at end of file diff --git a/js/locations/nations.js b/src/data/nations.ts similarity index 99% rename from js/locations/nations.js rename to src/data/nations.ts index 5092e1c..bcf2807 100644 --- a/js/locations/nations.js +++ b/src/data/nations.ts @@ -1,18 +1,6 @@ -/** - * @typedef {Object} NationData - * @property {string} name - * @property {string} capital - * @property {string} description - * @property {string} ruler - * @property {string} population - * @property {string?} url - * @property {string} points - */ +import {MapRegion} from "../types/MapRegion.ts"; -/** - * @type {[NationData]} - */ -export const nations = [ +const nations: MapRegion[] = [ { name: "Kingdom of Englassia", url: "https://tensura.fandom.com/wiki/Kingdom_of_Englassia", @@ -184,4 +172,6 @@ export const nations = [ description: "The Kingdom of Malukshure is a small coastal nation known for its high concentration of mages and close ties to the Magic Tower. With a rich magical history and diplomatic relations with Tempest, its economy thrives on the export of small ocean fish and magical advancements, while the capital, Sainte-Malukshure, is protected by a powerful barrier maintained by the Trinity Wisemen.", points: "1131, 1345, 1142, 1339, 1156, 1337, 1173, 1336, 1181, 1342, 1187, 1345, 1190, 1349, 1193, 1360, 1182, 1363, 1173, 1367, 1167, 1375, 1160, 1375, 1155, 1368, 1146, 1358, 1139, 1352" }, -] \ No newline at end of file +] + +export default nations; \ No newline at end of file diff --git a/js/locations/pois.js b/src/data/pois.ts similarity index 90% rename from js/locations/pois.js rename to src/data/pois.ts index fdab84f..8adb484 100644 --- a/js/locations/pois.js +++ b/src/data/pois.ts @@ -1,23 +1,6 @@ -/** - * @typedef {Object} POIData - * @property {string} name - * @property {string} description - * @property {POIImage?} image - * @property {string?} url - * @property {{x:string,y:string}} position - */ +import {MapLocation} from "../types/MapLocation.ts"; -/** - * @typedef {Object} POIImage - * @property {string} url - * @property {string} name - * @property {string} credit - */ - -/** - * @type {[POIData]} - */ -export const pois = [ +const pois: MapLocation[] = [ { name: "Sealed Cave", description: "The cave where Veldora was sealed and where Rimuru was born.", @@ -100,4 +83,6 @@ export const pois = [ description: "The uninhabitable region laid to waste during the battle between Guy and Milim 2,000 years ago.", position: { x: "509", y: "940" } }, -] \ No newline at end of file +]; + +export default pois; \ No newline at end of file diff --git a/css/styles.scss b/src/index.scss similarity index 77% rename from css/styles.scss rename to src/index.scss index 5d44f31..dc28da8 100644 --- a/css/styles.scss +++ b/src/index.scss @@ -1,9 +1,13 @@ -@import "fonts/cormant_garamont.css"; -//@import "fonts/crimson_pro.css"; +@import "variables"; -:root { - --header-font: "Cormorant Garamond", sans-serif; - --body-font: "Crimson Pro", sans-serif; +#root { + padding: 0; + margin: 0; + + width: 100%; + height: 100%; + + display: flex; } body { @@ -22,7 +26,7 @@ body { color: #d78453; font-size: 16px; - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";; + font-family: $font; } h1, h2, h3, h4, h5, h6 { @@ -75,31 +79,6 @@ a { fill: rgb(252, 161, 113, .3); } -.legend { - display: flex; - flex-direction: column; - padding: 0 15px; - flex-grow: 1; - - h1 { - font-size: 3rem; - font-weight: 700; - width: fit-content; - margin-bottom: 12px; - margin-top: 20px; - - @media (max-width: 768px) { - font-size: 1rem; - } - } - - .border { - width: 100%; - height: 1px; - background-color: #d78453; - } -} - @media (max-width: 1200px) { body { flex-direction: column; @@ -157,12 +136,24 @@ a { border-radius: 5px; } + a.more { + display: flex; + align-items: center; + gap: 7px; + + img { + --hw: 23px; + height: var(--hw); + width: var(--hw); + } + } + .detailbox { display: flex; //align-items: center; gap: 7px; - img,svg { + img, svg { --hw: 23px; height: var(--hw); width: var(--hw); diff --git a/src/main.tsx b/src/main.tsx new file mode 100644 index 0000000..46c0846 --- /dev/null +++ b/src/main.tsx @@ -0,0 +1,10 @@ +import { StrictMode } from 'react' +import { createRoot } from 'react-dom/client' +import './index.scss' +import App from './App.tsx' + +createRoot(document.getElementById('root')!).render( + + + , +) diff --git a/src/pages/Main.tsx b/src/pages/Main.tsx new file mode 100644 index 0000000..0b4bde0 --- /dev/null +++ b/src/pages/Main.tsx @@ -0,0 +1,23 @@ +import Legend from "../components/layout/Legend.tsx"; +import Map from "../components/map/Map.tsx"; +import nations from "../data/nations.ts"; +import cities from "../data/cities.ts"; +import capitals from "../data/capitals.ts"; +import pois from "../data/pois.ts"; + +const Main = () => { + return ( + <> + + + + ); +} + +export default Main; \ No newline at end of file diff --git a/src/types/MapLocation.ts b/src/types/MapLocation.ts new file mode 100644 index 0000000..fd4af6d --- /dev/null +++ b/src/types/MapLocation.ts @@ -0,0 +1,19 @@ +export interface MapLocation { + name: string; + description?: string; + image?: MapLocationImage; + url?: string; + icon?: string; + position: MapLocationPosition; +} + +export interface MapLocationImage { + name?: string; + url: string; + credit?: string; +} + +export interface MapLocationPosition { + x: string; + y: string; +} \ No newline at end of file diff --git a/src/types/MapRegion.ts b/src/types/MapRegion.ts new file mode 100644 index 0000000..e6ed748 --- /dev/null +++ b/src/types/MapRegion.ts @@ -0,0 +1,18 @@ +export interface MapRegion { + name: string; + description?: string; + capital?: string; + ruler?: string; + population?: string; + url?: string; + image?: MapRegionImage; + borderColor?: string; + fillColor?: string; + points: string; +} + +export interface MapRegionImage { + name?: string; + url: string; + credit?: string; +} \ No newline at end of file diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/tsconfig.app.json b/tsconfig.app.json new file mode 100644 index 0000000..5a2def4 --- /dev/null +++ b/tsconfig.app.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "Bundler", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["src"] +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..1ffef60 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,7 @@ +{ + "files": [], + "references": [ + { "path": "./tsconfig.app.json" }, + { "path": "./tsconfig.node.json" } + ] +} diff --git a/tsconfig.node.json b/tsconfig.node.json new file mode 100644 index 0000000..9dad701 --- /dev/null +++ b/tsconfig.node.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "target": "ES2022", + "lib": ["ES2023"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "Bundler", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..8b0f57b --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [react()], +})