UI redesign
This commit is contained in:
@@ -4,7 +4,6 @@ import { CABLE_COLORS, CABLE_LABELS } from '../../types/mapItem';
|
||||
|
||||
interface ToolbarProps {
|
||||
mapId: string;
|
||||
onShare: () => void;
|
||||
readOnly?: boolean;
|
||||
}
|
||||
|
||||
@@ -16,12 +15,20 @@ interface ToolButton {
|
||||
description: string;
|
||||
}
|
||||
|
||||
const TOOLS: ToolButton[] = [
|
||||
const SELECT_TOOL: ToolButton = {
|
||||
id: 'select',
|
||||
label: 'Select',
|
||||
icon: '👆',
|
||||
description: 'Select and edit items',
|
||||
};
|
||||
|
||||
const CONNECTION_TOOLS: ToolButton[] = [
|
||||
{
|
||||
id: 'select',
|
||||
label: 'Select',
|
||||
icon: '👆',
|
||||
description: 'Select and edit items',
|
||||
id: 'wireless_mesh',
|
||||
label: 'Wireless',
|
||||
icon: 'wireless',
|
||||
color: '#10B981',
|
||||
description: 'Wireless Mesh Link',
|
||||
},
|
||||
{
|
||||
id: 'fiber',
|
||||
@@ -44,83 +51,135 @@ const TOOLS: ToolButton[] = [
|
||||
color: CABLE_COLORS.cat6_poe,
|
||||
description: CABLE_LABELS.cat6_poe,
|
||||
},
|
||||
];
|
||||
|
||||
const DEVICE_TOOLS: ToolButton[] = [
|
||||
{
|
||||
id: 'switch',
|
||||
label: 'Switch',
|
||||
icon: '⚡',
|
||||
icon: 'switch',
|
||||
description: 'Network Switch',
|
||||
},
|
||||
{
|
||||
id: 'indoor_ap',
|
||||
label: 'Indoor AP',
|
||||
icon: '📡',
|
||||
icon: 'indoor_ap',
|
||||
description: 'Indoor Access Point',
|
||||
},
|
||||
{
|
||||
id: 'outdoor_ap',
|
||||
label: 'Outdoor AP',
|
||||
icon: '📶',
|
||||
icon: 'outdoor_ap',
|
||||
description: 'Outdoor Access Point',
|
||||
},
|
||||
{
|
||||
id: 'wireless_mesh',
|
||||
label: 'Wireless',
|
||||
icon: '⚡',
|
||||
color: '#10B981',
|
||||
description: 'Wireless Mesh Link',
|
||||
},
|
||||
];
|
||||
|
||||
export function Toolbar({ mapId, onShare, readOnly = false }: ToolbarProps) {
|
||||
export function Toolbar({ mapId, readOnly = false }: ToolbarProps) {
|
||||
const { activeTool, setActiveTool } = useDrawingStore();
|
||||
|
||||
const renderIcon = (tool: ToolButton, isDisabled: boolean) => {
|
||||
if (tool.icon === 'wireless') {
|
||||
return (
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" style={{ color: isDisabled ? undefined : tool.color }}>
|
||||
<line x1="2" y1="10" x2="18" y2="10" stroke="currentColor" strokeWidth="2" strokeDasharray="3,2" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
if (tool.icon === 'switch') {
|
||||
return (
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor" style={{ color: '#8B5CF6' }}>
|
||||
<rect x="2" y="4" width="20" height="16" rx="2" fill="none" stroke="currentColor" strokeWidth="2"/>
|
||||
<circle cx="6" cy="10" r="1.5"/>
|
||||
<circle cx="10" cy="10" r="1.5"/>
|
||||
<circle cx="14" cy="10" r="1.5"/>
|
||||
<circle cx="18" cy="10" r="1.5"/>
|
||||
<circle cx="6" cy="14" r="1.5"/>
|
||||
<circle cx="10" cy="14" r="1.5"/>
|
||||
<circle cx="14" cy="14" r="1.5"/>
|
||||
<circle cx="18" cy="14" r="1.5"/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
if (tool.icon === 'indoor_ap') {
|
||||
return (
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor" style={{ color: '#10B981' }}>
|
||||
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z" opacity="0.3"/>
|
||||
<circle cx="12" cy="12" r="3"/>
|
||||
<path d="M12 6c-3.31 0-6 2.69-6 6s2.69 6 6 6 6-2.69 6-6-2.69-6-6-6zm0 10c-2.21 0-4-1.79-4-4s1.79-4 4-4 4 1.79 4 4-1.79 4-4 4z"/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
if (tool.icon === 'outdoor_ap') {
|
||||
return (
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor" style={{ color: '#F59E0B' }}>
|
||||
<path d="M1 9l2 2c4.97-4.97 13.03-4.97 18 0l2-2C16.93 2.93 7.08 2.93 1 9z"/>
|
||||
<path d="M9 17l3 3 3-3c-1.65-1.66-4.34-1.66-6 0z"/>
|
||||
<path d="M5 13l2 2c2.76-2.76 7.24-2.76 10 0l2-2C15.14 9.14 8.87 9.14 5 13z"/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<span
|
||||
className="text-lg"
|
||||
style={tool.color && !isDisabled ? { color: tool.color } : undefined}
|
||||
>
|
||||
{tool.icon}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
const renderToolButton = (tool: ToolButton) => {
|
||||
const isDisabled = readOnly && tool.id !== 'select';
|
||||
return (
|
||||
<button
|
||||
key={tool.id}
|
||||
onClick={() => !isDisabled && setActiveTool(tool.id)}
|
||||
disabled={isDisabled}
|
||||
className={`w-full px-3 py-2 rounded text-left flex items-center gap-2 transition-colors ${
|
||||
isDisabled
|
||||
? 'opacity-50 cursor-not-allowed text-gray-400 dark:text-gray-600'
|
||||
: activeTool === tool.id
|
||||
? 'bg-blue-100 dark:bg-blue-900/30 text-blue-700 dark:text-blue-400 font-medium shadow-sm'
|
||||
: 'hover:bg-gray-100 dark:hover:bg-gray-700 text-gray-700 dark:text-gray-300'
|
||||
}`}
|
||||
title={isDisabled ? 'Not available in read-only mode' : tool.description}
|
||||
>
|
||||
{renderIcon(tool, isDisabled)}
|
||||
<span className="text-sm">{tool.label}</span>
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="bg-white shadow-lg rounded-lg p-2 space-y-1" style={{ minWidth: '150px' }}>
|
||||
<div className="bg-white dark:bg-gray-800 border-t border-gray-200 dark:border-gray-700 p-3 space-y-1 transition-colors" style={{ minWidth: '150px' }}>
|
||||
{/* Read-only indicator */}
|
||||
{readOnly && (
|
||||
<div className="w-full px-3 py-2 rounded bg-yellow-100 text-yellow-800 text-xs font-medium mb-2 text-center">
|
||||
<div className="w-full px-3 py-2 rounded bg-yellow-100 dark:bg-yellow-900/30 text-yellow-800 dark:text-yellow-400 text-xs font-medium mb-2 text-center border border-yellow-200 dark:border-yellow-800">
|
||||
Read-Only Mode
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Share button */}
|
||||
<button
|
||||
onClick={onShare}
|
||||
className="w-full px-3 py-2 rounded text-left flex items-center gap-2 transition-colors bg-green-100 text-green-700 hover:bg-green-200 font-medium mb-2"
|
||||
title="Share this map"
|
||||
>
|
||||
<span className="text-lg">🔗</span>
|
||||
<span className="text-sm">Share</span>
|
||||
</button>
|
||||
{/* Tools section header */}
|
||||
<h2 className="text-lg font-semibold mb-3 text-gray-900 dark:text-white">Tools</h2>
|
||||
|
||||
<div className="border-t border-gray-200 my-2"></div>
|
||||
{/* Select tool */}
|
||||
{renderToolButton(SELECT_TOOL)}
|
||||
|
||||
{TOOLS.map((tool) => {
|
||||
const isDisabled = readOnly && tool.id !== 'select';
|
||||
return (
|
||||
<button
|
||||
key={tool.id}
|
||||
onClick={() => !isDisabled && setActiveTool(tool.id)}
|
||||
disabled={isDisabled}
|
||||
className={`w-full px-3 py-2 rounded text-left flex items-center gap-2 transition-colors ${
|
||||
isDisabled
|
||||
? 'opacity-50 cursor-not-allowed text-gray-400'
|
||||
: activeTool === tool.id
|
||||
? 'bg-blue-100 text-blue-700 font-medium'
|
||||
: 'hover:bg-gray-100 text-gray-700'
|
||||
}`}
|
||||
title={isDisabled ? 'Not available in read-only mode' : tool.description}
|
||||
>
|
||||
<span
|
||||
className="text-lg"
|
||||
style={tool.color && !isDisabled ? { color: tool.color } : undefined}
|
||||
>
|
||||
{tool.icon}
|
||||
</span>
|
||||
<span className="text-sm">{tool.label}</span>
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
<div className="border-t border-gray-200 dark:border-gray-700 my-2"></div>
|
||||
|
||||
{/* Connections section */}
|
||||
<h3 className="text-sm font-semibold text-gray-700 dark:text-gray-300 px-1 mb-2">Connections</h3>
|
||||
{CONNECTION_TOOLS.map(renderToolButton)}
|
||||
|
||||
<div className="border-t border-gray-200 dark:border-gray-700 my-2"></div>
|
||||
|
||||
{/* Devices section */}
|
||||
<h3 className="text-sm font-semibold text-gray-700 dark:text-gray-300 px-1 mb-2">Devices</h3>
|
||||
{DEVICE_TOOLS.map(renderToolButton)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user