feat(utils): vanilla interactive minimap

This commit is contained in:
moklick
2023-06-05 17:32:40 +02:00
parent 1b4f4a034e
commit e0ccc0f980
22 changed files with 584 additions and 475 deletions
@@ -12,6 +12,7 @@
import { useStore } from '$lib/store';
import { Panel } from '$lib/container/Panel';
import MinimapNode from './MinimapNode.svelte';
import interactive from './interactive'
import type { GetMiniMapNodeAttribute, MiniMapProps } from './types';
type $$Props = MiniMapProps;
@@ -30,13 +31,17 @@
let maskStrokeWidth: $$Props['maskStrokeWidth'] = 1;
let width: $$Props['width'] = undefined;
let height: $$Props['height'] = undefined;
let pannable: $$Props['pannable'] = true;
let zoomable: $$Props['zoomable'] = true;
let inversePan: $$Props['inversePan'] = undefined;
let zoomStep: $$Props['zoomStep'] = undefined;
let className: $$Props['class'] = '';
export { className as class };
const defaultWidth = 200;
const defaultHeight = 150;
const { nodes, transform, width: containerWidth, height: containerHeight, flowId } = useStore();
const { nodes, transform, width: containerWidth, height: containerHeight, flowId, panZoom, translateExtent } = useStore();
const nodeColorFunc = getAttrFunction(nodeColor);
const nodeStrokeColorFunc = getAttrFunction(nodeStrokeColor);
@@ -65,6 +70,8 @@
$: y = boundingRect.y - (viewHeight - boundingRect.height) / 2 - offset;
$: viewboxWidth = viewWidth + offset * 2;
$: viewboxHeight = viewHeight + offset * 2;
const getViewScale = () => viewScale;
</script>
<Panel
@@ -73,44 +80,58 @@
style={`background-color: ${bgColor}; ${style}`}
data-testid="svelte-flow__minimap"
>
<svg
width={elementWidth}
height={elementHeight}
viewBox={`${x} ${y} ${viewboxWidth} ${viewboxHeight}`}
role="img"
aria-labelledby={labelledBy}
>
{#if ariaLabel}<title id={labelledBy}>{ariaLabel}</title>{/if}
{#if $panZoom}
<svg
width={elementWidth}
height={elementHeight}
viewBox={`${x} ${y} ${viewboxWidth} ${viewboxHeight}`}
role="img"
aria-labelledby={labelledBy}
use:interactive={{
panZoom: $panZoom,
transform,
getViewScale,
translateExtent: $translateExtent,
width: $containerWidth,
height: $containerHeight,
inversePan,
zoomStep,
pannable,
zoomable,
}}
>
{#if ariaLabel}<title id={labelledBy}>{ariaLabel}</title>{/if}
{#each $nodes as node (node.id)}
{#if node.width && node.height}
{@const pos = getNodePositionWithOrigin(node).positionAbsolute}
<MinimapNode
x={pos.x}
y={pos.y}
width={node.width}
height={node.height}
color={nodeColorFunc(node)}
borderRadius={nodeBorderRadius}
strokeColor={nodeStrokeColorFunc(node)}
strokeWidth={nodeStrokeWidth}
{shapeRendering}
class={nodeClassNameFunc(node)}
style={{}}
/>
{/if}
{/each}
<path
class="svelte-flow__minimap-mask"
d={`M${x - offset},${y - offset}h${viewboxWidth + offset * 2}v${viewboxHeight + offset * 2}h${
-viewboxWidth - offset * 2
}z
M${viewBB.x},${viewBB.y}h${viewBB.width}v${viewBB.height}h${-viewBB.width}z`}
fill={maskColor}
fill-rule="evenodd"
stroke={maskStrokeColor}
stroke-width={maskStrokeWidth}
pointer-events="none"
/>
</svg>
{#each $nodes as node (node.id)}
{#if node.width && node.height}
{@const pos = getNodePositionWithOrigin(node).positionAbsolute}
<MinimapNode
x={pos.x}
y={pos.y}
width={node.width}
height={node.height}
color={nodeColorFunc(node)}
borderRadius={nodeBorderRadius}
strokeColor={nodeStrokeColorFunc(node)}
strokeWidth={nodeStrokeWidth}
{shapeRendering}
class={nodeClassNameFunc(node)}
style={{}}
/>
{/if}
{/each}
<path
class="svelte-flow__minimap-mask"
d={`M${x - offset},${y - offset}h${viewboxWidth + offset * 2}v${viewboxHeight + offset * 2}h${
-viewboxWidth - offset * 2
}z
M${viewBB.x},${viewBB.y}h${viewBB.width}v${viewBB.height}h${-viewBB.width}z`}
fill={maskColor}
fill-rule="evenodd"
stroke={maskStrokeColor}
stroke-width={maskStrokeWidth}
pointer-events="none"
/>
</svg>
{/if}
</Panel>
@@ -1 +1 @@
export { default as Minimap } from './Minimap.svelte';
export { default as MiniMap } from './Minimap.svelte';
@@ -0,0 +1,41 @@
import { get, type Writable } from 'svelte/store';
import {
XYMinimap,
type PanZoomInstance,
type Transform,
type XYMinimapUpdate
} from '@xyflow/system';
export type UseInteractiveParams = {
panZoom: PanZoomInstance;
transform: Writable<Transform>;
getViewScale: () => number;
} & XYMinimapUpdate;
export default function interactive(domNode: Element, params: UseInteractiveParams) {
const minimap = XYMinimap({
domNode,
panZoom: params.panZoom,
getTransform: () => get(params.transform),
getViewScale: params.getViewScale
});
function update(params: UseInteractiveParams) {
minimap.update({
translateExtent: params.translateExtent,
width: params.width,
height: params.height,
inversePan: params.inversePan,
zoomStep: params.zoomStep,
pannable: params.pannable,
zoomable: params.zoomable
});
}
return {
update,
destroy() {
minimap.destroy();
}
};
}
@@ -22,6 +22,8 @@ export type MiniMapProps = {
height?: number;
// onClick?: (event: MouseEvent, position: XYPosition) => void;
// onNodeClick?: (event: MouseEvent, node: Node) => void;
// pannable?: boolean;
// zoomable?: boolean;
pannable?: boolean;
zoomable?: boolean;
inversePan?: boolean;
zoomStep?: number;
};