Metro map interaction redesign: fit-to-view zoom, grid, branch handles, custom tracks
Phase 1 — Fit-to-view zoom: - computeFitTransform() calculates bounding box and scales to fit all nodes - Replaces hardcoded scale=1 reset in animateZoomReset() and initCanvas() - Dim 1 no longer appears tiny after zooming out from dim 2 Phase 2 — Grid system: - Shared gridConstants.js (GRID=50, GRID_STEP_X=200, GRID_STEP_Y=150) - MapDataService snapToGrid() aligns all node positions server-side - Canvas renders subtle grid lines (shown on interaction only, with fade) - Line highlighting support via setHighlightedLine() for FAB hover Phase 3 — Branch handles: - Hover any station node → 3 "+" handles appear (0°/45°/315°) - 0° extends the current line, 45°/315° fork to create new branch - Ghost preview (dashed line + circle) on handle hover - Handles only show at unoccupied grid positions - Grid fades in during handle interaction, fades out after Phase 4 — Custom tracks database: - metro_lines table (project_id, naam, color, type, order) - metro_nodes table (metro_line_id, naam, status, x, y, order) - MetroLine + MetroNode models, controllers, routes - Project.metroLines() relationship added Phase 5+6 — FAB redesign + MetroMap wiring: - FAB shows "Nieuw thema (lijn)" at root, "Nieuwe lijn" in project dim - Track creation modal with retro-styled form - MetroMap handles create-node events from branch handles - Extend (0°) opens commitment/document form, fork opens track form - Canvas context menu replaced with "hover to branch" hint Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -8,38 +8,44 @@ const props = defineProps({
|
||||
})
|
||||
|
||||
const emit = defineEmits([
|
||||
'create-project',
|
||||
'create-theme',
|
||||
'create-commitment',
|
||||
'create-document',
|
||||
'create-track',
|
||||
'item-hover',
|
||||
'item-leave',
|
||||
])
|
||||
|
||||
const menuOpen = ref(false)
|
||||
|
||||
const toggle = () => {
|
||||
menuOpen.value = !menuOpen.value
|
||||
if (!menuOpen.value) emit('item-leave')
|
||||
}
|
||||
|
||||
/** Options change based on which dimension we're in */
|
||||
/** Menu items adapt to the current dimension */
|
||||
const menuItems = computed(() => {
|
||||
if (props.depth > 1 && props.parentEntityType === 'project') {
|
||||
// Inside a project dimension: create project-level items
|
||||
return [
|
||||
{ label: 'Nieuw commitment', event: 'create-commitment' },
|
||||
{ label: 'Nieuw document', event: 'create-document' },
|
||||
{ label: 'Nieuwe lijn', event: 'create-track', color: null, icon: '═' },
|
||||
]
|
||||
}
|
||||
// Root dimension: create top-level items
|
||||
return [
|
||||
{ label: 'Nieuw project', event: 'create-project' },
|
||||
{ label: 'Nieuw thema', event: 'create-theme' },
|
||||
{ label: 'Nieuw thema (lijn)', event: 'create-theme', color: '#00d2ff', icon: '═' },
|
||||
]
|
||||
})
|
||||
|
||||
const handleItemClick = (item) => {
|
||||
menuOpen.value = false
|
||||
emit('item-leave')
|
||||
emit(item.event)
|
||||
}
|
||||
|
||||
const handleItemHover = (item) => {
|
||||
emit('item-hover', item)
|
||||
}
|
||||
|
||||
const handleItemLeave = () => {
|
||||
emit('item-leave')
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -52,9 +58,14 @@ const handleItemClick = (item) => {
|
||||
:key="item.event"
|
||||
class="fab-menu-item"
|
||||
@click="handleItemClick(item)"
|
||||
@mouseenter="handleItemHover(item)"
|
||||
@mouseleave="handleItemLeave"
|
||||
>
|
||||
<span class="fab-menu-icon">+</span>
|
||||
<span class="fab-menu-icon" :style="item.color ? { color: item.color } : {}">
|
||||
{{ item.icon }}
|
||||
</span>
|
||||
<span class="fab-menu-label">{{ item.label }}</span>
|
||||
<span v-if="item.color" class="fab-color-dot" :style="{ background: item.color }"></span>
|
||||
</button>
|
||||
</div>
|
||||
</Transition>
|
||||
@@ -170,6 +181,13 @@ const handleItemClick = (item) => {
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.fab-color-dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
box-shadow: 0 0 6px currentColor;
|
||||
}
|
||||
|
||||
/* Menu transition */
|
||||
.fab-menu-enter-active,
|
||||
.fab-menu-leave-active {
|
||||
|
||||
Reference in New Issue
Block a user