浏览代码

Add map to gallery page.

Lee Morgan 2 周之前
父节点
当前提交
a8deb64f5e
共有 9 个文件被更改,包括 188 次插入 和 6 次删除
  1. 2 0
      AGENTS.md
  2. 15 0
      package-lock.json
  3. 3 0
      package.json
  4. 22 2
      src/app.css
  5. 8 0
      src/env.js
  6. 6 4
      src/routes/+layout.svelte
  7. 9 0
      src/routes/gallery/+page.server.js
  8. 120 0
      src/routes/gallery/+page.svelte
  9. 3 0
      vite.config.js

+ 2 - 0
AGENTS.md

@@ -84,3 +84,5 @@ k## Color Rules
 
 
 # Other
 # Other
 - Do not ever run the server, I will do that manually
 - Do not ever run the server, I will do that manually
+- Never edit environment variables
+- Whenever I type "mnc" anywhere in the message, that means "make no changes". Do not edit any of the code, just respond to my message

+ 15 - 0
package-lock.json

@@ -7,6 +7,9 @@
 		"": {
 		"": {
 			"name": "leemorgan-dev",
 			"name": "leemorgan-dev",
 			"version": "0.0.1",
 			"version": "0.0.1",
+			"dependencies": {
+				"mapbox-gl": "^3.33.0"
+			},
 			"devDependencies": {
 			"devDependencies": {
 				"@sveltejs/adapter-auto": "^8.0.0",
 				"@sveltejs/adapter-auto": "^8.0.0",
 				"@sveltejs/kit": "^3.0.0",
 				"@sveltejs/kit": "^3.0.0",
@@ -896,6 +899,18 @@
 				"@jridgewell/sourcemap-codec": "^1.6.0"
 				"@jridgewell/sourcemap-codec": "^1.6.0"
 			}
 			}
 		},
 		},
+		"node_modules/mapbox-gl": {
+			"version": "3.33.0",
+			"resolved": "https://registry.npmjs.org/mapbox-gl/-/mapbox-gl-3.33.0.tgz",
+			"integrity": "sha512-2HpIrVedccHedolZyMVG/7FXdpgmsLTENgEDsX71x3/gKDGfYMf1FCRY0prntknkn/JXcraUKXlBTv7vkr0EXg==",
+			"license": "SEE LICENSE IN LICENSE.txt",
+			"workspaces": [
+				"src/style-spec",
+				"plugins/mapbox-gl-pmtiles-provider",
+				"test/bundlers/*",
+				"test/build/typings"
+			]
+		},
 		"node_modules/mrmime": {
 		"node_modules/mrmime": {
 			"version": "2.0.1",
 			"version": "2.0.1",
 			"resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz",
 			"resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz",

+ 3 - 0
package.json

@@ -19,5 +19,8 @@
 	"imports": {
 	"imports": {
 		"#lib": "./src/lib/index.js",
 		"#lib": "./src/lib/index.js",
 		"#lib/*": "./src/lib/*"
 		"#lib/*": "./src/lib/*"
+	},
+	"dependencies": {
+		"mapbox-gl": "^3.33.0"
 	}
 	}
 }
 }

+ 22 - 2
src/app.css

@@ -30,6 +30,7 @@ html,
 body {
 body {
 	margin: 0;
 	margin: 0;
 	padding: 0;
 	padding: 0;
+	height: 100%;
 	min-height: 100%;
 	min-height: 100%;
 }
 }
 
 
@@ -95,7 +96,9 @@ a:hover {
 
 
 .shell {
 .shell {
 	display: grid;
 	display: grid;
-	grid-template-columns: var(--rail) 1fr;
+	grid-template-columns: var(--rail) minmax(0, 1fr);
+	grid-template-rows: minmax(0, 1fr);
+	height: 100svh;
 	min-height: 100svh;
 	min-height: 100svh;
 }
 }
 
 
@@ -249,12 +252,29 @@ a:hover {
 }
 }
 
 
 .page {
 .page {
+	min-height: 0;
+	overflow: auto;
 	padding: 2rem clamp(1.5rem, 4vw, 4rem) 4rem;
 	padding: 2rem clamp(1.5rem, 4vw, 4rem) 4rem;
 }
 }
 
 
