feat: add StandardizedToolWrapper and fix map interactions
- Wrap map component in StandardizedToolWrapper with header bar
- Add onPointerDown={stopPropagation} to all sidebar interactive elements
- Add handleMapWheel that forwards wheel zoom to map component
- Add pinnedToView, tags, isMinimized props for consistency
- Fix TypeScript type for stopPropagation handler
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
dc048d7aec
commit
ee3ec16cb6
|
|
@ -15,9 +15,10 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { BaseBoxShapeUtil, TLBaseShape, HTMLContainer, TLResizeInfo, resizeBox, T } from 'tldraw';
|
import { BaseBoxShapeUtil, TLBaseShape, HTMLContainer, TLResizeInfo, resizeBox, T } from 'tldraw';
|
||||||
import { useRef, useEffect, useState, useCallback, useMemo } from 'react';
|
import { useRef, useEffect, useState, useCallback } from 'react';
|
||||||
import maplibregl from 'maplibre-gl';
|
import maplibregl from 'maplibre-gl';
|
||||||
import 'maplibre-gl/dist/maplibre-gl.css';
|
import 'maplibre-gl/dist/maplibre-gl.css';
|
||||||
|
import { StandardizedToolWrapper } from '../components/StandardizedToolWrapper';
|
||||||
|
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
// Types
|
// Types
|
||||||
|
|
@ -76,6 +77,9 @@ export type IMapShape = TLBaseShape<
|
||||||
waypoints: Coordinate[];
|
waypoints: Coordinate[];
|
||||||
collaborators: CollaboratorPresence[];
|
collaborators: CollaboratorPresence[];
|
||||||
showSidebar: boolean;
|
showSidebar: boolean;
|
||||||
|
pinnedToView: boolean;
|
||||||
|
tags: string[];
|
||||||
|
isMinimized: boolean;
|
||||||
// Legacy compatibility properties
|
// Legacy compatibility properties
|
||||||
interactive: boolean;
|
interactive: boolean;
|
||||||
showGPS: boolean;
|
showGPS: boolean;
|
||||||
|
|
@ -168,6 +172,9 @@ type StyleKey = keyof typeof MAP_STYLES;
|
||||||
export class MapShape extends BaseBoxShapeUtil<IMapShape> {
|
export class MapShape extends BaseBoxShapeUtil<IMapShape> {
|
||||||
static override type = 'Map' as const;
|
static override type = 'Map' as const;
|
||||||
|
|
||||||
|
// Map theme color: Blue (consistent with mapping/navigation)
|
||||||
|
static readonly PRIMARY_COLOR = '#4890E8';
|
||||||
|
|
||||||
static override props = {
|
static override props = {
|
||||||
w: T.number,
|
w: T.number,
|
||||||
h: T.number,
|
h: T.number,
|
||||||
|
|
@ -180,6 +187,9 @@ export class MapShape extends BaseBoxShapeUtil<IMapShape> {
|
||||||
waypoints: T.any,
|
waypoints: T.any,
|
||||||
collaborators: T.any,
|
collaborators: T.any,
|
||||||
showSidebar: T.boolean,
|
showSidebar: T.boolean,
|
||||||
|
pinnedToView: T.boolean,
|
||||||
|
tags: T.any,
|
||||||
|
isMinimized: T.boolean,
|
||||||
// Legacy compatibility properties
|
// Legacy compatibility properties
|
||||||
interactive: T.boolean,
|
interactive: T.boolean,
|
||||||
showGPS: T.boolean,
|
showGPS: T.boolean,
|
||||||
|
|
@ -202,6 +212,9 @@ export class MapShape extends BaseBoxShapeUtil<IMapShape> {
|
||||||
waypoints: [],
|
waypoints: [],
|
||||||
collaborators: [],
|
collaborators: [],
|
||||||
showSidebar: true,
|
showSidebar: true,
|
||||||
|
pinnedToView: false,
|
||||||
|
tags: ['map'],
|
||||||
|
isMinimized: false,
|
||||||
// Legacy compatibility defaults
|
// Legacy compatibility defaults
|
||||||
interactive: true,
|
interactive: true,
|
||||||
showGPS: false,
|
showGPS: false,
|
||||||
|
|
@ -220,11 +233,13 @@ export class MapShape extends BaseBoxShapeUtil<IMapShape> {
|
||||||
}
|
}
|
||||||
|
|
||||||
indicator(shape: IMapShape) {
|
indicator(shape: IMapShape) {
|
||||||
return <rect x={0} y={0} width={shape.props.w} height={shape.props.h} fill="none" rx={8} />;
|
const height = shape.props.isMinimized ? 40 : shape.props.h + 40;
|
||||||
|
return <rect x={0} y={0} width={shape.props.w} height={height} fill="none" rx={8} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
component(shape: IMapShape) {
|
component(shape: IMapShape) {
|
||||||
return <MapComponent shape={shape} editor={this.editor} />;
|
const isSelected = this.editor.getSelectedShapeIds().includes(shape.id);
|
||||||
|
return <MapComponent shape={shape} editor={this.editor} isSelected={isSelected} />;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -303,7 +318,7 @@ const styles = {
|
||||||
// Map Component
|
// Map Component
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
|
||||||
function MapComponent({ shape, editor }: { shape: IMapShape; editor: MapShape['editor'] }) {
|
function MapComponent({ shape, editor, isSelected }: { shape: IMapShape; editor: MapShape['editor']; isSelected: boolean }) {
|
||||||
const containerRef = useRef<HTMLDivElement>(null);
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
const mapRef = useRef<maplibregl.Map | null>(null);
|
const mapRef = useRef<maplibregl.Map | null>(null);
|
||||||
const markersRef = useRef<Map<string, maplibregl.Marker>>(new Map());
|
const markersRef = useRef<Map<string, maplibregl.Marker>>(new Map());
|
||||||
|
|
@ -315,10 +330,9 @@ function MapComponent({ shape, editor }: { shape: IMapShape; editor: MapShape['e
|
||||||
const [showColorPicker, setShowColorPicker] = useState(false);
|
const [showColorPicker, setShowColorPicker] = useState(false);
|
||||||
const [searchQuery, setSearchQuery] = useState('');
|
const [searchQuery, setSearchQuery] = useState('');
|
||||||
const [searchResults, setSearchResults] = useState<any[]>([]);
|
const [searchResults, setSearchResults] = useState<any[]>([]);
|
||||||
const [nearbyPlaces, setNearbyPlaces] = useState<any[]>([]);
|
const [_nearbyPlaces, setNearbyPlaces] = useState<any[]>([]);
|
||||||
const [isSearching, setIsSearching] = useState(false);
|
const [isSearching, setIsSearching] = useState(false);
|
||||||
const [observingUser, setObservingUser] = useState<string | null>(null);
|
const [observingUser, setObservingUser] = useState<string | null>(null);
|
||||||
const [editingTitle, setEditingTitle] = useState(false);
|
|
||||||
|
|
||||||
const styleKey = (shape.props.styleKey || 'voyager') as StyleKey;
|
const styleKey = (shape.props.styleKey || 'voyager') as StyleKey;
|
||||||
const currentStyle = MAP_STYLES[styleKey] || MAP_STYLES.voyager;
|
const currentStyle = MAP_STYLES[styleKey] || MAP_STYLES.voyager;
|
||||||
|
|
@ -775,36 +789,74 @@ function MapComponent({ shape, editor }: { shape: IMapShape; editor: MapShape['e
|
||||||
// Event Handlers
|
// Event Handlers
|
||||||
// ==========================================================================
|
// ==========================================================================
|
||||||
|
|
||||||
const stopPropagation = useCallback((e: React.SyntheticEvent) => {
|
const stopPropagation = useCallback((e: { stopPropagation: () => void }) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// Prevent browser zoom when over the map - use map zoom instead
|
// Handle wheel events on map container - forward delta to map for zooming
|
||||||
const handleWheel = useCallback((e: React.WheelEvent) => {
|
const handleMapWheel = useCallback((e: React.WheelEvent) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
// The map will handle zooming via its own wheel handler
|
// Forward wheel event to the map for zooming
|
||||||
|
if (mapRef.current) {
|
||||||
|
const map = mapRef.current;
|
||||||
|
const delta = e.deltaY > 0 ? -1 : 1;
|
||||||
|
const currentZoom = map.getZoom();
|
||||||
|
map.easeTo({
|
||||||
|
zoom: currentZoom + delta * 0.5,
|
||||||
|
duration: 150,
|
||||||
|
});
|
||||||
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// Capture all pointer events to prevent tldraw from intercepting
|
// Close handler for StandardizedToolWrapper
|
||||||
const capturePointerEvents = useCallback((e: React.PointerEvent) => {
|
const handleClose = useCallback(() => {
|
||||||
e.stopPropagation();
|
editor.deleteShape(shape.id);
|
||||||
}, []);
|
}, [editor, shape.id]);
|
||||||
|
|
||||||
|
// Minimize handler
|
||||||
|
const handleMinimize = useCallback(() => {
|
||||||
|
editor.updateShape<IMapShape>({
|
||||||
|
id: shape.id,
|
||||||
|
type: 'Map',
|
||||||
|
props: { isMinimized: !shape.props.isMinimized },
|
||||||
|
});
|
||||||
|
}, [editor, shape.id, shape.props.isMinimized]);
|
||||||
|
|
||||||
|
// Pin handler
|
||||||
|
const handlePinToggle = useCallback(() => {
|
||||||
|
editor.updateShape<IMapShape>({
|
||||||
|
id: shape.id,
|
||||||
|
type: 'Map',
|
||||||
|
props: { pinnedToView: !shape.props.pinnedToView },
|
||||||
|
});
|
||||||
|
}, [editor, shape.id, shape.props.pinnedToView]);
|
||||||
|
|
||||||
|
// Tags handler
|
||||||
|
const handleTagsChange = useCallback((newTags: string[]) => {
|
||||||
|
editor.updateShape<IMapShape>({
|
||||||
|
id: shape.id,
|
||||||
|
type: 'Map',
|
||||||
|
props: { tags: newTags },
|
||||||
|
});
|
||||||
|
}, [editor, shape.id]);
|
||||||
|
|
||||||
// ==========================================================================
|
// ==========================================================================
|
||||||
// Render
|
// Render
|
||||||
// ==========================================================================
|
// ==========================================================================
|
||||||
|
|
||||||
|
const contentHeight = shape.props.h;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<HTMLContainer>
|
<HTMLContainer style={{ width: shape.props.w, height: contentHeight + 40 }}>
|
||||||
<style>{`
|
<style>{`
|
||||||
.mapus-sidebar::-webkit-scrollbar { width: 6px; }
|
.mapus-sidebar::-webkit-scrollbar { width: 6px; }
|
||||||
.mapus-sidebar::-webkit-scrollbar-thumb { background: #ddd; border-radius: 3px; }
|
.mapus-sidebar::-webkit-scrollbar-thumb { background: #ddd; border-radius: 3px; }
|
||||||
.mapus-btn:hover { background: #f7f7f7 !important; }
|
.mapus-btn:hover { background: #f7f7f7 !important; }
|
||||||
.mapus-btn:active { transform: scale(0.97); }
|
.mapus-btn:active { transform: scale(0.97); }
|
||||||
.mapus-category:hover { background: #f7f7f7; }
|
.mapus-category:hover { background: #f7f7f7 !important; }
|
||||||
.mapus-annotation:hover { background: #f7f7f7; }
|
.mapus-annotation:hover { background: #f7f7f7; }
|
||||||
.mapus-result:hover { background: #f3f4f6; }
|
.mapus-result:hover { background: #f3f4f6 !important; }
|
||||||
.mapus-tool:hover { background: #f7f7f7; }
|
.mapus-tool:hover { background: #f7f7f7; }
|
||||||
.mapus-tool.active { background: #222 !important; color: #fff !important; }
|
.mapus-tool.active { background: #222 !important; color: #fff !important; }
|
||||||
.mapus-color:hover { transform: scale(1.15); }
|
.mapus-color:hover { transform: scale(1.15); }
|
||||||
|
|
@ -812,67 +864,39 @@ function MapComponent({ shape, editor }: { shape: IMapShape; editor: MapShape['e
|
||||||
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } }
|
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } }
|
||||||
`}</style>
|
`}</style>
|
||||||
|
|
||||||
|
<StandardizedToolWrapper
|
||||||
|
title={shape.props.title || 'Collaborative Map'}
|
||||||
|
primaryColor={MapShape.PRIMARY_COLOR}
|
||||||
|
isSelected={isSelected}
|
||||||
|
width={shape.props.w}
|
||||||
|
height={contentHeight + 40}
|
||||||
|
onClose={handleClose}
|
||||||
|
onMinimize={handleMinimize}
|
||||||
|
isMinimized={shape.props.isMinimized}
|
||||||
|
editor={editor}
|
||||||
|
shapeId={shape.id}
|
||||||
|
isPinnedToView={shape.props.pinnedToView}
|
||||||
|
onPinToggle={handlePinToggle}
|
||||||
|
tags={shape.props.tags || ['map']}
|
||||||
|
onTagsChange={handleTagsChange}
|
||||||
|
tagsEditable={true}
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
width: shape.props.w,
|
width: '100%',
|
||||||
height: shape.props.h,
|
height: '100%',
|
||||||
borderRadius: 8,
|
|
||||||
overflow: 'hidden',
|
|
||||||
background: '#e5e7eb',
|
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
boxShadow: '0 2px 12px rgba(0,0,0,0.15)',
|
|
||||||
fontFamily: 'Inter, -apple-system, BlinkMacSystemFont, sans-serif',
|
fontFamily: 'Inter, -apple-system, BlinkMacSystemFont, sans-serif',
|
||||||
zIndex: 1,
|
|
||||||
}}
|
}}
|
||||||
onPointerDown={capturePointerEvents}
|
|
||||||
onPointerMove={capturePointerEvents}
|
|
||||||
onPointerUp={capturePointerEvents}
|
|
||||||
onWheel={handleWheel}
|
|
||||||
onClick={stopPropagation}
|
|
||||||
onDoubleClick={stopPropagation}
|
|
||||||
onKeyDown={stopPropagation}
|
|
||||||
onContextMenu={stopPropagation}
|
|
||||||
>
|
>
|
||||||
{/* Left Sidebar */}
|
{/* Left Sidebar */}
|
||||||
{shape.props.showSidebar && (
|
{shape.props.showSidebar && (
|
||||||
<div className="mapus-sidebar" style={styles.sidebar}>
|
<div
|
||||||
{/* Map Details */}
|
className="mapus-sidebar"
|
||||||
<div style={styles.section}>
|
style={styles.sidebar}
|
||||||
<input
|
onPointerDown={stopPropagation}
|
||||||
value={shape.props.title}
|
>
|
||||||
onChange={(e) => updateTitle(e.target.value)}
|
|
||||||
onFocus={() => setEditingTitle(true)}
|
|
||||||
onBlur={() => setEditingTitle(false)}
|
|
||||||
style={{
|
|
||||||
width: '100%',
|
|
||||||
border: 'none',
|
|
||||||
fontSize: 15,
|
|
||||||
fontWeight: 600,
|
|
||||||
color: '#222',
|
|
||||||
background: 'transparent',
|
|
||||||
borderBottom: editingTitle ? '2px solid #E8E8E8' : '2px solid transparent',
|
|
||||||
outline: 'none',
|
|
||||||
padding: 0,
|
|
||||||
marginBottom: 4,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
value={shape.props.description}
|
|
||||||
onChange={(e) => updateDescription(e.target.value)}
|
|
||||||
style={{
|
|
||||||
width: '100%',
|
|
||||||
border: 'none',
|
|
||||||
fontSize: 13,
|
|
||||||
color: '#626C72',
|
|
||||||
background: 'transparent',
|
|
||||||
outline: 'none',
|
|
||||||
padding: 0,
|
|
||||||
}}
|
|
||||||
placeholder="Add a description..."
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Search */}
|
{/* Search */}
|
||||||
<div style={styles.section}>
|
<div style={styles.section}>
|
||||||
<div style={{ display: 'flex', gap: 6 }}>
|
<div style={{ display: 'flex', gap: 6 }}>
|
||||||
|
|
@ -884,6 +908,7 @@ function MapComponent({ shape, editor }: { shape: IMapShape; editor: MapShape['e
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
if (e.key === 'Enter') searchPlaces();
|
if (e.key === 'Enter') searchPlaces();
|
||||||
}}
|
}}
|
||||||
|
onPointerDown={stopPropagation}
|
||||||
placeholder="Search for a place..."
|
placeholder="Search for a place..."
|
||||||
style={{
|
style={{
|
||||||
flex: 1,
|
flex: 1,
|
||||||
|
|
@ -896,6 +921,7 @@ function MapComponent({ shape, editor }: { shape: IMapShape; editor: MapShape['e
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
onClick={searchPlaces}
|
onClick={searchPlaces}
|
||||||
|
onPointerDown={stopPropagation}
|
||||||
className="mapus-btn"
|
className="mapus-btn"
|
||||||
style={{
|
style={{
|
||||||
...styles.button,
|
...styles.button,
|
||||||
|
|
@ -915,6 +941,7 @@ function MapComponent({ shape, editor }: { shape: IMapShape; editor: MapShape['e
|
||||||
key={i}
|
key={i}
|
||||||
className="mapus-result"
|
className="mapus-result"
|
||||||
onClick={() => selectSearchResult(result)}
|
onClick={() => selectSearchResult(result)}
|
||||||
|
onPointerDown={stopPropagation}
|
||||||
style={{
|
style={{
|
||||||
padding: '10px 8px',
|
padding: '10px 8px',
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
|
|
@ -938,6 +965,7 @@ function MapComponent({ shape, editor }: { shape: IMapShape; editor: MapShape['e
|
||||||
key={cat.key}
|
key={cat.key}
|
||||||
className="mapus-category"
|
className="mapus-category"
|
||||||
onClick={() => findNearby(cat)}
|
onClick={() => findNearby(cat)}
|
||||||
|
onPointerDown={stopPropagation}
|
||||||
style={{
|
style={{
|
||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
padding: '10px 4px',
|
padding: '10px 4px',
|
||||||
|
|
@ -963,6 +991,7 @@ function MapComponent({ shape, editor }: { shape: IMapShape; editor: MapShape['e
|
||||||
key={collab.id}
|
key={collab.id}
|
||||||
className="mapus-annotation"
|
className="mapus-annotation"
|
||||||
onClick={() => observeUser(observingUser === collab.id ? null : collab.id)}
|
onClick={() => observeUser(observingUser === collab.id ? null : collab.id)}
|
||||||
|
onPointerDown={stopPropagation}
|
||||||
style={{
|
style={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
|
|
@ -1004,6 +1033,7 @@ function MapComponent({ shape, editor }: { shape: IMapShape; editor: MapShape['e
|
||||||
<div style={styles.sectionTitle}>Annotations</div>
|
<div style={styles.sectionTitle}>Annotations</div>
|
||||||
<button
|
<button
|
||||||
onClick={hideAllAnnotations}
|
onClick={hideAllAnnotations}
|
||||||
|
onPointerDown={stopPropagation}
|
||||||
className="mapus-btn"
|
className="mapus-btn"
|
||||||
style={{ ...styles.button, fontSize: 12, padding: '4px 8px', color: '#626C72' }}
|
style={{ ...styles.button, fontSize: 12, padding: '4px 8px', color: '#626C72' }}
|
||||||
>
|
>
|
||||||
|
|
@ -1021,6 +1051,7 @@ function MapComponent({ shape, editor }: { shape: IMapShape; editor: MapShape['e
|
||||||
<div
|
<div
|
||||||
key={ann.id}
|
key={ann.id}
|
||||||
className="mapus-annotation"
|
className="mapus-annotation"
|
||||||
|
onPointerDown={stopPropagation}
|
||||||
style={{
|
style={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
|
|
@ -1042,6 +1073,7 @@ function MapComponent({ shape, editor }: { shape: IMapShape; editor: MapShape['e
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={(e) => { e.stopPropagation(); toggleAnnotationVisibility(ann.id); }}
|
onClick={(e) => { e.stopPropagation(); toggleAnnotationVisibility(ann.id); }}
|
||||||
|
onPointerDown={stopPropagation}
|
||||||
className="mapus-btn"
|
className="mapus-btn"
|
||||||
style={{ ...styles.button, padding: 4, fontSize: 14 }}
|
style={{ ...styles.button, padding: 4, fontSize: 14 }}
|
||||||
>
|
>
|
||||||
|
|
@ -1049,6 +1081,7 @@ function MapComponent({ shape, editor }: { shape: IMapShape; editor: MapShape['e
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={(e) => { e.stopPropagation(); removeAnnotation(ann.id); }}
|
onClick={(e) => { e.stopPropagation(); removeAnnotation(ann.id); }}
|
||||||
|
onPointerDown={stopPropagation}
|
||||||
className="mapus-btn"
|
className="mapus-btn"
|
||||||
style={{ ...styles.button, padding: 4, fontSize: 14, color: '#E15F59' }}
|
style={{ ...styles.button, padding: 4, fontSize: 14, color: '#E15F59' }}
|
||||||
>
|
>
|
||||||
|
|
@ -1068,12 +1101,16 @@ function MapComponent({ shape, editor }: { shape: IMapShape; editor: MapShape['e
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Map Container */}
|
{/* Map Container */}
|
||||||
<div style={{ flex: 1, position: 'relative' }}>
|
<div
|
||||||
|
style={{ flex: 1, position: 'relative' }}
|
||||||
|
onWheel={handleMapWheel}
|
||||||
|
>
|
||||||
<div ref={containerRef} style={{ width: '100%', height: '100%' }} />
|
<div ref={containerRef} style={{ width: '100%', height: '100%' }} />
|
||||||
|
|
||||||
{/* Sidebar Toggle */}
|
{/* Sidebar Toggle */}
|
||||||
<button
|
<button
|
||||||
onClick={toggleSidebar}
|
onClick={toggleSidebar}
|
||||||
|
onPointerDown={stopPropagation}
|
||||||
className="mapus-btn"
|
className="mapus-btn"
|
||||||
style={{
|
style={{
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
|
|
@ -1090,18 +1127,18 @@ function MapComponent({ shape, editor }: { shape: IMapShape; editor: MapShape['e
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
fontSize: 18,
|
fontSize: 18,
|
||||||
zIndex: 10000,
|
zIndex: 10,
|
||||||
pointerEvents: 'auto',
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{shape.props.showSidebar ? '◀' : '▶'}
|
{shape.props.showSidebar ? '◀' : '▶'}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{/* Style Picker */}
|
{/* Style Picker */}
|
||||||
<div style={{ position: 'absolute', top: 10, right: 10, zIndex: 10000, pointerEvents: 'auto' }}>
|
<div style={{ position: 'absolute', top: 10, right: 10, zIndex: 10 }}>
|
||||||
<select
|
<select
|
||||||
value={styleKey}
|
value={styleKey}
|
||||||
onChange={(e) => changeStyle(e.target.value as StyleKey)}
|
onChange={(e) => changeStyle(e.target.value as StyleKey)}
|
||||||
|
onPointerDown={stopPropagation}
|
||||||
style={{
|
style={{
|
||||||
padding: '8px 12px',
|
padding: '8px 12px',
|
||||||
borderRadius: 6,
|
borderRadius: 6,
|
||||||
|
|
@ -1110,7 +1147,6 @@ function MapComponent({ shape, editor }: { shape: IMapShape; editor: MapShape['e
|
||||||
boxShadow: '0 2px 8px rgba(0,0,0,0.15)',
|
boxShadow: '0 2px 8px rgba(0,0,0,0.15)',
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
pointerEvents: 'auto',
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{Object.entries(MAP_STYLES).map(([key, style]) => (
|
{Object.entries(MAP_STYLES).map(([key, style]) => (
|
||||||
|
|
@ -1120,9 +1156,10 @@ function MapComponent({ shape, editor }: { shape: IMapShape; editor: MapShape['e
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Zoom Controls */}
|
{/* Zoom Controls */}
|
||||||
<div style={{ position: 'absolute', bottom: 80, right: 10, display: 'flex', flexDirection: 'column', gap: 4, zIndex: 10000, pointerEvents: 'auto' }}>
|
<div style={{ position: 'absolute', bottom: 80, right: 10, display: 'flex', flexDirection: 'column', gap: 4, zIndex: 10 }}>
|
||||||
<button
|
<button
|
||||||
onClick={() => mapRef.current?.zoomIn()}
|
onClick={() => mapRef.current?.zoomIn()}
|
||||||
|
onPointerDown={stopPropagation}
|
||||||
className="mapus-btn"
|
className="mapus-btn"
|
||||||
style={{
|
style={{
|
||||||
width: 36,
|
width: 36,
|
||||||
|
|
@ -1133,13 +1170,13 @@ function MapComponent({ shape, editor }: { shape: IMapShape; editor: MapShape['e
|
||||||
boxShadow: '0 2px 8px rgba(0,0,0,0.15)',
|
boxShadow: '0 2px 8px rgba(0,0,0,0.15)',
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
fontSize: 18,
|
fontSize: 18,
|
||||||
pointerEvents: 'auto',
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
+
|
+
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => mapRef.current?.zoomOut()}
|
onClick={() => mapRef.current?.zoomOut()}
|
||||||
|
onPointerDown={stopPropagation}
|
||||||
className="mapus-btn"
|
className="mapus-btn"
|
||||||
style={{
|
style={{
|
||||||
width: 36,
|
width: 36,
|
||||||
|
|
@ -1150,7 +1187,6 @@ function MapComponent({ shape, editor }: { shape: IMapShape; editor: MapShape['e
|
||||||
boxShadow: '0 2px 8px rgba(0,0,0,0.15)',
|
boxShadow: '0 2px 8px rgba(0,0,0,0.15)',
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
fontSize: 18,
|
fontSize: 18,
|
||||||
pointerEvents: 'auto',
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
−
|
−
|
||||||
|
|
@ -1165,6 +1201,7 @@ function MapComponent({ shape, editor }: { shape: IMapShape; editor: MapShape['e
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
|
onPointerDown={stopPropagation}
|
||||||
className="mapus-btn"
|
className="mapus-btn"
|
||||||
style={{
|
style={{
|
||||||
width: 36,
|
width: 36,
|
||||||
|
|
@ -1176,7 +1213,6 @@ function MapComponent({ shape, editor }: { shape: IMapShape; editor: MapShape['e
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
marginTop: 4,
|
marginTop: 4,
|
||||||
pointerEvents: 'auto',
|
|
||||||
}}
|
}}
|
||||||
title="My location"
|
title="My location"
|
||||||
>
|
>
|
||||||
|
|
@ -1185,7 +1221,7 @@ function MapComponent({ shape, editor }: { shape: IMapShape; editor: MapShape['e
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Drawing Toolbar (Mapus-style) */}
|
{/* Drawing Toolbar (Mapus-style) */}
|
||||||
<div style={styles.toolbar}>
|
<div style={styles.toolbar} onPointerDown={stopPropagation}>
|
||||||
{/* Cursor Tool */}
|
{/* Cursor Tool */}
|
||||||
<button
|
<button
|
||||||
onClick={() => setActiveTool('cursor')}
|
onClick={() => setActiveTool('cursor')}
|
||||||
|
|
@ -1324,6 +1360,7 @@ function MapComponent({ shape, editor }: { shape: IMapShape; editor: MapShape['e
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</StandardizedToolWrapper>
|
||||||
</HTMLContainer>
|
</HTMLContainer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue