Merge pull request #5067 from xyflow/enhance/fitView

Enhance fitView
This commit is contained in:
Moritz Klack
2025-03-27 12:04:48 +01:00
committed by GitHub
30 changed files with 457 additions and 347 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@xyflow/react': minor
---
You can now express paddings in fitViewOptions as pixels ('30px'), as viewport percentages ('20%') and define different paddings for each side.
+7
View File
@@ -0,0 +1,7 @@
---
'@xyflow/react': patch
'@xyflow/svelte': patch
'@xyflow/system': patch
---
Fix fitView not working immediately after adding new nodes
+1 -1
View File
@@ -37,7 +37,7 @@
"cypress": "13.6.6", "cypress": "13.6.6",
"cypress-real-events": "1.12.0", "cypress-real-events": "1.12.0",
"start-server-and-test": "^2.0.2", "start-server-and-test": "^2.0.2",
"typescript": "5.2.2", "typescript": "5.4.5",
"vite": "4.5.0" "vite": "4.5.0"
} }
} }
+17 -2
View File
@@ -56,8 +56,18 @@ const initialEdges: Edge[] = [
const defaultEdgeOptions = {}; const defaultEdgeOptions = {};
const BasicFlow = () => { const BasicFlow = () => {
const { addNodes, setNodes, getNodes, setEdges, getEdges, deleteElements, updateNodeData, toObject, setViewport } = const {
useReactFlow(); addNodes,
setNodes,
getNodes,
setEdges,
getEdges,
deleteElements,
updateNodeData,
toObject,
setViewport,
fitView,
} = useReactFlow();
const updatePos = () => { const updatePos = () => {
setNodes((nodes) => setNodes((nodes) =>
@@ -104,6 +114,7 @@ const BasicFlow = () => {
]); ]);
setEdges([{ id: 'a-b', source: 'a', target: 'b' }]); setEdges([{ id: 'a-b', source: 'a', target: 'b' }]);
fitView();
}; };
const onUpdateNode = () => { const onUpdateNode = () => {
@@ -117,6 +128,7 @@ const BasicFlow = () => {
position: { x: Math.random() * 300, y: Math.random() * 300 }, position: { x: Math.random() * 300, y: Math.random() * 300 },
className: 'light', className: 'light',
}); });
fitView();
}; };
return ( return (
@@ -134,6 +146,9 @@ const BasicFlow = () => {
minZoom={0.2} minZoom={0.2}
maxZoom={4} maxZoom={4}
fitView fitView
fitViewOptions={{
padding: { top: '100px', left: '0%', right: '10%', bottom: 0.1 },
}}
defaultEdgeOptions={defaultEdgeOptions} defaultEdgeOptions={defaultEdgeOptions}
selectNodesOnDrag={false} selectNodesOnDrag={false}
elevateEdgesOnSelect elevateEdgesOnSelect
+13 -3
View File
@@ -12,6 +12,8 @@ import {
Controls, Controls,
Background, Background,
Panel, Panel,
ReactFlowProvider,
useReactFlow,
} from '@xyflow/react'; } from '@xyflow/react';
import { getNodesAndEdges } from './utils'; import { getNodesAndEdges } from './utils';
@@ -22,6 +24,7 @@ const { nodes: initialNodes, edges: initialEdges } = getNodesAndEdges(25, 25);
const StressFlow = () => { const StressFlow = () => {
const [nodes, setNodes] = useState<Node[]>(initialNodes); const [nodes, setNodes] = useState<Node[]>(initialNodes);
const [edges, setEdges] = useState<Edge[]>(initialEdges); const [edges, setEdges] = useState<Edge[]>(initialEdges);
const { fitView } = useReactFlow();
const onConnect = useCallback((connection: Connection) => { const onConnect = useCallback((connection: Connection) => {
setEdges((eds) => addEdge(connection, eds)); setEdges((eds) => addEdge(connection, eds));
}, []); }, []);
@@ -191,12 +194,13 @@ const StressFlow = () => {
return { return {
...n, ...n,
position: { position: {
x: Math.random() * window.innerWidth, x: Math.random() * window.innerWidth * 4,
y: Math.random() * window.innerHeight, y: Math.random() * window.innerHeight * 4,
}, },
}; };
}); });
}); });
fitView();
}; };
const updateElements = () => { const updateElements = () => {
@@ -240,4 +244,10 @@ const StressFlow = () => {
); );
}; };
export default StressFlow; export default function StressFlowProvider() {
return (
<ReactFlowProvider>
<StressFlow />
</ReactFlowProvider>
);
}
+1 -1
View File
@@ -24,7 +24,7 @@
"svelte": "^4.2.12", "svelte": "^4.2.12",
"svelte-check": "^3.6.6", "svelte-check": "^3.6.6",
"tslib": "^2.6.2", "tslib": "^2.6.2",
"typescript": "^5.2.2", "typescript": "^5.4.5",
"vite": "^5.2.12" "vite": "^5.2.12"
}, },
"type": "module", "type": "module",
@@ -198,7 +198,7 @@
attributionPosition={'top-center'} attributionPosition={'top-center'}
deleteKey={['Backspace', 'd']} deleteKey={['Backspace', 'd']}
> >
<Controls orientation="horizontal" {fitViewOptions}> <Controls orientation="horizontal">
<ControlButton slot="before">xy</ControlButton> <ControlButton slot="before">xy</ControlButton>
<ControlButton aria-label="log" on:click={() => console.log('control button')} <ControlButton aria-label="log" on:click={() => console.log('control button')}
>log</ControlButton >log</ControlButton
+1 -1
View File
@@ -33,7 +33,7 @@
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"rollup": "^4.18.0", "rollup": "^4.18.0",
"turbo": "^2.0.3", "turbo": "^2.0.3",
"typescript": "5.1.3" "typescript": "5.4.5"
}, },
"packageManager": "pnpm@9.2.0" "packageManager": "pnpm@9.2.0"
} }
+1 -1
View File
@@ -85,7 +85,7 @@
"postcss-nested": "^6.0.0", "postcss-nested": "^6.0.0",
"postcss-rename": "^0.6.1", "postcss-rename": "^0.6.1",
"react": "^18.2.0", "react": "^18.2.0",
"typescript": "5.1.3" "typescript": "5.4.5"
}, },
"rollup": { "rollup": {
"globals": { "globals": {
@@ -154,8 +154,8 @@ export function StoreUpdater<NodeType extends Node = Node, EdgeType extends Edge
else if (fieldName === 'nodeExtent') setNodeExtent(fieldValue as CoordinateExtent); else if (fieldName === 'nodeExtent') setNodeExtent(fieldValue as CoordinateExtent);
else if (fieldName === 'paneClickDistance') setPaneClickDistance(fieldValue as number); else if (fieldName === 'paneClickDistance') setPaneClickDistance(fieldValue as number);
// Renamed fields // Renamed fields
else if (fieldName === 'fitView') store.setState({ fitViewOnInit: fieldValue as boolean }); else if (fieldName === 'fitView') store.setState({ fitViewQueued: fieldValue as boolean });
else if (fieldName === 'fitViewOptions') store.setState({ fitViewOnInitOptions: fieldValue as FitViewOptions }); else if (fieldName === 'fitViewOptions') store.setState({ fitViewOptions: fieldValue as FitViewOptions });
// General case // General case
else store.setState({ [fieldName]: fieldValue }); else store.setState({ [fieldName]: fieldValue });
} }
+20 -1
View File
@@ -15,7 +15,15 @@ import useViewportHelper from './useViewportHelper';
import { useStore, useStoreApi } from './useStore'; import { useStore, useStoreApi } from './useStore';
import { useBatchContext } from '../components/BatchProvider'; import { useBatchContext } from '../components/BatchProvider';
import { elementToRemoveChange, isEdge, isNode } from '../utils'; import { elementToRemoveChange, isEdge, isNode } from '../utils';
import type { ReactFlowInstance, Node, Edge, InternalNode, ReactFlowState, GeneralHelpers } from '../types'; import type {
ReactFlowInstance,
Node,
Edge,
InternalNode,
ReactFlowState,
GeneralHelpers,
FitViewOptions,
} from '../types';
const selector = (s: ReactFlowState) => !!s.panZoom; const selector = (s: ReactFlowState) => !!s.panZoom;
@@ -271,6 +279,17 @@ export function useReactFlow<NodeType extends Node = Node, EdgeType extends Edge
.connectionLookup.get(`${nodeId}${type ? (handleId ? `-${type}-${handleId}` : `-${type}`) : ''}`) .connectionLookup.get(`${nodeId}${type ? (handleId ? `-${type}-${handleId}` : `-${type}`) : ''}`)
?.values() ?? [] ?.values() ?? []
), ),
fitView: async (options: FitViewOptions<NodeType> | undefined) => {
// We either create a new Promise or reuse the existing one
// Even if fitView is called multiple times in a row, we only end up with a single Promise
const fitViewResolver = store.getState().fitViewResolver ?? Promise.withResolvers<boolean>();
// We schedule a fitView by setting fitViewQueued and triggering a setNodes
store.setState({ fitViewQueued: true, fitViewOptions: options, fitViewResolver });
batchContext.nodeQueue.push((nodes) => [...nodes]);
return fitViewResolver.promise;
},
}; };
}, []); }, []);
@@ -2,11 +2,8 @@ import { useMemo } from 'react';
import { import {
pointToRendererPoint, pointToRendererPoint,
getViewportForBounds, getViewportForBounds,
getFitViewNodes,
fitView,
type XYPosition, type XYPosition,
rendererPointToPoint, rendererPointToPoint,
getDimensions,
SnapGrid, SnapGrid,
} from '@xyflow/system'; } from '@xyflow/system';
@@ -65,28 +62,6 @@ const useViewportHelper = (): ViewportHelperFunctions => {
const [x, y, zoom] = store.getState().transform; const [x, y, zoom] = store.getState().transform;
return { x, y, zoom }; return { x, y, zoom };
}, },
fitView: (options) => {
const { nodeLookup, minZoom, maxZoom, panZoom, domNode } = store.getState();
if (!panZoom || !domNode) {
return Promise.resolve(false);
}
const fitViewNodes = getFitViewNodes(nodeLookup, options);
const { width, height } = getDimensions(domNode);
return fitView(
{
nodes: fitViewNodes,
width,
height,
minZoom,
maxZoom,
panZoom,
},
options
);
},
setCenter: async (x, y, options) => { setCenter: async (x, y, options) => {
const { width, height, maxZoom, panZoom } = store.getState(); const { width, height, maxZoom, panZoom } = store.getState();
const nextZoom = typeof options?.zoom !== 'undefined' ? options.zoom : maxZoom; const nextZoom = typeof options?.zoom !== 'undefined' ? options.zoom : maxZoom;
+48 -92
View File
@@ -1,7 +1,5 @@
import { createWithEqualityFn } from 'zustand/traditional'; import { createWithEqualityFn } from 'zustand/traditional';
import { import {
getFitViewNodes,
fitView as fitViewSystem,
adoptUserNodes, adoptUserNodes,
updateAbsolutePositions, updateAbsolutePositions,
panBy as panBySystem, panBy as panBySystem,
@@ -15,11 +13,12 @@ import {
initialConnection, initialConnection,
NodeOrigin, NodeOrigin,
CoordinateExtent, CoordinateExtent,
fitViewport,
} from '@xyflow/system'; } from '@xyflow/system';
import { applyEdgeChanges, applyNodeChanges, createSelectionChange, getSelectionChanges } from '../utils/changes'; import { applyEdgeChanges, applyNodeChanges, createSelectionChange, getSelectionChanges } from '../utils/changes';
import getInitialState from './initialState'; import getInitialState from './initialState';
import type { ReactFlowState, Node, Edge, UnselectNodesAndEdgesParams, FitViewOptions } from '../types'; import type { ReactFlowState, Node, Edge, UnselectNodesAndEdgesParams } from '../types';
const createStore = ({ const createStore = ({
nodes, nodes,
@@ -42,11 +41,38 @@ const createStore = ({
nodeOrigin?: NodeOrigin; nodeOrigin?: NodeOrigin;
nodeExtent?: CoordinateExtent; nodeExtent?: CoordinateExtent;
}) => }) =>
createWithEqualityFn<ReactFlowState>( createWithEqualityFn<ReactFlowState>((set, get) => {
(set, get) => ({ async function resolveFitView() {
const { nodeLookup, panZoom, fitViewOptions, fitViewResolver, width, height, minZoom, maxZoom } = get();
if (!panZoom || !fitViewResolver) {
return;
}
await fitViewport(
{
nodes: nodeLookup,
width,
height,
panZoom,
minZoom,
maxZoom,
},
fitViewOptions
);
fitViewResolver.resolve(true);
/**
* wait for the fitViewport to resolve before deleting the resolver,
* we want to reuse the old resolver if the user calls fitView again in the mean time
*/
set({ fitViewResolver: null });
}
return {
...getInitialState({ nodes, edges, width, height, fitView, nodeOrigin, nodeExtent, defaultNodes, defaultEdges }), ...getInitialState({ nodes, edges, width, height, fitView, nodeOrigin, nodeExtent, defaultNodes, defaultEdges }),
setNodes: (nodes: Node[]) => { setNodes: (nodes: Node[]) => {
const { nodeLookup, parentLookup, nodeOrigin, elevateNodesOnSelect } = get(); const { nodeLookup, parentLookup, nodeOrigin, elevateNodesOnSelect, fitViewQueued } = get();
/* /*
* setNodes() is called exclusively in response to user actions: * setNodes() is called exclusively in response to user actions:
* - either when the `<ReactFlow nodes>` prop is updated in the controlled ReactFlow setup, * - either when the `<ReactFlow nodes>` prop is updated in the controlled ReactFlow setup,
@@ -55,14 +81,20 @@ const createStore = ({
* When this happens, we take the note objects passed by the user and extend them with fields * When this happens, we take the note objects passed by the user and extend them with fields
* relevant for internal React Flow operations. * relevant for internal React Flow operations.
*/ */
adoptUserNodes(nodes, nodeLookup, parentLookup, {
const nodesInitialized = adoptUserNodes(nodes, nodeLookup, parentLookup, {
nodeOrigin, nodeOrigin,
nodeExtent, nodeExtent,
elevateNodesOnSelect, elevateNodesOnSelect,
checkEquality: true, checkEquality: true,
}); });
set({ nodes }); if (fitViewQueued && nodesInitialized) {
resolveFitView();
set({ nodes, fitViewQueued: false, fitViewOptions: undefined });
} else {
set({ nodes });
}
}, },
setEdges: (edges: Edge[]) => { setEdges: (edges: Edge[]) => {
const { connectionLookup, edgeLookup } = get(); const { connectionLookup, edgeLookup } = get();
@@ -88,20 +120,9 @@ const createStore = ({
* changes its dimensions, this function is called to measure the * changes its dimensions, this function is called to measure the
* new dimensions and update the nodes. * new dimensions and update the nodes.
*/ */
updateNodeInternals: (updates, params = { triggerFitView: true }) => { updateNodeInternals: (updates) => {
const { const { triggerNodeChanges, nodeLookup, parentLookup, domNode, nodeOrigin, nodeExtent, debug, fitViewQueued } =
triggerNodeChanges, get();
nodeLookup,
parentLookup,
fitViewOnInit,
fitViewDone,
fitViewOnInitOptions,
domNode,
nodeOrigin,
nodeExtent,
debug,
fitViewSync,
} = get();
const { changes, updatedInternals } = updateNodeInternalsSystem( const { changes, updatedInternals } = updateNodeInternalsSystem(
updates, updates,
@@ -118,25 +139,9 @@ const createStore = ({
updateAbsolutePositions(nodeLookup, parentLookup, { nodeOrigin, nodeExtent }); updateAbsolutePositions(nodeLookup, parentLookup, { nodeOrigin, nodeExtent });
if (params.triggerFitView) { if (fitViewQueued) {
// we call fitView once initially after all dimensions are set resolveFitView();
let nextFitViewDone = fitViewDone; set({ fitViewQueued: false, fitViewOptions: undefined });
if (!fitViewDone && fitViewOnInit) {
nextFitViewDone = fitViewSync({
...fitViewOnInitOptions,
nodes: fitViewOnInitOptions?.nodes,
});
}
/*
* here we are cirmumventing the onNodesChange handler
* in order to be able to display nodes even if the user
* has not provided an onNodesChange handler.
* Nodes are only rendered if they have a width and height
* attribute which they get from this handler.
*/
set({ fitViewDone: nextFitViewDone });
} else { } else {
// we always want to trigger useStore calls whenever updateNodeInternals is called // we always want to trigger useStore calls whenever updateNodeInternals is called
set({}); set({});
@@ -332,54 +337,6 @@ const createStore = ({
return panBySystem({ delta, panZoom, transform, translateExtent, width, height }); return panBySystem({ delta, panZoom, transform, translateExtent, width, height });
}, },
fitView: (options?: FitViewOptions): Promise<boolean> => {
const { panZoom, width, height, minZoom, maxZoom, nodeLookup } = get();
if (!panZoom) {
return Promise.resolve(false);
}
const fitViewNodes = getFitViewNodes(nodeLookup, options);
return fitViewSystem(
{
nodes: fitViewNodes,
width,
height,
panZoom,
minZoom,
maxZoom,
},
options
);
},
/*
* we can't call an asnychronous function in updateNodeInternals
* for that we created this sync version of fitView
*/
fitViewSync: (options?: FitViewOptions): boolean => {
const { panZoom, width, height, minZoom, maxZoom, nodeLookup } = get();
if (!panZoom) {
return false;
}
const fitViewNodes = getFitViewNodes(nodeLookup, options);
fitViewSystem(
{
nodes: fitViewNodes,
width,
height,
panZoom,
minZoom,
maxZoom,
},
options
);
return fitViewNodes.size > 0;
},
cancelConnection: () => { cancelConnection: () => {
set({ set({
connection: { ...initialConnection }, connection: { ...initialConnection },
@@ -390,8 +347,7 @@ const createStore = ({
}, },
reset: () => set({ ...getInitialState() }), reset: () => set({ ...getInitialState() }),
}), };
Object.is }, Object.is);
);
export { createStore }; export { createStore };
+4 -3
View File
@@ -104,13 +104,14 @@ const getInitialState = ({
elementsSelectable: true, elementsSelectable: true,
elevateNodesOnSelect: true, elevateNodesOnSelect: true,
elevateEdgesOnSelect: false, elevateEdgesOnSelect: false,
fitViewOnInit: false,
fitViewDone: false,
fitViewOnInitOptions: undefined,
selectNodesOnDrag: true, selectNodesOnDrag: true,
multiSelectionActive: false, multiSelectionActive: false,
fitViewQueued: fitView ?? false,
fitViewOptions: undefined,
fitViewResolver: null,
connection: { ...initialConnection }, connection: { ...initialConnection },
connectionClickStartHandle: null, connectionClickStartHandle: null,
connectOnClick: true, connectOnClick: true,
+1 -12
View File
@@ -109,7 +109,7 @@ export type FitViewParams<NodeType extends Node = Node> = FitViewParamsBase<Node
* @public * @public
*/ */
export type FitViewOptions<NodeType extends Node = Node> = FitViewOptionsBase<NodeType>; export type FitViewOptions<NodeType extends Node = Node> = FitViewOptionsBase<NodeType>;
export type FitView = (fitViewOptions?: FitViewOptions) => Promise<boolean>; export type FitView<NodeType extends Node = Node> = (fitViewOptions?: FitViewOptions<NodeType>) => Promise<boolean>;
export type OnInit<NodeType extends Node = Node, EdgeType extends Edge = Edge> = ( export type OnInit<NodeType extends Node = Node, EdgeType extends Edge = Edge> = (
reactFlowInstance: ReactFlowInstance<NodeType, EdgeType> reactFlowInstance: ReactFlowInstance<NodeType, EdgeType>
) => void; ) => void;
@@ -156,17 +156,6 @@ export type ViewportHelperFunctions = {
* @returns Viewport * @returns Viewport
*/ */
getViewport: GetViewport; getViewport: GetViewport;
/**
* Fits the view.
*
* @param options.padding - optional padding
* @param options.includeHiddenNodes - optional includeHiddenNodes
* @param options.minZoom - optional minZoom
* @param options.maxZoom - optional maxZoom
* @param options.duration - optional duration. If set, a transition will be applied
* @param options.nodes - optional nodes to fit the view to
*/
fitView: FitView;
/** /**
* Sets the center of the view to the given position. * Sets the center of the view to the given position.
* *
+12 -1
View File
@@ -1,6 +1,6 @@
/* eslint-disable @typescript-eslint/no-namespace */ /* eslint-disable @typescript-eslint/no-namespace */
import type { HandleConnection, HandleType, NodeConnection, Rect, Viewport } from '@xyflow/system'; import type { HandleConnection, HandleType, NodeConnection, Rect, Viewport } from '@xyflow/system';
import type { Node, Edge, ViewportHelperFunctions, InternalNode } from '.'; import type { Node, Edge, ViewportHelperFunctions, InternalNode, FitView } from '.';
export type ReactFlowJsonObject<NodeType extends Node = Node, EdgeType extends Edge = Edge> = { export type ReactFlowJsonObject<NodeType extends Node = Node, EdgeType extends Edge = Edge> = {
nodes: NodeType[]; nodes: NodeType[];
@@ -217,6 +217,17 @@ export type GeneralHelpers<NodeType extends Node = Node, EdgeType extends Edge =
nodeId: string; nodeId: string;
handleId?: string | null; handleId?: string | null;
}) => NodeConnection[]; }) => NodeConnection[];
// /**
// * Fits the view.
// *
// * @param options.padding - optional padding
// * @param options.includeHiddenNodes - optional includeHiddenNodes
// * @param options.minZoom - optional minZoom
// * @param options.maxZoom - optional maxZoom
// * @param options.duration - optional duration. If set, a transition will be applied
// * @param options.nodes - optional nodes to fit the view to
// */
fitView: FitView<NodeType>;
}; };
/** /**
* The `ReactFlowInstance` provides a collection of methods to query and manipulate * The `ReactFlowInstance` provides a collection of methods to query and manipulate
+3 -5
View File
@@ -119,9 +119,9 @@ export type ReactFlowStore<NodeType extends Node = Node, EdgeType extends Edge =
connectOnClick: boolean; connectOnClick: boolean;
defaultEdgeOptions?: DefaultEdgeOptions; defaultEdgeOptions?: DefaultEdgeOptions;
fitViewOnInit: boolean; fitViewQueued: boolean;
fitViewDone: boolean; fitViewOptions: FitViewOptions | undefined;
fitViewOnInitOptions: FitViewOptions | undefined; fitViewResolver: PromiseWithResolvers<boolean> | null;
onNodesDelete?: OnNodesDelete<NodeType>; onNodesDelete?: OnNodesDelete<NodeType>;
onEdgesDelete?: OnEdgesDelete<EdgeType>; onEdgesDelete?: OnEdgesDelete<EdgeType>;
@@ -168,8 +168,6 @@ export type ReactFlowActions<NodeType extends Node, EdgeType extends Edge> = {
triggerNodeChanges: (changes: NodeChange<NodeType>[]) => void; triggerNodeChanges: (changes: NodeChange<NodeType>[]) => void;
triggerEdgeChanges: (changes: EdgeChange<EdgeType>[]) => void; triggerEdgeChanges: (changes: EdgeChange<EdgeType>[]) => void;
panBy: PanBy; panBy: PanBy;
fitView: (options?: FitViewOptions) => Promise<boolean>;
fitViewSync: (options?: FitViewOptions) => boolean;
setPaneClickDistance: (distance: number) => void; setPaneClickDistance: (distance: number) => void;
}; };
+1 -1
View File
@@ -80,7 +80,7 @@
"svelte-eslint-parser": "^0.33.1", "svelte-eslint-parser": "^0.33.1",
"svelte-preprocess": "^5.1.3", "svelte-preprocess": "^5.1.3",
"tslib": "^2.6.2", "tslib": "^2.6.2",
"typescript": "5.4.2" "typescript": "5.4.5"
}, },
"peerDependencies": { "peerDependencies": {
"svelte": "^3.0.0 || ^4.0.0 || ^5.0.0" "svelte": "^3.0.0 || ^4.0.0 || ^5.0.0"
@@ -117,7 +117,7 @@
store.syncViewport(viewport); store.syncViewport(viewport);
if (fitView !== undefined) { if (fitView !== undefined) {
store.fitViewOnInit.set(fitView); store.fitViewQueued.set(fitView);
} }
if (fitViewOptions) { if (fitViewOptions) {
+10 -58
View File
@@ -2,7 +2,6 @@ import { getContext, setContext } from 'svelte';
import { derived, get, writable } from 'svelte/store'; import { derived, get, writable } from 'svelte/store';
import { import {
createMarkerIds, createMarkerIds,
fitView as fitViewSystem,
getElementsToRemove, getElementsToRemove,
panBy as panBySystem, panBy as panBySystem,
updateNodeInternals as updateNodeInternalsSystem, updateNodeInternals as updateNodeInternalsSystem,
@@ -19,9 +18,7 @@ import {
type UpdateConnection, type UpdateConnection,
type ConnectionState, type ConnectionState,
type NodeOrigin, type NodeOrigin,
getFitViewNodes, updateAbsolutePositions
updateAbsolutePositions,
getDimensions
} from '@xyflow/system'; } from '@xyflow/system';
import type { EdgeTypes, NodeTypes, Node, Edge, FitViewOptions } from '$lib/types'; import type { EdgeTypes, NodeTypes, Node, Edge, FitViewOptions } from '$lib/types';
@@ -113,15 +110,6 @@ export function createStore({
updateAbsolutePositions(nodeLookup, parentLookup, { nodeOrigin, nodeExtent }); updateAbsolutePositions(nodeLookup, parentLookup, { nodeOrigin, nodeExtent });
if (!get(store.fitViewOnInitDone) && get(store.fitViewOnInit)) {
const fitViewOptions = get(store.fitViewOptions);
const fitViewOnInitDone = fitViewSync({
...fitViewOptions,
nodes: fitViewOptions?.nodes
});
store.fitViewOnInitDone.set(fitViewOnInitDone);
}
for (const change of changes) { for (const change of changes) {
const node = nodeLookup.get(change.id)?.internals.userNode; const node = nodeLookup.get(change.id)?.internals.userNode;
@@ -156,52 +144,17 @@ export function createStore({
} }
function fitView(options?: FitViewOptions) { function fitView(options?: FitViewOptions) {
const panZoom = get(store.panZoom); // We either create a new Promise or reuse the existing one
const domNode = get(store.domNode); // Even if fitView is called multiple times in a row, we only end up with a single Promise
const fitViewResolver = get(store.fitViewResolver) ?? Promise.withResolvers<boolean>();
if (!panZoom || !domNode) { // We schedule a fitView by setting fitViewQueued and triggering a setNodes
return Promise.resolve(false); store.fitViewQueued.set(true);
} store.fitViewOptions.set(options);
store.fitViewResolver.set(fitViewResolver);
store.nodes.set(get(store.nodes));
const { width, height } = getDimensions(domNode); return fitViewResolver.promise;
const fitViewNodes = getFitViewNodes(get(store.nodeLookup), options);
return fitViewSystem(
{
nodes: fitViewNodes,
width,
height,
minZoom: get(store.minZoom),
maxZoom: get(store.maxZoom),
panZoom
},
options
);
}
function fitViewSync(options?: FitViewOptions) {
const panZoom = get(store.panZoom);
if (!panZoom) {
return false;
}
const fitViewNodes = getFitViewNodes(get(store.nodeLookup), options);
fitViewSystem(
{
nodes: fitViewNodes,
width: get(store.width),
height: get(store.height),
minZoom: get(store.minZoom),
maxZoom: get(store.maxZoom),
panZoom
},
options
);
return fitViewNodes.size > 0;
} }
function zoomBy(factor: number, options?: ViewportHelperFunctionOptions) { function zoomBy(factor: number, options?: ViewportHelperFunctionOptions) {
@@ -395,7 +348,6 @@ export function createStore({
} }
function reset() { function reset() {
store.fitViewOnInitDone.set(false);
store.selectionRect.set(null); store.selectionRect.set(null);
store.selectionRectMode.set(null); store.selectionRectMode.set(null);
store.snapGrid.set(null); store.snapGrid.set(null);
+32 -9
View File
@@ -112,9 +112,32 @@ export const getInitialStore = ({
viewport = getViewportForBounds(bounds, width, height, 0.5, 2, 0.1); viewport = getViewportForBounds(bounds, width, height, 0.5, 2, 0.1);
} }
const fitViewQueued = writable<boolean>(false);
const fitViewOptions = writable<FitViewOptions | undefined>(undefined);
const fitViewResolver = writable<PromiseWithResolvers<boolean> | null>(null);
const panZoom = writable<PanZoomInstance | null>(null);
const widthStore = writable<number>(500);
const heightStore = writable<number>(500);
const minZoom = writable<number>(0.5);
const maxZoom = writable<number>(2);
return { return {
flowId: writable<string | null>(null), flowId: writable<string | null>(null),
nodes: createNodesStore(nodes, nodeLookup, parentLookup, storeNodeOrigin, storeNodeExtent), nodes: createNodesStore(
nodes,
nodeLookup,
parentLookup,
storeNodeOrigin,
storeNodeExtent,
fitViewQueued,
fitViewOptions,
fitViewResolver,
panZoom,
widthStore,
heightStore,
minZoom,
maxZoom
),
nodeLookup: readable<NodeLookup<InternalNode>>(nodeLookup), nodeLookup: readable<NodeLookup<InternalNode>>(nodeLookup),
parentLookup: readable<ParentLookup<InternalNode>>(parentLookup), parentLookup: readable<ParentLookup<InternalNode>>(parentLookup),
edgeLookup: readable<EdgeLookup<Edge>>(edgeLookup), edgeLookup: readable<EdgeLookup<Edge>>(edgeLookup),
@@ -122,20 +145,20 @@ export const getInitialStore = ({
edges: createEdgesStore(edges, connectionLookup, edgeLookup), edges: createEdgesStore(edges, connectionLookup, edgeLookup),
visibleEdges: readable<EdgeLayouted[]>([]), visibleEdges: readable<EdgeLayouted[]>([]),
connectionLookup: readable<ConnectionLookup>(connectionLookup), connectionLookup: readable<ConnectionLookup>(connectionLookup),
height: writable<number>(500), width: widthStore,
width: writable<number>(500), height: heightStore,
minZoom: writable<number>(0.5), minZoom,
maxZoom: writable<number>(2), maxZoom,
nodeOrigin: writable<NodeOrigin>(storeNodeOrigin), nodeOrigin: writable<NodeOrigin>(storeNodeOrigin),
nodeDragThreshold: writable<number>(1), nodeDragThreshold: writable<number>(1),
nodeExtent: writable<CoordinateExtent>(storeNodeExtent), nodeExtent: writable<CoordinateExtent>(storeNodeExtent),
translateExtent: writable<CoordinateExtent>(infiniteExtent), translateExtent: writable<CoordinateExtent>(infiniteExtent),
autoPanOnNodeDrag: writable<boolean>(true), autoPanOnNodeDrag: writable<boolean>(true),
autoPanOnConnect: writable<boolean>(true), autoPanOnConnect: writable<boolean>(true),
fitViewOnInit: writable<boolean>(false), fitViewQueued,
fitViewOnInitDone: writable<boolean>(false), fitViewOptions,
fitViewOptions: writable<FitViewOptions>(undefined), fitViewResolver,
panZoom: writable<PanZoomInstance | null>(null), panZoom,
snapGrid: writable<SnapGrid | null>(null), snapGrid: writable<SnapGrid | null>(null),
dragging: writable<boolean>(false), dragging: writable<boolean>(false),
selectionRect: writable<SelectionRect | null>(null), selectionRect: writable<SelectionRect | null>(null),
+41 -4
View File
@@ -17,10 +17,18 @@ import {
type ParentLookup, type ParentLookup,
type NodeOrigin, type NodeOrigin,
infiniteExtent, infiniteExtent,
type CoordinateExtent type CoordinateExtent,
fitViewport
} from '@xyflow/system'; } from '@xyflow/system';
import type { DefaultEdgeOptions, DefaultNodeOptions, Edge, InternalNode, Node } from '$lib/types'; import type {
DefaultEdgeOptions,
DefaultNodeOptions,
Edge,
FitViewOptions,
InternalNode,
Node
} from '$lib/types';
// we need to sync the user nodes and the internal nodes so that the user can receive the updates // we need to sync the user nodes and the internal nodes so that the user can receive the updates
// made by Svelte Flow (like dragging or selecting a node). // made by Svelte Flow (like dragging or selecting a node).
@@ -134,7 +142,15 @@ export const createNodesStore = (
nodeLookup: NodeLookup<InternalNode>, nodeLookup: NodeLookup<InternalNode>,
parentLookup: ParentLookup<InternalNode>, parentLookup: ParentLookup<InternalNode>,
nodeOrigin: NodeOrigin = [0, 0], nodeOrigin: NodeOrigin = [0, 0],
nodeExtent: CoordinateExtent = infiniteExtent nodeExtent: CoordinateExtent = infiniteExtent,
fitViewQueued: Writable<boolean>,
fitViewOptions: Writable<FitViewOptions | undefined>,
fitViewResolver: Writable<PromiseWithResolvers<boolean> | null>,
panZoom: Writable<PanZoomInstance | null>,
width: Writable<number>,
height: Writable<number>,
minZoom: Writable<number>,
maxZoom: Writable<number>
): { ): {
subscribe: (this: void, run: Subscriber<Node[]>) => Unsubscriber; subscribe: (this: void, run: Subscriber<Node[]>) => Unsubscriber;
update: (this: void, updater: Updater<Node[]>) => void; update: (this: void, updater: Updater<Node[]>) => void;
@@ -148,7 +164,7 @@ export const createNodesStore = (
let elevateNodesOnSelect = true; let elevateNodesOnSelect = true;
const _set = (nds: Node[]): Node[] => { const _set = (nds: Node[]): Node[] => {
adoptUserNodes(nds, nodeLookup, parentLookup, { const nodesInitialized = adoptUserNodes(nds, nodeLookup, parentLookup, {
elevateNodesOnSelect, elevateNodesOnSelect,
nodeOrigin, nodeOrigin,
nodeExtent, nodeExtent,
@@ -156,6 +172,27 @@ export const createNodesStore = (
checkEquality: false checkEquality: false
}); });
if (get(fitViewQueued) && nodesInitialized && get(panZoom)) {
const fitViewPromise = fitViewport(
{
nodes: nodeLookup,
width: get(width),
height: get(height),
panZoom: get(panZoom)!,
minZoom: get(minZoom),
maxZoom: get(maxZoom)
},
get(fitViewOptions)
);
fitViewPromise.then((value) => {
get(fitViewResolver)?.resolve(value);
fitViewResolver.set(null);
});
fitViewQueued.set(false);
fitViewOptions.set(undefined);
}
value = nds; value = nds;
set(value); set(value);
+1 -1
View File
@@ -63,7 +63,7 @@
"@xyflow/eslint-config": "workspace:*", "@xyflow/eslint-config": "workspace:*",
"@xyflow/rollup-config": "workspace:*", "@xyflow/rollup-config": "workspace:*",
"@xyflow/tsconfig": "workspace:*", "@xyflow/tsconfig": "workspace:*",
"typescript": "5.1.3" "typescript": "5.4.5"
}, },
"rollup": { "rollup": {
"globals": { "globals": {
+15 -1
View File
@@ -90,11 +90,25 @@ export type FitViewParamsBase<NodeType extends NodeBase> = {
maxZoom: number; maxZoom: number;
}; };
export type PaddingUnit = 'px' | '%';
export type PaddingWithUnit = `${number}${PaddingUnit}` | number;
export type Padding =
| PaddingWithUnit
| {
top?: PaddingWithUnit;
right?: PaddingWithUnit;
bottom?: PaddingWithUnit;
left?: PaddingWithUnit;
x?: PaddingWithUnit;
y?: PaddingWithUnit;
};
/** /**
* @inline * @inline
*/ */
export type FitViewOptionsBase<NodeType extends NodeBase = NodeBase> = { export type FitViewOptionsBase<NodeType extends NodeBase = NodeBase> = {
padding?: number; padding?: Padding;
includeHiddenNodes?: boolean; includeHiddenNodes?: boolean;
minZoom?: number; minZoom?: number;
maxZoom?: number; maxZoom?: number;
+128 -6
View File
@@ -10,6 +10,8 @@ import type {
Transform, Transform,
InternalNodeBase, InternalNodeBase,
NodeLookup, NodeLookup,
Padding,
PaddingWithUnit,
} from '../types'; } from '../types';
import { type Viewport } from '../types'; import { type Viewport } from '../types';
import { getNodePositionWithOrigin, isInternalNodeBase } from './graph'; import { getNodePositionWithOrigin, isInternalNodeBase } from './graph';
@@ -173,6 +175,105 @@ export const rendererPointToPoint = ({ x, y }: XYPosition, [tx, ty, tScale]: Tra
}; };
}; };
/**
* Parses a single padding value to a number
* @internal
* @param padding - Padding to parse
* @param viewport - Width or height of the viewport
* @returns The padding in pixels
*/
function parsePadding(padding: PaddingWithUnit, viewport: number): number {
if (typeof padding === 'number') {
return Math.floor(viewport - viewport / (1 + padding));
}
if (typeof padding === 'string' && padding.endsWith('px')) {
const paddingValue = parseFloat(padding);
if (!Number.isNaN(paddingValue)) {
return Math.floor(paddingValue);
}
}
if (typeof padding === 'string' && padding.endsWith('%')) {
const paddingValue = parseFloat(padding);
if (!Number.isNaN(paddingValue)) {
return Math.floor(viewport * paddingValue * 0.01);
}
}
console.error(
`[React Flow] The padding value "${padding}" is invalid. Please provide a number or a string with a valid unit (px or %).`
);
return 0;
}
/**
* Parses the paddings to an object with top, right, bottom, left, x and y paddings
* @internal
* @param padding - Padding to parse
* @param width - Width of the viewport
* @param height - Height of the viewport
* @returns An object with the paddings in pixels
*/
function parsePaddings(
padding: Padding,
width: number,
height: number
): { top: number; bottom: number; left: number; right: number; x: number; y: number } {
if (typeof padding === 'string' || typeof padding === 'number') {
const paddingY = parsePadding(padding, height);
const paddingX = parsePadding(padding, width);
return {
top: paddingY,
right: paddingX,
bottom: paddingY,
left: paddingX,
x: paddingX * 2,
y: paddingY * 2,
};
}
if (typeof padding === 'object') {
const top = parsePadding(padding.top ?? padding.y ?? 0, height);
const bottom = parsePadding(padding.bottom ?? padding.y ?? 0, height);
const left = parsePadding(padding.left ?? padding.x ?? 0, width);
const right = parsePadding(padding.right ?? padding.x ?? 0, width);
return { top, right, bottom, left, x: left + right, y: top + bottom };
}
return { top: 0, right: 0, bottom: 0, left: 0, x: 0, y: 0 };
}
/**
* Calculates the resulting paddings if the new viewport is applied
* @internal
* @param bounds - Bounds to fit inside viewport
* @param x - X position of the viewport
* @param y - Y position of the viewport
* @param zoom - Zoom level of the viewport
* @param width - Width of the viewport
* @param height - Height of the viewport
* @returns An object with the minimum padding required to fit the bounds inside the viewport
*/
function calculateAppliedPaddings(bounds: Rect, x: number, y: number, zoom: number, width: number, height: number) {
const { x: left, y: top } = rendererPointToPoint(bounds, [x, y, zoom]);
const { x: boundRight, y: boundBottom } = rendererPointToPoint(
{ x: bounds.x + bounds.width, y: bounds.y + bounds.height },
[x, y, zoom]
);
const right = width - boundRight;
const bottom = height - boundBottom;
return {
left: Math.floor(left),
top: Math.floor(top),
right: Math.floor(right),
bottom: Math.floor(bottom),
};
}
/** /**
* Returns a viewport that encloses the given bounds with optional padding. * Returns a viewport that encloses the given bounds with optional padding.
* @public * @public
@@ -186,8 +287,8 @@ export const rendererPointToPoint = ({ x, y }: XYPosition, [tx, ty, tScale]: Tra
* @returns A transforned {@link Viewport} that encloses the given bounds which you can pass to e.g. {@link setViewport} * @returns A transforned {@link Viewport} that encloses the given bounds which you can pass to e.g. {@link setViewport}
* @example * @example
* const { x, y, zoom } = getViewportForBounds( * const { x, y, zoom } = getViewportForBounds(
*{ x: 0, y: 0, width: 100, height: 100}, * { x: 0, y: 0, width: 100, height: 100},
*1200, 800, 0.5, 2); * 1200, 800, 0.5, 2);
*/ */
export const getViewportForBounds = ( export const getViewportForBounds = (
bounds: Rect, bounds: Rect,
@@ -195,18 +296,39 @@ export const getViewportForBounds = (
height: number, height: number,
minZoom: number, minZoom: number,
maxZoom: number, maxZoom: number,
padding: number padding: Padding
): Viewport => { ): Viewport => {
const xZoom = width / (bounds.width * (1 + padding)); // First we resolve all the paddings to actual pixel values
const yZoom = height / (bounds.height * (1 + padding)); const p = parsePaddings(padding, width, height);
const xZoom = (width - p.x) / bounds.width;
const yZoom = (height - p.y) / bounds.height;
// We calculate the new x, y, zoom for a centered view
const zoom = Math.min(xZoom, yZoom); const zoom = Math.min(xZoom, yZoom);
const clampedZoom = clamp(zoom, minZoom, maxZoom); const clampedZoom = clamp(zoom, minZoom, maxZoom);
const boundsCenterX = bounds.x + bounds.width / 2; const boundsCenterX = bounds.x + bounds.width / 2;
const boundsCenterY = bounds.y + bounds.height / 2; const boundsCenterY = bounds.y + bounds.height / 2;
const x = width / 2 - boundsCenterX * clampedZoom; const x = width / 2 - boundsCenterX * clampedZoom;
const y = height / 2 - boundsCenterY * clampedZoom; const y = height / 2 - boundsCenterY * clampedZoom;
return { x, y, zoom: clampedZoom }; // Then we calculate the minimum padding, to respect asymmetric paddings
const newPadding = calculateAppliedPaddings(bounds, x, y, clampedZoom, width, height);
// We only want to have an offset if the newPadding is smaller than the required padding
const offset = {
left: Math.min(newPadding.left - p.left, 0),
top: Math.min(newPadding.top - p.top, 0),
right: Math.min(newPadding.right - p.right, 0),
bottom: Math.min(newPadding.bottom - p.bottom, 0),
};
return {
x: x - offset.left + offset.right,
y: y - offset.top + offset.bottom,
zoom: clampedZoom,
};
}; };
export const isMacOs = () => typeof navigator !== 'undefined' && navigator?.userAgent?.indexOf('Mac') >= 0; export const isMacOs = () => typeof navigator !== 'undefined' && navigator?.userAgent?.indexOf('Mac') >= 0;
+10 -5
View File
@@ -333,10 +333,10 @@ export const getConnectedEdges = <NodeType extends NodeBase = NodeBase, EdgeType
return edges.filter((edge) => nodeIds.has(edge.source) || nodeIds.has(edge.target)); return edges.filter((edge) => nodeIds.has(edge.source) || nodeIds.has(edge.target));
}; };
export function getFitViewNodes< function getFitViewNodes<
Params extends NodeLookup<InternalNodeBase<NodeBase>>, Params extends NodeLookup<InternalNodeBase<NodeBase>>,
Options extends FitViewOptionsBase<NodeBase> Options extends FitViewOptionsBase<NodeBase>
>(nodeLookup: Params, options?: Pick<Options, 'nodes' | 'includeHiddenNodes'>) { >(nodeLookup: Params, options?: Options) {
const fitViewNodes: NodeLookup = new Map(); const fitViewNodes: NodeLookup = new Map();
const optionNodeIds = options?.nodes ? new Set(options.nodes.map((node) => node.id)) : null; const optionNodeIds = options?.nodes ? new Set(options.nodes.map((node) => node.id)) : null;
@@ -351,15 +351,20 @@ export function getFitViewNodes<
return fitViewNodes; return fitViewNodes;
} }
export async function fitView<Params extends FitViewParamsBase<NodeBase>, Options extends FitViewOptionsBase<NodeBase>>( export async function fitViewport<
Params extends FitViewParamsBase<NodeBase>,
Options extends FitViewOptionsBase<NodeBase>
>(
{ nodes, width, height, panZoom, minZoom, maxZoom }: Params, { nodes, width, height, panZoom, minZoom, maxZoom }: Params,
options?: Omit<Options, 'nodes' | 'includeHiddenNodes'> options?: Omit<Options, 'nodes' | 'includeHiddenNodes'>
): Promise<boolean> { ): Promise<boolean> {
if (nodes.size === 0) { if (nodes.size === 0) {
return Promise.resolve(false); return Promise.resolve(true);
} }
const bounds = getInternalNodesBounds(nodes); const nodesToFit = getFitViewNodes(nodes, options);
const bounds = getInternalNodesBounds(nodesToFit);
const viewport = getViewportForBounds( const viewport = getViewportForBounds(
bounds, bounds,
+11 -1
View File
@@ -85,9 +85,10 @@ export function adoptUserNodes<NodeType extends NodeBase>(
nodeLookup: NodeLookup<InternalNodeBase<NodeType>>, nodeLookup: NodeLookup<InternalNodeBase<NodeType>>,
parentLookup: ParentLookup<InternalNodeBase<NodeType>>, parentLookup: ParentLookup<InternalNodeBase<NodeType>>,
options?: UpdateNodesOptions<NodeType> options?: UpdateNodesOptions<NodeType>
) { ): boolean {
const _options = mergeObjects(adoptUserNodesDefaultOptions, options); const _options = mergeObjects(adoptUserNodesDefaultOptions, options);
let nodesInitialized = true;
const tmpLookup = new Map(nodeLookup); const tmpLookup = new Map(nodeLookup);
const selectedNodeZ: number = _options?.elevateNodesOnSelect ? 1000 : 0; const selectedNodeZ: number = _options?.elevateNodesOnSelect ? 1000 : 0;
@@ -123,10 +124,19 @@ export function adoptUserNodes<NodeType extends NodeBase>(
nodeLookup.set(userNode.id, internalNode); nodeLookup.set(userNode.id, internalNode);
} }
if (
(!internalNode.measured || !internalNode.measured.width || !internalNode.measured.height) &&
!internalNode.hidden
) {
nodesInitialized = false;
}
if (userNode.parentId) { if (userNode.parentId) {
updateChildNode(internalNode, nodeLookup, parentLookup, options); updateChildNode(internalNode, nodeLookup, parentLookup, options);
} }
} }
return nodesInitialized;
} }
function updateParentLookup<NodeType extends NodeBase>( function updateParentLookup<NodeType extends NodeBase>(
+67 -106
View File
@@ -42,8 +42,8 @@ importers:
specifier: ^2.0.3 specifier: ^2.0.3
version: 2.0.3 version: 2.0.3
typescript: typescript:
specifier: 5.1.3 specifier: 5.4.5
version: 5.1.3 version: 5.4.5
examples/astro-xyflow: examples/astro-xyflow:
dependencies: dependencies:
@@ -145,8 +145,8 @@ importers:
specifier: ^2.0.2 specifier: ^2.0.2
version: 2.0.2 version: 2.0.2
typescript: typescript:
specifier: 5.2.2 specifier: 5.4.5
version: 5.2.2 version: 5.4.5
vite: vite:
specifier: 4.5.0 specifier: 4.5.0
version: 4.5.0(@types/node@20.14.6)(terser@5.31.0) version: 4.5.0(@types/node@20.14.6)(terser@5.31.0)
@@ -171,10 +171,10 @@ importers:
version: 2.5.10(@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.12)(vite@5.2.12(@types/node@20.14.6)(terser@5.31.0)))(svelte@4.2.12)(vite@5.2.12(@types/node@20.14.6)(terser@5.31.0)) version: 2.5.10(@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.12)(vite@5.2.12(@types/node@20.14.6)(terser@5.31.0)))(svelte@4.2.12)(vite@5.2.12(@types/node@20.14.6)(terser@5.31.0))
'@typescript-eslint/eslint-plugin': '@typescript-eslint/eslint-plugin':
specifier: ^6.10.0 specifier: ^6.10.0
version: 6.10.0(@typescript-eslint/parser@6.10.0(eslint@8.53.0)(typescript@5.2.2))(eslint@8.53.0)(typescript@5.2.2) version: 6.10.0(@typescript-eslint/parser@6.10.0(eslint@8.53.0)(typescript@5.4.5))(eslint@8.53.0)(typescript@5.4.5)
'@typescript-eslint/parser': '@typescript-eslint/parser':
specifier: ^6.10.0 specifier: ^6.10.0
version: 6.10.0(eslint@8.53.0)(typescript@5.2.2) version: 6.10.0(eslint@8.53.0)(typescript@5.4.5)
eslint: eslint:
specifier: ^8.53.0 specifier: ^8.53.0
version: 8.53.0 version: 8.53.0
@@ -200,8 +200,8 @@ importers:
specifier: ^2.6.2 specifier: ^2.6.2
version: 2.6.2 version: 2.6.2
typescript: typescript:
specifier: ^5.2.2 specifier: ^5.4.5
version: 5.2.2 version: 5.4.5
vite: vite:
specifier: ^5.2.12 specifier: ^5.2.12
version: 5.2.12(@types/node@20.14.6)(terser@5.31.0) version: 5.2.12(@types/node@20.14.6)(terser@5.31.0)
@@ -267,8 +267,8 @@ importers:
specifier: ^18.2.0 specifier: ^18.2.0
version: 18.2.0 version: 18.2.0
typescript: typescript:
specifier: 5.1.3 specifier: 5.4.5
version: 5.1.3 version: 5.4.5
packages/svelte: packages/svelte:
dependencies: dependencies:
@@ -290,13 +290,13 @@ importers:
version: 2.5.4(@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.12)(vite@5.3.2(@types/node@20.14.6)(terser@5.31.0)))(svelte@4.2.12)(vite@5.3.2(@types/node@20.14.6)(terser@5.31.0)) version: 2.5.4(@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.12)(vite@5.3.2(@types/node@20.14.6)(terser@5.31.0)))(svelte@4.2.12)(vite@5.3.2(@types/node@20.14.6)(terser@5.31.0))
'@sveltejs/package': '@sveltejs/package':
specifier: ^2.3.0 specifier: ^2.3.0
version: 2.3.0(svelte@4.2.12)(typescript@5.4.2) version: 2.3.0(svelte@4.2.12)(typescript@5.4.5)
'@typescript-eslint/eslint-plugin': '@typescript-eslint/eslint-plugin':
specifier: ^7.2.0 specifier: ^7.2.0
version: 7.2.0(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.2))(eslint@8.57.0)(typescript@5.4.2) version: 7.2.0(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5)
'@typescript-eslint/parser': '@typescript-eslint/parser':
specifier: ^7.2.0 specifier: ^7.2.0
version: 7.2.0(eslint@8.57.0)(typescript@5.4.2) version: 7.2.0(eslint@8.57.0)(typescript@5.4.5)
autoprefixer: autoprefixer:
specifier: ^10.4.18 specifier: ^10.4.18
version: 10.4.18(postcss@8.4.35) version: 10.4.18(postcss@8.4.35)
@@ -350,13 +350,13 @@ importers:
version: 0.33.1(svelte@4.2.12) version: 0.33.1(svelte@4.2.12)
svelte-preprocess: svelte-preprocess:
specifier: ^5.1.3 specifier: ^5.1.3
version: 5.1.3(@babel/core@7.24.7)(postcss-load-config@5.0.2(postcss@8.4.35))(postcss@8.4.35)(svelte@4.2.12)(typescript@5.4.2) version: 5.1.3(@babel/core@7.24.7)(postcss-load-config@5.0.2(postcss@8.4.35))(postcss@8.4.35)(svelte@4.2.12)(typescript@5.4.5)
tslib: tslib:
specifier: ^2.6.2 specifier: ^2.6.2
version: 2.6.2 version: 2.6.2
typescript: typescript:
specifier: 5.4.2 specifier: 5.4.5
version: 5.4.2 version: 5.4.5
packages/system: packages/system:
dependencies: dependencies:
@@ -395,8 +395,8 @@ importers:
specifier: workspace:* specifier: workspace:*
version: link:../../tooling/tsconfig version: link:../../tooling/tsconfig
typescript: typescript:
specifier: 5.1.3 specifier: 5.4.5
version: 5.1.3 version: 5.4.5
tests/playwright: tests/playwright:
dependencies: dependencies:
@@ -463,7 +463,7 @@ importers:
version: 0.4.4(rollup@4.18.0) version: 0.4.4(rollup@4.18.0)
'@rollup/plugin-typescript': '@rollup/plugin-typescript':
specifier: 11.1.6 specifier: 11.1.6
version: 11.1.6(rollup@4.18.0)(tslib@2.6.2)(typescript@5.4.2) version: 11.1.6(rollup@4.18.0)(tslib@2.6.2)(typescript@5.4.5)
rollup: rollup:
specifier: ^4.18.0 specifier: ^4.18.0
version: 4.18.0 version: 4.18.0
@@ -471,8 +471,8 @@ importers:
specifier: ^2.2.4 specifier: ^2.2.4
version: 2.2.4(rollup@4.18.0) version: 2.2.4(rollup@4.18.0)
typescript: typescript:
specifier: ^5.1.3 specifier: ^5.4.5
version: 5.4.2 version: 5.4.5
tooling/tsconfig: {} tooling/tsconfig: {}
@@ -6300,21 +6300,6 @@ packages:
resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
typescript@5.1.3:
resolution: {integrity: sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw==}
engines: {node: '>=14.17'}
hasBin: true
typescript@5.2.2:
resolution: {integrity: sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==}
engines: {node: '>=14.17'}
hasBin: true
typescript@5.4.2:
resolution: {integrity: sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==}
engines: {node: '>=14.17'}
hasBin: true
typescript@5.4.5: typescript@5.4.5:
resolution: {integrity: sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==} resolution: {integrity: sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==}
engines: {node: '>=14.17'} engines: {node: '>=14.17'}
@@ -7884,11 +7869,11 @@ snapshots:
optionalDependencies: optionalDependencies:
rollup: 4.18.0 rollup: 4.18.0
'@rollup/plugin-typescript@11.1.6(rollup@4.18.0)(tslib@2.6.2)(typescript@5.4.2)': '@rollup/plugin-typescript@11.1.6(rollup@4.18.0)(tslib@2.6.2)(typescript@5.4.5)':
dependencies: dependencies:
'@rollup/pluginutils': 5.1.0(rollup@4.18.0) '@rollup/pluginutils': 5.1.0(rollup@4.18.0)
resolve: 1.22.8 resolve: 1.22.8
typescript: 5.4.2 typescript: 5.4.5
optionalDependencies: optionalDependencies:
rollup: 4.18.0 rollup: 4.18.0
tslib: 2.6.2 tslib: 2.6.2
@@ -8009,14 +7994,14 @@ snapshots:
tiny-glob: 0.2.9 tiny-glob: 0.2.9
vite: 5.3.2(@types/node@20.14.6)(terser@5.31.0) vite: 5.3.2(@types/node@20.14.6)(terser@5.31.0)
'@sveltejs/package@2.3.0(svelte@4.2.12)(typescript@5.4.2)': '@sveltejs/package@2.3.0(svelte@4.2.12)(typescript@5.4.5)':
dependencies: dependencies:
chokidar: 3.6.0 chokidar: 3.6.0
kleur: 4.1.5 kleur: 4.1.5
sade: 1.8.1 sade: 1.8.1
semver: 7.6.0 semver: 7.6.0
svelte: 4.2.12 svelte: 4.2.12
svelte2tsx: 0.7.3(svelte@4.2.12)(typescript@5.4.2) svelte2tsx: 0.7.3(svelte@4.2.12)(typescript@5.4.5)
transitivePeerDependencies: transitivePeerDependencies:
- typescript - typescript
@@ -8319,13 +8304,13 @@ snapshots:
'@types/node': 18.7.16 '@types/node': 18.7.16
optional: true optional: true
'@typescript-eslint/eslint-plugin@6.10.0(@typescript-eslint/parser@6.10.0(eslint@8.53.0)(typescript@5.2.2))(eslint@8.53.0)(typescript@5.2.2)': '@typescript-eslint/eslint-plugin@6.10.0(@typescript-eslint/parser@6.10.0(eslint@8.53.0)(typescript@5.4.5))(eslint@8.53.0)(typescript@5.4.5)':
dependencies: dependencies:
'@eslint-community/regexpp': 4.10.0 '@eslint-community/regexpp': 4.10.0
'@typescript-eslint/parser': 6.10.0(eslint@8.53.0)(typescript@5.2.2) '@typescript-eslint/parser': 6.10.0(eslint@8.53.0)(typescript@5.4.5)
'@typescript-eslint/scope-manager': 6.10.0 '@typescript-eslint/scope-manager': 6.10.0
'@typescript-eslint/type-utils': 6.10.0(eslint@8.53.0)(typescript@5.2.2) '@typescript-eslint/type-utils': 6.10.0(eslint@8.53.0)(typescript@5.4.5)
'@typescript-eslint/utils': 6.10.0(eslint@8.53.0)(typescript@5.2.2) '@typescript-eslint/utils': 6.10.0(eslint@8.53.0)(typescript@5.4.5)
'@typescript-eslint/visitor-keys': 6.10.0 '@typescript-eslint/visitor-keys': 6.10.0
debug: 4.3.4(supports-color@8.1.1) debug: 4.3.4(supports-color@8.1.1)
eslint: 8.53.0 eslint: 8.53.0
@@ -8333,19 +8318,19 @@ snapshots:
ignore: 5.2.4 ignore: 5.2.4
natural-compare: 1.4.0 natural-compare: 1.4.0
semver: 7.5.4 semver: 7.5.4
ts-api-utils: 1.0.3(typescript@5.2.2) ts-api-utils: 1.0.3(typescript@5.4.5)
optionalDependencies: optionalDependencies:
typescript: 5.2.2 typescript: 5.4.5
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
'@typescript-eslint/eslint-plugin@7.2.0(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.2))(eslint@8.57.0)(typescript@5.4.2)': '@typescript-eslint/eslint-plugin@7.2.0(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5)':
dependencies: dependencies:
'@eslint-community/regexpp': 4.10.0 '@eslint-community/regexpp': 4.10.0
'@typescript-eslint/parser': 7.2.0(eslint@8.57.0)(typescript@5.4.2) '@typescript-eslint/parser': 7.2.0(eslint@8.57.0)(typescript@5.4.5)
'@typescript-eslint/scope-manager': 7.2.0 '@typescript-eslint/scope-manager': 7.2.0
'@typescript-eslint/type-utils': 7.2.0(eslint@8.57.0)(typescript@5.4.2) '@typescript-eslint/type-utils': 7.2.0(eslint@8.57.0)(typescript@5.4.5)
'@typescript-eslint/utils': 7.2.0(eslint@8.57.0)(typescript@5.4.2) '@typescript-eslint/utils': 7.2.0(eslint@8.57.0)(typescript@5.4.5)
'@typescript-eslint/visitor-keys': 7.2.0 '@typescript-eslint/visitor-keys': 7.2.0
debug: 4.3.4(supports-color@8.1.1) debug: 4.3.4(supports-color@8.1.1)
eslint: 8.57.0 eslint: 8.57.0
@@ -8353,9 +8338,9 @@ snapshots:
ignore: 5.3.0 ignore: 5.3.0
natural-compare: 1.4.0 natural-compare: 1.4.0
semver: 7.6.0 semver: 7.6.0
ts-api-utils: 1.0.3(typescript@5.4.2) ts-api-utils: 1.0.3(typescript@5.4.5)
optionalDependencies: optionalDependencies:
typescript: 5.4.2 typescript: 5.4.5
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
@@ -8376,29 +8361,29 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
'@typescript-eslint/parser@6.10.0(eslint@8.53.0)(typescript@5.2.2)': '@typescript-eslint/parser@6.10.0(eslint@8.53.0)(typescript@5.4.5)':
dependencies: dependencies:
'@typescript-eslint/scope-manager': 6.10.0 '@typescript-eslint/scope-manager': 6.10.0
'@typescript-eslint/types': 6.10.0 '@typescript-eslint/types': 6.10.0
'@typescript-eslint/typescript-estree': 6.10.0(typescript@5.2.2) '@typescript-eslint/typescript-estree': 6.10.0(typescript@5.4.5)
'@typescript-eslint/visitor-keys': 6.10.0 '@typescript-eslint/visitor-keys': 6.10.0
debug: 4.3.4(supports-color@8.1.1) debug: 4.3.4(supports-color@8.1.1)
eslint: 8.53.0 eslint: 8.53.0
optionalDependencies: optionalDependencies:
typescript: 5.2.2 typescript: 5.4.5
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
'@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.2)': '@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.5)':
dependencies: dependencies:
'@typescript-eslint/scope-manager': 7.2.0 '@typescript-eslint/scope-manager': 7.2.0
'@typescript-eslint/types': 7.2.0 '@typescript-eslint/types': 7.2.0
'@typescript-eslint/typescript-estree': 7.2.0(typescript@5.4.2) '@typescript-eslint/typescript-estree': 7.2.0(typescript@5.4.5)
'@typescript-eslint/visitor-keys': 7.2.0 '@typescript-eslint/visitor-keys': 7.2.0
debug: 4.3.4(supports-color@8.1.1) debug: 4.3.4(supports-color@8.1.1)
eslint: 8.57.0 eslint: 8.57.0
optionalDependencies: optionalDependencies:
typescript: 5.4.2 typescript: 5.4.5
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
@@ -8429,27 +8414,27 @@ snapshots:
'@typescript-eslint/types': 8.23.0 '@typescript-eslint/types': 8.23.0
'@typescript-eslint/visitor-keys': 8.23.0 '@typescript-eslint/visitor-keys': 8.23.0
'@typescript-eslint/type-utils@6.10.0(eslint@8.53.0)(typescript@5.2.2)': '@typescript-eslint/type-utils@6.10.0(eslint@8.53.0)(typescript@5.4.5)':
dependencies: dependencies:
'@typescript-eslint/typescript-estree': 6.10.0(typescript@5.2.2) '@typescript-eslint/typescript-estree': 6.10.0(typescript@5.4.5)
'@typescript-eslint/utils': 6.10.0(eslint@8.53.0)(typescript@5.2.2) '@typescript-eslint/utils': 6.10.0(eslint@8.53.0)(typescript@5.4.5)
debug: 4.3.4(supports-color@8.1.1) debug: 4.3.4(supports-color@8.1.1)
eslint: 8.53.0 eslint: 8.53.0
ts-api-utils: 1.0.3(typescript@5.2.2) ts-api-utils: 1.0.3(typescript@5.4.5)
optionalDependencies: optionalDependencies:
typescript: 5.2.2 typescript: 5.4.5
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
'@typescript-eslint/type-utils@7.2.0(eslint@8.57.0)(typescript@5.4.2)': '@typescript-eslint/type-utils@7.2.0(eslint@8.57.0)(typescript@5.4.5)':
dependencies: dependencies:
'@typescript-eslint/typescript-estree': 7.2.0(typescript@5.4.2) '@typescript-eslint/typescript-estree': 7.2.0(typescript@5.4.5)
'@typescript-eslint/utils': 7.2.0(eslint@8.57.0)(typescript@5.4.2) '@typescript-eslint/utils': 7.2.0(eslint@8.57.0)(typescript@5.4.5)
debug: 4.3.4(supports-color@8.1.1) debug: 4.3.4(supports-color@8.1.1)
eslint: 8.57.0 eslint: 8.57.0
ts-api-utils: 1.0.3(typescript@5.4.2) ts-api-utils: 1.0.3(typescript@5.4.5)
optionalDependencies: optionalDependencies:
typescript: 5.4.2 typescript: 5.4.5
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
@@ -8470,7 +8455,7 @@ snapshots:
'@typescript-eslint/types@8.23.0': {} '@typescript-eslint/types@8.23.0': {}
'@typescript-eslint/typescript-estree@6.10.0(typescript@5.2.2)': '@typescript-eslint/typescript-estree@6.10.0(typescript@5.4.5)':
dependencies: dependencies:
'@typescript-eslint/types': 6.10.0 '@typescript-eslint/types': 6.10.0
'@typescript-eslint/visitor-keys': 6.10.0 '@typescript-eslint/visitor-keys': 6.10.0
@@ -8478,13 +8463,13 @@ snapshots:
globby: 11.1.0 globby: 11.1.0
is-glob: 4.0.3 is-glob: 4.0.3
semver: 7.5.4 semver: 7.5.4
ts-api-utils: 1.0.3(typescript@5.2.2) ts-api-utils: 1.0.3(typescript@5.4.5)
optionalDependencies: optionalDependencies:
typescript: 5.2.2 typescript: 5.4.5
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
'@typescript-eslint/typescript-estree@7.2.0(typescript@5.4.2)': '@typescript-eslint/typescript-estree@7.2.0(typescript@5.4.5)':
dependencies: dependencies:
'@typescript-eslint/types': 7.2.0 '@typescript-eslint/types': 7.2.0
'@typescript-eslint/visitor-keys': 7.2.0 '@typescript-eslint/visitor-keys': 7.2.0
@@ -8493,9 +8478,9 @@ snapshots:
is-glob: 4.0.3 is-glob: 4.0.3
minimatch: 9.0.3 minimatch: 9.0.3
semver: 7.6.0 semver: 7.6.0
ts-api-utils: 1.0.3(typescript@5.4.2) ts-api-utils: 1.0.3(typescript@5.4.5)
optionalDependencies: optionalDependencies:
typescript: 5.4.2 typescript: 5.4.5
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
@@ -8513,28 +8498,28 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
'@typescript-eslint/utils@6.10.0(eslint@8.53.0)(typescript@5.2.2)': '@typescript-eslint/utils@6.10.0(eslint@8.53.0)(typescript@5.4.5)':
dependencies: dependencies:
'@eslint-community/eslint-utils': 4.4.0(eslint@8.53.0) '@eslint-community/eslint-utils': 4.4.0(eslint@8.53.0)
'@types/json-schema': 7.0.15 '@types/json-schema': 7.0.15
'@types/semver': 7.5.6 '@types/semver': 7.5.6
'@typescript-eslint/scope-manager': 6.10.0 '@typescript-eslint/scope-manager': 6.10.0
'@typescript-eslint/types': 6.10.0 '@typescript-eslint/types': 6.10.0
'@typescript-eslint/typescript-estree': 6.10.0(typescript@5.2.2) '@typescript-eslint/typescript-estree': 6.10.0(typescript@5.4.5)
eslint: 8.53.0 eslint: 8.53.0
semver: 7.5.4 semver: 7.5.4
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
- typescript - typescript
'@typescript-eslint/utils@7.2.0(eslint@8.57.0)(typescript@5.4.2)': '@typescript-eslint/utils@7.2.0(eslint@8.57.0)(typescript@5.4.5)':
dependencies: dependencies:
'@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0)
'@types/json-schema': 7.0.15 '@types/json-schema': 7.0.15
'@types/semver': 7.5.6 '@types/semver': 7.5.6
'@typescript-eslint/scope-manager': 7.2.0 '@typescript-eslint/scope-manager': 7.2.0
'@typescript-eslint/types': 7.2.0 '@typescript-eslint/types': 7.2.0
'@typescript-eslint/typescript-estree': 7.2.0(typescript@5.4.2) '@typescript-eslint/typescript-estree': 7.2.0(typescript@5.4.5)
eslint: 8.57.0 eslint: 8.57.0
semver: 7.6.0 semver: 7.6.0
transitivePeerDependencies: transitivePeerDependencies:
@@ -13430,20 +13415,6 @@ snapshots:
dependencies: dependencies:
svelte: 4.2.12 svelte: 4.2.12
svelte-preprocess@5.1.3(@babel/core@7.24.7)(postcss-load-config@5.0.2(postcss@8.4.35))(postcss@8.4.35)(svelte@4.2.12)(typescript@5.4.2):
dependencies:
'@types/pug': 2.0.10
detect-indent: 6.1.0
magic-string: 0.30.8
sorcery: 0.11.0
strip-indent: 3.0.0
svelte: 4.2.12
optionalDependencies:
'@babel/core': 7.24.7
postcss: 8.4.35
postcss-load-config: 5.0.2(postcss@8.4.35)
typescript: 5.4.2
svelte-preprocess@5.1.3(@babel/core@7.24.7)(postcss-load-config@5.0.2(postcss@8.4.35))(postcss@8.4.35)(svelte@4.2.12)(typescript@5.4.5): svelte-preprocess@5.1.3(@babel/core@7.24.7)(postcss-load-config@5.0.2(postcss@8.4.35))(postcss@8.4.35)(svelte@4.2.12)(typescript@5.4.5):
dependencies: dependencies:
'@types/pug': 2.0.10 '@types/pug': 2.0.10
@@ -13465,12 +13436,12 @@ snapshots:
svelte: 4.2.1 svelte: 4.2.1
typescript: 5.4.5 typescript: 5.4.5
svelte2tsx@0.7.3(svelte@4.2.12)(typescript@5.4.2): svelte2tsx@0.7.3(svelte@4.2.12)(typescript@5.4.5):
dependencies: dependencies:
dedent-js: 1.0.1 dedent-js: 1.0.1
pascal-case: 3.1.2 pascal-case: 3.1.2
svelte: 4.2.12 svelte: 4.2.12
typescript: 5.4.2 typescript: 5.4.5
svelte@4.2.1: svelte@4.2.1:
dependencies: dependencies:
@@ -13608,13 +13579,9 @@ snapshots:
trough@2.1.0: {} trough@2.1.0: {}
ts-api-utils@1.0.3(typescript@5.2.2): ts-api-utils@1.0.3(typescript@5.4.5):
dependencies: dependencies:
typescript: 5.2.2 typescript: 5.4.5
ts-api-utils@1.0.3(typescript@5.4.2):
dependencies:
typescript: 5.4.2
ts-api-utils@2.0.1(typescript@5.4.5): ts-api-utils@2.0.1(typescript@5.4.5):
dependencies: dependencies:
@@ -13782,12 +13749,6 @@ snapshots:
possible-typed-array-names: 1.0.0 possible-typed-array-names: 1.0.0
reflect.getprototypeof: 1.0.10 reflect.getprototypeof: 1.0.10
typescript@5.1.3: {}
typescript@5.2.2: {}
typescript@5.4.2: {}
typescript@5.4.5: {} typescript@5.4.5: {}
ultrahtml@1.5.2: {} ultrahtml@1.5.2: {}
+2 -2
View File
@@ -30,8 +30,8 @@ test.describe('Pane default', () => {
const transformsAfter = await getTransform(viewport); const transformsAfter = await getTransform(viewport);
expect(transformsAfter.translateX - transformsBefore.translateX).toBe(100); expect(Math.floor(transformsAfter.translateX - transformsBefore.translateX)).toBe(100);
expect(transformsAfter.translateY - transformsBefore.translateY).toBe(100); expect(Math.floor(transformsAfter.translateY - transformsBefore.translateY)).toBe(100);
}); });
test('scrolling the default pane zooms it', async ({ page }) => { test('scrolling the default pane zooms it', async ({ page }) => {
+1 -1
View File
@@ -14,6 +14,6 @@
"@rollup/plugin-typescript": "11.1.6", "@rollup/plugin-typescript": "11.1.6",
"rollup": "^4.18.0", "rollup": "^4.18.0",
"rollup-plugin-peer-deps-external": "^2.2.4", "rollup-plugin-peer-deps-external": "^2.2.4",
"typescript": "^5.1.3" "typescript": "^5.4.5"
} }
} }