Merge branch 'main' into a11y
This commit is contained in:
5
.changeset/friendly-monkeys-fry.md
Normal file
5
.changeset/friendly-monkeys-fry.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@xyflow/svelte': patch
|
||||
---
|
||||
|
||||
Fix data in EdgeProps not typed correctly
|
||||
7
.changeset/grumpy-spiders-play.md
Normal file
7
.changeset/grumpy-spiders-play.md
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
'@xyflow/react': minor
|
||||
'@xyflow/svelte': minor
|
||||
'@xyflow/system': minor
|
||||
---
|
||||
|
||||
Improve typing for Nodes
|
||||
5
.changeset/hip-fireants-change.md
Normal file
5
.changeset/hip-fireants-change.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@xyflow/svelte': patch
|
||||
---
|
||||
|
||||
Fix setting nodesInitialized multiple times
|
||||
7
.changeset/late-taxis-press.md
Normal file
7
.changeset/late-taxis-press.md
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
'@xyflow/react': minor
|
||||
'@xyflow/svelte': minor
|
||||
'@xyflow/system': minor
|
||||
---
|
||||
|
||||
Add an `ease` and `interpolate` option to all function that alter the viewport
|
||||
5
.changeset/nine-crabs-help.md
Normal file
5
.changeset/nine-crabs-help.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@xyflow/svelte': patch
|
||||
---
|
||||
|
||||
Change a11y description inline styles to classes
|
||||
5
.changeset/tough-rivers-tell.md
Normal file
5
.changeset/tough-rivers-tell.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@xyflow/svelte': patch
|
||||
---
|
||||
|
||||
Export missing callback types
|
||||
@@ -20,19 +20,19 @@ That being said, our libraries are only interesting because of the people who ma
|
||||
|
||||
The things we need the most help for the library and its community are:
|
||||
|
||||
**🐛 Bug reports:** We simply can’t catch them all. Check [existing issues](https://github.com/wbkd/react-flow/issues/new/choose) and discussion first, then [create a new issue](https://github.com/wbkd/react-flow/issues/new/choose) to tell us what’s up.
|
||||
**🐛 Bug reports:** We simply can’t catch them all. Check [existing issues](https://github.com/xyflow/xyflow/issues/) and discussion first, then [create a new issue](https://github.com/xyflow/xyflow/issues/new/choose) to tell us what’s up.
|
||||
|
||||
**💬 Answering questions** in our [Discord Server](https://discord.gg/Bqt6xrs) and [Github discussions](https://github.com/wbkd/react-flow/discussions).
|
||||
**💬 Answering questions** in our [Discord Server](https://discord.gg/Bqt6xrs) and [Github discussions](https://github.com/xyflow/xyflow/discussions).
|
||||
|
||||
🎬 **Create tutorials**. Send them to us and we’ll happily share them!
|
||||
|
||||
**✏️ Edit our [Docs](https://reactflow.dev/docs/introduction/)**: Make changes in the [react-flow-docs repo](https://github.com/wbkd/react-flow-docs), or click the "edit this page” button that lives on every doc site.
|
||||
**✏️ Edit our [Docs](https://reactflow.dev/learn/concepts/introduction/)**: Make changes in our [web repo](https://github.com/xyflow/web), where our docs live.
|
||||
|
||||
All interactions should be done with care following our [Code of Conduct](https://github.com/wbkd/react-flow/blob/main/CODE_OF_CONDUCT.md).
|
||||
All interactions should be done with care following our [Code of Conduct](https://github.com/xyflow/xyflow/blob/main/CODE_OF_CONDUCT.md).
|
||||
|
||||
## Enhancements
|
||||
|
||||
If you have an idea or suggestion for an enhancement to the React Flow or Svelte Flow library, please use the [New Features](https://github.com/wbkd/react-flow/discussions/categories/new-features) discussion section. If you want to build it yourself, **please reach out to us before you dive into a new pull request.** The direction of React Flow/ Svelte Flow and which new features are added are discussed in our Discord Server and in [this Github discussions section](https://github.com/wbkd/react-flow/discussions/categories/new-features), and in the end they are decided by the core team.
|
||||
If you have an idea or suggestion for an enhancement to the React Flow or Svelte Flow library, please use the [New Features](https://github.com/xyflow/xyflow/discussions/categories/new-features) discussion section. If you want to build it yourself, **please reach out to us before you dive into a new pull request.** The direction of React Flow/ Svelte Flow and which new features are added are discussed in our Discord Server and in [this Github discussions section](https://github.com/xyflow/xyflow/discussions/categories/new-features), and in the end they are decided by the core team.
|
||||
|
||||
Talking to us first about the enhancement you want to build will be the most likely way to get your pull request into the library (see Our Contributing Philosophy above). We would hate to see you write code you’re proud of, just to learn that we’ve already been working on the same thing, or that we feel doesn’t fit into the core library.
|
||||
|
||||
|
||||
11
README.md
11
README.md
@@ -159,16 +159,9 @@ For releasing packages we are using [changesets](https://github.com/changesets/c
|
||||
3. changset creates a PR that bumps all packages based on the changesets
|
||||
4. merge changeset PR if you want to release to Github and npm
|
||||
|
||||
## The xyflow team
|
||||
|
||||
React Flow and Svelte Flow are maintained by the team behind [xyflow](https://xyflow.com). If you need help or want to talk to us about a collaboration, reach out through our [contact form](https://xyflow.com/contact) or by joining our [Discord Server](https://discord.gg/Bqt6xrs).
|
||||
|
||||
- Christopher • [Twitter](https://twitter.com/chrtze) • [Github](https://github.com/chrtze)
|
||||
- Hayleigh • [Twitter](https://twitter.com/hayleighdotdev) • [Github](https://github.com/hayleigh-dot-dev)
|
||||
- Abbey • [Github](https://github.com/printerscanner)
|
||||
- Moritz • [Twitter](https://twitter.com/moklick) • [Github](https://github.com/moklick)
|
||||
- Peter • [Github](https://github.com/peterkogo)
|
||||
## Built by [xyflow](https://xyflow.com)
|
||||
|
||||
React Flow and Svelte Flow are maintained by the [xyflow team](https://xyflow.com/about). If you need help or want to talk to us about a collaboration, reach out through our [contact form](https://xyflow.com/contact) or by joining our [Discord Server](https://discord.gg/Bqt6xrs).
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -170,7 +170,6 @@ const UseZoomPanHelperFlow = () => {
|
||||
onConnect={onConnect}
|
||||
onPaneClick={onPaneClick}
|
||||
fitView
|
||||
fitViewOptions={{ duration: 1200, padding: 0.2 }}
|
||||
maxZoom={Infinity}
|
||||
>
|
||||
<Panel position="top-right">
|
||||
@@ -183,7 +182,16 @@ const UseZoomPanHelperFlow = () => {
|
||||
console.log('fit view success');
|
||||
}}
|
||||
>
|
||||
fitView
|
||||
fitView default
|
||||
</button>
|
||||
<button
|
||||
onClick={async () => {
|
||||
console.log('fit view start');
|
||||
await fitView({ duration: 1200, padding: 0.3, ease: (t) => +t });
|
||||
console.log('fit view success');
|
||||
}}
|
||||
>
|
||||
fitView linear
|
||||
</button>
|
||||
<button onClick={onAddNode}>add node</button>
|
||||
<button onClick={onResetNodes}>reset nodes</button>
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
</script>
|
||||
|
||||
<main>
|
||||
<SvelteFlow bind:nodes bind:edges fitView>
|
||||
<SvelteFlow bind:nodes bind:edges fitView maxZoom={4}>
|
||||
<Controls />
|
||||
<Background variant={BackgroundVariant.Dots} />
|
||||
<MiniMap />
|
||||
|
||||
@@ -30,7 +30,10 @@
|
||||
<button onclick={() => zoomIn()}>zoom in</button>
|
||||
<button onclick={() => zoomOut({ duration: 1000 })}>zoom out transition</button>
|
||||
<button onclick={() => setZoom(2)}>set zoom</button>
|
||||
<button onclick={() => fitView()}>fitView</button>
|
||||
<button onclick={() => fitView({ duration: 600 })}>fitView</button>
|
||||
<button onclick={() => fitView({ duration: 600, ease: (t) => +t, interpolate: 'linear' })}
|
||||
>fitView linear</button
|
||||
>
|
||||
<button onclick={() => setCenter(0, 0)}>setCenter 0, 0</button>
|
||||
<button onclick={() => setViewport({ x: 100, y: 100, zoom: 2 })}>setViewport</button>
|
||||
<button onclick={() => console.log(getViewport())}>getViewport</button>
|
||||
|
||||
@@ -53,7 +53,7 @@ const useViewportHelper = (): ViewportHelperFunctions => {
|
||||
y: viewport.y ?? tY,
|
||||
zoom: viewport.zoom ?? tZoom,
|
||||
},
|
||||
{ duration: options?.duration }
|
||||
options
|
||||
);
|
||||
|
||||
return Promise.resolve(true);
|
||||
@@ -78,7 +78,7 @@ const useViewportHelper = (): ViewportHelperFunctions => {
|
||||
y: centerY,
|
||||
zoom: nextZoom,
|
||||
},
|
||||
{ duration: options?.duration }
|
||||
{ duration: options?.duration, ease: options?.ease, interpolate: options?.interpolate }
|
||||
);
|
||||
|
||||
return Promise.resolve(true);
|
||||
@@ -91,7 +91,11 @@ const useViewportHelper = (): ViewportHelperFunctions => {
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
await panZoom.setViewport(viewport, { duration: options?.duration });
|
||||
await panZoom.setViewport(viewport, {
|
||||
duration: options?.duration,
|
||||
ease: options?.ease,
|
||||
interpolate: options?.interpolate,
|
||||
});
|
||||
|
||||
return Promise.resolve(true);
|
||||
},
|
||||
|
||||
@@ -149,6 +149,7 @@ export type ViewportHelperFunctions = {
|
||||
*
|
||||
* @param viewport - the viewport to set
|
||||
* @param options.duration - optional duration. If set, a transition will be applied
|
||||
* @param options.ease - optional ease function.
|
||||
*/
|
||||
setViewport: SetViewport;
|
||||
/**
|
||||
@@ -164,6 +165,8 @@ export type ViewportHelperFunctions = {
|
||||
* @param x - x position
|
||||
* @param y - y position
|
||||
* @param options.zoom - optional zoom
|
||||
* @param options.duration - optional duration. If set, a transition will be applied
|
||||
* @param options.ease - optional ease function.
|
||||
*/
|
||||
setCenter: SetCenter;
|
||||
/**
|
||||
@@ -173,6 +176,8 @@ export type ViewportHelperFunctions = {
|
||||
*
|
||||
* @param bounds - the bounds ({ x: number, y: number, width: number, height: number }) to fit the view to
|
||||
* @param options.padding - optional padding
|
||||
* @param options.duration - optional duration. If set, a transition will be applied
|
||||
* @param options.ease - optional ease function.
|
||||
*/
|
||||
fitBounds: FitBounds;
|
||||
/**
|
||||
|
||||
@@ -12,7 +12,7 @@ import { NodeTypes } from './general';
|
||||
*/
|
||||
export type Node<
|
||||
NodeData extends Record<string, unknown> = Record<string, unknown>,
|
||||
NodeType extends string = string
|
||||
NodeType extends string | undefined = string | undefined
|
||||
> = NodeBase<NodeData, NodeType> & {
|
||||
style?: CSSProperties;
|
||||
className?: string;
|
||||
|
||||
@@ -1,21 +1,18 @@
|
||||
<script lang="ts" generics="NodeType extends Node = Node, EdgeType extends Edge = Edge">
|
||||
import { derived } from 'svelte/store';
|
||||
import type { SvelteFlowStore } from '$lib/store/types';
|
||||
import type { Node, Edge } from '$lib/types';
|
||||
import type { Node, Edge } from '$lib/types';
|
||||
import { ARIA_EDGE_DESC_KEY, ARIA_LIVE_MESSAGE, ARIA_NODE_DESC_KEY } from '.';
|
||||
|
||||
let { store }: { store: SvelteFlowStore<NodeType, EdgeType> } = $props();
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<div id={`${ARIA_NODE_DESC_KEY}-${store.flowId}`} style="display: none;">
|
||||
<div id={`${ARIA_NODE_DESC_KEY}-${store.flowId}`} class="a11y-hidden">
|
||||
{store.disableKeyboardA11y
|
||||
? store.labelConfig['a11yDescription.node.default']
|
||||
: store.labelConfig['a11yDescription.node.keyboardDisabled']}
|
||||
</div>
|
||||
<div id={`${ARIA_EDGE_DESC_KEY}-${store.flowId}`} style="display: none;">
|
||||
<div id={`${ARIA_EDGE_DESC_KEY}-${store.flowId}`} class="a11y-hidden">
|
||||
{store.labelConfig['a11yDescription.edge.default']}
|
||||
</div>
|
||||
|
||||
@@ -24,8 +21,26 @@
|
||||
id={`${ARIA_LIVE_MESSAGE}-${store.flowId}`}
|
||||
aria-live="assertive"
|
||||
aria-atomic="true"
|
||||
style="position: absolute; width: 1px; height: 1px; margin: -1px; border: 0; padding: 0; overflow: hidden; clip: rect(0px, 0px, 0px, 0px); clip-path: inset(100%);"
|
||||
class="a11y-live-msg"
|
||||
>
|
||||
{store.ariaLiveMessage}
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.a11y-hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.a11y-live-msg {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
margin: -1px;
|
||||
border: 0;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
clip: rect(0px, 0px, 0px, 0px);
|
||||
clip-path: inset(100%);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -360,7 +360,7 @@ export function useSvelteFlow<NodeType extends Node = Node, EdgeType extends Edg
|
||||
y: nextViewport.y ?? currentViewport.y,
|
||||
zoom: nextViewport.zoom ?? currentViewport.zoom
|
||||
},
|
||||
{ duration: options?.duration }
|
||||
options
|
||||
);
|
||||
|
||||
return Promise.resolve(true);
|
||||
@@ -380,7 +380,7 @@ export function useSvelteFlow<NodeType extends Node = Node, EdgeType extends Edg
|
||||
y: store.height / 2 - y * nextZoom,
|
||||
zoom: nextZoom
|
||||
},
|
||||
{ duration: options?.duration }
|
||||
{ duration: options?.duration, ease: options?.ease, interpolate: options?.interpolate }
|
||||
);
|
||||
|
||||
return Promise.resolve(true);
|
||||
@@ -402,7 +402,11 @@ export function useSvelteFlow<NodeType extends Node = Node, EdgeType extends Edg
|
||||
options?.padding ?? 0.1
|
||||
);
|
||||
|
||||
await store.panZoom.setViewport(viewport, { duration: options?.duration });
|
||||
await store.panZoom.setViewport(viewport, {
|
||||
duration: options?.duration,
|
||||
ease: options?.ease,
|
||||
interpolate: options?.interpolate
|
||||
});
|
||||
|
||||
return Promise.resolve(true);
|
||||
},
|
||||
|
||||
@@ -55,10 +55,10 @@ export type {
|
||||
EdgeTypes,
|
||||
DefaultEdgeOptions
|
||||
} from '$lib/types/edges';
|
||||
export type { FitViewOptions, OnBeforeDelete } from '$lib/types/general';
|
||||
export type * from '$lib/types/general';
|
||||
export type { Node, NodeTypes, BuiltInNode, NodeProps, InternalNode } from '$lib/types/nodes';
|
||||
export type { SvelteFlowStore } from '$lib/store/types';
|
||||
export * from '$lib/types/events';
|
||||
export type { SvelteFlowStore } from '$lib/store/types';
|
||||
|
||||
// system types
|
||||
export {
|
||||
|
||||
@@ -108,10 +108,6 @@ export function createStore<NodeType extends Node = Node, EdgeType extends Edge
|
||||
}
|
||||
|
||||
store.nodes = store.nodes.map((node) => newNodes.get(node.id) ?? node);
|
||||
|
||||
if (!store.nodesInitialized) {
|
||||
store.nodesInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
function fitView(options?: FitViewOptions) {
|
||||
|
||||
@@ -85,8 +85,6 @@ export type EdgeProps<EdgeType extends Edge = Edge> = Omit<
|
||||
'sourceHandle' | 'targetHandle'
|
||||
> &
|
||||
EdgePosition & {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
data?: any;
|
||||
type: string;
|
||||
markerStart?: string;
|
||||
markerEnd?: string;
|
||||
|
||||
@@ -16,7 +16,7 @@ export type InternalNode<NodeType extends Node = Node> = InternalNodeBase<NodeTy
|
||||
*/
|
||||
export type Node<
|
||||
NodeData extends Record<string, unknown> = Record<string, unknown>,
|
||||
NodeType extends string = string
|
||||
NodeType extends string | undefined = string | undefined
|
||||
> = NodeBase<NodeData, NodeType> & {
|
||||
class?: ClassValue;
|
||||
style?: string;
|
||||
|
||||
@@ -51,10 +51,12 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/d3-drag": "^3.0.7",
|
||||
"@types/d3-interpolate": "^3.0.4",
|
||||
"@types/d3-selection": "^3.0.10",
|
||||
"@types/d3-transition": "^3.0.8",
|
||||
"@types/d3-zoom": "^3.0.8",
|
||||
"d3-drag": "^3.0.0",
|
||||
"d3-interpolate": "^3.0.1",
|
||||
"d3-selection": "^3.0.0",
|
||||
"d3-zoom": "^3.0.0"
|
||||
},
|
||||
|
||||
@@ -130,6 +130,8 @@ export type FitViewOptionsBase<NodeType extends NodeBase = NodeBase> = {
|
||||
minZoom?: number;
|
||||
maxZoom?: number;
|
||||
duration?: number;
|
||||
ease?: (t: number) => number;
|
||||
interpolate?: 'smooth' | 'linear';
|
||||
nodes?: (NodeType | { id: string })[];
|
||||
};
|
||||
|
||||
@@ -173,6 +175,8 @@ export enum PanOnScrollMode {
|
||||
*/
|
||||
export type ViewportHelperFunctionOptions = {
|
||||
duration?: number;
|
||||
ease?: (t: number) => number;
|
||||
interpolate?: 'smooth' | 'linear';
|
||||
};
|
||||
|
||||
export type SetCenterOptions = ViewportHelperFunctionOptions & {
|
||||
|
||||
@@ -10,7 +10,7 @@ import { Optional } from '../utils/types';
|
||||
*/
|
||||
export type NodeBase<
|
||||
NodeData extends Record<string, unknown> = Record<string, unknown>,
|
||||
NodeType extends string = string
|
||||
NodeType extends string | undefined = string | undefined
|
||||
> = {
|
||||
/** Unique id of a node. */
|
||||
id: string;
|
||||
@@ -21,8 +21,6 @@ export type NodeBase<
|
||||
position: XYPosition;
|
||||
/** Arbitrary data passed to a node. */
|
||||
data: NodeData;
|
||||
/** Type of node defined in `nodeTypes`. */
|
||||
type?: NodeType;
|
||||
/**
|
||||
* Only relevant for default, source, target nodeType. Controls source position.
|
||||
* @example 'right', 'left', 'top', 'bottom'
|
||||
@@ -79,7 +77,15 @@ export type NodeBase<
|
||||
width?: number;
|
||||
height?: number;
|
||||
};
|
||||
};
|
||||
} & (undefined extends NodeType
|
||||
? {
|
||||
/** Type of node defined in nodeTypes */
|
||||
type?: string | undefined;
|
||||
}
|
||||
: {
|
||||
/** Type of node defined in nodeTypes */
|
||||
type: NodeType;
|
||||
});
|
||||
|
||||
export type InternalNodeBase<NodeType extends NodeBase = NodeBase> = Omit<NodeType, 'measured'> & {
|
||||
measured: {
|
||||
|
||||
@@ -20,6 +20,8 @@ export type PanZoomParams = {
|
||||
|
||||
export type PanZoomTransformOptions = {
|
||||
duration?: number;
|
||||
ease?: (t: number) => number;
|
||||
interpolate?: 'smooth' | 'linear';
|
||||
};
|
||||
|
||||
export type OnPanZoom = (event: MouseEvent | TouchEvent | null, viewport: Viewport) => void;
|
||||
|
||||
@@ -378,7 +378,11 @@ export async function fitViewport<
|
||||
options?.padding ?? 0.1
|
||||
);
|
||||
|
||||
await panZoom.setViewport(viewport, { duration: options?.duration });
|
||||
await panZoom.setViewport(viewport, {
|
||||
duration: options?.duration,
|
||||
ease: options?.ease,
|
||||
interpolate: options?.interpolate,
|
||||
});
|
||||
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { type ZoomTransform, zoom, zoomTransform } from 'd3-zoom';
|
||||
import { select } from 'd3-selection';
|
||||
import { interpolateZoom, interpolate } from 'd3-interpolate';
|
||||
|
||||
import {
|
||||
type CoordinateExtent,
|
||||
@@ -19,6 +20,7 @@ import {
|
||||
createZoomOnScrollHandler,
|
||||
} from './eventhandler';
|
||||
import { createFilter } from './filter';
|
||||
import { transition } from 'd3-transition';
|
||||
|
||||
export type ZoomPanValues = {
|
||||
isZoomingOrPanning: boolean;
|
||||
@@ -78,8 +80,8 @@ export function XYPanZoom({
|
||||
function setTransform(transform: ZoomTransform, options?: PanZoomTransformOptions) {
|
||||
if (d3Selection) {
|
||||
return new Promise<boolean>((resolve) => {
|
||||
d3ZoomInstance?.transform(
|
||||
getD3Transition(d3Selection, options?.duration, () => resolve(true)),
|
||||
d3ZoomInstance?.interpolate(options?.interpolate === 'linear' ? interpolate : interpolateZoom).transform(
|
||||
getD3Transition(d3Selection, options?.duration, options?.ease, () => resolve(true)),
|
||||
transform
|
||||
);
|
||||
});
|
||||
@@ -114,22 +116,22 @@ export function XYPanZoom({
|
||||
|
||||
const wheelHandler = isPanOnScroll
|
||||
? createPanOnScrollHandler({
|
||||
zoomPanValues,
|
||||
noWheelClassName,
|
||||
d3Selection,
|
||||
d3Zoom: d3ZoomInstance,
|
||||
panOnScrollMode,
|
||||
panOnScrollSpeed,
|
||||
zoomOnPinch,
|
||||
onPanZoomStart,
|
||||
onPanZoom,
|
||||
onPanZoomEnd,
|
||||
})
|
||||
zoomPanValues,
|
||||
noWheelClassName,
|
||||
d3Selection,
|
||||
d3Zoom: d3ZoomInstance,
|
||||
panOnScrollMode,
|
||||
panOnScrollSpeed,
|
||||
zoomOnPinch,
|
||||
onPanZoomStart,
|
||||
onPanZoom,
|
||||
onPanZoomEnd,
|
||||
})
|
||||
: createZoomOnScrollHandler({
|
||||
noWheelClassName,
|
||||
preventScrolling,
|
||||
d3ZoomHandler,
|
||||
});
|
||||
noWheelClassName,
|
||||
preventScrolling,
|
||||
d3ZoomHandler,
|
||||
});
|
||||
|
||||
d3Selection.on('wheel.zoom', wheelHandler, { passive: false });
|
||||
|
||||
@@ -242,8 +244,8 @@ export function XYPanZoom({
|
||||
function scaleTo(zoom: number, options?: PanZoomTransformOptions) {
|
||||
if (d3Selection) {
|
||||
return new Promise<boolean>((resolve) => {
|
||||
d3ZoomInstance?.scaleTo(
|
||||
getD3Transition(d3Selection, options?.duration, () => resolve(true)),
|
||||
d3ZoomInstance?.interpolate(options?.interpolate === 'linear' ? interpolate : interpolateZoom).scaleTo(
|
||||
getD3Transition(d3Selection, options?.duration, options?.ease, () => resolve(true)),
|
||||
zoom
|
||||
);
|
||||
});
|
||||
@@ -255,8 +257,8 @@ export function XYPanZoom({
|
||||
function scaleBy(factor: number, options?: PanZoomTransformOptions) {
|
||||
if (d3Selection) {
|
||||
return new Promise<boolean>((resolve) => {
|
||||
d3ZoomInstance?.scaleBy(
|
||||
getD3Transition(d3Selection, options?.duration, () => resolve(true)),
|
||||
d3ZoomInstance?.interpolate(options?.interpolate === 'linear' ? interpolate : interpolateZoom).scaleBy(
|
||||
getD3Transition(d3Selection, options?.duration, options?.ease, () => resolve(true)),
|
||||
factor
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { type ZoomTransform, zoomIdentity } from 'd3-zoom';
|
||||
import { transition } from 'd3-transition';
|
||||
|
||||
import { type D3SelectionInstance, type Viewport } from '../types';
|
||||
import { isMacOs } from '../utils';
|
||||
@@ -21,14 +22,17 @@ export const isWrappedWithClass = (event: any, className: string | undefined) =>
|
||||
export const isRightClickPan = (panOnDrag: boolean | number[], usedButton: number) =>
|
||||
usedButton === 2 && Array.isArray(panOnDrag) && panOnDrag.includes(2);
|
||||
|
||||
export const getD3Transition = (selection: D3SelectionInstance, duration = 0, onEnd = () => {}) => {
|
||||
// taken from d3-ease: https://github.com/d3/d3-ease/blob/main/src/cubic.js
|
||||
const defaultEase = (t: number) => ((t *= 2) <= 1 ? t * t * t : (t -= 2) * t * t + 2) / 2;
|
||||
|
||||
export const getD3Transition = (selection: D3SelectionInstance, duration = 0, ease = defaultEase, onEnd = () => {}) => {
|
||||
const hasDuration = typeof duration === 'number' && duration > 0;
|
||||
|
||||
if (!hasDuration) {
|
||||
onEnd();
|
||||
}
|
||||
|
||||
return hasDuration ? selection.transition().duration(duration).on('end', onEnd) : selection;
|
||||
return hasDuration ? selection.transition().duration(duration).ease(ease).on('end', onEnd) : selection;
|
||||
};
|
||||
|
||||
export const wheelDelta = (event: any) => {
|
||||
|
||||
6
pnpm-lock.yaml
generated
6
pnpm-lock.yaml
generated
@@ -372,6 +372,9 @@ importers:
|
||||
'@types/d3-drag':
|
||||
specifier: ^3.0.7
|
||||
version: 3.0.7
|
||||
'@types/d3-interpolate':
|
||||
specifier: ^3.0.4
|
||||
version: 3.0.4
|
||||
'@types/d3-selection':
|
||||
specifier: ^3.0.10
|
||||
version: 3.0.10
|
||||
@@ -384,6 +387,9 @@ importers:
|
||||
d3-drag:
|
||||
specifier: ^3.0.0
|
||||
version: 3.0.0
|
||||
d3-interpolate:
|
||||
specifier: ^3.0.1
|
||||
version: 3.0.1
|
||||
d3-selection:
|
||||
specifier: ^3.0.0
|
||||
version: 3.0.0
|
||||
|
||||
Reference in New Issue
Block a user