Merge branch 'main' into a11y
This commit is contained in:
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
'@xyflow/svelte': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Fix data in EdgeProps not typed correctly
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
'@xyflow/react': minor
|
||||||
|
'@xyflow/svelte': minor
|
||||||
|
'@xyflow/system': minor
|
||||||
|
---
|
||||||
|
|
||||||
|
Improve typing for Nodes
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
'@xyflow/svelte': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Fix setting nodesInitialized multiple times
|
||||||
@@ -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
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
'@xyflow/svelte': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Change a11y description inline styles to classes
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
'@xyflow/svelte': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Export missing callback types
|
||||||
+5
-5
@@ -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:
|
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!
|
🎬 **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
|
## 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.
|
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.
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
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
|
4. merge changeset PR if you want to release to Github and npm
|
||||||
|
|
||||||
## The xyflow team
|
## Built by [xyflow](https://xyflow.com)
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
|
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
|
## License
|
||||||
|
|
||||||
|
|||||||
@@ -170,7 +170,6 @@ const UseZoomPanHelperFlow = () => {
|
|||||||
onConnect={onConnect}
|
onConnect={onConnect}
|
||||||
onPaneClick={onPaneClick}
|
onPaneClick={onPaneClick}
|
||||||
fitView
|
fitView
|
||||||
fitViewOptions={{ duration: 1200, padding: 0.2 }}
|
|
||||||
maxZoom={Infinity}
|
maxZoom={Infinity}
|
||||||
>
|
>
|
||||||
<Panel position="top-right">
|
<Panel position="top-right">
|
||||||
@@ -183,7 +182,16 @@ const UseZoomPanHelperFlow = () => {
|
|||||||
console.log('fit view success');
|
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>
|
||||||
<button onClick={onAddNode}>add node</button>
|
<button onClick={onAddNode}>add node</button>
|
||||||
<button onClick={onResetNodes}>reset nodes</button>
|
<button onClick={onResetNodes}>reset nodes</button>
|
||||||
|
|||||||
@@ -44,7 +44,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<main>
|
<main>
|
||||||
<SvelteFlow bind:nodes bind:edges fitView>
|
<SvelteFlow bind:nodes bind:edges fitView maxZoom={4}>
|
||||||
<Controls />
|
<Controls />
|
||||||
<Background variant={BackgroundVariant.Dots} />
|
<Background variant={BackgroundVariant.Dots} />
|
||||||
<MiniMap />
|
<MiniMap />
|
||||||
|
|||||||
@@ -30,7 +30,10 @@
|
|||||||
<button onclick={() => zoomIn()}>zoom in</button>
|
<button onclick={() => zoomIn()}>zoom in</button>
|
||||||
<button onclick={() => zoomOut({ duration: 1000 })}>zoom out transition</button>
|
<button onclick={() => zoomOut({ duration: 1000 })}>zoom out transition</button>
|
||||||
<button onclick={() => setZoom(2)}>set zoom</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={() => setCenter(0, 0)}>setCenter 0, 0</button>
|
||||||
<button onclick={() => setViewport({ x: 100, y: 100, zoom: 2 })}>setViewport</button>
|
<button onclick={() => setViewport({ x: 100, y: 100, zoom: 2 })}>setViewport</button>
|
||||||
<button onclick={() => console.log(getViewport())}>getViewport</button>
|
<button onclick={() => console.log(getViewport())}>getViewport</button>
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ const useViewportHelper = (): ViewportHelperFunctions => {
|
|||||||
y: viewport.y ?? tY,
|
y: viewport.y ?? tY,
|
||||||
zoom: viewport.zoom ?? tZoom,
|
zoom: viewport.zoom ?? tZoom,
|
||||||
},
|
},
|
||||||
{ duration: options?.duration }
|
options
|
||||||
);
|
);
|
||||||
|
|
||||||
return Promise.resolve(true);
|
return Promise.resolve(true);
|
||||||
@@ -78,7 +78,7 @@ const useViewportHelper = (): ViewportHelperFunctions => {
|
|||||||
y: centerY,
|
y: centerY,
|
||||||
zoom: nextZoom,
|
zoom: nextZoom,
|
||||||
},
|
},
|
||||||
{ duration: options?.duration }
|
{ duration: options?.duration, ease: options?.ease, interpolate: options?.interpolate }
|
||||||
);
|
);
|
||||||
|
|
||||||
return Promise.resolve(true);
|
return Promise.resolve(true);
|
||||||
@@ -91,7 +91,11 @@ const useViewportHelper = (): ViewportHelperFunctions => {
|
|||||||
return Promise.resolve(false);
|
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);
|
return Promise.resolve(true);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -149,6 +149,7 @@ export type ViewportHelperFunctions = {
|
|||||||
*
|
*
|
||||||
* @param viewport - the viewport to set
|
* @param viewport - the viewport to set
|
||||||
* @param options.duration - optional duration. If set, a transition will be applied
|
* @param options.duration - optional duration. If set, a transition will be applied
|
||||||
|
* @param options.ease - optional ease function.
|
||||||
*/
|
*/
|
||||||
setViewport: SetViewport;
|
setViewport: SetViewport;
|
||||||
/**
|
/**
|
||||||
@@ -164,6 +165,8 @@ export type ViewportHelperFunctions = {
|
|||||||
* @param x - x position
|
* @param x - x position
|
||||||
* @param y - y position
|
* @param y - y position
|
||||||
* @param options.zoom - optional zoom
|
* @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;
|
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 bounds - the bounds ({ x: number, y: number, width: number, height: number }) to fit the view to
|
||||||
* @param options.padding - optional padding
|
* @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;
|
fitBounds: FitBounds;
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import { NodeTypes } from './general';
|
|||||||
*/
|
*/
|
||||||
export type Node<
|
export type Node<
|
||||||
NodeData extends Record<string, unknown> = Record<string, unknown>,
|
NodeData extends Record<string, unknown> = Record<string, unknown>,
|
||||||
NodeType extends string = string
|
NodeType extends string | undefined = string | undefined
|
||||||
> = NodeBase<NodeData, NodeType> & {
|
> = NodeBase<NodeData, NodeType> & {
|
||||||
style?: CSSProperties;
|
style?: CSSProperties;
|
||||||
className?: string;
|
className?: string;
|
||||||
|
|||||||
@@ -1,21 +1,18 @@
|
|||||||
<script lang="ts" generics="NodeType extends Node = Node, EdgeType extends Edge = Edge">
|
<script lang="ts" generics="NodeType extends Node = Node, EdgeType extends Edge = Edge">
|
||||||
import { derived } from 'svelte/store';
|
import { derived } from 'svelte/store';
|
||||||
import type { SvelteFlowStore } from '$lib/store/types';
|
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 '.';
|
import { ARIA_EDGE_DESC_KEY, ARIA_LIVE_MESSAGE, ARIA_NODE_DESC_KEY } from '.';
|
||||||
|
|
||||||
let { store }: { store: SvelteFlowStore<NodeType, EdgeType> } = $props();
|
let { store }: { store: SvelteFlowStore<NodeType, EdgeType> } = $props();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</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.disableKeyboardA11y
|
||||||
? store.labelConfig['a11yDescription.node.default']
|
? store.labelConfig['a11yDescription.node.default']
|
||||||
: store.labelConfig['a11yDescription.node.keyboardDisabled']}
|
: store.labelConfig['a11yDescription.node.keyboardDisabled']}
|
||||||
</div>
|
</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']}
|
{store.labelConfig['a11yDescription.edge.default']}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -24,8 +21,26 @@
|
|||||||
id={`${ARIA_LIVE_MESSAGE}-${store.flowId}`}
|
id={`${ARIA_LIVE_MESSAGE}-${store.flowId}`}
|
||||||
aria-live="assertive"
|
aria-live="assertive"
|
||||||
aria-atomic="true"
|
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}
|
{store.ariaLiveMessage}
|
||||||
</div>
|
</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,
|
y: nextViewport.y ?? currentViewport.y,
|
||||||
zoom: nextViewport.zoom ?? currentViewport.zoom
|
zoom: nextViewport.zoom ?? currentViewport.zoom
|
||||||
},
|
},
|
||||||
{ duration: options?.duration }
|
options
|
||||||
);
|
);
|
||||||
|
|
||||||
return Promise.resolve(true);
|
return Promise.resolve(true);
|
||||||
@@ -380,7 +380,7 @@ export function useSvelteFlow<NodeType extends Node = Node, EdgeType extends Edg
|
|||||||
y: store.height / 2 - y * nextZoom,
|
y: store.height / 2 - y * nextZoom,
|
||||||
zoom: nextZoom
|
zoom: nextZoom
|
||||||
},
|
},
|
||||||
{ duration: options?.duration }
|
{ duration: options?.duration, ease: options?.ease, interpolate: options?.interpolate }
|
||||||
);
|
);
|
||||||
|
|
||||||
return Promise.resolve(true);
|
return Promise.resolve(true);
|
||||||
@@ -402,7 +402,11 @@ export function useSvelteFlow<NodeType extends Node = Node, EdgeType extends Edg
|
|||||||
options?.padding ?? 0.1
|
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);
|
return Promise.resolve(true);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -55,10 +55,10 @@ export type {
|
|||||||
EdgeTypes,
|
EdgeTypes,
|
||||||
DefaultEdgeOptions
|
DefaultEdgeOptions
|
||||||
} from '$lib/types/edges';
|
} 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 { Node, NodeTypes, BuiltInNode, NodeProps, InternalNode } from '$lib/types/nodes';
|
||||||
export type { SvelteFlowStore } from '$lib/store/types';
|
|
||||||
export * from '$lib/types/events';
|
export * from '$lib/types/events';
|
||||||
|
export type { SvelteFlowStore } from '$lib/store/types';
|
||||||
|
|
||||||
// system types
|
// system types
|
||||||
export {
|
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);
|
store.nodes = store.nodes.map((node) => newNodes.get(node.id) ?? node);
|
||||||
|
|
||||||
if (!store.nodesInitialized) {
|
|
||||||
store.nodesInitialized = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function fitView(options?: FitViewOptions) {
|
function fitView(options?: FitViewOptions) {
|
||||||
|
|||||||
@@ -85,8 +85,6 @@ export type EdgeProps<EdgeType extends Edge = Edge> = Omit<
|
|||||||
'sourceHandle' | 'targetHandle'
|
'sourceHandle' | 'targetHandle'
|
||||||
> &
|
> &
|
||||||
EdgePosition & {
|
EdgePosition & {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
data?: any;
|
|
||||||
type: string;
|
type: string;
|
||||||
markerStart?: string;
|
markerStart?: string;
|
||||||
markerEnd?: string;
|
markerEnd?: string;
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ export type InternalNode<NodeType extends Node = Node> = InternalNodeBase<NodeTy
|
|||||||
*/
|
*/
|
||||||
export type Node<
|
export type Node<
|
||||||
NodeData extends Record<string, unknown> = Record<string, unknown>,
|
NodeData extends Record<string, unknown> = Record<string, unknown>,
|
||||||
NodeType extends string = string
|
NodeType extends string | undefined = string | undefined
|
||||||
> = NodeBase<NodeData, NodeType> & {
|
> = NodeBase<NodeData, NodeType> & {
|
||||||
class?: ClassValue;
|
class?: ClassValue;
|
||||||
style?: string;
|
style?: string;
|
||||||
|
|||||||
@@ -51,10 +51,12 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/d3-drag": "^3.0.7",
|
"@types/d3-drag": "^3.0.7",
|
||||||
|
"@types/d3-interpolate": "^3.0.4",
|
||||||
"@types/d3-selection": "^3.0.10",
|
"@types/d3-selection": "^3.0.10",
|
||||||
"@types/d3-transition": "^3.0.8",
|
"@types/d3-transition": "^3.0.8",
|
||||||
"@types/d3-zoom": "^3.0.8",
|
"@types/d3-zoom": "^3.0.8",
|
||||||
"d3-drag": "^3.0.0",
|
"d3-drag": "^3.0.0",
|
||||||
|
"d3-interpolate": "^3.0.1",
|
||||||
"d3-selection": "^3.0.0",
|
"d3-selection": "^3.0.0",
|
||||||
"d3-zoom": "^3.0.0"
|
"d3-zoom": "^3.0.0"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -130,6 +130,8 @@ export type FitViewOptionsBase<NodeType extends NodeBase = NodeBase> = {
|
|||||||
minZoom?: number;
|
minZoom?: number;
|
||||||
maxZoom?: number;
|
maxZoom?: number;
|
||||||
duration?: number;
|
duration?: number;
|
||||||
|
ease?: (t: number) => number;
|
||||||
|
interpolate?: 'smooth' | 'linear';
|
||||||
nodes?: (NodeType | { id: string })[];
|
nodes?: (NodeType | { id: string })[];
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -173,6 +175,8 @@ export enum PanOnScrollMode {
|
|||||||
*/
|
*/
|
||||||
export type ViewportHelperFunctionOptions = {
|
export type ViewportHelperFunctionOptions = {
|
||||||
duration?: number;
|
duration?: number;
|
||||||
|
ease?: (t: number) => number;
|
||||||
|
interpolate?: 'smooth' | 'linear';
|
||||||
};
|
};
|
||||||
|
|
||||||
export type SetCenterOptions = ViewportHelperFunctionOptions & {
|
export type SetCenterOptions = ViewportHelperFunctionOptions & {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import { Optional } from '../utils/types';
|
|||||||
*/
|
*/
|
||||||
export type NodeBase<
|
export type NodeBase<
|
||||||
NodeData extends Record<string, unknown> = Record<string, unknown>,
|
NodeData extends Record<string, unknown> = Record<string, unknown>,
|
||||||
NodeType extends string = string
|
NodeType extends string | undefined = string | undefined
|
||||||
> = {
|
> = {
|
||||||
/** Unique id of a node. */
|
/** Unique id of a node. */
|
||||||
id: string;
|
id: string;
|
||||||
@@ -21,8 +21,6 @@ export type NodeBase<
|
|||||||
position: XYPosition;
|
position: XYPosition;
|
||||||
/** Arbitrary data passed to a node. */
|
/** Arbitrary data passed to a node. */
|
||||||
data: NodeData;
|
data: NodeData;
|
||||||
/** Type of node defined in `nodeTypes`. */
|
|
||||||
type?: NodeType;
|
|
||||||
/**
|
/**
|
||||||
* Only relevant for default, source, target nodeType. Controls source position.
|
* Only relevant for default, source, target nodeType. Controls source position.
|
||||||
* @example 'right', 'left', 'top', 'bottom'
|
* @example 'right', 'left', 'top', 'bottom'
|
||||||
@@ -79,7 +77,15 @@ export type NodeBase<
|
|||||||
width?: number;
|
width?: number;
|
||||||
height?: 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'> & {
|
export type InternalNodeBase<NodeType extends NodeBase = NodeBase> = Omit<NodeType, 'measured'> & {
|
||||||
measured: {
|
measured: {
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ export type PanZoomParams = {
|
|||||||
|
|
||||||
export type PanZoomTransformOptions = {
|
export type PanZoomTransformOptions = {
|
||||||
duration?: number;
|
duration?: number;
|
||||||
|
ease?: (t: number) => number;
|
||||||
|
interpolate?: 'smooth' | 'linear';
|
||||||
};
|
};
|
||||||
|
|
||||||
export type OnPanZoom = (event: MouseEvent | TouchEvent | null, viewport: Viewport) => void;
|
export type OnPanZoom = (event: MouseEvent | TouchEvent | null, viewport: Viewport) => void;
|
||||||
|
|||||||
@@ -378,7 +378,11 @@ export async function fitViewport<
|
|||||||
options?.padding ?? 0.1
|
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);
|
return Promise.resolve(true);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { type ZoomTransform, zoom, zoomTransform } from 'd3-zoom';
|
import { type ZoomTransform, zoom, zoomTransform } from 'd3-zoom';
|
||||||
import { select } from 'd3-selection';
|
import { select } from 'd3-selection';
|
||||||
|
import { interpolateZoom, interpolate } from 'd3-interpolate';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
type CoordinateExtent,
|
type CoordinateExtent,
|
||||||
@@ -19,6 +20,7 @@ import {
|
|||||||
createZoomOnScrollHandler,
|
createZoomOnScrollHandler,
|
||||||
} from './eventhandler';
|
} from './eventhandler';
|
||||||
import { createFilter } from './filter';
|
import { createFilter } from './filter';
|
||||||
|
import { transition } from 'd3-transition';
|
||||||
|
|
||||||
export type ZoomPanValues = {
|
export type ZoomPanValues = {
|
||||||
isZoomingOrPanning: boolean;
|
isZoomingOrPanning: boolean;
|
||||||
@@ -78,8 +80,8 @@ export function XYPanZoom({
|
|||||||
function setTransform(transform: ZoomTransform, options?: PanZoomTransformOptions) {
|
function setTransform(transform: ZoomTransform, options?: PanZoomTransformOptions) {
|
||||||
if (d3Selection) {
|
if (d3Selection) {
|
||||||
return new Promise<boolean>((resolve) => {
|
return new Promise<boolean>((resolve) => {
|
||||||
d3ZoomInstance?.transform(
|
d3ZoomInstance?.interpolate(options?.interpolate === 'linear' ? interpolate : interpolateZoom).transform(
|
||||||
getD3Transition(d3Selection, options?.duration, () => resolve(true)),
|
getD3Transition(d3Selection, options?.duration, options?.ease, () => resolve(true)),
|
||||||
transform
|
transform
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -114,22 +116,22 @@ export function XYPanZoom({
|
|||||||
|
|
||||||
const wheelHandler = isPanOnScroll
|
const wheelHandler = isPanOnScroll
|
||||||
? createPanOnScrollHandler({
|
? createPanOnScrollHandler({
|
||||||
zoomPanValues,
|
zoomPanValues,
|
||||||
noWheelClassName,
|
noWheelClassName,
|
||||||
d3Selection,
|
d3Selection,
|
||||||
d3Zoom: d3ZoomInstance,
|
d3Zoom: d3ZoomInstance,
|
||||||
panOnScrollMode,
|
panOnScrollMode,
|
||||||
panOnScrollSpeed,
|
panOnScrollSpeed,
|
||||||
zoomOnPinch,
|
zoomOnPinch,
|
||||||
onPanZoomStart,
|
onPanZoomStart,
|
||||||
onPanZoom,
|
onPanZoom,
|
||||||
onPanZoomEnd,
|
onPanZoomEnd,
|
||||||
})
|
})
|
||||||
: createZoomOnScrollHandler({
|
: createZoomOnScrollHandler({
|
||||||
noWheelClassName,
|
noWheelClassName,
|
||||||
preventScrolling,
|
preventScrolling,
|
||||||
d3ZoomHandler,
|
d3ZoomHandler,
|
||||||
});
|
});
|
||||||
|
|
||||||
d3Selection.on('wheel.zoom', wheelHandler, { passive: false });
|
d3Selection.on('wheel.zoom', wheelHandler, { passive: false });
|
||||||
|
|
||||||
@@ -242,8 +244,8 @@ export function XYPanZoom({
|
|||||||
function scaleTo(zoom: number, options?: PanZoomTransformOptions) {
|
function scaleTo(zoom: number, options?: PanZoomTransformOptions) {
|
||||||
if (d3Selection) {
|
if (d3Selection) {
|
||||||
return new Promise<boolean>((resolve) => {
|
return new Promise<boolean>((resolve) => {
|
||||||
d3ZoomInstance?.scaleTo(
|
d3ZoomInstance?.interpolate(options?.interpolate === 'linear' ? interpolate : interpolateZoom).scaleTo(
|
||||||
getD3Transition(d3Selection, options?.duration, () => resolve(true)),
|
getD3Transition(d3Selection, options?.duration, options?.ease, () => resolve(true)),
|
||||||
zoom
|
zoom
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -255,8 +257,8 @@ export function XYPanZoom({
|
|||||||
function scaleBy(factor: number, options?: PanZoomTransformOptions) {
|
function scaleBy(factor: number, options?: PanZoomTransformOptions) {
|
||||||
if (d3Selection) {
|
if (d3Selection) {
|
||||||
return new Promise<boolean>((resolve) => {
|
return new Promise<boolean>((resolve) => {
|
||||||
d3ZoomInstance?.scaleBy(
|
d3ZoomInstance?.interpolate(options?.interpolate === 'linear' ? interpolate : interpolateZoom).scaleBy(
|
||||||
getD3Transition(d3Selection, options?.duration, () => resolve(true)),
|
getD3Transition(d3Selection, options?.duration, options?.ease, () => resolve(true)),
|
||||||
factor
|
factor
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
import { type ZoomTransform, zoomIdentity } from 'd3-zoom';
|
import { type ZoomTransform, zoomIdentity } from 'd3-zoom';
|
||||||
|
import { transition } from 'd3-transition';
|
||||||
|
|
||||||
import { type D3SelectionInstance, type Viewport } from '../types';
|
import { type D3SelectionInstance, type Viewport } from '../types';
|
||||||
import { isMacOs } from '../utils';
|
import { isMacOs } from '../utils';
|
||||||
@@ -21,14 +22,17 @@ export const isWrappedWithClass = (event: any, className: string | undefined) =>
|
|||||||
export const isRightClickPan = (panOnDrag: boolean | number[], usedButton: number) =>
|
export const isRightClickPan = (panOnDrag: boolean | number[], usedButton: number) =>
|
||||||
usedButton === 2 && Array.isArray(panOnDrag) && panOnDrag.includes(2);
|
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;
|
const hasDuration = typeof duration === 'number' && duration > 0;
|
||||||
|
|
||||||
if (!hasDuration) {
|
if (!hasDuration) {
|
||||||
onEnd();
|
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) => {
|
export const wheelDelta = (event: any) => {
|
||||||
|
|||||||
Generated
+6
@@ -372,6 +372,9 @@ importers:
|
|||||||
'@types/d3-drag':
|
'@types/d3-drag':
|
||||||
specifier: ^3.0.7
|
specifier: ^3.0.7
|
||||||
version: 3.0.7
|
version: 3.0.7
|
||||||
|
'@types/d3-interpolate':
|
||||||
|
specifier: ^3.0.4
|
||||||
|
version: 3.0.4
|
||||||
'@types/d3-selection':
|
'@types/d3-selection':
|
||||||
specifier: ^3.0.10
|
specifier: ^3.0.10
|
||||||
version: 3.0.10
|
version: 3.0.10
|
||||||
@@ -384,6 +387,9 @@ importers:
|
|||||||
d3-drag:
|
d3-drag:
|
||||||
specifier: ^3.0.0
|
specifier: ^3.0.0
|
||||||
version: 3.0.0
|
version: 3.0.0
|
||||||
|
d3-interpolate:
|
||||||
|
specifier: ^3.0.1
|
||||||
|
version: 3.0.1
|
||||||
d3-selection:
|
d3-selection:
|
||||||
specifier: ^3.0.0
|
specifier: ^3.0.0
|
||||||
version: 3.0.0
|
version: 3.0.0
|
||||||
|
|||||||
Reference in New Issue
Block a user