Skip to content

Commit 756498b

Browse files
committed
page layout update, zupass loading fix, map progress, update dss id
1 parent cb2d435 commit 756498b

12 files changed

Lines changed: 666 additions & 150 deletions

File tree

devconnect-app/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
"moment": "^2.30.1",
5757
"next": "15.4.4",
5858
"next-themes": "^0.4.6",
59+
"panzoom": "^9.4.3",
5960
"qrcode": "^1.5.4",
6061
"qrcode.react": "^4.2.0",
6162
"react": "^19.0.0",

devconnect-app/src/app/worlds-fair/page-content.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import PageLayout from '@/components/PageLayout';
33
import ProgrammeTab from './ScheduleTab';
44
import WorldsFairTab from './WorldsFairTab';
55
import { MapPinIcon, CalendarRangeIcon } from 'lucide-react';
6-
import { VenueMap } from './venue-map/VenueMap';
6+
import { VenueMap } from './venue-map/VenueMap2';
77

88
const tabs = (atprotoEvents: any[]) => [
99
{
Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
'use client';
2+
// https://github.com/timmywil/panzoom
3+
import { usePanzoom } from './panzoom';
4+
import MapTest from './maps/MapTest';
5+
import { useRef, useEffect, useState } from 'react';
6+
import {
7+
svgToLookup,
8+
svgToLookupWithGroups,
9+
SVGLookup,
10+
} from './utils/svgToLookup';
11+
import { Pin } from './components/Pin';
12+
import cn from 'classnames';
13+
import css from './map.module.scss';
14+
15+
/*
16+
1) Zoom into element programatically by id (so we can use it via search params / url deep link)
17+
2) Place pin on elements
18+
3) Highlight elements
19+
4) Get current zoom level
20+
5) Show different parts of the map depending on the zoom level (low to high fidelity)
21+
*/
22+
23+
const MapPane = (props: {
24+
selection: string | null;
25+
elementLookup: SVGLookup;
26+
}) => {
27+
const { selection, elementLookup } = props;
28+
29+
const element = selection ? elementLookup[selection] : null;
30+
31+
console.log(element, 'SELECTION');
32+
33+
return (
34+
<div
35+
className={cn(
36+
'absolute z-[1] bottom-0 left-0 right-0 border-t border-t-solid border-gray-200 p-4 transition-all duration-300 translate-y-[100%] opacity-0',
37+
selection && '!translate-y-[0%] opacity-100',
38+
css['map']
39+
)}
40+
>
41+
<div className={cn('text-sm font-bold', !selection && 'text-white')}>
42+
{element?.id || 'no-selection'}
43+
</div>
44+
</div>
45+
);
46+
};
47+
48+
export const VenueMap = () => {
49+
const svgRef = useRef<SVGSVGElement | null>(null);
50+
const containerRef = useRef<HTMLDivElement | null>(null);
51+
const [elementLookup, setElementLookup] = useState<SVGLookup>({});
52+
const [groupData, setGroupData] = useState<{ [key: string]: string[] }>({});
53+
const [svgScale, setSvgScale] = useState({ scaleX: 1, scaleY: 1 });
54+
const [hoveredElement, setHoveredElement] = useState<string | null>(null);
55+
const [selectedElement, setSelectedElement] = useState<string | null>(null);
56+
57+
const isHoveredOrSelected = hoveredElement || selectedElement;
58+
59+
const panzoomInstance = usePanzoom('venue-map');
60+
61+
useEffect(() => {
62+
// Wait for next frame to ensure SVG is fully rendered
63+
requestAnimationFrame(() => {
64+
const lookup = svgToLookup(svgRef.current);
65+
setElementLookup(lookup);
66+
67+
// Also get grouped data
68+
const { elements, groups } = svgToLookupWithGroups(svgRef.current);
69+
setGroupData(groups);
70+
71+
// Calculate scale between SVG viewBox and actual rendered size
72+
if (svgRef.current) {
73+
const svgRect = svgRef.current.getBoundingClientRect();
74+
const viewBox = svgRef.current.viewBox.baseVal;
75+
const scaleX = svgRect.width / viewBox.width;
76+
const scaleY = svgRect.height / viewBox.height;
77+
setSvgScale({ scaleX, scaleY });
78+
}
79+
});
80+
}, []);
81+
82+
// Apply hover effect to all SVG elements dynamically
83+
useEffect(() => {
84+
if (!svgRef.current) return;
85+
86+
const svgElements = svgRef.current.querySelectorAll('[id]:not(g)');
87+
88+
svgElements.forEach((element) => {
89+
const svgElement = element as SVGElement;
90+
if (isHoveredOrSelected === null) {
91+
svgElement.style.opacity = '1';
92+
svgElement.style.transition = 'opacity 0.5s ease-in-out';
93+
} else if (isHoveredOrSelected === element.id) {
94+
svgElement.style.opacity = '1';
95+
svgElement.style.transition = 'opacity 0.5s ease-in-out';
96+
} else {
97+
svgElement.style.opacity = '0.3';
98+
svgElement.style.transition = 'opacity 0.5s ease-in-out';
99+
}
100+
});
101+
}, [isHoveredOrSelected]);
102+
103+
const handleSVGMouseOver = (e: React.MouseEvent<HTMLDivElement>) => {
104+
const target = e.target as SVGElement;
105+
if (
106+
target &&
107+
target.id &&
108+
target.tagName !== 'g' &&
109+
target.tagName !== 'svg'
110+
) {
111+
if (hoveredElement !== target.id) {
112+
setHoveredElement(target.id);
113+
}
114+
}
115+
};
116+
117+
const handleSVGMouseOut = (e: React.MouseEvent<HTMLDivElement>) => {
118+
const target = e.target as SVGElement;
119+
if (
120+
target &&
121+
target.id &&
122+
target.tagName !== 'g' &&
123+
target.tagName !== 'svg'
124+
) {
125+
setHoveredElement(null);
126+
}
127+
};
128+
129+
const onSVGElementClick = (
130+
id: string,
131+
event: React.MouseEvent<SVGElement>
132+
) => {
133+
console.log('SVG element clicked:', id);
134+
if (elementLookup[id]) {
135+
setSelectedElement(selectedElement === id ? null : id);
136+
console.log('Element position data:', elementLookup[id]);
137+
}
138+
};
139+
140+
return (
141+
<div
142+
ref={containerRef}
143+
className="relative w-full aspect-[1200/800] bg-gray-100 overflow-hidden"
144+
onClick={() => setSelectedElement(null)}
145+
onTouchEnd={() => setSelectedElement(null)}
146+
>
147+
{/* Panzoom container */}
148+
<div
149+
id="venue-map"
150+
className="relative"
151+
onMouseOver={handleSVGMouseOver}
152+
onMouseOut={handleSVGMouseOut}
153+
>
154+
<MapTest ref={svgRef} onSVGElementClick={onSVGElementClick} />
155+
156+
{/* Pin layer overlay - moves with the panzoom */}
157+
<div
158+
className="absolute top-0 left-0 overflow-visible"
159+
style={{ pointerEvents: 'none' }}
160+
>
161+
{hoveredElement && elementLookup[hoveredElement] && (
162+
<Pin
163+
key={hoveredElement}
164+
x={elementLookup[hoveredElement].centerX * svgScale.scaleX}
165+
y={elementLookup[hoveredElement].centerY * svgScale.scaleY}
166+
label={hoveredElement}
167+
color="#FF0000"
168+
size={16}
169+
/>
170+
)}
171+
</div>
172+
</div>
173+
174+
<MapPane selection={selectedElement} elementLookup={elementLookup} />
175+
176+
{/* Zoom controls */}
177+
<div className="absolute top-4 right-4 flex flex-col gap-2 z-10">
178+
<button
179+
className="bg-white px-4 py-2 rounded shadow hover:bg-gray-100"
180+
onMouseDown={(e) => e.stopPropagation()}
181+
onClick={(e) => {
182+
e.stopPropagation();
183+
panzoomInstance?.smoothZoom(0, 0, 2);
184+
}}
185+
>
186+
+
187+
</button>
188+
<button
189+
className="bg-white px-4 py-2 rounded shadow hover:bg-gray-100"
190+
onMouseDown={(e) => e.stopPropagation()}
191+
onClick={(e) => {
192+
e.stopPropagation();
193+
panzoomInstance?.smoothZoom(0, 0, 0.5);
194+
}}
195+
>
196+
-
197+
</button>
198+
<button
199+
className="bg-white px-4 py-2 rounded shadow hover:bg-gray-100 text-xs"
200+
onMouseDown={(e) => e.stopPropagation()}
201+
onClick={(e) => {
202+
e.stopPropagation();
203+
panzoomInstance?.smoothZoom(0, 0, 1);
204+
}}
205+
>
206+
Reset
207+
</button>
208+
</div>
209+
</div>
210+
);
211+
};
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
.map {
2+
background: hsla(0, 0%, 100%, .97);
3+
backdrop-filter: blur(4px);
4+
-webkit-backdrop-filter: blur(4px);
5+
}

devconnect-app/src/app/worlds-fair/venue-map/maps/MapTest.tsx

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,21 @@ const MapTest = forwardRef<SVGSVGElement, MapTestProps>(
1414
viewBox="0 0 1200 800"
1515
fill="none"
1616
xmlns="http://www.w3.org/2000/svg"
17-
// onMouseUp={(e) => {
18-
// const target = e.target as SVGElement;
19-
// if (target.id) {
20-
// onSVGElementClick(target.id, e);
21-
// }
22-
// }}
23-
className="bg-red-500"
17+
onClick={(e) => {
18+
const target = e.target as SVGElement;
19+
if (target.id) {
20+
e.stopPropagation();
21+
onSVGElementClick(target.id, e);
22+
}
23+
}}
24+
onTouchEnd={(e) => {
25+
const target = e.target as SVGElement;
26+
if (target.id) {
27+
e.stopPropagation();
28+
onSVGElementClick(target.id, e as any);
29+
}
30+
}}
31+
// className="bg-red-500"
2432
>
2533
<g id="event-map-svg-test">
2634
<g id="defi">

0 commit comments

Comments
 (0)