diff --git a/src/lib/holocene/icon/paths.ts b/src/lib/holocene/icon/paths.ts index d92b45ef13..c211d81a42 100644 --- a/src/lib/holocene/icon/paths.ts +++ b/src/lib/holocene/icon/paths.ts @@ -91,6 +91,7 @@ import merge from './svg/merge.svelte'; import microchip from './svg/microchip.svelte'; import microsoft from './svg/microsoft.svelte'; import minimize from './svg/minimize.svelte'; +import minus from './svg/minus.svelte'; import moon from './svg/moon.svelte'; import namespaceSwitcher from './svg/namespace-switcher.svelte'; import namespace from './svg/namespace.svelte'; @@ -102,6 +103,7 @@ import pencil from './svg/pencil.svelte'; import pinFilled from './svg/pin-filled.svelte'; import pin from './svg/pin.svelte'; import play from './svg/play.svelte'; +import plus from './svg/plus.svelte'; import regions from './svg/regions.svelte'; import relationship from './svg/relationship.svelte'; import retention from './svg/retention.svelte'; @@ -249,6 +251,7 @@ export const icons = { microchip, microsoft, minimize, + minus, moon, 'namespace-switcher': namespaceSwitcher, namespace, @@ -256,6 +259,7 @@ export const icons = { 'office-buildings': officeBuildings, overview, play, + plus, pause, pencil, 'pin-filled': pinFilled, diff --git a/src/lib/holocene/icon/svg/minus.svelte b/src/lib/holocene/icon/svg/minus.svelte new file mode 100644 index 0000000000..46ebb1453f --- /dev/null +++ b/src/lib/holocene/icon/svg/minus.svelte @@ -0,0 +1,8 @@ + + + + + diff --git a/src/lib/holocene/icon/svg/plus.svelte b/src/lib/holocene/icon/svg/plus.svelte new file mode 100644 index 0000000000..c840b281f3 --- /dev/null +++ b/src/lib/holocene/icon/svg/plus.svelte @@ -0,0 +1,8 @@ + + + + + diff --git a/src/lib/holocene/zoom-svg.svelte b/src/lib/holocene/zoom-svg.svelte index 0f78cc462f..da39fcc9fa 100644 --- a/src/lib/holocene/zoom-svg.svelte +++ b/src/lib/holocene/zoom-svg.svelte @@ -28,6 +28,27 @@ let panOffsetX = 0; let panOffsetY = 0; + const PAN_STEP_RATIO = 0.1; + const ZOOM_STEP = 0.1; + + function panBy(dx: number, dy: number) { + if (!pannable) return; + viewBox.x += dx * viewBox.width; + viewBox.y += dy * viewBox.height; + } + + function zoomBy(factor: number, centerX = width / 2, centerY = height / 2) { + if (!zoomable) return; + const newZoomLevel = zoomLevel + factor; + if (newZoomLevel < maxZoomIn || newZoomLevel > maxZoomOut) return; + const zoomRatio = newZoomLevel / zoomLevel; + viewBox.x = centerX - (centerX - viewBox.x) * zoomRatio; + viewBox.y = centerY - (centerY - viewBox.y) * zoomRatio; + viewBox.width *= zoomRatio; + viewBox.height *= zoomRatio; + zoomLevel = newZoomLevel; + } + const handleWheel = (event: WheelEvent) => { if (!zoomable) return; event.preventDefault(); @@ -87,22 +108,118 @@ viewBox.height = height; zoomLevel = initialZoom; } + + function handleKeydown(event: KeyboardEvent) { + switch (event.key) { + case 'ArrowUp': + event.preventDefault(); + panBy(0, -PAN_STEP_RATIO); + break; + case 'ArrowDown': + event.preventDefault(); + panBy(0, PAN_STEP_RATIO); + break; + case 'ArrowLeft': + event.preventDefault(); + panBy(-PAN_STEP_RATIO, 0); + break; + case 'ArrowRight': + event.preventDefault(); + panBy(PAN_STEP_RATIO, 0); + break; + case '+': + case '=': + event.preventDefault(); + zoomBy(-ZOOM_STEP); + break; + case '-': + case '_': + event.preventDefault(); + zoomBy(ZOOM_STEP); + break; + } + }
+ {#if pannable} + +