+.page-flush {
+	position: relative;
+	height: 100%;
+	min-height: 0;
+	padding: 0;
+	overflow: hidden;
+}
+
+.page-flush .site-main {
+	position: relative;
+	height: 100%;
+	min-height: 0;
+}
+
 @media (max-width: 860px) {
 @media (max-width: 860px) {
 	.shell {
 	.shell {
 		grid-template-columns: 1fr;
 		grid-template-columns: 1fr;
+		grid-template-rows: auto minmax(0, 1fr);
 	}
 	}
 
 
 	.rail {
 	.rail {
@@ -274,7 +294,7 @@ a:hover {
 		display: none;
 		display: none;
 	}
 	}
 
 
-	.page {
+	.page:not(.page-flush) {
 		padding: 1.75rem 1.25rem 3rem;
 		padding: 1.75rem 1.25rem 3rem;
 	}
 	}
 }
 }

+ 8 - 0
src/env.js

@@ -0,0 +1,8 @@
+import { defineEnvVars } from '@sveltejs/kit/env';
+
+export const variables = defineEnvVars({
+	MAPBOX_KEY: {
+		description: 'Mapbox access token used to load the gallery map',
+		schema: (value) => value ?? ''
+	}
+});

+ 6 - 4
src/routes/+layout.svelte

@@ -7,6 +7,7 @@
 
 
 	const isHome = $derived(page.url.pathname === '/');
 	const isHome = $derived(page.url.pathname === '/');
 	const isProjects = $derived(page.url.pathname === '/projects');
 	const isProjects = $derived(page.url.pathname === '/projects');
+	const isGallery = $derived(page.url.pathname === '/gallery');
 </script>
 </script>
 
 
 <svelte:head>
 <svelte:head>
@@ -38,8 +39,9 @@
 					<li>
 					<li>
 						<a href="/projects" aria-current={isProjects ? 'page' : undefined}>Projects</a>
 						<a href="/projects" aria-current={isProjects ? 'page' : undefined}>Projects</a>
 					</li>
 					</li>
-					<li><a href="/">Blog</a></li>
-					<li><a href="/">Gallery</a></li>
+					<li>
+						<a href="/gallery" aria-current={isGallery ? 'page' : undefined}>Gallery</a>
+					</li>
 					<li><a href="/">Currency</a></li>
 					<li><a href="/">Currency</a></li>
 					<li><a href="/">Resume/CV</a></li>
 					<li><a href="/">Resume/CV</a></li>
 					<li><a href="/">Git</a></li>
 					<li><a href="/">Git</a></li>
@@ -53,8 +55,8 @@
 		</div>
 		</div>
 	</aside>
 	</aside>
 
 
-	<div class="page">
-		<main id="main">
+	<div class="page" class:page-flush={isGallery}>
+		<main id="main" class="site-main">
 			{@render children()}
 			{@render children()}
 		</main>
 		</main>
 	</div>
 	</div>

+ 9 - 0
src/routes/gallery/+page.server.js

@@ -0,0 +1,9 @@
+import { MAPBOX_KEY } from '$app/env/private';
+
+export const prerender = false;
+
+export function load() {
+	return {
+		mapboxKey: MAPBOX_KEY
+	};
+}

+ 120 - 0
src/routes/gallery/+page.svelte

@@ -0,0 +1,120 @@
+<script>
+	import { onMount } from 'svelte';
+	import 'mapbox-gl/dist/mapbox-gl.css';
+
+	let { data } = $props();
+
+	let mapEl = $state();
+	let webglError = $state(false);
+
+	function webgl2Available() {
+		const canvas = document.createElement('canvas');
+		canvas.width = 1;
+		canvas.height = 1;
+		return Boolean(canvas.getContext('webgl2'));
+	}
+
+	onMount(() => {
+		let map;
+		let observer;
+		let cancelled = false;
+
+		(async () => {
+			if (!data.mapboxKey || !mapEl) return;
+
+			const mod = await import('mapbox-gl');
+			const mapboxgl = mod.default?.Map ? mod.default : globalThis.mapboxgl;
+			const workerMod = await import('mapbox-gl/dist/mapbox-gl-csp-worker.js?worker');
+			if (cancelled || !mapEl || !mapboxgl?.Map) return;
+
+			if (!webgl2Available()) {
+				webglError = true;
+				return;
+			}
+
+			mapboxgl.workerClass = workerMod.default;
+			mapboxgl.accessToken = data.mapboxKey;
+
+			map = new mapboxgl.Map({
+				container: mapEl,
+				style: 'mapbox://styles/mapbox/dark-v11',
+				center: [-79.048, 33.553],
+				zoom: 10,
+				failIfMajorPerformanceCaveat: false
+			});
+
+			const resize = () => map?.resize();
+			map.on('load', resize);
+			requestAnimationFrame(resize);
+			observer = new ResizeObserver(resize);
+			observer.observe(mapEl);
+		})();
+
+		return () => {
+			cancelled = true;
+			observer?.disconnect();
+			map?.remove();
+		};
+	});
+</script>
+
+<svelte:head>
+	<title>Gallery — Lee Morgan</title>
+	<meta name="description" content="Photo galleries on a map." />
+</svelte:head>
+
+<div class="gallery">
+	<h1 class="sr-only">Gallery</h1>
+	{#if webglError}
+		<p class="missing">This browser could not start WebGL, which the map needs to draw.</p>
+	{:else if data.mapboxKey}
+		<div class="map" bind:this={mapEl}></div>
+	{:else}
+		<p class="missing">Set the MAPBOX_KEY environment variable to load the map.</p>
+	{/if}
+</div>
+
+<style>
+	.gallery {
+		position: absolute;
+		inset: 0;
+	}
+
+	.map {
+		position: absolute;
+		inset: 0;
+	}
+
+	.map :global(.mapboxgl-canvas-container),
+	.map :global(.mapboxgl-canvas) {
+		width: 100%;
+		height: 100%;
+	}
+
+	.map :global(.mapboxgl-ctrl-attrib) {
+		background: transparent;
+		color: var(--muted);
+	}
+
+	.map :global(.mapboxgl-ctrl-attrib a) {
+		color: var(--muted);
+	}
+
+	.sr-only {
+		position: absolute;
+		width: 1px;
+		height: 1px;
+		padding: 0;
+		margin: -1px;
+		overflow: hidden;
+		clip: rect(0, 0, 0, 0);
+		white-space: nowrap;
+		border: 0;
+	}
+
+	.missing {
+		margin: 0;
+		padding: 2rem;
+		color: var(--muted);
+	}
+</style>

+ 3 - 0
vite.config.js

@@ -3,6 +3,9 @@ import { sveltekit } from '@sveltejs/kit/vite';
 import { defineConfig } from 'vite';
 import { defineConfig } from 'vite';
 
 
 export default defineConfig({
 export default defineConfig({
+	optimizeDeps: {
+		include: ['mapbox-gl']
+	},
 	plugins: [
 	plugins: [
 		sveltekit({
 		sveltekit({
 			compilerOptions: {
 			compilerOptions: {