diff --git a/.changeset/hip-fireants-change.md b/.changeset/hip-fireants-change.md new file mode 100644 index 00000000..90137725 --- /dev/null +++ b/.changeset/hip-fireants-change.md @@ -0,0 +1,5 @@ +--- +'@xyflow/svelte': patch +--- + +Fix setting nodesInitialized multiple times diff --git a/.changeset/late-taxis-press.md b/.changeset/late-taxis-press.md new file mode 100644 index 00000000..e48f797b --- /dev/null +++ b/.changeset/late-taxis-press.md @@ -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 diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 2e94ce0e..ac478a2e 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -13,7 +13,7 @@ jobs: name: 'Playwright Tests' runs-on: ubuntu-latest container: - image: mcr.microsoft.com/playwright:v1.45.0-jammy + image: mcr.microsoft.com/playwright:v1.51.1-jammy steps: - name: Checkout repo uses: actions/checkout@v4 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ae36b619..bd16a854 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -25,5 +25,5 @@ jobs: title: Release packages commit: 'chore(packages): bump' env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }} NPM_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a6710280..dfd9ac5d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -20,19 +20,19 @@ That being said, our libraries are only interesting because of the people who ma The things we need the most help for the library and its community are: -**🐛 Bug reports:** We simply can’t catch them all. Check [existing issues](https://github.com/wbkd/react-flow/issues/new/choose) and discussion first, then [create a new issue](https://github.com/wbkd/react-flow/issues/new/choose) to tell us what’s up. +**🐛 Bug reports:** We simply can’t catch them all. Check [existing issues](https://github.com/xyflow/xyflow/issues/) and discussion first, then [create a new issue](https://github.com/xyflow/xyflow/issues/new/choose) to tell us what’s up. -**💬 Answering questions** in our [Discord Server](https://discord.gg/Bqt6xrs) and [Github discussions](https://github.com/wbkd/react-flow/discussions). +**💬 Answering questions** in our [Discord Server](https://discord.gg/Bqt6xrs) and [Github discussions](https://github.com/xyflow/xyflow/discussions). 🎬 **Create tutorials**. Send them to us and we’ll happily share them! -**✏️ Edit our [Docs](https://reactflow.dev/docs/introduction/)**: Make changes in the [react-flow-docs repo](https://github.com/wbkd/react-flow-docs), or click the "edit this page” button that lives on every doc site. +**✏️ Edit our [Docs](https://reactflow.dev/learn/concepts/introduction/)**: Make changes in our [web repo](https://github.com/xyflow/web), where our docs live. -All interactions should be done with care following our [Code of Conduct](https://github.com/wbkd/react-flow/blob/main/CODE_OF_CONDUCT.md). +All interactions should be done with care following our [Code of Conduct](https://github.com/xyflow/xyflow/blob/main/CODE_OF_CONDUCT.md). ## Enhancements -If you have an idea or suggestion for an enhancement to the React Flow or Svelte Flow library, please use the [New Features](https://github.com/wbkd/react-flow/discussions/categories/new-features) discussion section. If you want to build it yourself, **please reach out to us before you dive into a new pull request.** The direction of React Flow/ Svelte Flow and which new features are added are discussed in our Discord Server and in [this Github discussions section](https://github.com/wbkd/react-flow/discussions/categories/new-features), and in the end they are decided by the core team. +If you have an idea or suggestion for an enhancement to the React Flow or Svelte Flow library, please use the [New Features](https://github.com/xyflow/xyflow/discussions/categories/new-features) discussion section. If you want to build it yourself, **please reach out to us before you dive into a new pull request.** The direction of React Flow/ Svelte Flow and which new features are added are discussed in our Discord Server and in [this Github discussions section](https://github.com/xyflow/xyflow/discussions/categories/new-features), and in the end they are decided by the core team. Talking to us first about the enhancement you want to build will be the most likely way to get your pull request into the library (see Our Contributing Philosophy above). We would hate to see you write code you’re proud of, just to learn that we’ve already been working on the same thing, or that we feel doesn’t fit into the core library. diff --git a/README.md b/README.md index 8f6ce20b..585dac0e 100644 --- a/README.md +++ b/README.md @@ -165,7 +165,7 @@ React Flow and Svelte Flow are maintained by the team behind [xyflow](https://xy - Christopher • [Twitter](https://twitter.com/chrtze) • [Github](https://github.com/chrtze) - Hayleigh • [Twitter](https://twitter.com/hayleighdotdev) • [Github](https://github.com/hayleigh-dot-dev) -- John • [Website](https://johnrobbdesign.com/) • [Mastodon](https://mastodon.social/@johnrobbjr) +- Abbey • [Github](https://github.com/printerscanner) - Moritz • [Twitter](https://twitter.com/moklick) • [Github](https://github.com/moklick) - Peter • [Github](https://github.com/peterkogo) diff --git a/examples/astro-xyflow/src/env.d.ts b/examples/astro-xyflow/src/env.d.ts index f964fe0c..acef35f1 100644 --- a/examples/astro-xyflow/src/env.d.ts +++ b/examples/astro-xyflow/src/env.d.ts @@ -1 +1,2 @@ +/// /// diff --git a/examples/react/package.json b/examples/react/package.json index ecbcb09f..981f2054 100644 --- a/examples/react/package.json +++ b/examples/react/package.json @@ -7,6 +7,7 @@ "dev": "vite --port 3000 --open --host", "serve": "vite serve --port 3000", "build": "vite build", + "preview": "vite preview", "test:dev": "cypress open", "test": "pnpm test-component && pnpm test-e2e", "test-component": "cypress run --component", @@ -37,7 +38,7 @@ "cypress": "13.6.6", "cypress-real-events": "1.12.0", "start-server-and-test": "^2.0.2", - "typescript": "5.2.2", + "typescript": "5.4.5", "vite": "4.5.0" } } diff --git a/examples/react/src/App/routes.ts b/examples/react/src/App/routes.ts index 28c9e8cc..2256ce52 100644 --- a/examples/react/src/App/routes.ts +++ b/examples/react/src/App/routes.ts @@ -1,5 +1,6 @@ import Basic from '../examples/Basic'; import Backgrounds from '../examples/Backgrounds'; +import BrokenNodes from '../examples/BrokenNodes'; import ColorMode from '../examples/ColorMode'; import ClickDistance from '../examples/ClickDistance'; import ControlledUncontrolled from '../examples/ControlledUncontrolled'; @@ -49,7 +50,7 @@ import NodeToolbar from '../examples/NodeToolbar'; import UseConnection from '../examples/UseConnection'; import UseNodesInitialized from '../examples/UseNodesInit'; import UseNodesData from '../examples/UseNodesData'; -import UseHandleConnections from '../examples/UseHandleConnections'; +import UseNodeConnections from '../examples/UseNodeConnections'; import AddNodeOnEdgeDrop from '../examples/AddNodeOnEdgeDrop'; import DevTools from '../examples/DevTools'; import Redux from '../examples/Redux'; @@ -77,6 +78,11 @@ const routes: IRoute[] = [ path: 'backgrounds', component: Backgrounds, }, + { + name: 'Broken Nodes', + path: 'broken-nodes', + component: BrokenNodes, + }, { name: 'Color Mode', path: 'color-mode', @@ -313,9 +319,9 @@ const routes: IRoute[] = [ component: UseReactFlow, }, { - name: 'useHandleConnections', - path: 'usehandleconnections', - component: UseHandleConnections, + name: 'useNodeConnections', + path: 'usenodeconnections', + component: UseNodeConnections, }, { name: 'useNodesData', diff --git a/examples/react/src/examples/AddNodeOnEdgeDrop/index.tsx b/examples/react/src/examples/AddNodeOnEdgeDrop/index.tsx index cdd55a4b..7c0345d2 100644 --- a/examples/react/src/examples/AddNodeOnEdgeDrop/index.tsx +++ b/examples/react/src/examples/AddNodeOnEdgeDrop/index.tsx @@ -47,7 +47,7 @@ const AddNodeOnEdgeDrop = () => { (event) => { if (!connectingNodeId.current) return; - const targetIsPane = (event.target as HTMLDivElement)?.classList.contains('react-flow__pane'); + const targetIsPane = (event.target as Partial | null)?.classList?.contains('react-flow__pane'); if (targetIsPane && 'clientX' in event && 'clientY' in event) { // we need to remove the wrapper bounds, in order to get the correct position diff --git a/examples/react/src/examples/Basic/index.tsx b/examples/react/src/examples/Basic/index.tsx index 62232a35..168bcff6 100644 --- a/examples/react/src/examples/Basic/index.tsx +++ b/examples/react/src/examples/Basic/index.tsx @@ -11,6 +11,7 @@ import { useReactFlow, Panel, OnNodeDrag, + FitViewOptions, } from '@xyflow/react'; const onNodeDrag: OnNodeDrag = (_, node: Node, nodes: Node[]) => console.log('drag', node, nodes); @@ -54,10 +55,23 @@ const initialEdges: Edge[] = [ ]; const defaultEdgeOptions = {}; +const fitViewOptions: FitViewOptions = { + padding: { top: '100px', left: '0%', right: '10%', bottom: 0.1 }, +}; const BasicFlow = () => { - const { addNodes, setNodes, getNodes, setEdges, getEdges, deleteElements, updateNodeData, toObject, setViewport } = - useReactFlow(); + const { + addNodes, + setNodes, + getNodes, + setEdges, + getEdges, + deleteElements, + updateNodeData, + toObject, + setViewport, + fitView, + } = useReactFlow(); const updatePos = () => { setNodes((nodes) => @@ -104,6 +118,7 @@ const BasicFlow = () => { ]); setEdges([{ id: 'a-b', source: 'a', target: 'b' }]); + fitView(); }; const onUpdateNode = () => { @@ -117,12 +132,14 @@ const BasicFlow = () => { position: { x: Math.random() * 300, y: Math.random() * 300 }, className: 'light', }); + fitView(); }; return ( { minZoom={0.2} maxZoom={4} fitView + fitViewOptions={fitViewOptions} defaultEdgeOptions={defaultEdgeOptions} selectNodesOnDrag={false} elevateEdgesOnSelect diff --git a/examples/react/src/examples/BrokenNodes/index.tsx b/examples/react/src/examples/BrokenNodes/index.tsx new file mode 100644 index 00000000..990612f9 --- /dev/null +++ b/examples/react/src/examples/BrokenNodes/index.tsx @@ -0,0 +1,80 @@ +import { useCallback, useState } from 'react'; +import { ReactFlow, addEdge, Node, Connection, Edge, OnNodeDrag } from '@xyflow/react'; + +const nodesInit: Node[] = [ + { + id: '1a', + type: 'input', + data: { label: 'Node 1' }, + position: { x: 250, y: 5 }, + className: 'light', + ariaLabel: 'Input Node 1', + }, + { + id: '2a', + data: { label: 'Node 2' }, + position: { x: 100, y: 100 }, + className: 'light', + ariaLabel: 'Default Node 2', + }, + { + id: '3a', + data: { label: 'Node 3' }, + position: { x: 400, y: 100 }, + className: 'light', + }, + { + id: '4a', + data: { label: 'Node 4' }, + position: { x: 400, y: 200 }, + className: 'light', + }, +]; + +const edgesInit: Edge[] = [ + { id: 'e1-2', source: '1a', target: '2a', ariaLabel: undefined }, + { id: 'e1-3', source: '1a', target: '3a' }, +]; + +const onNodesChange = () => {}; +const onEdgesChange = () => {}; +const BasicFlow = () => { + const [nodes, setNodes] = useState(nodesInit); + const [edges, setEdges] = useState(edgesInit); + + const onConnect = useCallback((params: Connection | Edge) => setEdges((eds) => addEdge(params, eds)), [setEdges]); + + const onNodeDrag: OnNodeDrag = useCallback((e, node) => { + if (isNaN(node.position.x) || isNaN(node.position.y)) { + console.log('received NaN', node.position); + } + + setNodes((nds) => { + return nds.map((item) => { + if (item.id === node.id) { + return { + ...item, + position: { + x: node.position.x, + y: node.position.y, + }, + }; + } + return item; + }); + }); + }, []); + + return ( + + ); +}; + +export default BasicFlow; diff --git a/examples/react/src/examples/Edges/CustomEdge3.css b/examples/react/src/examples/Edges/CustomEdge3.css new file mode 100644 index 00000000..570b7c75 --- /dev/null +++ b/examples/react/src/examples/Edges/CustomEdge3.css @@ -0,0 +1,14 @@ +@keyframes react-flow-edge-dash { + from { + stroke-dashoffset: 100; + } + to { + stroke-dashoffset: 0; + } +} + +.react-flow__edge-custom3 { + stroke-dasharray: 100; + stroke-dashoffset: 100; + animation: react-flow-edge-dash 1s linear forwards; +} diff --git a/examples/react/src/examples/Edges/CustomEdge3.tsx b/examples/react/src/examples/Edges/CustomEdge3.tsx new file mode 100644 index 00000000..37a3d40f --- /dev/null +++ b/examples/react/src/examples/Edges/CustomEdge3.tsx @@ -0,0 +1,38 @@ +import { FC } from 'react'; +import { BaseEdge, EdgeProps, EdgeText, getSmoothStepPath } from '@xyflow/react'; +import './CustomEdge3.css'; + +const CustomEdge: FC = ({ + id, + sourceX, + sourceY, + targetX, + targetY, + sourcePosition, + targetPosition, + data, +}) => { + const [edgePath, labelX, labelY] = getSmoothStepPath({ + sourceX, + sourceY, + sourcePosition, + targetX, + targetY, + targetPosition, + }); + + return ( + <> + + console.log(data)} + /> + + ); +}; + +export default CustomEdge; diff --git a/examples/react/src/examples/Edges/index.tsx b/examples/react/src/examples/Edges/index.tsx index bbba30fb..6e18d24a 100644 --- a/examples/react/src/examples/Edges/index.tsx +++ b/examples/react/src/examples/Edges/index.tsx @@ -16,6 +16,7 @@ import { import CustomEdge from './CustomEdge'; import CustomEdge2 from './CustomEdge2'; +import CustomEdge3 from './CustomEdge3'; const onNodeDragStop = (_: MouseEvent, node: Node) => console.log('drag stop', node); const onNodeClick = (_: MouseEvent, node: Node) => console.log('click', node); @@ -63,6 +64,12 @@ const initialNodes: Node[] = [ data: { label: 'Output 9' }, position: { x: 675, y: 500 }, }, + { + id: '10', + type: 'output', + data: { label: 'Output 10' }, + position: { x: 50, y: 400 }, + }, ]; const initialEdges: Edge[] = [ @@ -137,6 +144,13 @@ const initialEdges: Edge[] = [ type: 'custom2', data: { text: 'custom edge 2' }, }, + { + id: 'e3a-10', + source: '3a', + target: '10', + type: 'custom3', + data: { text: 'custom edge 3' }, + }, { id: 'e5-6', source: '5', @@ -173,6 +187,7 @@ const initialEdges: Edge[] = [ const edgeTypes: EdgeTypes = { custom: CustomEdge, custom2: CustomEdge2, + custom3: CustomEdge3, }; const defaultEdgeOptions = { diff --git a/examples/react/src/examples/NodeResizer/BottomRightResizer.tsx b/examples/react/src/examples/NodeResizer/BottomRightResizer.tsx new file mode 100644 index 00000000..ca860fdf --- /dev/null +++ b/examples/react/src/examples/NodeResizer/BottomRightResizer.tsx @@ -0,0 +1,21 @@ +import { memo, FC } from 'react'; +import { Handle, Position, NodeProps, NodeResizeControl, ResizeControlVariant } from '@xyflow/react'; + +const CustomResizerNode: FC = ({ data }) => { + return ( + <> + + +
{data.label}
+ + + ); +}; + +export default memo(CustomResizerNode); diff --git a/examples/react/src/examples/NodeResizer/index.tsx b/examples/react/src/examples/NodeResizer/index.tsx index 397c1f22..ce6faf3b 100644 --- a/examples/react/src/examples/NodeResizer/index.tsx +++ b/examples/react/src/examples/NodeResizer/index.tsx @@ -15,12 +15,14 @@ import DefaultResizer from './DefaultResizer'; import CustomResizer from './CustomResizer'; import VerticalResizer from './VerticalResizer'; import HorizontalResizer from './HorizontalResizer'; +import BottomRightResizer from './BottomRightResizer'; const nodeTypes = { defaultResizer: DefaultResizer, customResizer: CustomResizer, verticalResizer: VerticalResizer, horizontalResizer: HorizontalResizer, + bottomRightResizer: BottomRightResizer, }; const nodeStyle = { @@ -166,6 +168,13 @@ const initialNodes: Node[] = [ expandParent: true, style: { ...nodeStyle }, }, + { + id: '6', + type: 'bottomRightResizer', + data: { label: 'Bottom Right with horizontal direction' }, + position: { x: 500, y: 500 }, + style: { ...nodeStyle }, + }, ]; const CustomNodeFlow = () => { diff --git a/examples/react/src/examples/Stress/index.tsx b/examples/react/src/examples/Stress/index.tsx index 9020ddd6..496fd500 100644 --- a/examples/react/src/examples/Stress/index.tsx +++ b/examples/react/src/examples/Stress/index.tsx @@ -12,6 +12,8 @@ import { Controls, Background, Panel, + ReactFlowProvider, + useReactFlow, } from '@xyflow/react'; import { getNodesAndEdges } from './utils'; @@ -22,6 +24,7 @@ const { nodes: initialNodes, edges: initialEdges } = getNodesAndEdges(25, 25); const StressFlow = () => { const [nodes, setNodes] = useState(initialNodes); const [edges, setEdges] = useState(initialEdges); + const { fitView } = useReactFlow(); const onConnect = useCallback((connection: Connection) => { setEdges((eds) => addEdge(connection, eds)); }, []); @@ -191,12 +194,13 @@ const StressFlow = () => { return { ...n, position: { - x: Math.random() * window.innerWidth, - y: Math.random() * window.innerHeight, + x: Math.random() * window.innerWidth * 4, + y: Math.random() * window.innerHeight * 4, }, }; }); }); + fitView(); }; const updateElements = () => { @@ -240,4 +244,10 @@ const StressFlow = () => { ); }; -export default StressFlow; +export default function StressFlowProvider() { + return ( + + + + ); +} diff --git a/examples/react/src/examples/UseHandleConnections/MultiHandleNode.tsx b/examples/react/src/examples/UseNodeConnections/MultiHandleNode.tsx similarity index 71% rename from examples/react/src/examples/UseHandleConnections/MultiHandleNode.tsx rename to examples/react/src/examples/UseNodeConnections/MultiHandleNode.tsx index 4b8fd2c1..3191221c 100644 --- a/examples/react/src/examples/UseHandleConnections/MultiHandleNode.tsx +++ b/examples/react/src/examples/UseNodeConnections/MultiHandleNode.tsx @@ -1,5 +1,5 @@ import { memo, FC, useEffect, useCallback } from 'react'; -import { Handle, Position, NodeProps, useHandleConnections, Connection, HandleProps } from '@xyflow/react'; +import { Handle, Position, NodeProps, useNodeConnections, Connection, HandleProps } from '@xyflow/react'; function CustomHandle({ nodeId, ...handleProps }: HandleProps & { nodeId: string }) { const onConnect = useCallback( @@ -11,9 +11,9 @@ function CustomHandle({ nodeId, ...handleProps }: HandleProps & { nodeId: string (connections: Connection[]) => console.log('onDisconnect handler, node id:', nodeId, connections), [nodeId] ); - const connections = useHandleConnections({ - type: handleProps.type, - id: handleProps.id, + const connections = useNodeConnections({ + handleType: handleProps.type, + handleId: handleProps.id, onConnect, onDisconnect, }); @@ -28,10 +28,11 @@ function CustomHandle({ nodeId, ...handleProps }: HandleProps & { nodeId: string const CustomNode: FC = ({ id }) => { return (
- + +
node {id}
- - + +
); }; diff --git a/examples/react/src/examples/UseHandleConnections/SingleHandleNode.tsx b/examples/react/src/examples/UseNodeConnections/SingleHandleNode.tsx similarity index 83% rename from examples/react/src/examples/UseHandleConnections/SingleHandleNode.tsx rename to examples/react/src/examples/UseNodeConnections/SingleHandleNode.tsx index 1c13dcb0..9361f833 100644 --- a/examples/react/src/examples/UseHandleConnections/SingleHandleNode.tsx +++ b/examples/react/src/examples/UseNodeConnections/SingleHandleNode.tsx @@ -1,5 +1,5 @@ import { memo, FC, useEffect, useCallback } from 'react'; -import { Handle, Position, NodeProps, useHandleConnections, Connection, HandleProps } from '@xyflow/react'; +import { Handle, Position, NodeProps, useNodeConnections, Connection, HandleProps } from '@xyflow/react'; function CustomHandle({ nodeId, ...handleProps }: HandleProps & { nodeId: string }) { const onConnect = useCallback( @@ -14,9 +14,9 @@ function CustomHandle({ nodeId, ...handleProps }: HandleProps & { nodeId: string }, [nodeId] ); - const connections = useHandleConnections({ - type: handleProps.type, - id: handleProps.id, + const connections = useNodeConnections({ + handleType: handleProps.type, + handleId: handleProps.id, onConnect, onDisconnect, }); diff --git a/examples/react/src/examples/UseHandleConnections/index.tsx b/examples/react/src/examples/UseNodeConnections/index.tsx similarity index 96% rename from examples/react/src/examples/UseHandleConnections/index.tsx rename to examples/react/src/examples/UseNodeConnections/index.tsx index c6aa4a68..6b41018e 100644 --- a/examples/react/src/examples/UseHandleConnections/index.tsx +++ b/examples/react/src/examples/UseNodeConnections/index.tsx @@ -69,17 +69,16 @@ const initEdges = [ source: '1', target: '3', }, - { id: 'e4a-5', source: '4', - sourceHandle: 'a', + sourceHandle: 's1', target: '5', }, { id: 'e4b-5', source: '4', - sourceHandle: 'b', + sourceHandle: 's2', target: '6', }, ]; @@ -92,6 +91,8 @@ const CustomNodeFlow = () => { const [nodes, setNodes, onNodesChange] = useNodesState(initNodes); const [edges, setEdges, onEdgesChange] = useEdgesState(initEdges); + console.log(edges); + const onConnect = useCallback((connection: Connection) => setEdges((eds) => addEdge(connection, eds)), [setEdges]); return ( diff --git a/examples/react/src/examples/UseNodesData/ResultNode.tsx b/examples/react/src/examples/UseNodesData/ResultNode.tsx index bf8f7a35..0caa228d 100644 --- a/examples/react/src/examples/UseNodesData/ResultNode.tsx +++ b/examples/react/src/examples/UseNodesData/ResultNode.tsx @@ -1,10 +1,10 @@ import { memo } from 'react'; -import { Handle, Position, useHandleConnections, useNodesData } from '@xyflow/react'; +import { Handle, Position, useNodeConnections, useNodesData } from '@xyflow/react'; import { isTextNode, type MyNode } from '.'; function ResultNode() { - const connections = useHandleConnections({ - type: 'target', + const connections = useNodeConnections({ + handleType: 'target', }); const nodesData = useNodesData(connections.map((connection) => connection.source)); const textNodes = nodesData.filter(isTextNode); diff --git a/examples/react/src/examples/UseNodesData/UppercaseNode.tsx b/examples/react/src/examples/UseNodesData/UppercaseNode.tsx index 70c6079d..792b7c55 100644 --- a/examples/react/src/examples/UseNodesData/UppercaseNode.tsx +++ b/examples/react/src/examples/UseNodesData/UppercaseNode.tsx @@ -1,11 +1,11 @@ import { memo, useEffect } from 'react'; -import { Position, NodeProps, useReactFlow, Handle, useHandleConnections, useNodesData } from '@xyflow/react'; +import { Position, NodeProps, useReactFlow, Handle, useNodeConnections, useNodesData } from '@xyflow/react'; import { isTextNode, type TextNode, type MyNode } from '.'; function UppercaseNode({ id }: NodeProps) { const { updateNodeData } = useReactFlow(); - const connections = useHandleConnections({ - type: 'target', + const connections = useNodeConnections({ + handleType: 'target', }); const nodesData = useNodesData(connections[0]?.source); const textNode = isTextNode(nodesData) ? nodesData : null; diff --git a/examples/react/src/examples/UseNodesData/index.tsx b/examples/react/src/examples/UseNodesData/index.tsx index dabc1d4d..c00e26a1 100644 --- a/examples/react/src/examples/UseNodesData/index.tsx +++ b/examples/react/src/examples/UseNodesData/index.tsx @@ -21,7 +21,7 @@ export type UppercaseNode = Node<{ text: string }, 'uppercase'>; export type MyNode = TextNode | ResultNode | UppercaseNode; export function isTextNode(node: any): node is TextNode | UppercaseNode { - return node.type === 'text' || node.type === 'uppercase'; + return !node || !node.type ? false : node.type === 'text' || node.type === 'uppercase'; } const nodeTypes = { diff --git a/examples/react/src/examples/UseOnSelectionChange/index.tsx b/examples/react/src/examples/UseOnSelectionChange/index.tsx index b9795951..460ee9c3 100644 --- a/examples/react/src/examples/UseOnSelectionChange/index.tsx +++ b/examples/react/src/examples/UseOnSelectionChange/index.tsx @@ -10,7 +10,7 @@ import { useEdgesState, useOnSelectionChange, OnSelectionChangeParams, - OnSelectionChangeFunc, + Panel, } from '@xyflow/react'; const initialNodes: Node[] = [ @@ -52,6 +52,12 @@ const Flow = () => { const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes); const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges); const onConnect = useCallback((params: Edge | Connection) => setEdges((els) => addEdge(params, els)), [setEdges]); + const [elementsSelectable, setElementsSelectable] = useState(true); + const [secondLoggerActive, setSecondLoggerActive] = useState(true); + + const toggleSecondLogger = () => { + setSecondLoggerActive(!secondLoggerActive); + }; return ( { onNodesChange={onNodesChange} onEdgesChange={onEdgesChange} onConnect={onConnect} - /> - ); -}; + elementsSelectable={elementsSelectable} + > + + + + -const WrappedFlow = () => { - const [secondLoggerActive, setSecondLoggerActive] = useState(true); - - const toggleSecondLogger = () => { - setSecondLoggerActive(!secondLoggerActive); - }; - - return ( - - {secondLoggerActive && } -
- -
-
+
); }; -export default WrappedFlow; +export default Flow; diff --git a/examples/react/src/examples/UseReactFlow/index.tsx b/examples/react/src/examples/UseReactFlow/index.tsx index cf6ae59f..3e1d8f92 100644 --- a/examples/react/src/examples/UseReactFlow/index.tsx +++ b/examples/react/src/examples/UseReactFlow/index.tsx @@ -170,7 +170,6 @@ const UseZoomPanHelperFlow = () => { onConnect={onConnect} onPaneClick={onPaneClick} fitView - fitViewOptions={{ duration: 1200, padding: 0.2 }} maxZoom={Infinity} > @@ -183,7 +182,16 @@ const UseZoomPanHelperFlow = () => { console.log('fit view success'); }} > - fitView + fitView default + + diff --git a/examples/react/src/generic-tests/edges/general.ts b/examples/react/src/generic-tests/edges/general.ts index 3daceec8..d00c57cc 100644 --- a/examples/react/src/generic-tests/edges/general.ts +++ b/examples/react/src/generic-tests/edges/general.ts @@ -4,6 +4,7 @@ export default { flowProps: { fitView: true, multiSelectionKeyCode: 's', + deleteKeyCode: 'd', nodes: [ { id: '1', diff --git a/examples/react/src/generic-tests/nodes/general.ts b/examples/react/src/generic-tests/nodes/general.ts index d45091dd..c8fb744a 100644 --- a/examples/react/src/generic-tests/nodes/general.ts +++ b/examples/react/src/generic-tests/nodes/general.ts @@ -3,6 +3,8 @@ import DragHandleNode from './components/DragHandleNode'; export default { flowProps: { fitView: true, + deleteKeyCode: 'd', + multiSelectionKeyCode: 's', nodeTypes: { DragHandleNode, }, diff --git a/examples/svelte/package.json b/examples/svelte/package.json index e8c221ae..feeb3b7e 100644 --- a/examples/svelte/package.json +++ b/examples/svelte/package.json @@ -12,25 +12,25 @@ "format": "prettier --plugin-search-dir . --write ." }, "devDependencies": { - "@sveltejs/adapter-auto": "^3.2.1", - "@sveltejs/kit": "^2.5.10", - "@typescript-eslint/eslint-plugin": "^6.10.0", - "@typescript-eslint/parser": "^6.10.0", - "eslint": "^8.53.0", - "eslint-config-prettier": "^9.0.0", - "eslint-plugin-svelte": "^2.35.0", - "prettier": "^3.0.3", - "prettier-plugin-svelte": "^3.0.3", - "svelte": "^4.2.12", - "svelte-check": "^3.6.6", - "tslib": "^2.6.2", - "typescript": "^5.2.2", - "vite": "^5.2.12" + "@sveltejs/adapter-auto": "^6.0.0", + "@sveltejs/kit": "^2.20.7", + "@typescript-eslint/eslint-plugin": "^8.30.1", + "@typescript-eslint/parser": "^8.30.1", + "eslint": "^9.24.0", + "eslint-config-prettier": "^10.1.2", + "eslint-plugin-svelte": "^3.5.1", + "prettier": "^3.5.3", + "prettier-plugin-svelte": "^3.3.3", + "svelte": "^5.27.0", + "svelte-check": "^4.1.6", + "tslib": "^2.8.1", + "typescript": "^5.8.3", + "vite": "^6.3.0" }, "type": "module", "dependencies": { - "@dagrejs/dagre": "^1.0.4", - "@sveltejs/vite-plugin-svelte": "^3.1.1", + "@dagrejs/dagre": "^1.1.4", + "@sveltejs/vite-plugin-svelte": "^5.0.3", "@xyflow/svelte": "workspace:^" } } diff --git a/examples/svelte/src/components/Header/Header.svelte b/examples/svelte/src/components/Header/Header.svelte index 007d8ded..1c251c67 100644 --- a/examples/svelte/src/components/Header/Header.svelte +++ b/examples/svelte/src/components/Header/Header.svelte @@ -35,7 +35,7 @@
- {#each routes as route} {/each} diff --git a/examples/svelte/src/generic-tests/edges/general.ts b/examples/svelte/src/generic-tests/edges/general.ts index 5e94d2fb..e17da374 100644 --- a/examples/svelte/src/generic-tests/edges/general.ts +++ b/examples/svelte/src/generic-tests/edges/general.ts @@ -3,6 +3,8 @@ import { MarkerType } from '@xyflow/svelte'; export default { flowProps: { fitView: true, + multiSelectionKey: ['Meta', 's'], + deleteKey: 'd', nodes: [ { id: '1', diff --git a/examples/svelte/src/generic-tests/node-toolbar/components/ToolbarNode.svelte b/examples/svelte/src/generic-tests/node-toolbar/components/ToolbarNode.svelte index 0a998217..d6fb5b0d 100644 --- a/examples/svelte/src/generic-tests/node-toolbar/components/ToolbarNode.svelte +++ b/examples/svelte/src/generic-tests/node-toolbar/components/ToolbarNode.svelte @@ -1,9 +1,18 @@ import type { NodeProps } from '@xyflow/svelte'; - type $$Props = NodeProps; + let {}: NodeProps = $props();
-
+
+ + + diff --git a/examples/svelte/src/routes/examples/overview/CustomEdge.svelte b/examples/svelte/src/routes/examples/overview/CustomEdge.svelte index 5f22a826..96be0b03 100644 --- a/examples/svelte/src/routes/examples/overview/CustomEdge.svelte +++ b/examples/svelte/src/routes/examples/overview/CustomEdge.svelte @@ -1,46 +1,35 @@ - + - - - + + + diff --git a/examples/svelte/src/routes/examples/overview/InitTracker.svelte b/examples/svelte/src/routes/examples/overview/InitTracker.svelte index 8b486f8c..a040625e 100644 --- a/examples/svelte/src/routes/examples/overview/InitTracker.svelte +++ b/examples/svelte/src/routes/examples/overview/InitTracker.svelte @@ -1,18 +1,18 @@ diff --git a/examples/svelte/src/routes/examples/reset/+page.svelte b/examples/svelte/src/routes/examples/reset/+page.svelte index e7857d70..4fff7a6a 100644 --- a/examples/svelte/src/routes/examples/reset/+page.svelte +++ b/examples/svelte/src/routes/examples/reset/+page.svelte @@ -1,16 +1,17 @@ - +
- + diff --git a/examples/svelte/src/routes/examples/stress/+page.svelte b/examples/svelte/src/routes/examples/stress/+page.svelte index 0d67394b..e8e434dc 100644 --- a/examples/svelte/src/routes/examples/stress/+page.svelte +++ b/examples/svelte/src/routes/examples/stress/+page.svelte @@ -1,5 +1,4 @@ - + diff --git a/examples/svelte/src/routes/examples/subflows/+page.svelte b/examples/svelte/src/routes/examples/subflows/+page.svelte index a4574a6e..fee35ba5 100644 --- a/examples/svelte/src/routes/examples/subflows/+page.svelte +++ b/examples/svelte/src/routes/examples/subflows/+page.svelte @@ -1,5 +1,4 @@ - + diff --git a/examples/svelte/src/routes/examples/subflows/DebugNode.svelte b/examples/svelte/src/routes/examples/subflows/DebugNode.svelte index ecacb06b..c207659c 100644 --- a/examples/svelte/src/routes/examples/subflows/DebugNode.svelte +++ b/examples/svelte/src/routes/examples/subflows/DebugNode.svelte @@ -3,12 +3,23 @@ type $$Props = NodeProps; - export let id: string; - export let positionAbsoluteX: number = 0; - export let positionAbsoluteY: number = 0; - export let zIndex: number = 0; + interface Props { + id: string; + positionAbsoluteX?: number; + positionAbsoluteY?: number; + zIndex?: number; + [key: string]: any + } - $$restProps; + let { + id, + positionAbsoluteX = 0, + positionAbsoluteY = 0, + zIndex = 0, + ...rest + }: Props = $props(); + + rest; diff --git a/examples/svelte/src/routes/examples/two-way-viewport/Flow.svelte b/examples/svelte/src/routes/examples/two-way-viewport/Flow.svelte index 73d3a073..da369442 100644 --- a/examples/svelte/src/routes/examples/two-way-viewport/Flow.svelte +++ b/examples/svelte/src/routes/examples/two-way-viewport/Flow.svelte @@ -1,5 +1,4 @@ - + - - + + diff --git a/examples/svelte/src/routes/examples/usenodesdata/+page.svelte b/examples/svelte/src/routes/examples/usenodesdata/+page.svelte index 91fd8432..ed258370 100644 --- a/examples/svelte/src/routes/examples/usenodesdata/+page.svelte +++ b/examples/svelte/src/routes/examples/usenodesdata/+page.svelte @@ -1,19 +1,18 @@ - - + diff --git a/examples/svelte/src/routes/examples/usenodesdata/ResultNode.svelte b/examples/svelte/src/routes/examples/usenodesdata/ResultNode.svelte index 436d2109..a9f14360 100644 --- a/examples/svelte/src/routes/examples/usenodesdata/ResultNode.svelte +++ b/examples/svelte/src/routes/examples/usenodesdata/ResultNode.svelte @@ -2,24 +2,24 @@ import { Handle, Position, - useHandleConnections, + useNodeConnections, useNodesData, - type NodeProps + type NodeProps, + type Node } from '@xyflow/svelte'; import { isTextNode, type MyNode } from './+page.svelte'; - type $$Props = NodeProps; + let { id }: NodeProps = $props(); - export let id: $$Props['id']; - $$restProps; - - const connections = useHandleConnections({ - nodeId: id, - type: 'target' + const connections = useNodeConnections({ + id: id, + handleType: 'target' }); - $: nodeData = useNodesData($connections.map((connection) => connection.source)); - $: textNodes = $nodeData.filter(isTextNode); + let nodeData = $derived( + useNodesData(connections.current.map((connection) => connection.source)) + ); + let textNodes = $derived(nodeData.current.filter(isTextNode));
diff --git a/examples/svelte/src/routes/examples/usenodesdata/TextNode.svelte b/examples/svelte/src/routes/examples/usenodesdata/TextNode.svelte index 1fe5ff70..10ea8e70 100644 --- a/examples/svelte/src/routes/examples/usenodesdata/TextNode.svelte +++ b/examples/svelte/src/routes/examples/usenodesdata/TextNode.svelte @@ -1,13 +1,9 @@
@@ -15,7 +11,7 @@
updateNodeData(id, { text: evt.currentTarget.value })} + oninput={(evt) => updateNodeData(id, { text: evt.currentTarget.value })} />
diff --git a/examples/svelte/src/routes/examples/usenodesdata/UppercaseNode.svelte b/examples/svelte/src/routes/examples/usenodesdata/UppercaseNode.svelte index 55960cb5..33990b6d 100644 --- a/examples/svelte/src/routes/examples/usenodesdata/UppercaseNode.svelte +++ b/examples/svelte/src/routes/examples/usenodesdata/UppercaseNode.svelte @@ -2,39 +2,33 @@ import { Handle, Position, - useHandleConnections, + useNodeConnections, useNodesData, useSvelteFlow, + type Node, type NodeProps } from '@xyflow/svelte'; import { isTextNode, type MyNode } from './+page.svelte'; - type $$Props = NodeProps; - - export let id: $$Props['id']; - export let data: $$Props['data']; - $$restProps; + let { id }: NodeProps> = $props(); const { updateNodeData } = useSvelteFlow(); - const connections = useHandleConnections({ - nodeId: id, - type: 'target' + const connections = useNodeConnections({ + id: id, + handleType: 'target' }); - $: nodeData = useNodesData($connections[0]?.source); - $: textNode = isTextNode($nodeData) ? $nodeData : null; + let nodeData = $derived(useNodesData(connections.current[0]?.source)); + let textNodeData = $derived(isTextNode(nodeData.current) ? nodeData.current.data.text : null); - $: console.log(textNode?.data, data); - - $: { - const input = textNode?.data.text.toUpperCase() ?? ''; + $effect.pre(() => { + const input = textNodeData?.toUpperCase() ?? ''; updateNodeData(id, { text: input }); - console.log('updatedNodeData with', input); - } + });
- +
uppercase transform
diff --git a/examples/svelte/src/routes/examples/usesvelteflow/Flow.svelte b/examples/svelte/src/routes/examples/usesvelteflow/Flow.svelte index 2ba652a4..cab0bdde 100644 --- a/examples/svelte/src/routes/examples/usesvelteflow/Flow.svelte +++ b/examples/svelte/src/routes/examples/usesvelteflow/Flow.svelte @@ -1,12 +1,11 @@
- + diff --git a/examples/svelte/src/routes/examples/usesvelteflow/Sidebar.svelte b/examples/svelte/src/routes/examples/usesvelteflow/Sidebar.svelte index 0a27afd9..3b687eb3 100644 --- a/examples/svelte/src/routes/examples/usesvelteflow/Sidebar.svelte +++ b/examples/svelte/src/routes/examples/usesvelteflow/Sidebar.svelte @@ -1,5 +1,5 @@ diff --git a/examples/svelte/src/routes/examples/useupdatenodeinternals/CustomNode.svelte b/examples/svelte/src/routes/examples/useupdatenodeinternals/CustomNode.svelte index 89a73e1c..1b302a8b 100644 --- a/examples/svelte/src/routes/examples/useupdatenodeinternals/CustomNode.svelte +++ b/examples/svelte/src/routes/examples/useupdatenodeinternals/CustomNode.svelte @@ -1,23 +1,20 @@ - + {#each Array.from({ length: handleCount }) as handle, i} diff --git a/examples/svelte/src/routes/examples/useupdatenodeinternals/Flow.svelte b/examples/svelte/src/routes/examples/useupdatenodeinternals/Flow.svelte index 42c76cd0..a24c2467 100644 --- a/examples/svelte/src/routes/examples/useupdatenodeinternals/Flow.svelte +++ b/examples/svelte/src/routes/examples/useupdatenodeinternals/Flow.svelte @@ -1,5 +1,4 @@
- + - +
diff --git a/examples/svelte/src/routes/examples/validation/+page.svelte b/examples/svelte/src/routes/examples/validation/+page.svelte index d2a7cd2e..123a7e90 100644 --- a/examples/svelte/src/routes/examples/validation/+page.svelte +++ b/examples/svelte/src/routes/examples/validation/+page.svelte @@ -1,5 +1,4 @@ diff --git a/examples/svelte/src/routes/tests/generic/[topic]/[example]/Flow.svelte b/examples/svelte/src/routes/tests/generic/[topic]/[example]/Flow.svelte index bb04fcb8..389804d6 100644 --- a/examples/svelte/src/routes/tests/generic/[topic]/[example]/Flow.svelte +++ b/examples/svelte/src/routes/tests/generic/[topic]/[example]/Flow.svelte @@ -1,19 +1,25 @@ - + {#if flowConfig.panelProps} {/if} diff --git a/examples/svelte/svelte.config.js b/examples/svelte/svelte.config.js index 56ed51a8..94011562 100644 --- a/examples/svelte/svelte.config.js +++ b/examples/svelte/svelte.config.js @@ -5,8 +5,10 @@ import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; const config = { // Consult https://kit.svelte.dev/docs/integrations#preprocessors // for more information about preprocessors - preprocess: vitePreprocess(), - + preprocess: vitePreprocess({ script: true }), + compilerOptions: { + runes: true + }, kit: { // adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list. // If your environment is not supported or you settled on a specific environment, switch out the adapter. diff --git a/examples/svelte/tsconfig.json b/examples/svelte/tsconfig.json index 6ae0c8c4..2bb201c3 100644 --- a/examples/svelte/tsconfig.json +++ b/examples/svelte/tsconfig.json @@ -8,7 +8,10 @@ "resolveJsonModule": true, "skipLibCheck": true, "sourceMap": true, - "strict": true + "strict": true, + "target": "ES2022", + "verbatimModuleSyntax": true, + "isolatedModules": true } // Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias // diff --git a/examples/svelte/vite.config.ts b/examples/svelte/vite.config.ts index 17c69201..6b35eada 100644 --- a/examples/svelte/vite.config.ts +++ b/examples/svelte/vite.config.ts @@ -3,6 +3,10 @@ import { defineConfig } from 'vite'; export default defineConfig({ plugins: [sveltekit()], + build: { + sourcemap: true, + minify: false + }, server: { fs: { // Allow serving files from one level up to the project root diff --git a/package.json b/package.json index 7a33c404..0e52b690 100644 --- a/package.json +++ b/package.json @@ -25,20 +25,15 @@ "@changesets/changelog-github": "^0.4.7", "@changesets/cli": "^2.25.0", "@playwright/test": "^1.44.1", - "@typescript-eslint/eslint-plugin": "latest", - "@typescript-eslint/parser": "latest", "concurrently": "^7.6.0", "eslint": "^8.22.0", - "eslint-config-prettier": "^8.5.0", - "eslint-plugin-prettier": "^4.2.1", - "eslint-plugin-react": "latest", "prettier": "^2.7.1", "react": "^18.2.0", "react-dom": "^18.2.0", "rimraf": "^3.0.2", "rollup": "^4.18.0", "turbo": "^2.0.3", - "typescript": "5.1.3" + "typescript": "5.4.5" }, "packageManager": "pnpm@9.2.0" } diff --git a/packages/react/CHANGELOG.md b/packages/react/CHANGELOG.md index b8a68ca6..1fe6fa4b 100644 --- a/packages/react/CHANGELOG.md +++ b/packages/react/CHANGELOG.md @@ -1,5 +1,344 @@ # @xyflow/react +## 12.6.4 + +### Patch Changes + +- [#5263](https://github.com/xyflow/xyflow/pull/5263) [`e4a8d4b4`](https://github.com/xyflow/xyflow/commit/e4a8d4b43fdd3a9f493db758bdf02e1a10c6e640) Thanks [@moklick](https://github.com/moklick)! - Multi select key works when input is focused + +- [#5266](https://github.com/xyflow/xyflow/pull/5266) [`77107453`](https://github.com/xyflow/xyflow/commit/77107453fa6f34cb08ef91640b8b02d58e31275e) Thanks [@peterkogo](https://github.com/peterkogo)! - Fix connection snapping for handles larger than connectionRadius + +- Updated dependencies [[`77107453`](https://github.com/xyflow/xyflow/commit/77107453fa6f34cb08ef91640b8b02d58e31275e)]: + - @xyflow/system@0.0.61 + +## 12.6.3 + +### Patch Changes + +- [#5259](https://github.com/xyflow/xyflow/pull/5259) [`77bf79c4`](https://github.com/xyflow/xyflow/commit/77bf79c40e71e3da449ace3b1a1ed5bceff46b51) Thanks [@peterkogo](https://github.com/peterkogo)! - Fix background-color css variable fallback. + +- Updated dependencies [[`77bf79c4`](https://github.com/xyflow/xyflow/commit/77bf79c40e71e3da449ace3b1a1ed5bceff46b51)]: + - @xyflow/system@0.0.60 + +## 12.6.2 + +### Patch Changes + +- [#5257](https://github.com/xyflow/xyflow/pull/5257) [`b1314be0`](https://github.com/xyflow/xyflow/commit/b1314be04dee07e7589a8c99c9c953006788a152) Thanks [@moklick](https://github.com/moklick)! - Use OnReconnect from system + +- Updated dependencies [[`a95f0e2f`](https://github.com/xyflow/xyflow/commit/a95f0e2fbfc2d070d9bd70b753d1606a87332e3f)]: + - @xyflow/system@0.0.59 + +## 12.6.1 + +### Patch Changes + +- [#5249](https://github.com/xyflow/xyflow/pull/5249) [`895b5d81`](https://github.com/xyflow/xyflow/commit/895b5d81c8ee5236009820ecd0ed6806c6e59e29) Thanks [@moklick](https://github.com/moklick)! - Call `onNodesChange` for uncontrolled flows that use `updateNode` + +- [#5247](https://github.com/xyflow/xyflow/pull/5247) [`67e1cb68`](https://github.com/xyflow/xyflow/commit/67e1cb6891078dbcb9e1d06b9f9fdbfc79860ab6) Thanks [@moklick](https://github.com/moklick)! - Cleanup TSDoc annotations for ReactFlow + +- Updated dependencies [[`2a03213b`](https://github.com/xyflow/xyflow/commit/2a03213b0695d504f831579ec9df3f9de2d3e0bd)]: + - @xyflow/system@0.0.58 + +## 12.6.0 + +### Minor Changes + +- [#5219](https://github.com/xyflow/xyflow/pull/5219) [`4236adbc`](https://github.com/xyflow/xyflow/commit/4236adbc462b3f65ee41869ef426491ed3fa8ba0) Thanks [@moklick](https://github.com/moklick)! - Add initialMinZoom, initialMaxZoom and initialFitViewOptions to ReactFlowProvider + +- [#5227](https://github.com/xyflow/xyflow/pull/5227) [`a7d10ffc`](https://github.com/xyflow/xyflow/commit/a7d10ffce5a0195471681980f97b1b5f6c448f35) Thanks [@moklick](https://github.com/moklick)! - Add `resizeDirection` prop for the `NodeResizeControl` component + +### Patch Changes + +- [#5217](https://github.com/xyflow/xyflow/pull/5217) [`bce74e88`](https://github.com/xyflow/xyflow/commit/bce74e8811a98c967b5bd06c3e5aecde24c8b679) Thanks [@moklick](https://github.com/moklick)! - Keep node seleciton on pane click if elementsSelectable=false + +- Updated dependencies [[`a7d10ffc`](https://github.com/xyflow/xyflow/commit/a7d10ffce5a0195471681980f97b1b5f6c448f35), [`4e681f9c`](https://github.com/xyflow/xyflow/commit/4e681f9c529c3f4f8b2ac5d25b4db7878c197e14)]: + - @xyflow/system@0.0.57 + +## 12.5.6 + +### Patch Changes + +- [#5212](https://github.com/xyflow/xyflow/pull/5212) [`0f43b8ea`](https://github.com/xyflow/xyflow/commit/0f43b8ea45bd293e50e4a86d83868074bb323f13) Thanks [@moklick](https://github.com/moklick)! - Add polyfill for `Promise.withResolvers` + +- [#5192](https://github.com/xyflow/xyflow/pull/5192) [`fc241253`](https://github.com/xyflow/xyflow/commit/fc241253d5dba35f5febf411e77dbc5acb91d5d7) Thanks [@peterkogo](https://github.com/peterkogo)! - Optimize performance of nodesInitialized + +- [#5196](https://github.com/xyflow/xyflow/pull/5196) [`7f902db4`](https://github.com/xyflow/xyflow/commit/7f902db46b6f1ea4adc94390db8d5db47f8c5903) Thanks [@moklick](https://github.com/moklick)! - Hide edge marker and attribution for screenreaders + +- [#5213](https://github.com/xyflow/xyflow/pull/5213) [`78782c16`](https://github.com/xyflow/xyflow/commit/78782c1696323ee9be0d38bbd807bd3fde5f549d) Thanks [@moklick](https://github.com/moklick)! - Do not trigger selection events if elementsSelectable=false + +- [#5191](https://github.com/xyflow/xyflow/pull/5191) [`e5735b51`](https://github.com/xyflow/xyflow/commit/e5735b514a54d86ba0ca7bd725e8bfead89fc08e) Thanks [@peterkogo](https://github.com/peterkogo)! - Fix legacy padding being slightly larger than before + +- Updated dependencies [[`0f43b8ea`](https://github.com/xyflow/xyflow/commit/0f43b8ea45bd293e50e4a86d83868074bb323f13), [`fc241253`](https://github.com/xyflow/xyflow/commit/fc241253d5dba35f5febf411e77dbc5acb91d5d7), [`98fe23c7`](https://github.com/xyflow/xyflow/commit/98fe23c7c2b12972f1b7def866215ce82a86e2c0), [`e5735b51`](https://github.com/xyflow/xyflow/commit/e5735b514a54d86ba0ca7bd725e8bfead89fc08e)]: + - @xyflow/system@0.0.56 + +## 12.5.5 + +### Patch Changes + +- [#5172](https://github.com/xyflow/xyflow/pull/5172) [`e6139a00`](https://github.com/xyflow/xyflow/commit/e6139a00d4414ba2c1d3e500cdfa67d7e66e655a) Thanks [@dimaMachina](https://github.com/dimaMachina)! - Improve TSDoc comments for `useNodesData`, `useReactFlow`, `isNode` and `isEdge` + +- [#5165](https://github.com/xyflow/xyflow/pull/5165) [`d536abea`](https://github.com/xyflow/xyflow/commit/d536abea9240bad7f5c1064efc0a4713ebef87d1) Thanks [@dimaMachina](https://github.com/dimaMachina)! - Improve TSDoc comments for `useViewport`, `useUpdateNodeInternals`, `useOnSelectionChange`, `useNodesInitialized` hooks and `UseOnSelectionChangeOptions`, `UseNodesInitializedOptions` types + +- [#5171](https://github.com/xyflow/xyflow/pull/5171) [`62d87409`](https://github.com/xyflow/xyflow/commit/62d874097337a022bebe54b559834c0d582f435e) Thanks [@dimaMachina](https://github.com/dimaMachina)! - Improve TSDoc comments for `ReactFlowProps` + +- [#5154](https://github.com/xyflow/xyflow/pull/5154) [`d0237166`](https://github.com/xyflow/xyflow/commit/d02371662669aab91cd2ac7c45b412491c3377bd) Thanks [@ibagov](https://github.com/ibagov)! - Improve TSDoc comments for `onNodesChange` + +- [#5174](https://github.com/xyflow/xyflow/pull/5174) [`ae585d13`](https://github.com/xyflow/xyflow/commit/ae585d136e34c9e9ad9d45f75c059bc5367e73ae) Thanks [@dimaMachina](https://github.com/dimaMachina)! - Improve TSDoc comments for `BaseEdgeProps` + +- [#5159](https://github.com/xyflow/xyflow/pull/5159) [`0c1436d6`](https://github.com/xyflow/xyflow/commit/0c1436d6a371240cfa0adecea573c44fd42df7b3) Thanks [@dimaMachina](https://github.com/dimaMachina)! - Improve TSDoc comments for `useConnection` hook + +- [#5167](https://github.com/xyflow/xyflow/pull/5167) [`934ea42d`](https://github.com/xyflow/xyflow/commit/934ea42d9af6027a3164e1196a78140bdd05d347) Thanks [@dimaMachina](https://github.com/dimaMachina)! - Improve TSDoc comments for `useEdges`, `useInternalNode`, `useNodes` and `useNodeId` hooks + +- [#5163](https://github.com/xyflow/xyflow/pull/5163) [`ab800054`](https://github.com/xyflow/xyflow/commit/ab800054a50c68f9c27dfad2b0d3833b782f4797) Thanks [@dimaMachina](https://github.com/dimaMachina)! - Improve TSDoc comments for `useNodesState` and `useEdgesState` hook + +- [#5160](https://github.com/xyflow/xyflow/pull/5160) [`b357f43d`](https://github.com/xyflow/xyflow/commit/b357f43dfa262205059ac9714198196b4aaf8870) Thanks [@dimaMachina](https://github.com/dimaMachina)! - Improve TSDoc comments for `type UseHandleConnectionsParams` and `useHandleConnections` hook + +- [#5162](https://github.com/xyflow/xyflow/pull/5162) [`29f4aeb2`](https://github.com/xyflow/xyflow/commit/29f4aeb260df0a5d83e775c7c2ed788f997006a7) Thanks [@dimaMachina](https://github.com/dimaMachina)! - Improve TSDoc comments for `type UseNodeConnectionsParams` and `useNodeConnections` hook + +- [#5164](https://github.com/xyflow/xyflow/pull/5164) [`09021550`](https://github.com/xyflow/xyflow/commit/09021550dc72ac240fcbb6adb3cf530d91575f79) Thanks [@dimaMachina](https://github.com/dimaMachina)! - Improve TSDoc comments for `type UseOnViewportChangeOptions` and `useOnViewportChange` hook + +- [#5166](https://github.com/xyflow/xyflow/pull/5166) [`701ad17e`](https://github.com/xyflow/xyflow/commit/701ad17ed3f523389e7d0bf9a006bef249645c3d) Thanks [@dimaMachina](https://github.com/dimaMachina)! - Improve TSDoc comments for `useStore` hook + +- [#5170](https://github.com/xyflow/xyflow/pull/5170) [`eb2a33c6`](https://github.com/xyflow/xyflow/commit/eb2a33c6dfb0629e851ab3a0f2cd70eca92efc42) Thanks [@dimaMachina](https://github.com/dimaMachina)! - Improve TSDoc comments for `interface GetSimpleBezierPathParams` and `getSimpleBezierPath` + +- [#5161](https://github.com/xyflow/xyflow/pull/5161) [`c4efe749`](https://github.com/xyflow/xyflow/commit/c4efe749208e854dc9ced5bdd00933269d5b4382) Thanks [@dimaMachina](https://github.com/dimaMachina)! - Improve TSDoc comments for `type UseKeyPressOptions` and `useKeyPress` hook + +- Updated dependencies [[`02a3b746`](https://github.com/xyflow/xyflow/commit/02a3b74645799a3f0ce670b69365fa86ecb0616e), [`cbe305e1`](https://github.com/xyflow/xyflow/commit/cbe305e15a5c5d3b92583e0ec12364b2509f49bd), [`1f671bd4`](https://github.com/xyflow/xyflow/commit/1f671bd48f06230da841fdd1d7a312413ef16d03), [`aaebc462`](https://github.com/xyflow/xyflow/commit/aaebc462951ded8e91374c3e084d77af5ed7380a), [`6ec942fc`](https://github.com/xyflow/xyflow/commit/6ec942fc6501f81009c278cc995764bef3e8d03b)]: + - @xyflow/system@0.0.55 + +## 12.5.4 + +### Patch Changes + +- [#5134](https://github.com/xyflow/xyflow/pull/5134) [`7acab1e1`](https://github.com/xyflow/xyflow/commit/7acab1e123944c296180fbf826a3fd488963608f) Thanks [@dimaMachina](https://github.com/dimaMachina)! - Adjust `@default` TSDoc tags for `BackgroundProps` + +- [#5135](https://github.com/xyflow/xyflow/pull/5135) [`754a1671`](https://github.com/xyflow/xyflow/commit/754a167134f22fa00a139de4c7e10aaaa1953ac8) Thanks [@dimaMachina](https://github.com/dimaMachina)! - Improve TSDoc comments for `NodeResizerProps` and `ResizeControlProps` + +- [#5140](https://github.com/xyflow/xyflow/pull/5140) [`82e6860e`](https://github.com/xyflow/xyflow/commit/82e6860e1354b8bb8047399b7773fd090be206d7) Thanks [@dimaMachina](https://github.com/dimaMachina)! - Improve TSDoc comments for `MiniMapProps` and `PanelProps` + +- [#5147](https://github.com/xyflow/xyflow/pull/5147) [`f819005b`](https://github.com/xyflow/xyflow/commit/f819005be362d044b16ce4c0b85432f3f300a13a) Thanks [@moklick](https://github.com/moklick)! - Pass dimensions to final resize change event + +- [#5143](https://github.com/xyflow/xyflow/pull/5143) [`b1e1cc11`](https://github.com/xyflow/xyflow/commit/b1e1cc1125d106cf1521a1524286404483e38f30) Thanks [@dimaMachina](https://github.com/dimaMachina)! - Improve TSDoc comments for `ReactFlowInstance` and `GeneralHelpers` + +- [#5138](https://github.com/xyflow/xyflow/pull/5138) [`d4eb8d52`](https://github.com/xyflow/xyflow/commit/d4eb8d52d0e26e9534ec5fc347211ce91d1ddd32) Thanks [@dimaMachina](https://github.com/dimaMachina)! - Improve TSDoc comments for `ViewportHelperFunctions` and `NodeToolbarProps` + +- [#5144](https://github.com/xyflow/xyflow/pull/5144) [`5a1ce56e`](https://github.com/xyflow/xyflow/commit/5a1ce56e8ce83f01e01ef531d90c52181c3e3a1a) Thanks [@dimaMachina](https://github.com/dimaMachina)! - Compare `nodeStrokeWidth` with `number`, not with `string` within `MiniMap` + +- [#5141](https://github.com/xyflow/xyflow/pull/5141) [`06cf4c10`](https://github.com/xyflow/xyflow/commit/06cf4c10f5d8a43f57ee0fde19d9a3fe1044cf48) Thanks [@dimaMachina](https://github.com/dimaMachina)! - Improve TSDoc comments for `Edge`, `BaseEdgeProps` and `ConnectionLineComponentProps` + +- Updated dependencies [[`f819005b`](https://github.com/xyflow/xyflow/commit/f819005be362d044b16ce4c0b85432f3f300a13a), [`24a1bc89`](https://github.com/xyflow/xyflow/commit/24a1bc89348817ed9b5c87f74bf2519c705143be), [`36657cd6`](https://github.com/xyflow/xyflow/commit/36657cd66322c911e87eb37275c584a80025adfe), [`89de9ca8`](https://github.com/xyflow/xyflow/commit/89de9ca83fbf9263a687a0f5f915efb2beb31654), [`2ac6e155`](https://github.com/xyflow/xyflow/commit/2ac6e155e35256ca436281df16344366e7d05761), [`f0f378e5`](https://github.com/xyflow/xyflow/commit/f0f378e5b6918c2c30d9dc1e32587063cb942d4e)]: + - @xyflow/system@0.0.54 + +## 12.5.3 + +### Patch Changes + +- [#5132](https://github.com/xyflow/xyflow/pull/5132) [`75ab8942`](https://github.com/xyflow/xyflow/commit/75ab89420e3cd0fdc34baf06eabdc50113d4de7c) Thanks [@peterkogo](https://github.com/peterkogo)! - Fix fitView not working when onNodesChange is not defined. + +## 12.5.2 + +### Patch Changes + +- [#5124](https://github.com/xyflow/xyflow/pull/5124) [`b76f7f9e`](https://github.com/xyflow/xyflow/commit/b76f7f9eb4841f139b1468b8eda0430ddd19a1ae) Thanks [@bjornosal](https://github.com/bjornosal)! - Export NodeConnection type + +- [#5127](https://github.com/xyflow/xyflow/pull/5127) [`3079c2c9`](https://github.com/xyflow/xyflow/commit/3079c2c911426f54e8d295083ddbe97ed3aad201) Thanks [@peterkogo](https://github.com/peterkogo)! - Fix `fitView` not working when returning early in `onNodesChange`. + +## 12.5.1 + +### Patch Changes + +- [#5120](https://github.com/xyflow/xyflow/pull/5120) [`6dfea686`](https://github.com/xyflow/xyflow/commit/6dfea6863a3cbd91f932bf54a6dba549bd248bd5) Thanks [@moklick](https://github.com/moklick)! - Handle fitView for uncontrolled flows + +## 12.5.0 + +### Minor Changes + +- [#5067](https://github.com/xyflow/xyflow/pull/5067) [`acba901d`](https://github.com/xyflow/xyflow/commit/acba901d861aa84cb5beba60b24fff4cfde7ada6) Thanks [@peterkogo](https://github.com/peterkogo)! - You can now express paddings in fitViewOptions as pixels ('30px'), as viewport percentages ('20%') and define different paddings for each side. + +### Patch Changes + +- [#5109](https://github.com/xyflow/xyflow/pull/5109) [`0cdda42c`](https://github.com/xyflow/xyflow/commit/0cdda42cdd1cd43d43d43c44e54b7b9f7a716ca9) Thanks [@dimaMachina](https://github.com/dimaMachina)! - fix: improve TSDoc comments for `BackgroundProps` + +- [#5059](https://github.com/xyflow/xyflow/pull/5059) [`065ff89d`](https://github.com/xyflow/xyflow/commit/065ff89d10488f9c76c56870511e45eaed299778) Thanks [@bcakmakoglu](https://github.com/bcakmakoglu)! - Prevent onPaneClick when connection is in progress. Closes [#5057](https://github.com/xyflow/xyflow/issues/5057) + +- [#5110](https://github.com/xyflow/xyflow/pull/5110) [`7eb6eb07`](https://github.com/xyflow/xyflow/commit/7eb6eb0709e451d7628bfdbc3ced89b3bb57b626) Thanks [@dimaMachina](https://github.com/dimaMachina)! - fix: improve TSDoc comments for `EdgeLabelOptions` and `BaseEdgeProps` + +- [#5113](https://github.com/xyflow/xyflow/pull/5113) [`bce8542d`](https://github.com/xyflow/xyflow/commit/bce8542df19c33f3cd9225f483435e8a7aa4ed94) Thanks [@dimaMachina](https://github.com/dimaMachina)! - fix: improve TSDoc comments for `ControlProps` + +- [#5116](https://github.com/xyflow/xyflow/pull/5116) [`58942154`](https://github.com/xyflow/xyflow/commit/589421542386906ec49d8469cac551b8f7ea1c47) Thanks [@dimaMachina](https://github.com/dimaMachina)! - fix: improve TSDoc comments for `NodeToolbarProps` + +- [#5114](https://github.com/xyflow/xyflow/pull/5114) [`ba2bfbb4`](https://github.com/xyflow/xyflow/commit/ba2bfbb49aafac979f94b0136bb408faea12d5c6) Thanks [@dimaMachina](https://github.com/dimaMachina)! - feat: export `EdgeLabelRendererProps` + +- [#5107](https://github.com/xyflow/xyflow/pull/5107) [`c5a8c237`](https://github.com/xyflow/xyflow/commit/c5a8c23773e5985be6b37abacdac743911be8c09) Thanks [@moklick](https://github.com/moklick)! - Add TSDoc annotations for exported edges + +- [#5067](https://github.com/xyflow/xyflow/pull/5067) [`cb685281`](https://github.com/xyflow/xyflow/commit/cb685281d0eaf03e9833271c31f92b1d143af2fe) Thanks [@peterkogo](https://github.com/peterkogo)! - Fix fitView not working immediately after adding new nodes + +- [#5115](https://github.com/xyflow/xyflow/pull/5115) [`c2154557`](https://github.com/xyflow/xyflow/commit/c215455735385ef5e12e4130164b9d01f9c18aa2) Thanks [@dimaMachina](https://github.com/dimaMachina)! - fix: improve TSDoc comments for `EdgeLabelOptions` and `EdgeTextProps` + +- [#5093](https://github.com/xyflow/xyflow/pull/5093) [`65825e89`](https://github.com/xyflow/xyflow/commit/65825e89a6e2e7591087eb41ac89da4da7095f8f) Thanks [@moklick](https://github.com/moklick)! - Hidden nodes are not displayed in the mini map anymore + +- [#5090](https://github.com/xyflow/xyflow/pull/5090) [`8da1748a`](https://github.com/xyflow/xyflow/commit/8da1748a6ad5cdde9f03737ff786bd29c9c968de) Thanks [@moklick](https://github.com/moklick)! - Release key even when an inout field is focused + +- Updated dependencies [[`5d15b01b`](https://github.com/xyflow/xyflow/commit/5d15b01ba8cb349d6397a6ed8162848b4dfec293), [`cb685281`](https://github.com/xyflow/xyflow/commit/cb685281d0eaf03e9833271c31f92b1d143af2fe), [`a79f30b3`](https://github.com/xyflow/xyflow/commit/a79f30b3dd7c8ff6400c8d22214b2c2282e5bac1)]: + - @xyflow/system@0.0.53 + +## 12.4.4 + +### Patch Changes + +- [#5052](https://github.com/xyflow/xyflow/pull/5052) [`99dd7d35`](https://github.com/xyflow/xyflow/commit/99dd7d3549e7423e7d103b2c956c8b37f5747b90) Thanks [@moklick](https://github.com/moklick)! - Show an error if user drags uninitialized node + +- [#5042](https://github.com/xyflow/xyflow/pull/5042) [`2fe0e850`](https://github.com/xyflow/xyflow/commit/2fe0e850a8c415c6a3113796a2c5c80e7cad2376) Thanks [@moklick](https://github.com/moklick)! - Allow click connections when target sets `isConnectableStart` + +- [#5047](https://github.com/xyflow/xyflow/pull/5047) [`b3bf5693`](https://github.com/xyflow/xyflow/commit/b3bf5693c659069cea90bf1cb215ae65d06c5509) Thanks [@moklick](https://github.com/moklick)! - Pass generics to OnSelectionChangeFunc so that users can type it correctly + +- [#5053](https://github.com/xyflow/xyflow/pull/5053) [`25fb45b5`](https://github.com/xyflow/xyflow/commit/25fb45b5e9d6da391b9aff652b8e6e34eaf757fc) Thanks [@moklick](https://github.com/moklick)! - Remove incorrect deprecation warning + +- [#5033](https://github.com/xyflow/xyflow/pull/5033) [`7b4a81fb`](https://github.com/xyflow/xyflow/commit/7b4a81fb6b3d88f8ee7b4f070aef7ac3b962d5a6) Thanks [@dimaMachina](https://github.com/dimaMachina)! - lint: use `React.JSX` type instead of the deprecated global `JSX` namespace + +- [#5043](https://github.com/xyflow/xyflow/pull/5043) [`0292ad20`](https://github.com/xyflow/xyflow/commit/0292ad20109a3b2518dc686a82e100a0a6964fb8) Thanks [@moklick](https://github.com/moklick)! - Use current expandParent value on drag to be able to update it while dragging + +- [#5032](https://github.com/xyflow/xyflow/pull/5032) [`5867bba8`](https://github.com/xyflow/xyflow/commit/5867bba8050d07378a45a2026557c4bce7bda239) Thanks [@dimaMachina](https://github.com/dimaMachina)! - lint: remove unnecessary type assertions + +- Updated dependencies [[`99dd7d35`](https://github.com/xyflow/xyflow/commit/99dd7d3549e7423e7d103b2c956c8b37f5747b90)]: + - @xyflow/system@0.0.52 + +## 12.4.3 + +### Patch Changes + +- [#5010](https://github.com/xyflow/xyflow/pull/5010) [`6c121d42`](https://github.com/xyflow/xyflow/commit/6c121d427fea9a11e86a85f95d2c12ba8af34919) Thanks [@moklick](https://github.com/moklick)! - Add more TSDocs to components, hooks, utils funcs and types + +- [#4991](https://github.com/xyflow/xyflow/pull/4991) [`ea54d9bc`](https://github.com/xyflow/xyflow/commit/ea54d9bcb197d02d248ef3e4eaabc033a43d966a) Thanks [@waynetee](https://github.com/waynetee)! - Fix viewport shifting on node focus + +- [#5013](https://github.com/xyflow/xyflow/pull/5013) [`cde899c5`](https://github.com/xyflow/xyflow/commit/cde899c5be9715c4ff2cc331ea93821102604c62) Thanks [@moklick](https://github.com/moklick)! - Pass `NodeType` type argument from `ReactFlowProps` to `connectionLineComponent` property. + +- [#5008](https://github.com/xyflow/xyflow/pull/5008) [`12d859fe`](https://github.com/xyflow/xyflow/commit/12d859fe297593d44cf8493a4d6bf2c664b9139c) Thanks [@moklick](https://github.com/moklick)! - Add package.json to exports + +- [#5012](https://github.com/xyflow/xyflow/pull/5012) [`4d3f19e8`](https://github.com/xyflow/xyflow/commit/4d3f19e88b984ce6743970560d7367d174500f32) Thanks [@moklick](https://github.com/moklick)! - Add snapGrid option to screenToFlowPosition and set snapToGrid to false + +- [#5003](https://github.com/xyflow/xyflow/pull/5003) [`e8e0d684`](https://github.com/xyflow/xyflow/commit/e8e0d684957b95d53a6cc11598c8755ff02117c7) Thanks [@dimaMachina](https://github.com/dimaMachina)! - repair lint command + +- [#4991](https://github.com/xyflow/xyflow/pull/4991) [`4c62f19b`](https://github.com/xyflow/xyflow/commit/4c62f19b3afac4b3db84b14e2c36f8c9e0a96116) Thanks [@waynetee](https://github.com/waynetee)! - Prevent viewport shift after using Tab + +- Updated dependencies [[`6c121d42`](https://github.com/xyflow/xyflow/commit/6c121d427fea9a11e86a85f95d2c12ba8af34919), [`4947029c`](https://github.com/xyflow/xyflow/commit/4947029cd6cda0f695e1fb4815e4030adb232234), [`e8e0d684`](https://github.com/xyflow/xyflow/commit/e8e0d684957b95d53a6cc11598c8755ff02117c7)]: + - @xyflow/system@0.0.51 + +## 12.4.2 + +### Patch Changes + +- [#4957](https://github.com/xyflow/xyflow/pull/4957) [`fe843982`](https://github.com/xyflow/xyflow/commit/fe843982bfc7d7579d54772b201426b4c3f549c6) Thanks [@peterkogo](https://github.com/peterkogo)! - Narrow properties selected, selectable, deletable, draggable of NodeProps type to be required. + +- Updated dependencies [[`fe843982`](https://github.com/xyflow/xyflow/commit/fe843982bfc7d7579d54772b201426b4c3f549c6), [`e73ef09f`](https://github.com/xyflow/xyflow/commit/e73ef09fbc8d872b46cf52c9d6a32dbb388c220b)]: + - @xyflow/system@0.0.50 + +## 12.4.1 + +### Patch Changes + +- [#4949](https://github.com/xyflow/xyflow/pull/4949) [`592c7eaf`](https://github.com/xyflow/xyflow/commit/592c7eaf9574fc69df3123837da95f85877b23e8) Thanks [@peterkogo](https://github.com/peterkogo)! - Fix useNodeConnection hook not returning all connected edges. + +- Updated dependencies [[`592c7eaf`](https://github.com/xyflow/xyflow/commit/592c7eaf9574fc69df3123837da95f85877b23e8)]: + - @xyflow/system@0.0.49 + +## 12.4.0 + +### Minor Changes + +- [#4725](https://github.com/xyflow/xyflow/pull/4725) [`e10f53cf`](https://github.com/xyflow/xyflow/commit/e10f53cf898a56f954783d6efcf6977a0d88f4a9) Thanks [@peterkogo](https://github.com/peterkogo)! - Add useNodeConnections hook to track all connections to a node. Can be filtered by handleType and handleId. + +### Patch Changes + +- [#4947](https://github.com/xyflow/xyflow/pull/4947) [`868aa3f3`](https://github.com/xyflow/xyflow/commit/868aa3f3db8223ea1b04a68aa027ea99fd1e91c8) Thanks [@moklick](https://github.com/moklick)! - Export ResizeControlVariant correctly as a value. + +- [#4880](https://github.com/xyflow/xyflow/pull/4880) [`e2d849dc`](https://github.com/xyflow/xyflow/commit/e2d849dca63aee5952f676aef1c675c6232bb69a) Thanks [@crimx](https://github.com/crimx)! - Add type check for all event targets + +- [#4929](https://github.com/xyflow/xyflow/pull/4929) [`4947f683`](https://github.com/xyflow/xyflow/commit/4947f683b7530f8e6684865ab53ea38633de0f4d) Thanks [@peterkogo](https://github.com/peterkogo)! - Optimize selections and take into account if edges connected to selected nodes are actually selectable. + +- Updated dependencies [[`e2d849dc`](https://github.com/xyflow/xyflow/commit/e2d849dca63aee5952f676aef1c675c6232bb69a), [`e10f53cf`](https://github.com/xyflow/xyflow/commit/e10f53cf898a56f954783d6efcf6977a0d88f4a9), [`4947f683`](https://github.com/xyflow/xyflow/commit/4947f683b7530f8e6684865ab53ea38633de0f4d)]: + - @xyflow/system@0.0.48 + +## 12.3.6 + +### Patch Changes + +- [#4846](https://github.com/xyflow/xyflow/pull/4846) [`75017939`](https://github.com/xyflow/xyflow/commit/7501793900fdb125fe906fa3d9d83f1bd11c6e20) Thanks [@moklick](https://github.com/moklick)! - Make it possible to use expandParent with immer and other immutable helpers + +- [#4865](https://github.com/xyflow/xyflow/pull/4865) [`2c4acc2b`](https://github.com/xyflow/xyflow/commit/2c4acc2bd9ec271468bd4c904e19d7fca627d9e1) Thanks [@moklick](https://github.com/moklick)! - Add group node to BuiltInNode type. Thanks [@sjdemartini](https://github.com/sjdemartini)! + +- [#4877](https://github.com/xyflow/xyflow/pull/4877) [`9a8309da`](https://github.com/xyflow/xyflow/commit/9a8309dab892f047fce10d3c763466cf84f726b0) Thanks [@peterkogo](https://github.com/peterkogo)! - Fix intersections for nodes with origins other than [0,0]. Thanks [@gmvrpw](https://github.com/gmvrpw)! + +- [#4844](https://github.com/xyflow/xyflow/pull/4844) [`6f11e552`](https://github.com/xyflow/xyflow/commit/6f11e552c3f51b5931b98d5ff798156cdb8cc2d9) Thanks [@moklick](https://github.com/moklick)! - Allow custom data-testid for ReactFlow component + +- [#4816](https://github.com/xyflow/xyflow/pull/4816) [`43aa52a8`](https://github.com/xyflow/xyflow/commit/43aa52a8cd93e6a01c01afa314c7036eb5b77131) Thanks [@moklick](https://github.com/moklick)! - Type isValidConnection prop correctly by passing EdgeType + +- [#4855](https://github.com/xyflow/xyflow/pull/4855) [`106c2cf8`](https://github.com/xyflow/xyflow/commit/106c2cf8e5e9a944ea7ea9a2558e26cb75f9ac93) Thanks [@mhuggins](https://github.com/mhuggins)! - Support passing `path` element attributes to `BaseEdge` component. + +- [#4862](https://github.com/xyflow/xyflow/pull/4862) [`adf4fb4e`](https://github.com/xyflow/xyflow/commit/adf4fb4e7bb136cce47ef540e9bcc0f82cf17ff4) Thanks [@bcakmakoglu](https://github.com/bcakmakoglu)! - Prevent default scrolling behavior when nodes or a selection is moved with an arrow key press. + +- [#4875](https://github.com/xyflow/xyflow/pull/4875) [`41d4743a`](https://github.com/xyflow/xyflow/commit/41d4743a690f054f6dc2b9084b5fad8e54ade921) Thanks [@peterkogo](https://github.com/peterkogo)! - Prevent unnecessary rerenders of edges when resizing the flow. + +- [#4826](https://github.com/xyflow/xyflow/pull/4826) [`5f90acda`](https://github.com/xyflow/xyflow/commit/5f90acdab1f4748bb04f8b1ec67d569983684058) Thanks [@chrtze](https://github.com/chrtze)! - Forward ref of the div inside Panel components. + +- Updated dependencies [[`d60331e6`](https://github.com/xyflow/xyflow/commit/d60331e6baa7931c46af219e35c1bedbd156187c)]: + - @xyflow/system@0.0.47 + +## 12.3.5 + +### Patch Changes + +- [#4789](https://github.com/xyflow/xyflow/pull/4789) [`358eb355`](https://github.com/xyflow/xyflow/commit/358eb355e0b2bea4ffa47b0f04d6edf834343cd7) Thanks [@peterkogo](https://github.com/peterkogo)! - Support key combinations which include '+' (e.g., 'Control++' resolves to the combination 'Control' and '+'). + +- [#4796](https://github.com/xyflow/xyflow/pull/4796) [`73402779`](https://github.com/xyflow/xyflow/commit/734027798799f4a98212dda115d33b4c54a95a45) Thanks [@Aki-7](https://github.com/Aki-7)! - Fix number of issues connected to batching node & edge updates. + +- [#4790](https://github.com/xyflow/xyflow/pull/4790) [`2fa9a920`](https://github.com/xyflow/xyflow/commit/2fa9a92042ba11986abbababb7e8b294e208d6cb) Thanks [@peterkogo](https://github.com/peterkogo)! - Fix node dragging & resizing while zooming on flow that does not cover whole browser window. + +- [#4782](https://github.com/xyflow/xyflow/pull/4782) [`323e1b35`](https://github.com/xyflow/xyflow/commit/323e1b35c58bca80deb824bc8b136705593a5257) Thanks [@peterkogo](https://github.com/peterkogo)! - Fix node intersections in nested flow. + +- Updated dependencies [[`2fa9a920`](https://github.com/xyflow/xyflow/commit/2fa9a92042ba11986abbababb7e8b294e208d6cb), [`323e1b35`](https://github.com/xyflow/xyflow/commit/323e1b35c58bca80deb824bc8b136705593a5257)]: + - @xyflow/system@0.0.46 + +## 12.3.4 + +### Patch Changes + +- [#4772](https://github.com/xyflow/xyflow/pull/4772) [`7f670ab0`](https://github.com/xyflow/xyflow/commit/7f670ab0423b3848a50398027297f6ec11deeaa4) Thanks [@mistic](https://github.com/mistic)! - Splits exports field in package.json to support an explicit resolution for browser, node and default to resolve issues with webpack esm module resolution. + +- Updated dependencies [[`7f670ab0`](https://github.com/xyflow/xyflow/commit/7f670ab0423b3848a50398027297f6ec11deeaa4)]: + - @xyflow/system@0.0.45 + +## 12.3.3 + +### Patch Changes + +- [#4755](https://github.com/xyflow/xyflow/pull/4755) [`005ae1c0`](https://github.com/xyflow/xyflow/commit/005ae1c05f6a10c1f519cd789f4f3f2fdf293bc6) Thanks [@peterkogo](https://github.com/peterkogo)! - Add module to exports in package.json. This should resolve possible issues with Webpack ESM Module Resolution. + +- [#4730](https://github.com/xyflow/xyflow/pull/4730) [`2c590b90`](https://github.com/xyflow/xyflow/commit/2c590b90787aabce42de2b4108174bdf31ad6155) Thanks [@peterkogo](https://github.com/peterkogo)! - Fixed rare crash while dragging + +- Updated dependencies [[`005ae1c0`](https://github.com/xyflow/xyflow/commit/005ae1c05f6a10c1f519cd789f4f3f2fdf293bc6), [`2c590b90`](https://github.com/xyflow/xyflow/commit/2c590b90787aabce42de2b4108174bdf31ad6155)]: + - @xyflow/system@0.0.44 + +## 12.3.2 + +### Patch Changes + +- [#4722](https://github.com/xyflow/xyflow/pull/4722) [`e816bb69`](https://github.com/xyflow/xyflow/commit/e816bb6953486e37dd39d93252aa9b94fe5d4ec1) Thanks [@moklick](https://github.com/moklick)! - Fix internal behaviour that mutated user nodes which led to an issue with Redux and immer + +## 12.3.1 + +### Patch Changes + +- [#4670](https://github.com/xyflow/xyflow/pull/4670) [`b056564c`](https://github.com/xyflow/xyflow/commit/b056564c9658bb43b882eebfad5a7e224717ffb5) Thanks [@peterkogo](https://github.com/peterkogo)! - Fix initial `fitView` not working correctly for `nodeOrigin` other than [0,0] + +- [#4670](https://github.com/xyflow/xyflow/pull/4670) [`b056564c`](https://github.com/xyflow/xyflow/commit/b056564c9658bb43b882eebfad5a7e224717ffb5) Thanks [@peterkogo](https://github.com/peterkogo)! - Improve `fitView` to respect clamped node positions based on `nodeExtent` + +- [#4653](https://github.com/xyflow/xyflow/pull/4653) [`02390f99`](https://github.com/xyflow/xyflow/commit/02390f9966d51c80e4e1b488733b5bf7322ad710) Thanks [@bcakmakoglu](https://github.com/bcakmakoglu)! - Calculate viewport dimensions in `fitView` instead of using stored dimensions. Fixes [#4652](https://github.com/xyflow/xyflow/issues/4652) + +- Updated dependencies [[`99ba64ac`](https://github.com/xyflow/xyflow/commit/99ba64ac2e1ce9c5ac3cab85a3d574edc0ecf4cc), [`b056564c`](https://github.com/xyflow/xyflow/commit/b056564c9658bb43b882eebfad5a7e224717ffb5)]: + - @xyflow/system@0.0.43 + ## 12.3.0 ### Minor Changes diff --git a/packages/react/package.json b/packages/react/package.json index 5f8496ee..25846b72 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -1,6 +1,6 @@ { "name": "@xyflow/react", - "version": "12.3.0", + "version": "12.6.4", "description": "React Flow - A highly customizable React library for building node-based editors and interactive flow charts.", "keywords": [ "react", @@ -28,10 +28,19 @@ "module": "dist/esm/index.js", "types": "dist/esm/index.d.ts", "exports": { + "./package.json": "./package.json", ".": { - "types": "./dist/esm/index.d.ts", - "import": "./dist/esm/index.mjs", - "require": "./dist/umd/index.js" + "node": { + "types": "./dist/esm/index.d.ts", + "module": "./dist/esm/index.js", + "require": "./dist/umd/index.js", + "import": "./dist/esm/index.mjs" + }, + "browser": { + "import": "./dist/esm/index.js", + "require": "./dist/umd/index.js" + }, + "default": "./dist/esm/index.js" }, "./dist/base.css": "./dist/base.css", "./dist/style.css": "./dist/style.css" @@ -76,7 +85,7 @@ "postcss-nested": "^6.0.0", "postcss-rename": "^0.6.1", "react": "^18.2.0", - "typescript": "5.1.3" + "typescript": "5.4.5" }, "rollup": { "globals": { diff --git a/packages/react/src/additional-components/Background/Background.tsx b/packages/react/src/additional-components/Background/Background.tsx index e4470f10..ee89624d 100644 --- a/packages/react/src/additional-components/Background/Background.tsx +++ b/packages/react/src/additional-components/Background/Background.tsx @@ -90,4 +90,57 @@ function BackgroundComponent({ BackgroundComponent.displayName = 'Background'; +/** + * The `` component makes it convenient to render different types of backgrounds common in node-based UIs. It comes with three variants: lines, dots and cross. + * + * @example + * + * A simple example of how to use the Background component. + * + * ```tsx + * import { useState } from 'react'; + * import { ReactFlow, Background, BackgroundVariant } from '@xyflow/react'; + * + * export default function Flow() { + * return ( + * + * + * + * ); + * } + * ``` + * + * @example + * + * In this example you can see how to combine multiple backgrounds + * + * ```tsx + * import { ReactFlow, Background, BackgroundVariant } from '@xyflow/react'; + * import '@xyflow/react/dist/style.css'; + * + * export default function Flow() { + * return ( + * + * + * + * + * ); + * } + * ``` + * + * @remarks + * + * When combining multiple components it’s important to give each of them a unique id prop! + * + */ export const Background = memo(BackgroundComponent); diff --git a/packages/react/src/additional-components/Background/types.ts b/packages/react/src/additional-components/Background/types.ts index 0a035aae..8e8bf944 100644 --- a/packages/react/src/additional-components/Background/types.ts +++ b/packages/react/src/additional-components/Background/types.ts @@ -1,34 +1,60 @@ import { CSSProperties } from 'react'; +/** + * The three variants are exported as an enum for convenience. You can either import + * the enum and use it like `BackgroundVariant.Lines` or you can use the raw string + * value directly. + * @public + */ export enum BackgroundVariant { Lines = 'lines', Dots = 'dots', Cross = 'cross', } +/** + * @expand + */ export type BackgroundProps = { + /** When multiple backgrounds are present on the page, each one should have a unique id. */ id?: string; - /** Color of the pattern */ + /** Color of the pattern. */ color?: string; - /** Color of the background */ + /** Color of the background. */ bgColor?: string; - /** Class applied to the container */ + /** Class applied to the container. */ className?: string; - /** Class applied to the pattern */ + /** Class applied to the pattern. */ patternClassName?: string; - /** Gap between repetitions of the pattern */ + /** + * The gap between patterns. Passing in a tuple allows you to control the x and y gap + * independently. + * @default 20 + */ gap?: number | [number, number]; - /** Size of a single pattern element */ + /** + * The radius of each dot or the size of each rectangle if `BackgroundVariant.Dots` or + * `BackgroundVariant.Cross` is used. This defaults to 1 or 6 respectively, or ignored if + * `BackgroundVariant.Lines` is used. + */ size?: number; - /** Offset of the pattern */ + /** + * Offset of the pattern. + * @default 0 + */ offset?: number | [number, number]; - /** Line width of the Line pattern */ + /** + * The stroke thickness used when drawing the pattern. + * @default 1 + */ lineWidth?: number; - /** Variant of the pattern + /** + * Variant of the pattern. + * @default BackgroundVariant.Dots * @example BackgroundVariant.Lines, BackgroundVariant.Dots, BackgroundVariant.Cross * 'lines', 'dots', 'cross' */ variant?: BackgroundVariant; - /** Style applied to the container */ + /** Style applied to the container. */ style?: CSSProperties; }; diff --git a/packages/react/src/additional-components/Controls/ControlButton.tsx b/packages/react/src/additional-components/Controls/ControlButton.tsx index 744f46d4..a3cc81ad 100644 --- a/packages/react/src/additional-components/Controls/ControlButton.tsx +++ b/packages/react/src/additional-components/Controls/ControlButton.tsx @@ -2,6 +2,29 @@ import cc from 'classcat'; import type { ControlButtonProps } from './types'; +/** + * You can add buttons to the control panel by using the `` component + * and pass it as a child to the [``](/api-reference/components/controls) component. + * + * @public + * @example + *```jsx + *import { MagicWand } from '@radix-ui/react-icons' + *import { ReactFlow, Controls, ControlButton } from '@xyflow/react' + * + *export default function Flow() { + * return ( + * + * + * alert('Something magical just happened. ✨')}> + * + * + * + * + * ) + *} + *``` + */ export function ControlButton({ children, className, ...rest }: ControlButtonProps) { return ( + * + * + * + * + *
+ * {data.label} + *
+ * + * + * + * + * ); + *}; + * + *export default memo(CustomNode); + *``` + * @remarks By default, the toolbar is only visible when a node is selected. If multiple + * nodes are selected it will not be visible to prevent overlapping toolbars or + * clutter. You can override this behavior by setting the `isVisible` prop to `true`. + */ export function NodeToolbar({ nodeId, children, @@ -74,7 +109,7 @@ export function NodeToolbar({ const isActive = typeof isVisible === 'boolean' ? isVisible - : nodes.size === 1 && nodes.values().next().value.selected && selectedNodesCount === 1; + : nodes.size === 1 && nodes.values().next().value?.selected && selectedNodesCount === 1; if (!isActive || !nodes.size) { return null; diff --git a/packages/react/src/additional-components/NodeToolbar/types.ts b/packages/react/src/additional-components/NodeToolbar/types.ts index 4de679bd..6655547d 100644 --- a/packages/react/src/additional-components/NodeToolbar/types.ts +++ b/packages/react/src/additional-components/NodeToolbar/types.ts @@ -1,19 +1,31 @@ import type { HTMLAttributes } from 'react'; import type { Position, Align } from '@xyflow/system'; +/** + * @expand + */ export type NodeToolbarProps = HTMLAttributes & { - /** Id of the node, or array of ids the toolbar should be displayed at */ + /** + * By passing in an array of node id's you can render a single tooltip for a group or collection + * of nodes. + */ nodeId?: string | string[]; - /** If true, node toolbar is visible even if node is not selected */ + /** If `true`, node toolbar is visible even if node is not selected. */ isVisible?: boolean; - /** Position of the toolbar relative to the node - * @example Position.TopLeft, Position.TopRight, - * Position.BottomLeft, Position.BottomRight + /** + * Position of the toolbar relative to the node. + * @default Position.Top + * @example Position.TopLeft, Position.TopRight, Position.BottomLeft, Position.BottomRight */ position?: Position; - /** Offset the toolbar from the node */ + /** + * The space between the node and the toolbar, measured in pixels. + * @default 10 + */ offset?: number; - /** Align the toolbar relative to the node + /** + * Align the toolbar relative to the node. + * @default "center" * @example Align.Start, Align.Center, Align.End */ align?: Align; diff --git a/packages/react/src/components/BatchProvider/index.tsx b/packages/react/src/components/BatchProvider/index.tsx index 1dbab718..b2ed270f 100644 --- a/packages/react/src/components/BatchProvider/index.tsx +++ b/packages/react/src/components/BatchProvider/index.tsx @@ -28,33 +28,48 @@ export function BatchProvider(); const nodeQueueHandler = useCallback((queueItems: QueueItem[]) => { - const { nodes = [], setNodes, hasDefaultNodes, onNodesChange, nodeLookup } = store.getState(); + const { nodes = [], setNodes, hasDefaultNodes, onNodesChange, nodeLookup, fitViewQueued } = store.getState(); - // This is essentially an `Array.reduce` in imperative clothing. Processing - // this queue is a relatively hot path so we'd like to avoid the overhead of - // array methods where we can. - let next = nodes as NodeType[]; + /* + * This is essentially an `Array.reduce` in imperative clothing. Processing + * this queue is a relatively hot path so we'd like to avoid the overhead of + * array methods where we can. + */ + let next = nodes; for (const payload of queueItems) { next = typeof payload === 'function' ? payload(next) : payload; } + const changes = getElementsDiffChanges({ + items: next, + lookup: nodeLookup, + }) as NodeChange[]; + if (hasDefaultNodes) { setNodes(next); - } else if (onNodesChange) { - onNodesChange( - getElementsDiffChanges({ - items: next, - lookup: nodeLookup, - }) as NodeChange[] - ); + } + + // We only want to fire onNodesChange if there are changes to the nodes + if (changes.length > 0) { + onNodesChange?.(changes); + } else if (fitViewQueued) { + // If there are no changes to the nodes, we still need to call setNodes + // to trigger a re-render and fitView. + window.requestAnimationFrame(() => { + const { fitViewQueued, nodes, setNodes } = store.getState(); + if (fitViewQueued) { + setNodes(nodes); + } + }); } }, []); + const nodeQueue = useQueue(nodeQueueHandler); const edgeQueueHandler = useCallback((queueItems: QueueItem[]) => { const { edges = [], setEdges, hasDefaultEdges, onEdgesChange, edgeLookup } = store.getState(); - let next = edges as EdgeType[]; + let next = edges; for (const payload of queueItems) { next = typeof payload === 'function' ? payload(next) : payload; } diff --git a/packages/react/src/components/BatchProvider/useQueue.ts b/packages/react/src/components/BatchProvider/useQueue.ts index 4170fa7b..b1f9f793 100644 --- a/packages/react/src/components/BatchProvider/useQueue.ts +++ b/packages/react/src/components/BatchProvider/useQueue.ts @@ -12,29 +12,28 @@ import { Queue, QueueItem } from './types'; * @returns a Queue object */ export function useQueue(runQueue: (items: QueueItem[]) => void) { - // Because we're using a ref above, we need some way to let React know when to - // actually process the queue. We flip this bit of state to `true` any time we - // mutate the queue and then flip it back to `false` after flushing the queue. - const [shouldFlush, setShouldFlush] = useState(false); + /* + * Because we're using a ref above, we need some way to let React know when to + * actually process the queue. We increment this number any time we mutate the + * queue, creating a new state to trigger the layout effect below. + * Using a boolean dirty flag here instead would lead to issues related to + * automatic batching. (https://github.com/xyflow/xyflow/issues/4779) + */ + const [serial, setSerial] = useState(BigInt(0)); - // A reference of all the batched updates to process before the next render. We - // want a reference here so multiple synchronous calls to `setNodes` etc can be - // batched together. - const [queue] = useState(() => createQueue(() => setShouldFlush(true))); + /* + * A reference of all the batched updates to process before the next render. We + * want a reference here so multiple synchronous calls to `setNodes` etc can be + * batched together. + */ + const [queue] = useState(() => createQueue(() => setSerial(n => n + BigInt(1)))); - // Layout effects are guaranteed to run before the next render which means we - // shouldn't run into any issues with stale state or weird issues that come from - // rendering things one frame later than expected (we used to use `setTimeout`). + /* + * Layout effects are guaranteed to run before the next render which means we + * shouldn't run into any issues with stale state or weird issues that come from + * rendering things one frame later than expected (we used to use `setTimeout`). + */ useIsomorphicLayoutEffect(() => { - // Because we need to flip the state back to false after flushing, this should - // trigger the hook again (!). If the hook is being run again we know that any - // updates should have been processed by now and we can safely clear the queue - // and bail early. - if (!shouldFlush) { - queue.reset(); - return; - } - const queueItems = queue.get(); if (queueItems.length) { @@ -42,11 +41,7 @@ export function useQueue(runQueue: (items: QueueItem[]) => void) { queue.reset(); } - - // Because we're using reactive state to trigger this effect, we need to flip - // it back to false. - setShouldFlush(false); - }, [shouldFlush]); + }, [serial]); return queue; } diff --git a/packages/react/src/components/ConnectionLine/index.tsx b/packages/react/src/components/ConnectionLine/index.tsx index a5f7e1e2..c179fae2 100644 --- a/packages/react/src/components/ConnectionLine/index.tsx +++ b/packages/react/src/components/ConnectionLine/index.tsx @@ -11,12 +11,12 @@ import { import { useStore } from '../../hooks/useStore'; import { getSimpleBezierPath } from '../Edges/SimpleBezierEdge'; -import type { ConnectionLineComponent, ReactFlowState } from '../../types'; +import type { ConnectionLineComponent, Node, ReactFlowState } from '../../types'; import { useConnection } from '../../hooks/useConnection'; -type ConnectionLineWrapperProps = { +type ConnectionLineWrapperProps = { type: ConnectionLineType; - component?: ConnectionLineComponent; + component?: ConnectionLineComponent; containerStyle?: CSSProperties; style?: CSSProperties; }; @@ -29,7 +29,12 @@ const selector = (s: ReactFlowState) => ({ height: s.height, }); -export function ConnectionLineWrapper({ containerStyle, style, type, component }: ConnectionLineWrapperProps) { +export function ConnectionLineWrapper({ + containerStyle, + style, + type, + component, +}: ConnectionLineWrapperProps) { const { nodesConnectable, width, height, isValid, inProgress } = useStore(selector, shallow); const renderConnection = !!(width && nodesConnectable && inProgress); @@ -45,21 +50,27 @@ export function ConnectionLineWrapper({ containerStyle, style, type, component } className="react-flow__connectionline react-flow__container" > - + style={style} type={type} CustomComponent={component} isValid={isValid} /> ); } -type ConnectionLineProps = { +type ConnectionLineProps = { type: ConnectionLineType; style?: CSSProperties; - CustomComponent?: ConnectionLineComponent; + CustomComponent?: ConnectionLineComponent; isValid: boolean | null; }; -const ConnectionLine = ({ style, type = ConnectionLineType.Bezier, CustomComponent, isValid }: ConnectionLineProps) => { - const { inProgress, from, fromNode, fromHandle, fromPosition, to, toNode, toHandle, toPosition } = useConnection(); +const ConnectionLine = ({ + style, + type = ConnectionLineType.Bezier, + CustomComponent, + isValid, +}: ConnectionLineProps) => { + const { inProgress, from, fromNode, fromHandle, fromPosition, to, toNode, toHandle, toPosition } = + useConnection(); if (!inProgress) { return; diff --git a/packages/react/src/components/EdgeLabelRenderer/index.tsx b/packages/react/src/components/EdgeLabelRenderer/index.tsx index c66b70ae..7115b495 100644 --- a/packages/react/src/components/EdgeLabelRenderer/index.tsx +++ b/packages/react/src/components/EdgeLabelRenderer/index.tsx @@ -6,7 +6,52 @@ import type { ReactFlowState } from '../../types'; const selector = (s: ReactFlowState) => s.domNode?.querySelector('.react-flow__edgelabel-renderer'); -export function EdgeLabelRenderer({ children }: { children: ReactNode }) { +export type EdgeLabelRendererProps = { + children: ReactNode +} + +/** + * Edges are SVG-based. If you want to render more complex labels you can use the + * `` component to access a div based renderer. This component + * is a portal that renders the label in a `
` that is positioned on top of + * the edges. You can see an example usage of the component in the + * [edge label renderer example](/examples/edges/edge-label-renderer). + * @public + * + * @example + * ```jsx + * import React from 'react'; + * import { getBezierPath, EdgeLabelRenderer, BaseEdge } from '@xyflow/react'; + * + * export function CustomEdge({ id, data, ...props }) { + * const [edgePath, labelX, labelY] = getBezierPath(props); + * + * return ( + * <> + * + * + *
+ * {data.label} + *
+ *
+ * + * ); + * }; + * ``` + * + * @remarks The `` has no pointer events by default. If you want to + * add mouse interactions you need to set the style `pointerEvents: all` and add + * the `nopan` class on the label or the element you want to interact with. + */ +export function EdgeLabelRenderer({ children }: EdgeLabelRendererProps) { const edgeLabelRenderer = useStore(selector); if (!edgeLabelRenderer) { diff --git a/packages/react/src/components/EdgeWrapper/index.tsx b/packages/react/src/components/EdgeWrapper/index.tsx index f50ed4b9..72c5c2f8 100644 --- a/packages/react/src/components/EdgeWrapper/index.tsx +++ b/packages/react/src/components/EdgeWrapper/index.tsx @@ -1,4 +1,4 @@ -import { useState, useMemo, useRef, type KeyboardEvent, useCallback } from 'react'; +import { useState, useMemo, useRef, type KeyboardEvent, useCallback, JSX } from 'react'; import cc from 'classcat'; import { shallow } from 'zustand/shallow'; import { @@ -136,28 +136,28 @@ export function EdgeWrapper({ const onEdgeDoubleClick = onDoubleClick ? (event: React.MouseEvent) => { - onDoubleClick(event, { ...edge }); - } + onDoubleClick(event, { ...edge }); + } : undefined; const onEdgeContextMenu = onContextMenu ? (event: React.MouseEvent) => { - onContextMenu(event, { ...edge }); - } + onContextMenu(event, { ...edge }); + } : undefined; const onEdgeMouseEnter = onMouseEnter ? (event: React.MouseEvent) => { - onMouseEnter(event, { ...edge }); - } + onMouseEnter(event, { ...edge }); + } : undefined; const onEdgeMouseMove = onMouseMove ? (event: React.MouseEvent) => { - onMouseMove(event, { ...edge }); - } + onMouseMove(event, { ...edge }); + } : undefined; const onEdgeMouseLeave = onMouseLeave ? (event: React.MouseEvent) => { - onMouseLeave(event, { ...edge }); - } + onMouseLeave(event, { ...edge }); + } : undefined; const onKeyDown = (event: KeyboardEvent) => { diff --git a/packages/react/src/components/Edges/BaseEdge.tsx b/packages/react/src/components/Edges/BaseEdge.tsx index fab06220..4d1ac1da 100644 --- a/packages/react/src/components/Edges/BaseEdge.tsx +++ b/packages/react/src/components/Edges/BaseEdge.tsx @@ -4,8 +4,34 @@ import cc from 'classcat'; import { EdgeText } from './EdgeText'; import type { BaseEdgeProps } from '../../types'; +/** + * The `` component gets used internally for all the edges. It can be + * used inside a custom edge and handles the invisible helper edge and the edge label + * for you. + * + * @public + * @example + * ```jsx + *import { BaseEdge } from '@xyflow/react'; + * + *export function CustomEdge({ sourceX, sourceY, targetX, targetY, ...props }) { + * const [edgePath] = getStraightPath({ + * sourceX, + * sourceY, + * targetX, + * targetY, + * }); + * + * return ; + *} + *``` + * + * @remarks If you want to use an edge marker with the [``](/api-reference/components/base-edge) component, + * you can pass the `markerStart` or `markerEnd` props passed to your custom edge + * through to the [``](/api-reference/components/base-edge) component. + * You can see all the props passed to a custom edge by looking at the [`EdgeProps`](/api-reference/types/edge-props) type. + */ export function BaseEdge({ - id, path, labelX, labelY, @@ -15,23 +41,12 @@ export function BaseEdge({ labelBgStyle, labelBgPadding, labelBgBorderRadius, - style, - markerEnd, - markerStart, - className, interactionWidth = 20, + ...props }: BaseEdgeProps) { return ( <> - + {interactionWidth && ( + * ); + * } + * ``` + */ const BezierEdge = createBezierEdge({ isInternal: false }); + +/** + * @internal + */ const BezierEdgeInternal = createBezierEdge({ isInternal: true }); BezierEdge.displayName = 'BezierEdge'; diff --git a/packages/react/src/components/Edges/EdgeAnchor.tsx b/packages/react/src/components/Edges/EdgeAnchor.tsx index a975eaf5..9ee5f600 100644 --- a/packages/react/src/components/Edges/EdgeAnchor.tsx +++ b/packages/react/src/components/Edges/EdgeAnchor.tsx @@ -27,6 +27,9 @@ export interface EdgeAnchorProps extends SVGAttributes { const EdgeUpdaterClassName = 'react-flow__edgeupdater'; +/** + * @internal + */ export function EdgeAnchor({ position, centerX, diff --git a/packages/react/src/components/Edges/EdgeText.tsx b/packages/react/src/components/Edges/EdgeText.tsx index 2e3ad041..6646f683 100644 --- a/packages/react/src/components/Edges/EdgeText.tsx +++ b/packages/react/src/components/Edges/EdgeText.tsx @@ -8,9 +8,9 @@ function EdgeTextComponent({ x, y, label, - labelStyle = {}, + labelStyle, labelShowBg = true, - labelBgStyle = {}, + labelBgStyle, labelBgPadding = [2, 4], labelBgBorderRadius = 2, children, @@ -34,7 +34,7 @@ function EdgeTextComponent({ } }, [label]); - if (typeof label === 'undefined' || !label) { + if (!label) { return null; } @@ -73,4 +73,30 @@ function EdgeTextComponent({ EdgeTextComponent.displayName = 'EdgeText'; +/** + * You can use the `` component as a helper component to display text + * within your custom edges. + * + * @public + * + * @example + * ```jsx + * import { EdgeText } from '@xyflow/react'; + * + * export function CustomEdgeLabel({ label }) { + * return ( + * + * ); + * } + *``` + */ export const EdgeText = memo(EdgeTextComponent); diff --git a/packages/react/src/components/Edges/SimpleBezierEdge.tsx b/packages/react/src/components/Edges/SimpleBezierEdge.tsx index b3378c48..3d1cdb3d 100644 --- a/packages/react/src/components/Edges/SimpleBezierEdge.tsx +++ b/packages/react/src/components/Edges/SimpleBezierEdge.tsx @@ -7,9 +7,11 @@ import type { SimpleBezierEdgeProps } from '../../types'; export interface GetSimpleBezierPathParams { sourceX: number; sourceY: number; + /** @default Position.Bottom */ sourcePosition?: Position; targetX: number; targetY: number; + /** @default Position.Top */ targetPosition?: Position; } @@ -29,6 +31,19 @@ function getControl({ pos, x1, y1, x2, y2 }: GetControlParams): [number, number] return [x1, 0.5 * (y1 + y2)]; } +/** + * The `getSimpleBezierPath` util returns everything you need to render a simple + * bezier edge between two nodes. + * @public + * @returns + * - `path`: the path to use in an SVG `` element. + * - `labelX`: the `x` position you can use to render a label for this edge. + * - `labelY`: the `y` position you can use to render a label for this edge. + * - `offsetX`: the absolute difference between the source `x` position and the `x` position of the + * middle of this path. + * - `offsetY`: the absolute difference between the source `y` position and the `y` position of the + * middle of this path. + */ export function getSimpleBezierPath({ sourceX, sourceY, @@ -80,8 +95,8 @@ function createSimpleBezierEdge(params: { isInternal: boolean }) { sourceY, targetX, targetY, - sourcePosition = Position.Bottom, - targetPosition = Position.Top, + sourcePosition, + targetPosition, label, labelStyle, labelShowBg, diff --git a/packages/react/src/components/Edges/SmoothStepEdge.tsx b/packages/react/src/components/Edges/SmoothStepEdge.tsx index 1f6bcfd1..8b1475cf 100644 --- a/packages/react/src/components/Edges/SmoothStepEdge.tsx +++ b/packages/react/src/components/Edges/SmoothStepEdge.tsx @@ -62,7 +62,34 @@ function createSmoothStepEdge(params: { isInternal: boolean }) { ); } +/** + * Component that can be used inside a custom edge to render a smooth step edge. + * + * @public + * @example + * + * ```tsx + * import { SmoothStepEdge } from '@xyflow/react'; + * + * function CustomEdge({ sourceX, sourceY, targetX, targetY, sourcePosition, targetPosition }) { + * return ( + * + * ); + * } + * ``` + */ const SmoothStepEdge = createSmoothStepEdge({ isInternal: false }); + +/** + * @internal + */ const SmoothStepEdgeInternal = createSmoothStepEdge({ isInternal: true }); SmoothStepEdge.displayName = 'SmoothStepEdge'; diff --git a/packages/react/src/components/Edges/StepEdge.tsx b/packages/react/src/components/Edges/StepEdge.tsx index 6385e8f0..cd08758c 100644 --- a/packages/react/src/components/Edges/StepEdge.tsx +++ b/packages/react/src/components/Edges/StepEdge.tsx @@ -21,7 +21,34 @@ function createStepEdge(params: { isInternal: boolean }) { }); } +/** + * Component that can be used inside a custom edge to render a step edge. + * + * @public + * @example + * + * ```tsx + * import { StepEdge } from '@xyflow/react'; + * + * function CustomEdge({ sourceX, sourceY, targetX, targetY, sourcePosition, targetPosition }) { + * return ( + * + * ); + * } + * ``` + */ const StepEdge = createStepEdge({ isInternal: false }); + +/** + * @internal + */ const StepEdgeInternal = createStepEdge({ isInternal: true }); StepEdge.displayName = 'StepEdge'; diff --git a/packages/react/src/components/Edges/StraightEdge.tsx b/packages/react/src/components/Edges/StraightEdge.tsx index 53d1ac29..4f4cd76b 100644 --- a/packages/react/src/components/Edges/StraightEdge.tsx +++ b/packages/react/src/components/Edges/StraightEdge.tsx @@ -50,7 +50,32 @@ function createStraightEdge(params: { isInternal: boolean }) { ); } +/** + * Component that can be used inside a custom edge to render a straight line. + * + * @public + * @example + * + * ```tsx + * import { StraightEdge } from '@xyflow/react'; + * + * function CustomEdge({ sourceX, sourceY, targetX, targetY }) { + * return ( + * + * ); + * } + * ``` + */ const StraightEdge = createStraightEdge({ isInternal: false }); + +/** + * @internal + */ const StraightEdgeInternal = createStraightEdge({ isInternal: true }); StraightEdge.displayName = 'StraightEdge'; diff --git a/packages/react/src/components/Edges/index.ts b/packages/react/src/components/Edges/index.ts index 21af9667..88b274eb 100644 --- a/packages/react/src/components/Edges/index.ts +++ b/packages/react/src/components/Edges/index.ts @@ -1,6 +1,8 @@ -// We distinguish between internal and exported edges -// The internal edges are used directly like custom edges and always get an id, source and target props -// If you import an edge from the library, the id is optional and source and target are not used at all +/* + * We distinguish between internal and exported edges + * The internal edges are used directly like custom edges and always get an id, source and target props + * If you import an edge from the library, the id is optional and source and target are not used at all + */ export { SimpleBezierEdge, SimpleBezierEdgeInternal } from './SimpleBezierEdge'; export { SmoothStepEdge, SmoothStepEdgeInternal } from './SmoothStepEdge'; diff --git a/packages/react/src/components/Handle/index.tsx b/packages/react/src/components/Handle/index.tsx index 3ea98530..b2c3373e 100644 --- a/packages/react/src/components/Handle/index.tsx +++ b/packages/react/src/components/Handle/index.tsx @@ -28,10 +28,14 @@ import { useNodeId } from '../../contexts/NodeIdContext'; import { type ReactFlowState } from '../../types'; import { fixedForwardRef } from '../../utils'; -export interface HandleProps extends HandlePropsSystem, Omit, 'id'> { - /** Callback called when connection is made */ - onConnect?: OnConnect; -} +/** + * @expand + */ +export type HandleProps = HandlePropsSystem & + Omit, 'id'> & { + /** Callback called when connection is made */ + onConnect?: OnConnect; + }; const selector = (s: ReactFlowState) => ({ connectOnClick: s.connectOnClick, @@ -42,9 +46,7 @@ const selector = (s: ReactFlowState) => ({ const connectingSelector = (nodeId: string | null, handleId: string | null, type: HandleType) => (state: ReactFlowState) => { const { connectionClickStartHandle: clickHandle, connectionMode, connection } = state; - const { fromHandle, toHandle, isValid } = connection; - const connectingTo = toHandle?.nodeId === nodeId && toHandle?.id === handleId && toHandle?.type === type; return { @@ -56,6 +58,7 @@ const connectingSelector = ? fromHandle?.type !== type : nodeId !== fromHandle?.nodeId || handleId !== fromHandle?.id, connectionInProcess: !!fromHandle, + clickConnectionInProcess: !!clickHandle, valid: connectingTo && isValid, }; }; @@ -83,11 +86,15 @@ function HandleComponent( const store = useStoreApi(); const nodeId = useNodeId(); const { connectOnClick, noPanClassName, rfId } = useStore(selector, shallow); - const { connectingFrom, connectingTo, clickConnecting, isPossibleEndHandle, connectionInProcess, valid } = useStore( - connectingSelector(nodeId, handleId, type), - shallow - ); - + const { + connectingFrom, + connectingTo, + clickConnecting, + isPossibleEndHandle, + connectionInProcess, + clickConnectionInProcess, + valid, + } = useStore(connectingSelector(nodeId, handleId, type), shallow); if (!nodeId) { store.getState().onError?.('010', errorMessages['error010']()); } @@ -175,7 +182,7 @@ function HandleComponent( return; } - const doc = getHostForElement(event.target as HTMLElement); + const doc = getHostForElement(event.target); const isValidConnectionHandler = isValidConnection || isValidConnectionStore; const { connection, isValid } = XYHandle.isValid(event.nativeEvent, { handle: { @@ -228,12 +235,14 @@ function HandleComponent( connectingfrom: connectingFrom, connectingto: connectingTo, valid, - // shows where you can start a connection from - // and where you can end it while connecting + /* + * shows where you can start a connection from + * and where you can end it while connecting + */ connectionindicator: isConnectable && (!connectionInProcess || isPossibleEndHandle) && - (connectionInProcess ? isConnectableEnd : isConnectableStart), + (connectionInProcess || clickConnectionInProcess ? isConnectableEnd : isConnectableStart), }, ])} onMouseDown={onPointerDown} @@ -248,6 +257,28 @@ function HandleComponent( } /** - * The Handle component is a UI element that is used to connect nodes. + * The `` component is used in your [custom nodes](/learn/customization/custom-nodes) + * to define connection points. + * + *@public + * + *@example + * + *```jsx + *import { Handle, Position } from '@xyflow/react'; + * + *export function CustomNode({ data }) { + * return ( + * <> + *
+ * {data.label} + *
+ * + * + * + * + * ); + *}; + *``` */ export const Handle = memo(fixedForwardRef(HandleComponent)); diff --git a/packages/react/src/components/NodeWrapper/index.tsx b/packages/react/src/components/NodeWrapper/index.tsx index 5f528486..5c0ce08b 100644 --- a/packages/react/src/components/NodeWrapper/index.tsx +++ b/packages/react/src/components/NodeWrapper/index.tsx @@ -37,7 +37,6 @@ export function NodeWrapper({ disableKeyboardA11y, rfId, nodeTypes, - nodeExtent, nodeClickDistance, onError, }: NodeWrapperProps) { @@ -109,8 +108,10 @@ export function NodeWrapper({ const { selectNodesOnDrag, nodeDragThreshold } = store.getState(); if (isSelectable && (!selectNodesOnDrag || !isDraggable || nodeDragThreshold > 0)) { - // this handler gets called by XYDrag on drag start when selectNodesOnDrag=true - // here we only need to call it when selectNodesOnDrag=false + /* + * this handler gets called by XYDrag on drag start when selectNodesOnDrag=true + * here we only need to call it when selectNodesOnDrag=false + */ handleNodeClick({ id, store, @@ -138,6 +139,9 @@ export function NodeWrapper({ nodeRef, }); } else if (isDraggable && node.selected && Object.prototype.hasOwnProperty.call(arrowKeyDiffs, event.key)) { + // prevent default scrolling behavior on arrow key press when node is moved + event.preventDefault(); + store.setState({ ariaLiveMessage: `Moved selected node ${event.key .replace('Arrow', '') @@ -199,7 +203,7 @@ export function NodeWrapper({ type={nodeType} positionAbsoluteX={internals.positionAbsolute.x} positionAbsoluteY={internals.positionAbsolute.y} - selected={node.selected} + selected={node.selected ?? false} selectable={isSelectable} draggable={isDraggable} deletable={node.deletable ?? true} diff --git a/packages/react/src/components/NodeWrapper/useNodeObserver.ts b/packages/react/src/components/NodeWrapper/useNodeObserver.ts index 2c2910fd..de1d31e7 100644 --- a/packages/react/src/components/NodeWrapper/useNodeObserver.ts +++ b/packages/react/src/components/NodeWrapper/useNodeObserver.ts @@ -49,8 +49,10 @@ export function useNodeObserver({ useEffect(() => { if (nodeRef.current) { - // when the user programmatically changes the source or handle position, we need to update the internals - // to make sure the edges are updated correctly + /* + * when the user programmatically changes the source or handle position, we need to update the internals + * to make sure the edges are updated correctly + */ const typeChanged = prevType.current !== nodeType; const sourcePosChanged = prevSourcePosition.current !== node.sourcePosition; const targetPosChanged = prevTargetPosition.current !== node.targetPosition; diff --git a/packages/react/src/components/Nodes/utils.ts b/packages/react/src/components/Nodes/utils.ts index 81939bbc..146e05f8 100644 --- a/packages/react/src/components/Nodes/utils.ts +++ b/packages/react/src/components/Nodes/utils.ts @@ -4,10 +4,12 @@ import { errorMessages } from '@xyflow/system'; import type { ReactFlowState } from '../../types'; -// this handler is called by -// 1. the click handler when node is not draggable or selectNodesOnDrag = false -// or -// 2. the on drag start handler when node is draggable and selectNodesOnDrag = true +/* + * this handler is called by + * 1. the click handler when node is not draggable or selectNodesOnDrag = false + * or + * 2. the on drag start handler when node is draggable and selectNodesOnDrag = true + */ export function handleNodeClick({ id, store, diff --git a/packages/react/src/components/NodesSelection/index.tsx b/packages/react/src/components/NodesSelection/index.tsx index e2bcd238..ecaef17b 100644 --- a/packages/react/src/components/NodesSelection/index.tsx +++ b/packages/react/src/components/NodesSelection/index.tsx @@ -68,6 +68,8 @@ export function NodesSelection({ const onKeyDown = (event: KeyboardEvent) => { if (Object.prototype.hasOwnProperty.call(arrowKeyDiffs, event.key)) { + event.preventDefault(); + moveSelectedNodes({ direction: arrowKeyDiffs[event.key], factor: event.shiftKey ? 4 : 1, diff --git a/packages/react/src/components/Panel/index.tsx b/packages/react/src/components/Panel/index.tsx index eeb01a15..16fe7841 100644 --- a/packages/react/src/components/Panel/index.tsx +++ b/packages/react/src/components/Panel/index.tsx @@ -1,31 +1,64 @@ -import type { HTMLAttributes, ReactNode } from 'react'; +import { HTMLAttributes, forwardRef } from 'react'; import cc from 'classcat'; import type { PanelPosition } from '@xyflow/system'; import { useStore } from '../../hooks/useStore'; import type { ReactFlowState } from '../../types'; +/** + * @expand + */ export type PanelProps = HTMLAttributes & { - /** Set position of the panel - * @example 'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right' + /** + * The position of the panel. + * @default "top-left" */ position?: PanelPosition; - children: ReactNode; }; const selector = (s: ReactFlowState) => (s.userSelectionActive ? 'none' : 'all'); -export function Panel({ position = 'top-left', children, className, style, ...rest }: PanelProps) { - const pointerEvents = useStore(selector); - const positionClasses = `${position}`.split('-'); +/** + * The `` component helps you position content above the viewport. + * It is used internally by the [``](/api-reference/components/minimap) + * and [``](/api-reference/components/controls) components. + * + * @public + * + * @example + * ```jsx + *import { ReactFlow, Background, Panel } from '@xyflow/react'; + * + *export default function Flow() { + * return ( + * + * top-left + * top-center + * top-right + * bottom-left + * bottom-center + * bottom-right + * + * ); + *} + *``` + */ +export const Panel = forwardRef( + ({ position = 'top-left', children, className, style, ...rest }, ref) => { + const pointerEvents = useStore(selector); + const positionClasses = `${position}`.split('-'); - return ( -
- {children} -
- ); -} + return ( +
+ {children} +
+ ); + } +); + +Panel.displayName = 'Panel'; diff --git a/packages/react/src/components/ReactFlowProvider/index.tsx b/packages/react/src/components/ReactFlowProvider/index.tsx index b29f5f17..11a9c6b8 100644 --- a/packages/react/src/components/ReactFlowProvider/index.tsx +++ b/packages/react/src/components/ReactFlowProvider/index.tsx @@ -3,22 +3,87 @@ import { useState, type ReactNode } from 'react'; import { Provider } from '../../contexts/StoreContext'; import { createStore } from '../../store'; import { BatchProvider } from '../BatchProvider'; -import type { Node, Edge } from '../../types'; +import type { Node, Edge, FitViewOptions } from '../../types'; import { CoordinateExtent, NodeOrigin } from '@xyflow/system'; export type ReactFlowProviderProps = { + /** These nodes are used to initialize the flow. They are not dynamic. */ initialNodes?: Node[]; + /** These edges are used to initialize the flow. They are not dynamic. */ initialEdges?: Edge[]; + /** These nodes are used to initialize the flow. They are not dynamic. */ defaultNodes?: Node[]; + /** These edges are used to initialize the flow. They are not dynamic. */ defaultEdges?: Edge[]; + /** The initial width is necessary to be able to use fitView on the server */ initialWidth?: number; + /** The initial height is necessary to be able to use fitView on the server */ initialHeight?: number; + /** When `true`, the flow will be zoomed and panned to fit all the nodes initially provided. */ fitView?: boolean; + /** + * You can provide an object of options to customize the initial fitView behavior. + */ + initialFitViewOptions?: FitViewOptions; + /** Initial minimum zoom level */ + initialMinZoom?: number; + /** Initial maximum zoom level */ + initialMaxZoom?: number; + /** + * The origin of the node to use when placing it in the flow or looking up its `x` and `y` + * position. An origin of `[0, 0]` means that a node's top left corner will be placed at the `x` + * and `y` position. + * @default [0, 0] + * @example + * [0, 0] // default, top left + * [0.5, 0.5] // center + * [1, 1] // bottom right + */ nodeOrigin?: NodeOrigin; + /** + * By default, nodes can be placed on an infinite flow. You can use this prop to set a boundary. + * + * The first pair of coordinates is the top left boundary and the second pair is the bottom right. + * @example [[-1000, -10000], [1000, 1000]] + */ nodeExtent?: CoordinateExtent; children: ReactNode; }; +/** + * The `` component is a [context provider](https://react.dev/learn/passing-data-deeply-with-context#) + * that makes it possible to access a flow's internal state outside of the + * [``](/api-reference/react-flow) component. Many of the hooks we + * provide rely on this component to work. + * @public + * + * @example + * ```tsx + *import { ReactFlow, ReactFlowProvider, useNodes } from '@xyflow/react' + * + *export default function Flow() { + * return ( + * + * + * + * + * ); + *} + * + *function Sidebar() { + * // This hook will only work if the component it's used in is a child of a + * // . + * const nodes = useNodes() + * + * return ; + *} + *``` + * + * @remarks If you're using a router and want your flow's state to persist across routes, + * it's vital that you place the `` component _outside_ of + * your router. If you have multiple flows on the same page you will need to use a separate + * `` for each flow. + */ export function ReactFlowProvider({ initialNodes: nodes, initialEdges: edges, @@ -26,6 +91,9 @@ export function ReactFlowProvider({ defaultEdges, initialWidth: width, initialHeight: height, + initialMinZoom: minZoom, + initialMaxZoom: maxZoom, + initialFitViewOptions: fitViewOptions, fitView, nodeOrigin, nodeExtent, @@ -40,6 +108,9 @@ export function ReactFlowProvider({ width, height, fitView, + minZoom, + maxZoom, + fitViewOptions, nodeOrigin, nodeExtent, }) diff --git a/packages/react/src/components/SelectionListener/index.tsx b/packages/react/src/components/SelectionListener/index.tsx index 6a9a03a6..89ed2c26 100644 --- a/packages/react/src/components/SelectionListener/index.tsx +++ b/packages/react/src/components/SelectionListener/index.tsx @@ -10,8 +10,8 @@ import { shallow } from 'zustand/shallow'; import { useStore, useStoreApi } from '../../hooks/useStore'; import type { ReactFlowState, OnSelectionChangeFunc, Node, Edge } from '../../types'; -type SelectionListenerProps = { - onSelectionChange?: OnSelectionChangeFunc; +type SelectionListenerProps = { + onSelectionChange?: OnSelectionChangeFunc; }; const selector = (s: ReactFlowState) => { @@ -44,12 +44,14 @@ function areEqual(a: SelectorSlice, b: SelectorSlice) { ); } -function SelectionListenerInner({ onSelectionChange }: SelectionListenerProps) { - const store = useStoreApi(); +function SelectionListenerInner({ + onSelectionChange, +}: SelectionListenerProps) { + const store = useStoreApi(); const { selectedNodes, selectedEdges } = useStore(selector, areEqual); useEffect(() => { - const params = { nodes: selectedNodes, edges: selectedEdges }; + const params = { nodes: selectedNodes as NodeType[], edges: selectedEdges as EdgeType[] }; onSelectionChange?.(params); store.getState().onSelectionChangeHandlers.forEach((fn) => fn(params)); @@ -60,11 +62,13 @@ function SelectionListenerInner({ onSelectionChange }: SelectionListenerProps) { const changeSelector = (s: ReactFlowState) => !!s.onSelectionChangeHandlers; -export function SelectionListener({ onSelectionChange }: SelectionListenerProps) { +export function SelectionListener({ + onSelectionChange, +}: SelectionListenerProps) { const storeHasSelectionChangeHandlers = useStore(changeSelector); if (onSelectionChange || storeHasSelectionChangeHandlers) { - return ; + return onSelectionChange={onSelectionChange} />; } return null; diff --git a/packages/react/src/components/StoreUpdater/index.tsx b/packages/react/src/components/StoreUpdater/index.tsx index cac8df0e..5cad0358 100644 --- a/packages/react/src/components/StoreUpdater/index.tsx +++ b/packages/react/src/components/StoreUpdater/index.tsx @@ -11,7 +11,7 @@ import { useStore, useStoreApi } from '../../hooks/useStore'; import type { Node, Edge, ReactFlowState, ReactFlowProps, FitViewOptions } from '../../types'; import { defaultNodeOrigin } from '../../container/ReactFlow/init-values'; -// these fields exist in the global store and we need to keep them up to date +// These fields exist in the global store, and we need to keep them up to date const reactFlowFieldsToTrack = [ 'nodes', 'edges', @@ -94,9 +94,11 @@ const selector = (s: ReactFlowState) => ({ }); const initPrevValues = { - // these are values that are also passed directly to other components - // than the StoreUpdater. We can reduce the number of setStore calls - // by setting the same values here as prev fields. + /* + * these are values that are also passed directly to other components + * than the StoreUpdater. We can reduce the number of setStore calls + * by setting the same values here as prev fields. + */ translateExtent: infiniteExtent, nodeOrigin: defaultNodeOrigin, minZoom: 0.5, @@ -152,8 +154,8 @@ export function StoreUpdater s.domNode?.querySelector('.react-flow__viewport-portal'); +/** + * The `` component can be used to add components to the same viewport + * of the flow where nodes and edges are rendered. This is useful when you want to render + * your own components that are adhere to the same coordinate system as the nodes & edges + * and are also affected by zooming and panning + * @public + * @example + * + * ```jsx + *import React from 'react'; + *import { ViewportPortal } from '@xyflow/react'; + * + *export default function () { + * return ( + * + *
+ * This div is positioned at [100, 100] on the flow. + *
+ *
+ * ); + *} + *``` + */ export function ViewportPortal({ children }: { children: ReactNode }) { const viewPortalDiv = useStore(selector); diff --git a/packages/react/src/container/EdgeRenderer/MarkerDefinitions.tsx b/packages/react/src/container/EdgeRenderer/MarkerDefinitions.tsx index 5b6194c5..1c12775d 100644 --- a/packages/react/src/container/EdgeRenderer/MarkerDefinitions.tsx +++ b/packages/react/src/container/EdgeRenderer/MarkerDefinitions.tsx @@ -42,9 +42,11 @@ const Marker = ({ ); }; -// when you have multiple flows on a page and you hide the first one, the other ones have no markers anymore -// when they do have markers with the same ids. To prevent this the user can pass a unique id to the react flow wrapper -// that we can then use for creating our unique marker ids +/* + * when you have multiple flows on a page and you hide the first one, the other ones have no markers anymore + * when they do have markers with the same ids. To prevent this the user can pass a unique id to the react flow wrapper + * that we can then use for creating our unique marker ids + */ const MarkerDefinitions = ({ defaultColor, rfId }: MarkerDefinitionsProps) => { const edges = useStore((s) => s.edges); const defaultEdgeOptions = useStore((s) => s.defaultEdgeOptions); @@ -65,7 +67,7 @@ const MarkerDefinitions = ({ defaultColor, rfId }: MarkerDefinitionsProps) => { } return ( - +
( width={width} height={height} fitView={fitView} + fitViewOptions={fitViewOptions} + minZoom={minZoom} + maxZoom={maxZoom} nodeOrigin={nodeOrigin} nodeExtent={nodeExtent} > @@ -292,7 +306,7 @@ function ReactFlow( paneClickDistance={paneClickDistance} debug={debug} /> - + onSelectionChange={onSelectionChange} /> {children} @@ -301,4 +315,24 @@ function ReactFlow( ); } +/** + * The `` component is the heart of your React Flow application. + * It renders your nodes and edges and handles user interaction + * + * @public + * + * @example + * ```tsx + *import { ReactFlow } from '@xyflow/react' + * + *export default function Flow() { + * return (); + *} + *``` + */ export default fixedForwardRef(ReactFlow); diff --git a/packages/react/src/container/ZoomPane/index.tsx b/packages/react/src/container/ZoomPane/index.tsx index 35fbb248..61af78e6 100644 --- a/packages/react/src/container/ZoomPane/index.tsx +++ b/packages/react/src/container/ZoomPane/index.tsx @@ -95,7 +95,7 @@ export function ZoomPane({ }, }); - const { x, y, zoom } = panZoom.current!.getViewport(); + const { x, y, zoom } = panZoom.current.getViewport(); store.setState({ panZoom: panZoom.current, diff --git a/packages/react/src/contexts/NodeIdContext.ts b/packages/react/src/contexts/NodeIdContext.ts index 28a14b78..4fdef045 100644 --- a/packages/react/src/contexts/NodeIdContext.ts +++ b/packages/react/src/contexts/NodeIdContext.ts @@ -4,6 +4,34 @@ export const NodeIdContext = createContext(null); export const Provider = NodeIdContext.Provider; export const Consumer = NodeIdContext.Consumer; +/** + * You can use this hook to get the id of the node it is used inside. It is useful + * if you need the node's id deeper in the render tree but don't want to manually + * drill down the id as a prop. + * + * @public + * @returns The id for a node in the flow. + * + * @example + *```jsx + *import { useNodeId } from '@xyflow/react'; + * + *export default function CustomNode() { + * return ( + *
+ * This node has an id of + * + *
+ * ); + *} + * + *function NodeIdDisplay() { + * const nodeId = useNodeId(); + * + * return {nodeId}; + *} + *``` + */ export const useNodeId = (): string | null => { const nodeId = useContext(NodeIdContext); return nodeId; diff --git a/packages/react/src/hooks/useConnection.ts b/packages/react/src/hooks/useConnection.ts index 94db5140..7c748003 100644 --- a/packages/react/src/hooks/useConnection.ts +++ b/packages/react/src/hooks/useConnection.ts @@ -24,9 +24,32 @@ function getSelector {connection ? `Someone is trying to make a connection from ${connection.fromNode} to this one.` : 'There are currently no incoming connections!'} + * + *
+ * ); + * } + * ``` + * * @returns ConnectionState */ export function useConnection>>( diff --git a/packages/react/src/hooks/useEdges.ts b/packages/react/src/hooks/useEdges.ts index 3ed9de39..46db9939 100644 --- a/packages/react/src/hooks/useEdges.ts +++ b/packages/react/src/hooks/useEdges.ts @@ -6,10 +6,22 @@ import type { Edge, ReactFlowState } from '../types'; const edgesSelector = (state: ReactFlowState) => state.edges; /** - * Hook for getting the current edges from the store. + * This hook returns an array of the current edges. Components that use this hook + * will re-render **whenever any edge changes**. * * @public - * @returns An array of edges + * @returns An array of all edges currently in the flow. + * + * @example + * ```tsx + *import { useEdges } from '@xyflow/react'; + * + *export default function () { + * const edges = useEdges(); + * + * return
There are currently {edges.length} edges!
; + *} + *``` */ export function useEdges(): EdgeType[] { const edges = useStore(edgesSelector, shallow) as EdgeType[]; diff --git a/packages/react/src/hooks/useGlobalKeyHandler.ts b/packages/react/src/hooks/useGlobalKeyHandler.ts index 3e350a6a..57e74dbb 100644 --- a/packages/react/src/hooks/useGlobalKeyHandler.ts +++ b/packages/react/src/hooks/useGlobalKeyHandler.ts @@ -2,13 +2,12 @@ import { useEffect } from 'react'; import type { KeyCode } from '@xyflow/system'; import { useStoreApi } from '../hooks/useStore'; -import { useKeyPress, UseKeyPressOptions } from './useKeyPress'; +import { useKeyPress } from './useKeyPress'; import { useReactFlow } from './useReactFlow'; import { Edge, Node } from '../types'; const selected = (item: Node | Edge) => item.selected; -const deleteKeyOptions: UseKeyPressOptions = { actInsideInputWithModifier: false }; const win = typeof window !== 'undefined' ? window : undefined; /** @@ -26,7 +25,7 @@ export function useGlobalKeyHandler({ const store = useStoreApi(); const { deleteElements } = useReactFlow(); - const deleteKeyPressed = useKeyPress(deleteKeyCode, deleteKeyOptions); + const deleteKeyPressed = useKeyPress(deleteKeyCode, { actInsideInputWithModifier: false }); const multiSelectionKeyPressed = useKeyPress(multiSelectionKeyCode, { target: win }); useEffect(() => { diff --git a/packages/react/src/hooks/useHandleConnections.ts b/packages/react/src/hooks/useHandleConnections.ts index 20b65a2d..a53eb2c7 100644 --- a/packages/react/src/hooks/useHandleConnections.ts +++ b/packages/react/src/hooks/useHandleConnections.ts @@ -10,11 +10,16 @@ import { import { useStore } from './useStore'; import { useNodeId } from '../contexts/NodeIdContext'; -type useHandleConnectionsParams = { +type UseHandleConnectionsParams = { + /** What type of handle connections do you want to observe? */ type: HandleType; + /** The handle id (this is only needed if the node has multiple handles of the same type). */ id?: string | null; + /** If node id is not provided, the node id from the `NodeIdContext` is used. */ nodeId?: string; + /** Gets called when a connection is established. */ onConnect?: (connections: Connection[]) => void; + /** Gets called when a connection is removed. */ onDisconnect?: (connections: Connection[]) => void; }; @@ -22,27 +27,27 @@ type useHandleConnectionsParams = { * Hook to check if a is connected to another and get the connections. * * @public - * @param param.type - handle type 'source' or 'target' - * @param param.nodeId - node id - if not provided, the node id from the NodeIdContext is used - * @param param.id - the handle id (this is only needed if the node has multiple handles of the same type) - * @param param.onConnect - gets called when a connection is established - * @param param.onDisconnect - gets called when a connection is removed - * @returns an array with handle connections + * @deprecated Use `useNodeConnections` instead. + * @returns An array with handle connections. */ export function useHandleConnections({ type, - id = null, + id, nodeId, onConnect, onDisconnect, -}: useHandleConnectionsParams): HandleConnection[] { +}: UseHandleConnectionsParams): HandleConnection[] { + console.warn( + '[DEPRECATED] `useHandleConnections` is deprecated. Instead use `useNodeConnections` https://reactflow.dev/api-reference/hooks/useNodeConnections' + ); + const _nodeId = useNodeId(); const currentNodeId = nodeId ?? _nodeId; const prevConnections = useRef | null>(null); const connections = useStore( - (state) => state.connectionLookup.get(`${currentNodeId}-${type}-${id}`), + (state) => state.connectionLookup.get(`${currentNodeId}-${type}${id ? `-${id}` : ''}`), areConnectionMapsEqual ); diff --git a/packages/react/src/hooks/useInternalNode.ts b/packages/react/src/hooks/useInternalNode.ts index d16d697b..a4678aaa 100644 --- a/packages/react/src/hooks/useInternalNode.ts +++ b/packages/react/src/hooks/useInternalNode.ts @@ -5,11 +5,31 @@ import { useStore } from './useStore'; import type { InternalNode, Node } from '../types'; /** - * Hook for getting an internal node by id + * This hook returns the internal representation of a specific node. + * Components that use this hook will re-render **whenever the node changes**, + * including when a node is selected or moved. * * @public - * @param id - id of the node - * @returns array with visible node ids + * @param id - The ID of a node you want to observe. + * @returns The `InternalNode` object for the node with the given ID. + * + * @example + * ```tsx + *import { useInternalNode } from '@xyflow/react'; + * + *export default function () { + * const internalNode = useInternalNode('node-1'); + * const absolutePosition = internalNode.internals.positionAbsolute; + * + * return ( + *
+ * The absolute position of the node is at: + *

x: {absolutePosition.x}

+ *

y: {absolutePosition.y}

+ *
+ * ); + *} + *``` */ export function useInternalNode(id: string): InternalNode | undefined { const node = useStore( diff --git a/packages/react/src/hooks/useKeyPress.ts b/packages/react/src/hooks/useKeyPress.ts index beca4000..a0d44053 100644 --- a/packages/react/src/hooks/useKeyPress.ts +++ b/packages/react/src/hooks/useKeyPress.ts @@ -6,25 +6,58 @@ type PressedKeys = Set; type KeyOrCode = 'key' | 'code'; export type UseKeyPressOptions = { + /** + * Listen to key presses on a specific element. + * @default document + */ target?: Window | Document | HTMLElement | ShadowRoot | null; + /** + * You can use this flag to prevent triggering the key press hook when an input field is focused. + * @default true + */ actInsideInputWithModifier?: boolean; + preventDefault?: boolean; }; const defaultDoc = typeof document !== 'undefined' ? document : null; /** - * Hook for handling key events. + * This hook lets you listen for specific key codes and tells you whether they are + * currently pressed or not. * * @public - * @param param.keyCode - The key code (string or array of strings) to use - * @param param.options - Options - * @returns boolean + * @param options - Options + * + * @example + * ```tsx + *import { useKeyPress } from '@xyflow/react'; + * + *export default function () { + * const spacePressed = useKeyPress('Space'); + * const cmdAndSPressed = useKeyPress(['Meta+s', 'Strg+s']); + * + * return ( + *
+ * {spacePressed &&

Space pressed!

} + * {cmdAndSPressed &&

Cmd + S pressed!

} + *
+ * ); + *} + *``` */ export function useKeyPress( - // the keycode can be a string 'a' or an array of strings ['a', 'a+d'] - // a string means a single key 'a' or a combination when '+' is used 'a+d' - // an array means different possibilites. Explainer: ['a', 'd+s'] here the - // user can use the single key 'a' or the combination 'd' + 's' + /** + * The key code (string or array of strings) specifies which key(s) should trigger + * an action. + * + * A **string** can represent: + * - A **single key**, e.g. `'a'` + * - A **key combination**, using `'+'` to separate keys, e.g. `'a+d'` + * + * An **array of strings** represents **multiple possible key inputs**. For example, `['a', 'd+s']` + * means the user can press either the single key `'a'` or the combination of `'d'` and `'s'`. + * @default null + */ keyCode: KeyCode | null = null, options: UseKeyPressOptions = { target: defaultDoc, actInsideInputWithModifier: true } ): boolean { @@ -36,16 +69,25 @@ export function useKeyPress( // we need to remember the pressed keys in order to support combinations const pressedKeys = useRef(new Set([])); - // keyCodes = array with single keys [['a']] or key combinations [['a', 's']] - // keysToWatch = array with all keys flattened ['a', 'd', 'ShiftLeft'] - // used to check if we store event.code or event.key. When the code is in the list of keysToWatch - // we use the code otherwise the key. Explainer: When you press the left "command" key, the code is "MetaLeft" - // and the key is "Meta". We want users to be able to pass keys and codes so we assume that the key is meant when - // we can't find it in the list of keysToWatch. + /* + * keyCodes = array with single keys [['a']] or key combinations [['a', 's']] + * keysToWatch = array with all keys flattened ['a', 'd', 'ShiftLeft'] + * used to check if we store event.code or event.key. When the code is in the list of keysToWatch + * we use the code otherwise the key. Explainer: When you press the left "command" key, the code is "MetaLeft" + * and the key is "Meta". We want users to be able to pass keys and codes so we assume that the key is meant when + * we can't find it in the list of keysToWatch. + */ const [keyCodes, keysToWatch] = useMemo<[Array, Keys]>(() => { if (keyCode !== null) { const keyCodeArr = Array.isArray(keyCode) ? keyCode : [keyCode]; - const keys = keyCodeArr.filter((kc) => typeof kc === 'string').map((kc) => kc.split('+')); + const keys = keyCodeArr + .filter((kc) => typeof kc === 'string') + /* + * we first replace all '+' with '\n' which we will use to split the keys on + * then we replace '\n\n' with '\n+', this way we can also support the combination 'key++' + * in the end we simply split on '\n' to get the key array + */ + .map((kc) => kc.replace('+', '\n').replace('\n\n', '\n+').split('\n')); const keysFlat = keys.reduce((res: Keys, item) => res.concat(...item), []); return [keys, keysFlat]; @@ -55,13 +97,14 @@ export function useKeyPress( }, [keyCode]); useEffect(() => { - const target = options?.target || defaultDoc; + const target = options?.target ?? defaultDoc; + const actInsideInputWithModifier = options?.actInsideInputWithModifier ?? true; if (keyCode !== null) { const downHandler = (event: KeyboardEvent) => { - modifierPressed.current = event.ctrlKey || event.metaKey || event.shiftKey; + modifierPressed.current = event.ctrlKey || event.metaKey || event.shiftKey || event.altKey; const preventAction = - (!modifierPressed.current || (modifierPressed.current && !options.actInsideInputWithModifier)) && + (!modifierPressed.current || (modifierPressed.current && !actInsideInputWithModifier)) && isInputDOMNode(event); if (preventAction) { @@ -71,19 +114,18 @@ export function useKeyPress( pressedKeys.current.add(event[keyOrCode]); if (isMatchingKey(keyCodes, pressedKeys.current, false)) { - event.preventDefault(); + const target = (event.composedPath?.()?.[0] || event.target) as Element | null; + const isInteractiveElement = target?.nodeName === 'BUTTON' || target?.nodeName === 'A'; + + if (options.preventDefault !== false && (modifierPressed.current || !isInteractiveElement)) { + event.preventDefault(); + } + setKeyPressed(true); } }; const upHandler = (event: KeyboardEvent) => { - const preventAction = - (!modifierPressed.current || (modifierPressed.current && !options.actInsideInputWithModifier)) && - isInputDOMNode(event); - - if (preventAction) { - return false; - } const keyOrCode = useKeyOrCode(event.code, keysToWatch); if (isMatchingKey(keyCodes, pressedKeys.current, true)) { @@ -128,12 +170,16 @@ export function useKeyPress( function isMatchingKey(keyCodes: Array, pressedKeys: PressedKeys, isUp: boolean): boolean { return ( keyCodes - // we only want to compare same sizes of keyCode definitions - // and pressed keys. When the user specified 'Meta' as a key somewhere - // this would also be truthy without this filter when user presses 'Meta' + 'r' + /* + * we only want to compare same sizes of keyCode definitions + * and pressed keys. When the user specified 'Meta' as a key somewhere + * this would also be truthy without this filter when user presses 'Meta' + 'r' + */ .filter((keys) => isUp || keys.length === pressedKeys.size) - // since we want to support multiple possibilities only one of the - // combinations need to be part of the pressed keys + /* + * since we want to support multiple possibilities only one of the + * combinations need to be part of the pressed keys + */ .some((keys) => keys.every((k) => pressedKeys.has(k))) ); } diff --git a/packages/react/src/hooks/useMoveSelectedNodes.ts b/packages/react/src/hooks/useMoveSelectedNodes.ts index 52503c04..0b2dacc9 100644 --- a/packages/react/src/hooks/useMoveSelectedNodes.ts +++ b/packages/react/src/hooks/useMoveSelectedNodes.ts @@ -22,8 +22,10 @@ export function useMoveSelectedNodes() { const nodeUpdates = new Map(); const isSelected = selectedAndDraggable(nodesDraggable); - // by default a node moves 5px on each key press - // if snap grid is enabled, we use that for the velocity + /* + * by default a node moves 5px on each key press + * if snap grid is enabled, we use that for the velocity + */ const xVelo = snapToGrid ? snapGrid[0] : 5; const yVelo = snapToGrid ? snapGrid[1] : 5; diff --git a/packages/react/src/hooks/useNodeConnections.ts b/packages/react/src/hooks/useNodeConnections.ts new file mode 100644 index 00000000..6a8155ab --- /dev/null +++ b/packages/react/src/hooks/useNodeConnections.ts @@ -0,0 +1,87 @@ +import { useEffect, useMemo, useRef } from 'react'; +import { + Connection, + NodeConnection, + HandleType, + areConnectionMapsEqual, + handleConnectionChange, + errorMessages, +} from '@xyflow/system'; + +import { useStore } from './useStore'; +import { useNodeId } from '../contexts/NodeIdContext'; + +const error014 = errorMessages['error014'](); + +type UseNodeConnectionsParams = { + /** ID of the node, filled in automatically if used inside custom node. */ + id?: string; + /** What type of handle connections do you want to observe? */ + handleType?: HandleType; + /** Filter by handle id (this is only needed if the node has multiple handles of the same type). */ + handleId?: string; + /** Gets called when a connection is established. */ + onConnect?: (connections: Connection[]) => void; + /** Gets called when a connection is removed. */ + onDisconnect?: (connections: Connection[]) => void; +}; + +/** + * This hook returns an array of connections on a specific node, handle type ('source', 'target') or handle ID. + * + * @public + * @returns An array with connections. + * + * @example + * ```jsx + *import { useNodeConnections } from '@xyflow/react'; + * + *export default function () { + * const connections = useNodeConnections({ + * handleType: 'target', + * handleId: 'my-handle', + * }); + * + * return ( + *
There are currently {connections.length} incoming connections!
+ * ); + *} + *``` + */ +export function useNodeConnections({ + id, + handleType, + handleId, + onConnect, + onDisconnect, +}: UseNodeConnectionsParams = {}): NodeConnection[] { + const nodeId = useNodeId(); + const currentNodeId = id ?? nodeId; + + if (!currentNodeId) { + throw new Error(error014); + } + + const prevConnections = useRef | null>(null); + + const connections = useStore( + (state) => + state.connectionLookup.get( + `${currentNodeId}${handleType ? (handleId ? `-${handleType}-${handleId}` : `-${handleType}`) : ''}` + ), + areConnectionMapsEqual + ); + + useEffect(() => { + // @todo discuss if onConnect/onDisconnect should be called when the component mounts/unmounts + if (prevConnections.current && prevConnections.current !== connections) { + const _connections = connections ?? new Map(); + handleConnectionChange(prevConnections.current, _connections, onDisconnect); + handleConnectionChange(_connections, prevConnections.current, onConnect); + } + + prevConnections.current = connections ?? new Map(); + }, [connections, onConnect, onDisconnect]); + + return useMemo(() => Array.from(connections?.values() ?? []), [connections]); +} diff --git a/packages/react/src/hooks/useNodes.ts b/packages/react/src/hooks/useNodes.ts index 5e558e90..6166327d 100644 --- a/packages/react/src/hooks/useNodes.ts +++ b/packages/react/src/hooks/useNodes.ts @@ -6,10 +6,23 @@ import type { Node, ReactFlowState } from '../types'; const nodesSelector = (state: ReactFlowState) => state.nodes; /** - * Hook for getting the current nodes from the store. + * This hook returns an array of the current nodes. Components that use this hook + * will re-render **whenever any node changes**, including when a node is selected + * or moved. * * @public - * @returns An array of nodes + * @returns An array of all nodes currently in the flow. + * + * @example + * ```jsx + *import { useNodes } from '@xyflow/react'; + * + *export default function() { + * const nodes = useNodes(); + * + * return
There are currently {nodes.length} nodes!
; + *} + *``` */ export function useNodes(): NodeType[] { const nodes = useStore(nodesSelector, shallow) as NodeType[]; diff --git a/packages/react/src/hooks/useNodesData.ts b/packages/react/src/hooks/useNodesData.ts index 7472cbf7..6503f405 100644 --- a/packages/react/src/hooks/useNodesData.ts +++ b/packages/react/src/hooks/useNodesData.ts @@ -5,17 +5,31 @@ import { useStore } from '../hooks/useStore'; import type { Node } from '../types'; /** - * Hook for receiving data of one or multiple nodes + * This hook lets you subscribe to changes of a specific nodes `data` object. * * @public - * @param nodeId - The id (or ids) of the node to get the data from - * @param guard - Optional guard function to narrow down the node type - * @returns An object (or array of object) with {id, type, data} representing each node + * @returns An object (or array of object) with `id`, `type`, `data` representing each node. + * + * @example + *```jsx + *import { useNodesData } from '@xyflow/react'; + * + *export default function() { + * const nodeData = useNodesData('nodeId-1'); + * const nodesData = useNodesData(['nodeId-1', 'nodeId-2']); + * + * return null; + *} + *``` */ export function useNodesData( + /** The id of the node to get the data from. */ nodeId: string ): Pick | null; -export function useNodesData(nodeIds: string[]): Pick[]; +export function useNodesData( + /** The ids of the nodes to get the data from. */ + nodeIds: string[] +): Pick[]; // eslint-disable-next-line @typescript-eslint/no-explicit-any export function useNodesData(nodeIds: any): any { const nodesData = useStore( diff --git a/packages/react/src/hooks/useNodesEdgesState.ts b/packages/react/src/hooks/useNodesEdgesState.ts index fae87c02..21d9d108 100644 --- a/packages/react/src/hooks/useNodesEdgesState.ts +++ b/packages/react/src/hooks/useNodesEdgesState.ts @@ -4,15 +4,58 @@ import { applyNodeChanges, applyEdgeChanges } from '../utils/changes'; import type { Node, Edge, OnNodesChange, OnEdgesChange } from '../types'; /** - * Hook for managing the state of nodes - should only be used for prototyping / simple use cases. + * This hook makes it easy to prototype a controlled flow where you manage the + * state of nodes and edges outside the `ReactFlowInstance`. You can think of it + * like React's `useState` hook with an additional helper callback. * * @public - * @param initialNodes - * @returns an array [nodes, setNodes, onNodesChange] + * @returns + * - `nodes`: The current array of nodes. You might pass this directly to the `nodes` prop of your + * `` component, or you may want to manipulate it first to perform some layouting, + * for example. + * - `setNodes`: A function that you can use to update the nodes. You can pass it a new array of + * nodes or a callback that receives the current array of nodes and returns a new array of nodes. + * This is the same as the second element of the tuple returned by React's `useState` hook. + * - `onNodesChange`: A handy callback that can take an array of `NodeChanges` and update the nodes + * state accordingly. You'll typically pass this directly to the `onNodesChange` prop of your + * `` component. + * @example + * + *```tsx + *import { ReactFlow, useNodesState, useEdgesState } from '@xyflow/react'; + * + *const initialNodes = []; + *const initialEdges = []; + * + *export default function () { + * const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes); + * const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges); + * + * return ( + * + * ); + *} + *``` + * + * @remarks This hook was created to make prototyping easier and our documentation + * examples clearer. Although it is OK to use this hook in production, in + * practice you may want to use a more sophisticated state management solution + * like Zustand {@link https://reactflow.dev/docs/guides/state-management/} instead. + * */ export function useNodesState( initialNodes: NodeType[] -): [NodeType[], Dispatch>, OnNodesChange] { +): [ + // + nodes: NodeType[], + setNodes: Dispatch>, + onNodesChange: OnNodesChange +] { const [nodes, setNodes] = useState(initialNodes); const onNodesChange: OnNodesChange = useCallback( (changes) => setNodes((nds) => applyNodeChanges(changes, nds)), @@ -23,15 +66,60 @@ export function useNodesState( } /** - * Hook for managing the state of edges - should only be used for prototyping / simple use cases. + * This hook makes it easy to prototype a controlled flow where you manage the + * state of nodes and edges outside the `ReactFlowInstance`. You can think of it + * like React's `useState` hook with an additional helper callback. * * @public - * @param initialEdges - * @returns an array [edges, setEdges, onEdgesChange] + * @returns + * - `edges`: The current array of edges. You might pass this directly to the `edges` prop of your + * `` component, or you may want to manipulate it first to perform some layouting, + * for example. + * + * - `setEdges`: A function that you can use to update the edges. You can pass it a new array of + * edges or a callback that receives the current array of edges and returns a new array of edges. + * This is the same as the second element of the tuple returned by React's `useState` hook. + * + * - `onEdgesChange`: A handy callback that can take an array of `EdgeChanges` and update the edges + * state accordingly. You'll typically pass this directly to the `onEdgesChange` prop of your + * `` component. + * @example + * + *```tsx + *import { ReactFlow, useNodesState, useEdgesState } from '@xyflow/react'; + * + *const initialNodes = []; + *const initialEdges = []; + * + *export default function () { + * const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes); + * const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges); + * + * return ( + * + * ); + *} + *``` + * + * @remarks This hook was created to make prototyping easier and our documentation + * examples clearer. Although it is OK to use this hook in production, in + * practice you may want to use a more sophisticated state management solution + * like Zustand {@link https://reactflow.dev/docs/guides/state-management/} instead. + * */ export function useEdgesState( initialEdges: EdgeType[] -): [EdgeType[], Dispatch>, OnEdgesChange] { +): [ + // + edges: EdgeType[], + setEdges: Dispatch>, + onEdgesChange: OnEdgesChange +] { const [edges, setEdges] = useState(initialEdges); const onEdgesChange: OnEdgesChange = useCallback( (changes) => setEdges((eds) => applyEdgeChanges(changes, eds)), diff --git a/packages/react/src/hooks/useNodesInitialized.ts b/packages/react/src/hooks/useNodesInitialized.ts index 98bfa6b7..6fd042a5 100644 --- a/packages/react/src/hooks/useNodesInitialized.ts +++ b/packages/react/src/hooks/useNodesInitialized.ts @@ -1,39 +1,69 @@ -import { useStore } from './useStore'; -import type { ReactFlowState } from '../types'; import { nodeHasDimensions } from '@xyflow/system'; +import { useStore } from './useStore'; +import type { ReactFlowState } from '../types'; + export type UseNodesInitializedOptions = { + /** @default false */ includeHiddenNodes?: boolean; }; const selector = (options: UseNodesInitializedOptions) => (s: ReactFlowState) => { + if (!options.includeHiddenNodes) { + return s.nodesInitialized; + } + if (s.nodeLookup.size === 0) { return false; } - for (const [, { hidden, internals }] of s.nodeLookup) { - if (options.includeHiddenNodes || !hidden) { - if (internals.handleBounds === undefined || !nodeHasDimensions(internals.userNode)) { - return false; - } + for (const [, { internals }] of s.nodeLookup) { + if (internals.handleBounds === undefined || !nodeHasDimensions(internals.userNode)) { + return false; } } return true; }; -const defaultOptions = { - includeHiddenNodes: false, -}; - /** - * Hook which returns true when all nodes are initialized. + * This hook tells you whether all the nodes in a flow have been measured and given + *a width and height. When you add a node to the flow, this hook will return + *`false` and then `true` again once the node has been measured. * * @public - * @param options.includeHiddenNodes - defaults to false - * @returns boolean indicating whether all nodes are initialized + * @returns Whether or not the nodes have been initialized by the `` component and + * given a width and height. + * + * @example + * ```jsx + *import { useReactFlow, useNodesInitialized } from '@xyflow/react'; + *import { useEffect, useState } from 'react'; + * + *const options = { + * includeHiddenNodes: false, + *}; + * + *export default function useLayout() { + * const { getNodes } = useReactFlow(); + * const nodesInitialized = useNodesInitialized(options); + * const [layoutedNodes, setLayoutedNodes] = useState(getNodes()); + * + * useEffect(() => { + * if (nodesInitialized) { + * setLayoutedNodes(yourLayoutingFunction(getNodes())); + * } + * }, [nodesInitialized]); + * + * return layoutedNodes; + *} + *``` */ -export function useNodesInitialized(options: UseNodesInitializedOptions = defaultOptions): boolean { +export function useNodesInitialized( + options: UseNodesInitializedOptions = { + includeHiddenNodes: false, + } +): boolean { const initialized = useStore(selector(options)); return initialized; diff --git a/packages/react/src/hooks/useOnSelectionChange.ts b/packages/react/src/hooks/useOnSelectionChange.ts index 8e08191b..feff9fe3 100644 --- a/packages/react/src/hooks/useOnSelectionChange.ts +++ b/packages/react/src/hooks/useOnSelectionChange.ts @@ -1,20 +1,53 @@ import { useEffect } from 'react'; import { useStoreApi } from './useStore'; -import type { OnSelectionChangeFunc } from '../types'; +import type { OnSelectionChangeFunc, Node, Edge } from '../types'; -export type UseOnSelectionChangeOptions = { - onChange: OnSelectionChangeFunc; +export type UseOnSelectionChangeOptions = { + /** The handler to register. */ + onChange: OnSelectionChangeFunc; }; /** - * Hook for registering an onSelectionChange handler. + * This hook lets you listen for changes to both node and edge selection. As the + *name implies, the callback you provide will be called whenever the selection of + *_either_ nodes or edges changes. * * @public - * @param params.onChange - The handler to register + * @example + * ```jsx + *import { useState } from 'react'; + *import { ReactFlow, useOnSelectionChange } from '@xyflow/react'; + * + *function SelectionDisplay() { + * const [selectedNodes, setSelectedNodes] = useState([]); + * const [selectedEdges, setSelectedEdges] = useState([]); + * + * // the passed handler has to be memoized, otherwise the hook will not work correctly + * const onChange = useCallback(({ nodes, edges }) => { + * setSelectedNodes(nodes.map((node) => node.id)); + * setSelectedEdges(edges.map((edge) => edge.id)); + * }, []); + * + * useOnSelectionChange({ + * onChange, + * }); + * + * return ( + *
+ *

Selected nodes: {selectedNodes.join(', ')}

+ *

Selected edges: {selectedEdges.join(', ')}

+ *
+ * ); + *} + *``` + * + * @remarks You need to memoize the passed `onChange` handler, otherwise the hook will not work correctly. */ -export function useOnSelectionChange({ onChange }: UseOnSelectionChangeOptions) { - const store = useStoreApi(); +export function useOnSelectionChange({ + onChange, +}: UseOnSelectionChangeOptions) { + const store = useStoreApi(); useEffect(() => { const nextOnSelectionChangeHandlers = [...store.getState().onSelectionChangeHandlers, onChange]; diff --git a/packages/react/src/hooks/useOnViewportChange.ts b/packages/react/src/hooks/useOnViewportChange.ts index 61e4d64f..9a13c4fb 100644 --- a/packages/react/src/hooks/useOnViewportChange.ts +++ b/packages/react/src/hooks/useOnViewportChange.ts @@ -4,18 +4,35 @@ import type { OnViewportChange } from '@xyflow/system'; import { useStoreApi } from './useStore'; export type UseOnViewportChangeOptions = { + /** Gets called when the viewport starts changing. */ onStart?: OnViewportChange; + /** Gets called when the viewport changes. */ onChange?: OnViewportChange; + /** Gets called when the viewport stops changing. */ onEnd?: OnViewportChange; }; /** - * Hook for registering an onViewportChange handler. + * The `useOnViewportChange` hook lets you listen for changes to the viewport such + * as panning and zooming. You can provide a callback for each phase of a viewport + * change: `onStart`, `onChange`, and `onEnd`. * * @public - * @param params.onStart - gets called when the viewport starts changing - * @param params.onChange - gets called when the viewport changes - * @param params.onEnd - gets called when the viewport stops changing + * @example + * ```jsx + *import { useCallback } from 'react'; + *import { useOnViewportChange } from '@xyflow/react'; + * + *function ViewportChangeLogger() { + * useOnViewportChange({ + * onStart: (viewport: Viewport) => console.log('start', viewport), + * onChange: (viewport: Viewport) => console.log('change', viewport), + * onEnd: (viewport: Viewport) => console.log('end', viewport), + * }); + * + * return null; + *} + *``` */ export function useOnViewportChange({ onStart, onChange, onEnd }: UseOnViewportChangeOptions) { const store = useStoreApi(); diff --git a/packages/react/src/hooks/useReactFlow.ts b/packages/react/src/hooks/useReactFlow.ts index c4d81e2d..84b1cb77 100644 --- a/packages/react/src/hooks/useReactFlow.ts +++ b/packages/react/src/hooks/useReactFlow.ts @@ -8,6 +8,7 @@ import { isRectObject, NodeRemoveChange, nodeToRect, + withResolvers, type Rect, } from '@xyflow/system'; @@ -15,15 +16,44 @@ import useViewportHelper from './useViewportHelper'; import { useStore, useStoreApi } from './useStore'; import { useBatchContext } from '../components/BatchProvider'; 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; /** - * Hook for accessing the ReactFlow instance. + * This hook returns a ReactFlowInstance that can be used to update nodes and edges, manipulate the viewport, or query the current state of the flow. * * @public - * @returns ReactFlowInstance + * @example + * ```jsx + *import { useCallback, useState } from 'react'; + *import { useReactFlow } from '@xyflow/react'; + * + *export function NodeCounter() { + * const reactFlow = useReactFlow(); + * const [count, setCount] = useState(0); + * const countNodes = useCallback(() => { + * setCount(reactFlow.getNodes().length); + * // you need to pass it as a dependency if you are using it with useEffect or useCallback + * // because at the first render, it's not initialized yet and some functions might not work. + * }, [reactFlow]); + * + * return ( + *
+ * + *

There are {count} nodes in the flow.

+ *
+ * ); + *} + *``` */ export function useReactFlow(): ReactFlowInstance< NodeType, @@ -55,11 +85,10 @@ export function useReactFlow prevNodes.map((node) => { if (node.id === id) { - const nextNode = typeof nodeUpdate === 'function' ? nodeUpdate(node as NodeType) : nodeUpdate; + const nextNode = typeof nodeUpdate === 'function' ? nodeUpdate(node) : nodeUpdate; return options.replace && isNode(nextNode) ? (nextNode as NodeType) : { ...node, ...nextNode }; } @@ -90,7 +119,7 @@ export function useReactFlow prevEdges.map((edge) => { if (edge.id === id) { - const nextEdge = typeof edgeUpdate === 'function' ? edgeUpdate(edge as EdgeType) : edgeUpdate; + const nextEdge = typeof edgeUpdate === 'function' ? edgeUpdate(edge) : edgeUpdate; return options.replace && isEdge(nextEdge) ? (nextEdge as EdgeType) : { ...edge, ...nextEdge }; } @@ -185,7 +214,7 @@ export function useReactFlow { const internalNode = store.getState().nodeLookup.get(n.id); - if (internalNode && !isRect && (n.id === nodeOrRect!.id || !internalNode.internals.positionAbsolute)) { + if (internalNode && !isRect && (n.id === nodeOrRect.id || !internalNode.internals.positionAbsolute)) { return false; } @@ -239,9 +268,27 @@ export function useReactFlow + Array.from( + store + .getState() + .connectionLookup.get(`${nodeId}${type ? (handleId ? `-${type}-${handleId}` : `-${type}`) : ''}`) + ?.values() ?? [] + ), + fitView: async (options: FitViewOptions | 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 ?? withResolvers(); + + // 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; + }, }; }, []); diff --git a/packages/react/src/hooks/useResizeHandler.ts b/packages/react/src/hooks/useResizeHandler.ts index 2db37499..4348208a 100644 --- a/packages/react/src/hooks/useResizeHandler.ts +++ b/packages/react/src/hooks/useResizeHandler.ts @@ -16,7 +16,7 @@ export function useResizeHandler(domNode: MutableRefObject) => state.nodes); + * ```ts + * const nodes = useStore((state) => state.nodes); + * ``` * + * @remarks This hook should only be used if there is no other way to access the internal + * state. For many of the common use cases, there are dedicated hooks available + * such as {@link useReactFlow}, {@link useViewport}, etc. */ function useStore( selector: (state: ReactFlowState) => StateSlice, @@ -33,6 +44,19 @@ function useStore( return useZustandStore(store, selector, equalityFn); } +/** + * In some cases, you might need to access the store directly. This hook returns the store object which can be used on demand to access the state or dispatch actions. + * + * @returns The store object. + * @example + * ```ts + * const store = useStoreApi(); + * ``` + * + * @remarks This hook should only be used if there is no other way to access the internal + * state. For many of the common use cases, there are dedicated hooks available + * such as {@link useReactFlow}, {@link useViewport}, etc. + */ function useStoreApi() { const store = useContext(StoreContext) as UseBoundStoreWithEqualityFn< StoreApi> diff --git a/packages/react/src/hooks/useUpdateNodeInternals.ts b/packages/react/src/hooks/useUpdateNodeInternals.ts index 6486afd8..55765b80 100644 --- a/packages/react/src/hooks/useUpdateNodeInternals.ts +++ b/packages/react/src/hooks/useUpdateNodeInternals.ts @@ -4,10 +4,49 @@ import type { UpdateNodeInternals, InternalNodeUpdate } from '@xyflow/system'; import { useStoreApi } from '../hooks/useStore'; /** - * Hook for updating node internals. + * When you programmatically add or remove handles to a node or update a node's + * handle position, you need to let React Flow know about it using this hook. This + * will update the internal dimensions of the node and properly reposition handles + * on the canvas if necessary. * * @public - * @returns function for updating node internals + * @returns Use this function to tell React Flow to update the internal state of one or more nodes + * that you have changed programmatically. + * + * @example + * ```jsx + *import { useCallback, useState } from 'react'; + *import { Handle, useUpdateNodeInternals } from '@xyflow/react'; + * + *export default function RandomHandleNode({ id }) { + * const updateNodeInternals = useUpdateNodeInternals(); + * const [handleCount, setHandleCount] = useState(0); + * const randomizeHandleCount = useCallback(() => { + * setHandleCount(Math.floor(Math.random() * 10)); + * updateNodeInternals(id); + * }, [id, updateNodeInternals]); + * + * return ( + * <> + * {Array.from({ length: handleCount }).map((_, index) => ( + * + * ))} + * + *
+ * + *

There are {handleCount} handles on this node.

+ *
+ * + * ); + *} + *``` + * @remarks This hook can only be used in a component that is a child of a + *{@link ReactFlowProvider} or a {@link ReactFlow} component. */ export function useUpdateNodeInternals(): UpdateNodeInternals { const store = useStoreApi(); diff --git a/packages/react/src/hooks/useViewport.ts b/packages/react/src/hooks/useViewport.ts index 0b9758c1..1528fbf5 100644 --- a/packages/react/src/hooks/useViewport.ts +++ b/packages/react/src/hooks/useViewport.ts @@ -11,10 +11,33 @@ const viewportSelector = (state: ReactFlowState) => ({ }); /** - * Hook for getting the current viewport from the store. + * The `useViewport` hook is a convenient way to read the current state of the + * {@link Viewport} in a component. Components that use this hook + * will re-render **whenever the viewport changes**. * * @public - * @returns The current viewport + * @returns The current viewport. + * + * @example + * + *```jsx + *import { useViewport } from '@xyflow/react'; + * + *export default function ViewportDisplay() { + * const { x, y, zoom } = useViewport(); + * + * return ( + *
+ *

+ * The viewport is currently at ({x}, {y}) and zoomed to {zoom}. + *

+ *
+ * ); + *} + *``` + * + * @remarks This hook can only be used in a component that is a child of a + *{@link ReactFlowProvider} or a {@link ReactFlow} component. */ export function useViewport(): Viewport { const viewport = useStore(viewportSelector, shallow); diff --git a/packages/react/src/hooks/useViewportHelper.ts b/packages/react/src/hooks/useViewportHelper.ts index a495ec89..fc01a150 100644 --- a/packages/react/src/hooks/useViewportHelper.ts +++ b/packages/react/src/hooks/useViewportHelper.ts @@ -2,10 +2,9 @@ import { useMemo } from 'react'; import { pointToRendererPoint, getViewportForBounds, - getFitViewNodes, - fitView, type XYPosition, rendererPointToPoint, + SnapGrid, } from '@xyflow/system'; import { useStoreApi } from '../hooks/useStore'; @@ -54,7 +53,7 @@ const useViewportHelper = (): ViewportHelperFunctions => { y: viewport.y ?? tY, zoom: viewport.zoom ?? tZoom, }, - { duration: options?.duration } + options ); return Promise.resolve(true); @@ -63,24 +62,6 @@ const useViewportHelper = (): ViewportHelperFunctions => { const [x, y, zoom] = store.getState().transform; return { x, y, zoom }; }, - fitView: (options) => { - const { nodeLookup, width, height, minZoom, maxZoom, panZoom } = store.getState(); - const fitViewNodes = getFitViewNodes(nodeLookup, options); - - return panZoom - ? fitView( - { - nodes: fitViewNodes, - width, - height, - minZoom, - maxZoom, - panZoom, - }, - options - ) - : Promise.resolve(false); - }, setCenter: async (x, y, options) => { const { width, height, maxZoom, panZoom } = store.getState(); const nextZoom = typeof options?.zoom !== 'undefined' ? options.zoom : maxZoom; @@ -97,7 +78,7 @@ const useViewportHelper = (): ViewportHelperFunctions => { y: centerY, zoom: nextZoom, }, - { duration: options?.duration } + { duration: options?.duration, ease: options?.ease, interpolate: options?.interpolate } ); return Promise.resolve(true); @@ -110,25 +91,33 @@ const useViewportHelper = (): ViewportHelperFunctions => { return Promise.resolve(false); } - await panZoom.setViewport(viewport, { duration: options?.duration }); + await panZoom.setViewport(viewport, { + duration: options?.duration, + ease: options?.ease, + interpolate: options?.interpolate, + }); return Promise.resolve(true); }, - screenToFlowPosition: (clientPosition: XYPosition, options: { snapToGrid: boolean } = { snapToGrid: true }) => { - const { transform, snapGrid, domNode } = store.getState(); + screenToFlowPosition: ( + clientPosition: XYPosition, + options: { snapToGrid?: boolean; snapGrid?: SnapGrid } = {} + ) => { + const { transform, snapGrid, snapToGrid, domNode } = store.getState(); if (!domNode) { return clientPosition; } const { x: domX, y: domY } = domNode.getBoundingClientRect(); - const correctedPosition = { x: clientPosition.x - domX, y: clientPosition.y - domY, }; + const _snapGrid = options.snapGrid ?? snapGrid; + const _snapToGrid = options.snapToGrid ?? snapToGrid; - return pointToRendererPoint(correctedPosition, transform, options.snapToGrid, snapGrid); + return pointToRendererPoint(correctedPosition, transform, _snapToGrid, _snapGrid); }, flowToScreenPosition: (flowPosition: XYPosition) => { const { transform, domNode } = store.getState(); diff --git a/packages/react/src/hooks/useVisibleNodeIds.ts b/packages/react/src/hooks/useVisibleNodeIds.ts index e8d14c8e..a0006940 100644 --- a/packages/react/src/hooks/useVisibleNodeIds.ts +++ b/packages/react/src/hooks/useVisibleNodeIds.ts @@ -8,8 +8,8 @@ import type { Node, ReactFlowState } from '../types'; const selector = (onlyRenderVisible: boolean) => (s: ReactFlowState) => { return onlyRenderVisible ? getNodesInside(s.nodeLookup, { x: 0, y: 0, width: s.width, height: s.height }, s.transform, true).map( - (node) => node.id - ) + (node) => node.id + ) : Array.from(s.nodeLookup.keys()); }; diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts index cb36e305..3397a677 100644 --- a/packages/react/src/index.ts +++ b/packages/react/src/index.ts @@ -9,7 +9,7 @@ export { SmoothStepEdge } from './components/Edges/SmoothStepEdge'; export { BaseEdge } from './components/Edges/BaseEdge'; export { ReactFlowProvider } from './components/ReactFlowProvider'; export { Panel, type PanelProps } from './components/Panel'; -export { EdgeLabelRenderer } from './components/EdgeLabelRenderer'; +export { EdgeLabelRenderer, type EdgeLabelRendererProps } from './components/EdgeLabelRenderer'; export { ViewportPortal } from './components/ViewportPortal'; export { useReactFlow } from './hooks/useReactFlow'; @@ -24,6 +24,7 @@ export { useOnViewportChange, type UseOnViewportChangeOptions } from './hooks/us export { useOnSelectionChange, type UseOnSelectionChangeOptions } from './hooks/useOnSelectionChange'; export { useNodesInitialized, type UseNodesInitializedOptions } from './hooks/useNodesInitialized'; export { useHandleConnections } from './hooks/useHandleConnections'; +export { useNodeConnections } from './hooks/useNodeConnections'; export { useNodesData } from './hooks/useNodesData'; export { useConnection } from './hooks/useConnection'; export { useInternalNode } from './hooks/useInternalNode'; @@ -84,7 +85,7 @@ export { type OnResizeEnd, type ControlPosition, type ControlLinePosition, - type ResizeControlVariant, + ResizeControlVariant, type ResizeParams, type ResizeParamsWithDirection, type ResizeDragEvent, @@ -105,6 +106,8 @@ export { type FinalConnectionState, type ConnectionInProgress, type NoConnection, + type NodeConnection, + type OnReconnect, } from '@xyflow/system'; // we need this workaround to prevent a duplicate identifier error diff --git a/packages/react/src/store/index.ts b/packages/react/src/store/index.ts index ea06fb8b..39702bd0 100644 --- a/packages/react/src/store/index.ts +++ b/packages/react/src/store/index.ts @@ -1,7 +1,5 @@ import { createWithEqualityFn } from 'zustand/traditional'; import { - getFitViewNodes, - fitView as fitViewSystem, adoptUserNodes, updateAbsolutePositions, panBy as panBySystem, @@ -15,6 +13,7 @@ import { initialConnection, NodeOrigin, CoordinateExtent, + fitViewport, } from '@xyflow/system'; import { applyEdgeChanges, applyNodeChanges, createSelectionChange, getSelectionChanges } from '../utils/changes'; @@ -29,6 +28,9 @@ const createStore = ({ width, height, fitView, + fitViewOptions, + minZoom, + maxZoom, nodeOrigin, nodeExtent, }: { @@ -39,28 +41,79 @@ const createStore = ({ width?: number; height?: number; fitView?: boolean; + fitViewOptions?: FitViewOptions; + minZoom?: number; + maxZoom?: number; nodeOrigin?: NodeOrigin; nodeExtent?: CoordinateExtent; }) => - createWithEqualityFn( - (set, get) => ({ - ...getInitialState({ nodes, edges, width, height, fitView, nodeOrigin, nodeExtent, defaultNodes, defaultEdges }), + createWithEqualityFn((set, get) => { + async function resolveFitView() { + const { nodeLookup, panZoom, fitViewOptions, fitViewResolver, width, height, minZoom, maxZoom } = get(); + + if (!panZoom) { + 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, + fitViewOptions, + minZoom, + maxZoom, + nodeOrigin, + nodeExtent, + defaultNodes, + defaultEdges, + }), setNodes: (nodes: Node[]) => { - const { nodeLookup, parentLookup, nodeOrigin, elevateNodesOnSelect } = get(); - // setNodes() is called exclusively in response to user actions: - // - either when the `` prop is updated in the controlled ReactFlow setup, - // - or when the user calls something like `reactFlowInstance.setNodes()` in an uncontrolled ReactFlow setup. - // - // When this happens, we take the note objects passed by the user and extend them with fields - // relevant for internal React Flow operations. - adoptUserNodes(nodes, nodeLookup, parentLookup, { + const { nodeLookup, parentLookup, nodeOrigin, elevateNodesOnSelect, fitViewQueued } = get(); + /* + * setNodes() is called exclusively in response to user actions: + * - either when the `` prop is updated in the controlled ReactFlow setup, + * - or when the user calls something like `reactFlowInstance.setNodes()` in an uncontrolled ReactFlow setup. + * + * When this happens, we take the note objects passed by the user and extend them with fields + * relevant for internal React Flow operations. + */ + + const nodesInitialized = adoptUserNodes(nodes, nodeLookup, parentLookup, { nodeOrigin, nodeExtent, elevateNodesOnSelect, checkEquality: true, }); - set({ nodes }); + if (fitViewQueued && nodesInitialized) { + resolveFitView(); + set({ nodes, nodesInitialized, fitViewQueued: false, fitViewOptions: undefined }); + } else { + set({ nodes, nodesInitialized }); + } }, setEdges: (edges: Edge[]) => { const { connectionLookup, edgeLookup } = get(); @@ -81,23 +134,14 @@ const createStore = ({ set({ hasDefaultEdges: true }); } }, - // Every node gets registerd at a ResizeObserver. Whenever a node - // changes its dimensions, this function is called to measure the - // new dimensions and update the nodes. - updateNodeInternals: (updates, params = { triggerFitView: true }) => { - const { - triggerNodeChanges, - nodeLookup, - parentLookup, - fitViewOnInit, - fitViewDone, - fitViewOnInitOptions, - domNode, - nodeOrigin, - nodeExtent, - debug, - fitViewSync, - } = get(); + /* + * Every node gets registerd at a ResizeObserver. Whenever a node + * changes its dimensions, this function is called to measure the + * new dimensions and update the nodes. + */ + updateNodeInternals: (updates) => { + const { triggerNodeChanges, nodeLookup, parentLookup, domNode, nodeOrigin, nodeExtent, debug, fitViewQueued } = + get(); const { changes, updatedInternals } = updateNodeInternalsSystem( updates, @@ -114,23 +158,9 @@ const createStore = ({ updateAbsolutePositions(nodeLookup, parentLookup, { nodeOrigin, nodeExtent }); - if (params.triggerFitView) { - // we call fitView once initially after all dimensions are set - let nextFitViewDone = fitViewDone; - - 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 }); + if (fitViewQueued) { + resolveFitView(); + set({ fitViewQueued: false, fitViewOptions: undefined }); } else { // we always want to trigger useStore calls whenever updateNodeInternals is called set({}); @@ -146,40 +176,47 @@ const createStore = ({ updateNodePositions: (nodeDragItems, dragging = false) => { const parentExpandChildren: ParentExpandChild[] = []; const changes = []; + const { nodeLookup, triggerNodeChanges } = get(); for (const [id, dragItem] of nodeDragItems) { + // we are using the nodelookup to be sure to use the current expandParent and parentId value + const node = nodeLookup.get(id); + const expandParent = !!(node?.expandParent && node?.parentId && dragItem?.position); + const change: NodeChange = { id, type: 'position', - position: dragItem.position, + position: expandParent + ? { + x: Math.max(0, dragItem.position.x), + y: Math.max(0, dragItem.position.y), + } + : dragItem.position, dragging, }; - if (dragItem?.expandParent && dragItem?.parentId && change.position) { + if (expandParent && node.parentId) { parentExpandChildren.push({ id, - parentId: dragItem.parentId, + parentId: node.parentId, rect: { ...dragItem.internals.positionAbsolute, - width: dragItem.measured.width!, - height: dragItem.measured.height!, + width: dragItem.measured.width ?? 0, + height: dragItem.measured.height ?? 0, }, }); - - change.position.x = Math.max(0, change.position.x); - change.position.y = Math.max(0, change.position.y); } changes.push(change); } if (parentExpandChildren.length > 0) { - const { nodeLookup, parentLookup, nodeOrigin } = get(); + const { parentLookup, nodeOrigin } = get(); const parentExpandChanges = handleExpandParent(parentExpandChildren, nodeLookup, parentLookup, nodeOrigin); changes.push(...parentExpandChanges); } - get().triggerNodeChanges(changes); + triggerNodeChanges(changes); }, triggerNodeChanges: (changes) => { const { onNodesChange, setNodes, nodes, hasDefaultNodes, debug } = get(); @@ -218,7 +255,7 @@ const createStore = ({ if (multiSelectionActive) { const nodeChanges = selectedNodeIds.map((nodeId) => createSelectionChange(nodeId, true)); - triggerNodeChanges(nodeChanges as NodeSelectionChange[]); + triggerNodeChanges(nodeChanges); return; } @@ -230,7 +267,7 @@ const createStore = ({ if (multiSelectionActive) { const changedEdges = selectedEdgeIds.map((edgeId) => createSelectionChange(edgeId, true)); - triggerEdgeChanges(changedEdges as EdgeSelectionChange[]); + triggerEdgeChanges(changedEdges); return; } @@ -238,18 +275,25 @@ const createStore = ({ triggerNodeChanges(getSelectionChanges(nodeLookup, new Set(), true)); }, unselectNodesAndEdges: ({ nodes, edges }: UnselectNodesAndEdgesParams = {}) => { - const { edges: storeEdges, nodes: storeNodes, triggerNodeChanges, triggerEdgeChanges } = get(); + const { edges: storeEdges, nodes: storeNodes, nodeLookup, triggerNodeChanges, triggerEdgeChanges } = get(); const nodesToUnselect = nodes ? nodes : storeNodes; const edgesToUnselect = edges ? edges : storeEdges; - const nodeChanges = nodesToUnselect.map((n) => { - n.selected = false; + const internalNode = nodeLookup.get(n.id); + if (internalNode) { + /* + * we need to unselect the internal node that was selected previously before we + * send the change to the user to prevent it to be selected while dragging the new node + */ + internalNode.selected = false; + } + return createSelectionChange(n.id, false); }); const edgeChanges = edgesToUnselect.map((edge) => createSelectionChange(edge.id, false)); - triggerNodeChanges(nodeChanges as NodeSelectionChange[]); - triggerEdgeChanges(edgeChanges as EdgeSelectionChange[]); + triggerNodeChanges(nodeChanges); + triggerEdgeChanges(edgeChanges); }, setMinZoom: (minZoom) => { const { panZoom, maxZoom } = get(); @@ -272,14 +316,18 @@ const createStore = ({ get().panZoom?.setClickDistance(clickDistance); }, resetSelectedElements: () => { - const { edges, nodes, triggerNodeChanges, triggerEdgeChanges } = get(); + const { edges, nodes, triggerNodeChanges, triggerEdgeChanges, elementsSelectable } = get(); + + if (!elementsSelectable) { + return; + } const nodeChanges = nodes.reduce( - (res, node) => (node.selected ? [...res, createSelectionChange(node.id, false) as NodeSelectionChange] : res), + (res, node) => (node.selected ? [...res, createSelectionChange(node.id, false)] : res), [] ); const edgeChanges = edges.reduce( - (res, edge) => (edge.selected ? [...res, createSelectionChange(edge.id, false) as EdgeSelectionChange] : res), + (res, edge) => (edge.selected ? [...res, createSelectionChange(edge.id, false)] : res), [] ); @@ -312,52 +360,6 @@ const createStore = ({ return panBySystem({ delta, panZoom, transform, translateExtent, width, height }); }, - fitView: (options?: FitViewOptions): Promise => { - 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: () => { set({ connection: { ...initialConnection }, @@ -368,8 +370,7 @@ const createStore = ({ }, reset: () => set({ ...getInitialState() }), - }), - Object.is - ); + }; + }, Object.is); export { createStore }; diff --git a/packages/react/src/store/initialState.ts b/packages/react/src/store/initialState.ts index af52f2a2..34507efd 100644 --- a/packages/react/src/store/initialState.ts +++ b/packages/react/src/store/initialState.ts @@ -12,7 +12,7 @@ import { CoordinateExtent, } from '@xyflow/system'; -import type { Edge, InternalNode, Node, ReactFlowStore } from '../types'; +import type { Edge, FitViewOptions, InternalNode, Node, ReactFlowStore } from '../types'; const getInitialState = ({ nodes, @@ -22,6 +22,9 @@ const getInitialState = ({ width, height, fitView, + fitViewOptions, + minZoom = 0.5, + maxZoom = 2, nodeOrigin, nodeExtent, }: { @@ -32,6 +35,9 @@ const getInitialState = ({ width?: number; height?: number; fitView?: boolean; + fitViewOptions?: FitViewOptions; + minZoom?: number; + maxZoom?: number; nodeOrigin?: NodeOrigin; nodeExtent?: CoordinateExtent; } = {}): ReactFlowStore => { @@ -46,7 +52,7 @@ const getInitialState = ({ const storeNodeExtent = nodeExtent ?? infiniteExtent; updateConnectionLookup(connectionLookup, edgeLookup, storeEdges); - adoptUserNodes(storeNodes, nodeLookup, parentLookup, { + const nodesInitialized = adoptUserNodes(storeNodes, nodeLookup, parentLookup, { nodeOrigin: storeNodeOrigin, nodeExtent: storeNodeExtent, elevateNodesOnSelect: false, @@ -59,7 +65,14 @@ const getInitialState = ({ filter: (node) => !!((node.width || node.initialWidth) && (node.height || node.initialHeight)), }); - const { x, y, zoom } = getViewportForBounds(bounds, width, height, 0.5, 2, 0.1); + const { x, y, zoom } = getViewportForBounds( + bounds, + width, + height, + minZoom, + maxZoom, + fitViewOptions?.padding ?? 0.1 + ); transform = [x, y, zoom]; } @@ -69,6 +82,7 @@ const getInitialState = ({ height: 0, transform, nodes: storeNodes, + nodesInitialized, nodeLookup, parentLookup, edges: storeEdges, @@ -79,8 +93,8 @@ const getInitialState = ({ hasDefaultNodes: defaultNodes !== undefined, hasDefaultEdges: defaultEdges !== undefined, panZoom: null, - minZoom: 0.5, - maxZoom: 2, + minZoom, + maxZoom, translateExtent: infiniteExtent, nodeExtent: storeNodeExtent, nodesSelectionActive: false, @@ -104,13 +118,14 @@ const getInitialState = ({ elementsSelectable: true, elevateNodesOnSelect: true, elevateEdgesOnSelect: false, - fitViewOnInit: false, - fitViewDone: false, - fitViewOnInitOptions: undefined, selectNodesOnDrag: true, multiSelectionActive: false, + fitViewQueued: fitView ?? false, + fitViewOptions, + fitViewResolver: null, + connection: { ...initialConnection }, connectionClickStartHandle: null, connectOnClick: true, diff --git a/packages/react/src/types/component-props.ts b/packages/react/src/types/component-props.ts index 623992b2..9bea9004 100644 --- a/packages/react/src/types/component-props.ts +++ b/packages/react/src/types/component-props.ts @@ -20,6 +20,7 @@ import type { OnError, ColorMode, SnapGrid, + OnReconnect, } from '@xyflow/system'; import type { @@ -29,7 +30,6 @@ import type { Node, Edge, ConnectionLineComponent, - OnReconnect, OnInit, DefaultEdgeOptions, FitViewOptions, @@ -52,7 +52,9 @@ import type { */ export interface ReactFlowProps extends Omit, 'onError'> { - /** An array of nodes to render in a controlled flow. + /** + * An array of nodes to render in a controlled flow. + * @default [] * @example * const nodes = [ * { @@ -64,7 +66,9 @@ export interface ReactFlowProps; - /** This event handler is called when a user double clicks on a node */ + /** This event handler is called when a user double-clicks on a node. */ onNodeDoubleClick?: NodeMouseHandler; - /** This event handler is called when mouse of a user enters a node */ + /** This event handler is called when mouse of a user enters a node. */ onNodeMouseEnter?: NodeMouseHandler; - /** This event handler is called when mouse of a user moves over a node */ + /** This event handler is called when mouse of a user moves over a node. */ onNodeMouseMove?: NodeMouseHandler; - /** This event handler is called when mouse of a user leaves a node */ + /** This event handler is called when mouse of a user leaves a node. */ onNodeMouseLeave?: NodeMouseHandler; - /** This event handler is called when a user right clicks on a node */ + /** This event handler is called when a user right-clicks on a node. */ onNodeContextMenu?: NodeMouseHandler; - /** This event handler is called when a user starts to drag a node */ + /** This event handler is called when a user starts to drag a node. */ onNodeDragStart?: OnNodeDrag; - /** This event handler is called when a user drags a node */ + /** This event handler is called when a user drags a node. */ onNodeDrag?: OnNodeDrag; - /** This event handler is called when a user stops dragging a node */ + /** This event handler is called when a user stops dragging a node. */ onNodeDragStop?: OnNodeDrag; - /** This event handler is called when a user clicks on an edge */ + /** This event handler is called when a user clicks on an edge. */ onEdgeClick?: (event: ReactMouseEvent, edge: EdgeType) => void; - /** This event handler is called when a user right clicks on an edge */ + /** This event handler is called when a user right-clicks on an edge. */ onEdgeContextMenu?: EdgeMouseHandler; - /** This event handler is called when mouse of a user enters an edge */ + /** This event handler is called when mouse of a user enters an edge. */ onEdgeMouseEnter?: EdgeMouseHandler; - /** This event handler is called when mouse of a user moves over an edge */ + /** This event handler is called when mouse of a user moves over an edge. */ onEdgeMouseMove?: EdgeMouseHandler; - /** This event handler is called when mouse of a user leaves an edge */ + /** This event handler is called when mouse of a user leaves an edge. */ onEdgeMouseLeave?: EdgeMouseHandler; - /** This event handler is called when a user double clicks on an edge */ + /** This event handler is called when a user double-clicks on an edge. */ onEdgeDoubleClick?: EdgeMouseHandler; + /** + * This handler is called when the source or target of a reconnectable edge is dragged from the + * current node. It will fire even if the edge's source or target do not end up changing. + * You can use the `reconnectEdge` utility to convert the connection to a new edge. + */ onReconnect?: OnReconnect; + /** + * This event fires when the user begins dragging the source or target of an editable edge. + */ onReconnectStart?: (event: ReactMouseEvent, edge: EdgeType, handleType: HandleType) => void; + /** + * This event fires when the user releases the source or target of an editable edge. It is called + * even if an edge update does not occur. + */ onReconnectEnd?: (event: MouseEvent | TouchEvent, edge: EdgeType, handleType: HandleType) => void; - /** This event handler is called when a Node is updated + /** + * Use this event handler to add interactivity to a controlled flow. + * It is called on node drag, select, and move. * @example // Use NodesState hook to create edges and get onNodesChange handler * import ReactFlow, { useNodesState } from '@xyflow/react'; - * const [edges, setNodes, onNodesChange] = useNodesState(initialNodes); + * const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes); * * return () - * @example // Use helper function to update edge + * @example // Use helper function to update node * import ReactFlow, { applyNodeChanges } from '@xyflow/react'; * * const onNodeChange = useCallback( - * (changes) => setNode((eds) => applyNodeChanges(changes, eds)), + * (changes) => setNode((nds) => applyNodeChanges(changes, nds)), * [], * ); * * return () */ onNodesChange?: OnNodesChange; - /** This event handler is called when a Edge is updated + /** + * Use this event handler to add interactivity to a controlled flow. It is called on edge select + * and remove. * @example // Use EdgesState hook to create edges and get onEdgesChange handler * import ReactFlow, { useEdgesState } from '@xyflow/react'; * const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges); @@ -166,24 +176,27 @@ export interface ReactFlowProps) */ onEdgesChange?: OnEdgesChange; - /** This event handler gets called when a Node is deleted */ + /** This event handler gets called when a node is deleted. */ onNodesDelete?: OnNodesDelete; - /** This event handler gets called when a Edge is deleted */ + /** This event handler gets called when an edge is deleted. */ onEdgesDelete?: OnEdgesDelete; - /** This event handler gets called when a Node or Edge is deleted */ + /** This event handler gets called when a node or edge is deleted. */ onDelete?: OnDelete; - /** This event handler gets called when a user starts to drag a selection box */ + /** This event handler gets called when a user starts to drag a selection box. */ onSelectionDragStart?: SelectionDragHandler; - /** This event handler gets called when a user drags a selection box */ + /** This event handler gets called when a user drags a selection box. */ onSelectionDrag?: SelectionDragHandler; - /** This event handler gets called when a user stops dragging a selection box */ + /** This event handler gets called when a user stops dragging a selection box. */ onSelectionDragStop?: SelectionDragHandler; onSelectionStart?: (event: ReactMouseEvent) => void; onSelectionEnd?: (event: ReactMouseEvent) => void; + /** + * This event handler is called when a user right-clicks on a node selection. + */ onSelectionContextMenu?: (event: ReactMouseEvent, nodes: NodeType[]) => void; - /** When a connection line is completed and two nodes are connected by the user, this event fires with the new connection. - * - * You can use the addEdge utility to convert the connection to a complete edge. + /** + * When a connection line is completed and two nodes are connected by the user, this event fires with the new connection. + * You can use the `addEdge` utility to convert the connection to a complete edge. * @example // Use helper function to update edges onConnect * import ReactFlow, { addEdge } from '@xyflow/react'; * @@ -195,178 +208,254 @@ export interface ReactFlowProps) */ onConnect?: OnConnect; - /** This event handler gets called when a user starts to drag a connection line */ + /** This event handler gets called when a user starts to drag a connection line. */ onConnectStart?: OnConnectStart; - /** This event handler gets called when a user stops dragging a connection line */ + /** + * This callback will fire regardless of whether a valid connection could be made or not. You can + * use the second `connectionState` parameter to have different behavior when a connection was + * unsuccessful. + */ onConnectEnd?: OnConnectEnd; onClickConnectStart?: OnConnectStart; onClickConnectEnd?: OnConnectEnd; - /** This event handler gets called when a flow has finished initializing */ + /** + * The `onInit` callback is called when the viewport is initialized. At this point you can use the + * instance to call methods like `fitView` or `zoomTo`. + */ onInit?: OnInit; /** This event handler is called while the user is either panning or zooming the viewport. */ onMove?: OnMove; - /** This event handler gets called when a user starts to pan or zoom the viewport */ + /** This event handler is called when the user begins to pan or zoom the viewport. */ onMoveStart?: OnMoveStart; - /** This event handler gets called when a user stops panning or zooming the viewport */ + /** + * This event handler is called when panning or zooming viewport movement stops. + * If the movement is not user-initiated, the event parameter will be `null`. + */ onMoveEnd?: OnMoveEnd; - /** This event handler gets called when a user changes group of selected elements in the flow */ - onSelectionChange?: OnSelectionChangeFunc; - /** This event handler gets called when user scroll inside the pane */ + /** This event handler gets called when a user changes group of selected elements in the flow. */ + onSelectionChange?: OnSelectionChangeFunc; + /** This event handler gets called when user scroll inside the pane. */ onPaneScroll?: (event?: WheelEvent) => void; - /** This event handler gets called when user clicks inside the pane */ + /** This event handler gets called when user clicks inside the pane. */ onPaneClick?: (event: ReactMouseEvent) => void; - /** This event handler gets called when user right clicks inside the pane */ + /** This event handler gets called when user right clicks inside the pane. */ onPaneContextMenu?: (event: ReactMouseEvent | MouseEvent) => void; - /** This event handler gets called when mouse enters the pane */ + /** This event handler gets called when mouse enters the pane. */ onPaneMouseEnter?: (event: ReactMouseEvent) => void; - /** This event handler gets called when mouse moves over the pane */ + /** This event handler gets called when mouse moves over the pane. */ onPaneMouseMove?: (event: ReactMouseEvent) => void; - /** This event handler gets called when mouse leaves the pane */ + /** This event handler gets called when mouse leaves the pane. */ onPaneMouseLeave?: (event: ReactMouseEvent) => void; - /** Distance that the mouse can move between mousedown/up that will trigger a click + /** + * Distance that the mouse can move between mousedown/up that will trigger a click. * @default 0 */ paneClickDistance?: number; - /** Distance that the mouse can move between mousedown/up that will trigger a click + /** + * Distance that the mouse can move between mousedown/up that will trigger a click. * @default 0 */ nodeClickDistance?: number; - /** This handler gets called before the user deletes nodes or edges and provides a way to abort the deletion by returning false. */ + /** + * This handler is called before nodes or edges are deleted, allowing the deletion to be aborted + * by returning `false` or modified by returning updated nodes and edges. + */ onBeforeDelete?: OnBeforeDelete; - /** Custom node types to be available in a flow. - * - * React Flow matches a node's type to a component in the nodeTypes object. + /** + * Custom node types to be available in a flow. + * React Flow matches a node's type to a component in the `nodeTypes` object. + * @default { + * input: InputNode, + * default: DefaultNode, + * output: OutputNode, + * group: GroupNode + * } * @example * import CustomNode from './CustomNode'; * * const nodeTypes = { nameOfNodeType: CustomNode }; */ nodeTypes?: NodeTypes; - /** Custom edge types to be available in a flow. - * - * React Flow matches an edge's type to a component in the edgeTypes object. + /** + * Custom edge types to be available in a flow. + * React Flow matches an edge's type to a component in the `edgeTypes` object. + * @default { + * default: BezierEdge, + * straight: StraightEdge, + * step: StepEdge, + * smoothstep: SmoothStepEdge, + * simplebezier: SimpleBezier + * } * @example * import CustomEdge from './CustomEdge'; * * const edgeTypes = { nameOfEdgeType: CustomEdge }; */ edgeTypes?: EdgeTypes; - /** The type of edge path to use for connection lines. - * + /** + * The type of edge path to use for connection lines. * Although created edges can be of any type, React Flow needs to know what type of path to render for the connection line before the edge is created! + * @default ConnectionLineType.Bezier */ connectionLineType?: ConnectionLineType; - /** Styles to be applied to the connection line */ + /** Styles to be applied to the connection line. */ connectionLineStyle?: CSSProperties; - /** React Component to be used as a connection line */ - connectionLineComponent?: ConnectionLineComponent; - /** Styles to be applied to the container of the connection line */ + /** React Component to be used as a connection line. */ + connectionLineComponent?: ConnectionLineComponent; + /** Styles to be applied to the container of the connection line. */ connectionLineContainerStyle?: CSSProperties; - /** 'strict' connection mode will only allow you to connect source handles to target handles. - * - * 'loose' connection mode will allow you to connect handles of any type to one another. + /** + * A loose connection mode will allow you to connect handles with differing types, including + * source-to-source connections. However, it does not support target-to-target connections. Strict + * mode allows only connections between source handles and target handles. * @default 'strict' */ connectionMode?: ConnectionMode; - /** Pressing down this key deletes all selected nodes & edges. + /** + * If set, pressing the key or chord will delete any selected nodes and edges. Passing an array + * represents multiple keys that can be pressed. + + * For example, `["Delete", "Backspace"]` will delete selected elements when either key is pressed. * @default 'Backspace' */ deleteKeyCode?: KeyCode | null; - /** If a key is set, you can pan the viewport while that key is held down even if panOnScroll is set to false. + /** + * If set, holding this key will let you click and drag to draw a selection box around multiple + * nodes and edges. Passing an array represents multiple keys that can be pressed. * - * By setting this prop to null you can disable this functionality. - * @default 'Space' + * For example, `["Shift", "Meta"]` will allow you to draw a selection box when either key is + * pressed. + * @default 'Shift' */ selectionKeyCode?: KeyCode | null; - /** Select multiple elements with a selection box, without pressing down selectionKey */ + /** + * Select multiple elements with a selection box, without pressing down `selectionKey`. + * @default false + */ selectionOnDrag?: boolean; - /** When set to "partial", when the user creates a selection box by click and dragging nodes that are only partially in the box are still selected. + /** + * When set to `"partial"`, when the user creates a selection box by click and dragging nodes that + * are only partially in the box are still selected. * @default 'full' */ selectionMode?: SelectionMode; - /** If a key is set, you can pan the viewport while that key is held down even if panOnScroll is set to false. + /** + * If a key is set, you can pan the viewport while that key is held down even if `panOnScroll` + * is set to `false`. * - * By setting this prop to null you can disable this functionality. + * By setting this prop to `null` you can disable this functionality. * @default 'Space' */ panActivationKeyCode?: KeyCode | null; - /** Pressing down this key you can select multiple elements by clicking. - * @default 'Meta' for macOS, "Ctrl" for other systems + /** + * Pressing down this key you can select multiple elements by clicking. + * @default "Meta" for macOS, "Control" for other systems */ multiSelectionKeyCode?: KeyCode | null; - /** If a key is set, you can zoom the viewport while that key is held down even if panOnScroll is set to false. + /** + * If a key is set, you can zoom the viewport while that key is held down even if `panOnScroll` + * is set to `false`. * - * By setting this prop to null you can disable this functionality. - * @default 'Meta' for macOS, "Ctrl" for other systems - * */ + * By setting this prop to `null` you can disable this functionality. + * @default "Meta" for macOS, "Control" for other systems + * + */ zoomActivationKeyCode?: KeyCode | null; - /** Set this prop to make the flow snap to the grid */ + /** When enabled, nodes will snap to the grid when dragged. */ snapToGrid?: boolean; - /** Grid all nodes will snap to + /** + * If `snapToGrid` is enabled, this prop configures the grid that nodes will snap to. * @example [20, 20] */ snapGrid?: SnapGrid; - /** You can enable this optimisation to instruct Svelte Flow to only render nodes and edges that would be visible in the viewport. + /** + * You can enable this optimisation to instruct React Flow to only render nodes and edges that would be visible in the viewport. * * This might improve performance when you have a large number of nodes and edges but also adds an overhead. * @default false */ onlyRenderVisibleElements?: boolean; - /** Controls if all nodes should be draggable + /** + * Controls whether all nodes should be draggable or not. Individual nodes can override this + * setting by setting their `draggable` prop. If you want to use the mouse handlers on + * non-draggable nodes, you need to add the `"nopan"` class to those nodes. * @default true */ nodesDraggable?: boolean; - /** Controls if all nodes should be connectable to each other + /** + * Controls whether all nodes should be connectable or not. Individual nodes can override this + * setting by setting their `connectable` prop. * @default true */ nodesConnectable?: boolean; - /** Controls if all nodes should be focusable + /** + * When `true`, focus between nodes can be cycled with the `Tab` key and selected with the `Enter` + * key. This option can be overridden by individual nodes by setting their `focusable` prop. * @default true */ nodesFocusable?: boolean; - /** Defines nodes relative position to its coordinates + /** + * The origin of the node to use when placing it in the flow or looking up its `x` and `y` + * position. An origin of `[0, 0]` means that a node's top left corner will be placed at the `x` + * and `y` position. + * @default [0, 0] * @example * [0, 0] // default, top left * [0.5, 0.5] // center * [1, 1] // bottom right */ nodeOrigin?: NodeOrigin; - /** Controls if all edges should be focusable + /** + * When `true`, focus between edges can be cycled with the `Tab` key and selected with the `Enter` + * key. This option can be overridden by individual edges by setting their `focusable` prop. * @default true */ edgesFocusable?: boolean; - /** Controls if all edges should be updateable + /** + * Whether edges can be updated once they are created. When both this prop is `true` and an + * `onReconnect` handler is provided, the user can drag an existing edge to a new source or + * target. Individual edges can override this value with their reconnectable property. * @default true */ edgesReconnectable?: boolean; - /** Controls if all elements should (nodes & edges) be selectable + /** + * When `true`, elements (nodes and edges) can be selected by clicking on them. This option can be + * overridden by individual elements by setting their `selectable` prop. * @default true */ elementsSelectable?: boolean; - /** If true, nodes get selected on drag + /** + * If `true`, nodes get selected on drag. * @default true */ selectNodesOnDrag?: boolean; - /** Enableing this prop allows users to pan the viewport by clicking and dragging. - * + /** + * Enabling this prop allows users to pan the viewport by clicking and dragging. * You can also set this prop to an array of numbers to limit which mouse buttons can activate panning. + * @default true * @example [0, 2] // allows panning with the left and right mouse buttons * [0, 1, 2, 3, 4] // allows panning with all mouse buttons */ panOnDrag?: boolean | number[]; - /** Minimum zoom level + /** + * Minimum zoom level. * @default 0.5 */ minZoom?: number; - /** Maximum zoom level + /** + * Maximum zoom level. * @default 2 */ maxZoom?: number; - /** Controlled viewport to be used instead of internal one */ + /** + * When you pass a `viewport` prop, it's controlled, and you also need to pass `onViewportChange` + * to handle internal changes. + */ viewport?: Viewport; - /** Sets the initial position and zoom of the viewport. - * - * If a default viewport is provided but fitView is enabled, the default viewport will be ignored. + /** + * Sets the initial position and zoom of the viewport. If a default viewport is provided but + * `fitView` is enabled, the default viewport will be ignored. + * @default { x: 0, y: 0, zoom: 1 } * @example * const initialViewport = { * zoom: 0.5, @@ -375,59 +464,98 @@ export interface ReactFlowProps void; - /** By default the viewport extends infinitely. You can use this prop to set a boundary. - * + /** + * By default, the viewport extends infinitely. You can use this prop to set a boundary. * The first pair of coordinates is the top left boundary and the second pair is the bottom right. + * @default [[-∞, -∞], [+∞, +∞]] * @example [[-1000, -10000], [1000, 1000]] */ translateExtent?: CoordinateExtent; - /** Disabling this prop will allow the user to scroll the page even when their pointer is over the flow. + /** + * Disabling this prop will allow the user to scroll the page even when their pointer is over the flow. * @default true */ preventScrolling?: boolean; - /** By default nodes can be placed on an infinite flow. You can use this prop to set a boundary. - * + /** + * By default, nodes can be placed on an infinite flow. You can use this prop to set a boundary. * The first pair of coordinates is the top left boundary and the second pair is the bottom right. * @example [[-1000, -10000], [1000, 1000]] */ nodeExtent?: CoordinateExtent; - /** Color of edge markers - * @example "#b1b1b7" + /** + * Color of edge markers. + * @default '#b1b1b7' */ defaultMarkerColor?: string; - /** Controls if the viewport should zoom by scrolling inside the container */ + /** + * Controls if the viewport should zoom by scrolling inside the container. + * @default true + */ zoomOnScroll?: boolean; - /** Controls if the viewport should zoom by pinching on a touch screen */ + /** + * Controls if the viewport should zoom by pinching on a touch screen. + * @default true + */ zoomOnPinch?: boolean; - /** Controls if the viewport should pan by scrolling inside the container - * - * Can be limited to a specific direction with panOnScrollMode + /** + * Controls if the viewport should pan by scrolling inside the container. + * Can be limited to a specific direction with `panOnScrollMode`. + * @default false */ panOnScroll?: boolean; - /** Controls how fast viewport should be panned on scroll. - * - * Use togther with panOnScroll prop. + /** + * Controls how fast viewport should be panned on scroll. + * Use together with `panOnScroll` prop. + * @default 0.5 */ panOnScrollSpeed?: number; - /** This prop is used to limit the direction of panning when panOnScroll is enabled. - * - * The "free" option allows panning in any direction. + /** + * This prop is used to limit the direction of panning when `panOnScroll` is enabled. + * The `"free"` option allows panning in any direction. * @default "free" * @example "horizontal" | "vertical" */ panOnScrollMode?: PanOnScrollMode; - /** Controls if the viewport should zoom by double clicking somewhere on the flow */ + /** + * Controls if the viewport should zoom by double-clicking somewhere on the flow. + * @default true + */ zoomOnDoubleClick?: boolean; + /** + * The radius around an edge connection that can trigger an edge reconnection. + * @default 10 + */ reconnectRadius?: number; + /** + * If a node is draggable, clicking and dragging that node will move it around the canvas. Adding + * the `"nodrag"` class prevents this behavior and this prop allows you to change the name of that + * class. + * @default "nodrag" + */ noDragClassName?: string; + /** + * Typically, scrolling the mouse wheel when the mouse is over the canvas will zoom the viewport. + * Adding the `"nowheel"` class to an element n the canvas will prevent this behavior and this prop + * allows you to change the name of that class. + * @default "nowheel" + */ noWheelClassName?: string; + /** + * If an element in the canvas does not stop mouse events from propagating, clicking and dragging + * that element will pan the viewport. Adding the `"nopan"` class prevents this behavior and this + * prop allows you to change the name of that class. + * @default "nopan" + */ noPanClassName?: string; - /** If set, initial viewport will show all nodes & edges */ + /** When `true`, the flow will be zoomed and panned to fit all the nodes initially provided. */ fitView?: boolean; - /** Options to be used in combination with fitView + /** + * When you typically call `fitView` on a `ReactFlowInstance`, you can provide an object of + * options to customize its behavior. This prop lets you do the same for the initial `fitView` + * call. * @example * const fitViewOptions = { * padding: 0.1, @@ -439,85 +567,103 @@ export interface ReactFlowProps true + * If you return `false`, the edge will not be added to your flow. + * If you have custom connection logic its preferred to use this callback over the + * `isValidConnection` prop on the handle component for performance reasons. */ - isValidConnection?: IsValidConnection; - /** With a threshold greater than zero you can control the distinction between node drag and click events. - * + isValidConnection?: IsValidConnection; + /** + * With a threshold greater than zero you can delay node drag events. * If threshold equals 1, you need to drag the node 1 pixel before a drag event is fired. + * 1 is the default value, so that clicks don't trigger drag events. * @default 1 */ nodeDragThreshold?: number; - /** Sets a fixed width for the flow */ + /** Sets a fixed width for the flow. */ width?: number; - /** Sets a fixed height for the flow */ + /** Sets a fixed height for the flow. */ height?: number; - /** Controls color scheme used for styling the flow - * @default 'system' + /** + * Controls color scheme used for styling the flow. + * @default 'light' * @example 'system' | 'light' | 'dark' */ colorMode?: ColorMode; - /** If set true, some debug information will be logged to the console like which events are fired. - * - * @default undefined + /** + * If set `true`, some debug information will be logged to the console like which events are fired. + * @default false */ debug?: boolean; } diff --git a/packages/react/src/types/edges.ts b/packages/react/src/types/edges.ts index 8b0cebab..a5c22c75 100644 --- a/packages/react/src/types/edges.ts +++ b/packages/react/src/types/edges.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import type { CSSProperties, HTMLAttributes, ReactNode, MouseEvent as ReactMouseEvent, ComponentType } from 'react'; +import type { CSSProperties, SVGAttributes, ReactNode, MouseEvent as ReactMouseEvent, ComponentType } from 'react'; import type { EdgeBase, BezierPathOptions, @@ -8,20 +8,29 @@ import type { SmoothStepPathOptions, DefaultEdgeOptionsBase, HandleType, - Connection, ConnectionLineType, Handle, EdgePosition, StepPathOptions, OnError, - ConnectionState, + OnReconnect, FinalConnectionState, } from '@xyflow/system'; import { EdgeTypes, InternalNode, Node } from '.'; +/** + * @inline + */ export type EdgeLabelOptions = { - label?: string | ReactNode; + /** + * The label or custom element to render along the edge. This is commonly a text label or some + * custom controls. + */ + label?: ReactNode; + /** + * Custom styles to apply to the label. + */ labelStyle?: CSSProperties; labelShowBg?: boolean; labelBgStyle?: CSSProperties; @@ -30,7 +39,8 @@ export type EdgeLabelOptions = { }; /** - * The Edge type is mainly used for the `edges` that get passed to the ReactFlow component + * An `Edge` is the complete description with everything React Flow needs + * to know in order to render it. * @public */ export type Edge< @@ -40,6 +50,11 @@ export type Edge< EdgeLabelOptions & { style?: CSSProperties; className?: string; + /** + * Determines whether the edge can be updated by dragging the source or target to a new node. + * This property will override the default set by the `edgesReconnectable` prop on the + * `` component. + */ reconnectable?: boolean | HandleType; focusable?: boolean; }; @@ -92,17 +107,26 @@ export type EdgeWrapperProps = { disableKeyboardA11y?: boolean; }; +/** + * Many properties on an [`Edge`](/api-reference/types/edge) are optional. When a new edge is created, + * the properties that are not provided will be filled in with the default values + * passed to the `defaultEdgeOptions` prop of the [``](/api-reference/react-flow#defaultedgeoptions) component. + */ export type DefaultEdgeOptions = DefaultEdgeOptionsBase; -export type EdgeTextProps = HTMLAttributes & +export type EdgeTextProps = Omit, 'x' | 'y'> & EdgeLabelOptions & { + /** The x position where the label should be rendered. */ x: number; + /** The y position where the label should be rendered. */ y: number; }; /** - * Custom edge component props + * When you implement a custom edge it is wrapped in a component that enables some + * basic functionality. The `EdgeProps` type is the props that are passed to this. * @public + * @expand */ export type EdgeProps = Pick< EdgeType, @@ -122,33 +146,42 @@ export type EdgeProps = Pick< /** * BaseEdge component props * @public + * @expand */ -export type BaseEdgeProps = EdgeLabelOptions & { - /** Unique id of edge */ - id?: string; - /** Additional padding where interacting with an edge is still possible */ - interactionWidth?: number; - className?: string; - /** The x position of edge label */ - labelX?: number; - /** The y position of edge label */ - labelY?: number; - /** Marker at start of edge - * @example 'url(#arrow)' - */ - markerStart?: string; - /** Marker at end of edge - * @example 'url(#arrow)' - */ - markerEnd?: string; - /** SVG path of the edge */ - path: string; - style?: CSSProperties; -}; +export type BaseEdgeProps = Omit, 'd' | 'path' | 'markerStart' | 'markerEnd'> & + EdgeLabelOptions & { + /** + * The width of the invisible area around the edge that the user can interact with. This is + * useful for making the edge easier to click or hover over. + * @default 20 + */ + interactionWidth?: number; + /** The x position of edge label */ + labelX?: number; + /** The y position of edge label */ + labelY?: number; + /** + * The SVG path string that defines the edge. This should look something like + * `'M 0 0 L 100 100'` for a simple line. The utility functions like `getSimpleBezierEdge` can + * be used to generate this string for you. + */ + path: string; + /** + * The id of the SVG marker to use at the start of the edge. This should be defined in a + * `` element in a separate SVG document or element. + */ + markerStart?: string; + /** + * The id of the SVG marker to use at the end of the edge. This should be defined in a `` + * element in a separate SVG document or element. + */ + markerEnd?: string; + }; /** * Helper type for edge components that get exported by the library * @public + * @expand */ export type EdgeComponentProps = EdgePosition & EdgeLabelOptions & { @@ -168,39 +201,51 @@ export type EdgeComponentWithPathOptions = EdgeComponentProps & { /** * BezierEdge component props * @public + * @expand */ export type BezierEdgeProps = EdgeComponentWithPathOptions; /** * SmoothStepEdge component props * @public + * @expand */ export type SmoothStepEdgeProps = EdgeComponentWithPathOptions; /** * StepEdge component props * @public + * @expand */ export type StepEdgeProps = EdgeComponentWithPathOptions; /** * StraightEdge component props * @public + * @expand */ export type StraightEdgeProps = Omit; /** * SimpleBezier component props * @public + * @expand */ export type SimpleBezierEdgeProps = EdgeComponentProps; -export type OnReconnect = (oldEdge: EdgeType, newConnection: Connection) => void; - +/** + * If you want to render a custom component for connection lines, you can set the + * `connectionLineComponent` prop on the [``](/api-reference/react-flow#connection-connectionLineComponent) + * component. The `ConnectionLineComponentProps` are passed to your custom component. + * + * @public + */ export type ConnectionLineComponentProps = { connectionLineStyle?: CSSProperties; connectionLineType: ConnectionLineType; + /** The node the connection line originates from. */ fromNode: InternalNode; + /** The handle on the `fromNode` that the connection line originates from. */ fromHandle: Handle; fromX: number; fromY: number; @@ -208,6 +253,10 @@ export type ConnectionLineComponentProps = { toY: number; fromPosition: Position; toPosition: Position; + /** + * If there is an `isValidConnection` callback, this prop will be set to `"valid"` or `"invalid"` + * based on the return value of that callback. Otherwise, it will be `null`. + */ connectionStatus: 'valid' | 'invalid' | null; toNode: InternalNode | null; toHandle: Handle | null; diff --git a/packages/react/src/types/general.ts b/packages/react/src/types/general.ts index 48e5b2fe..80acc45c 100644 --- a/packages/react/src/types/general.ts +++ b/packages/react/src/types/general.ts @@ -18,11 +18,44 @@ import { import type { Node, Edge, ReactFlowInstance, EdgeProps, NodeProps } from '.'; +/** + * This type can be used to type the `onNodesChange` function with a custom node type. + * + * @public + * + * @example + * + * ```ts + * const onNodesChange: OnNodesChange = useCallback((changes) => { + * setNodes((nodes) => applyNodeChanges(nodes, changes)); + * },[]); + * ``` + */ export type OnNodesChange = (changes: NodeChange[]) => void; + +/** + * This type can be used to type the `onEdgesChange` function with a custom edge type. + * + * @public + * + * @example + * + * ```ts + * const onEdgesChange: OnEdgesChange = useCallback((changes) => { + * setEdges((edges) => applyEdgeChanges(edges, changes)); + * },[]); + * ``` + */ export type OnEdgesChange = (changes: EdgeChange[]) => void; export type OnNodesDelete = (nodes: NodeType[]) => void; export type OnEdgesDelete = (edges: EdgeType[]) => void; + +/** + * This type can be used to type the `onDelete` function with a custom node and edge type. + * + * @public + */ export type OnDelete = (params: { nodes: NodeType[]; edges: EdgeType[]; @@ -39,6 +72,7 @@ export type NodeTypes = Record< } > >; + export type EdgeTypes = Record< string, ComponentType< @@ -51,25 +85,38 @@ export type EdgeTypes = Record< > >; -export type UnselectNodesAndEdgesParams = { - nodes?: Node[]; - edges?: Edge[]; +export type UnselectNodesAndEdgesParams = { + nodes?: NodeType[]; + edges?: EdgeType[]; }; -export type OnSelectionChangeParams = { - nodes: Node[]; - edges: Edge[]; +export type OnSelectionChangeParams = { + nodes: NodeType[]; + edges: EdgeType[]; }; -export type OnSelectionChangeFunc = (params: OnSelectionChangeParams) => void; +export type OnSelectionChangeFunc = ( + params: OnSelectionChangeParams +) => void; export type FitViewParams = FitViewParamsBase; + +/** + * When calling [`fitView`](/api-reference/types/react-flow-instance#fitview) these options + * can be used to customize the behaviour. For example, the `duration` option can be used to + * transform the viewport smoothly over a given amount of time. + * + * @public + */ export type FitViewOptions = FitViewOptionsBase; -export type FitView = (fitViewOptions?: FitViewOptions) => Promise; +export type FitView = (fitViewOptions?: FitViewOptions) => Promise; export type OnInit = ( reactFlowInstance: ReactFlowInstance ) => void; +/** + * @inline + */ export type ViewportHelperFunctions = { /** * Zooms viewport in by 1.2. @@ -84,14 +131,15 @@ export type ViewportHelperFunctions = { */ zoomOut: ZoomInOut; /** - * Sets the current zoom level. + * Zoom the viewport to a given zoom level. Passing in a `duration` will animate the viewport to + * the new zoom level. * * @param zoomLevel - the zoom level to set * @param options.duration - optional duration. If set, a transition will be applied */ zoomTo: ZoomTo; /** - * Returns the current zoom level. + * Get the current zoom level of the viewport. * * @returns current zoom as a number */ @@ -101,6 +149,7 @@ export type ViewportHelperFunctions = { * * @param viewport - the viewport to set * @param options.duration - optional duration. If set, a transition will be applied + * @param options.ease - optional ease function. */ setViewport: SetViewport; /** @@ -110,33 +159,30 @@ export type ViewportHelperFunctions = { */ 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. + * Center the viewport on a given position. Passing in a `duration` will animate the viewport to + * the new position. * * @param x - x position * @param y - y position * @param options.zoom - optional zoom + * @param options.duration - optional duration. If set, a transition will be applied + * @param options.ease - optional ease function. */ setCenter: SetCenter; /** - * Fits the view to the given bounds . + * A low-level utility function to fit the viewport to a given rectangle. By passing in a + * `duration`, the viewport will animate from its current position to the new position. The + * `padding` option can be used to add space around the bounds. * * @param bounds - the bounds ({ x: number, y: number, width: number, height: number }) to fit the view to * @param options.padding - optional padding + * @param options.duration - optional duration. If set, a transition will be applied + * @param options.ease - optional ease function. */ fitBounds: FitBounds; /** - * Converts a screen / client position to a flow position. + * With this function you can translate a screen pixel position to a flow position. It is useful + * for implementing drag and drop from a sidebar for example. * * @param clientPosition - the screen / client position. When you are working with events you can use event.clientX and event.clientY * @param options.snapToGrid - if true, the converted position will be snapped to the grid @@ -147,7 +193,7 @@ export type ViewportHelperFunctions = { */ screenToFlowPosition: (clientPosition: XYPosition, options?: { snapToGrid: boolean }) => XYPosition; /** - * Converts a flow position to a screen / client position. + * Translate a position inside the flow's canvas to a screen pixel position. * * @param flowPosition - the screen / client position. When you are working with events you can use event.clientX and event.clientY * @returns position as { x: number, y: number } diff --git a/packages/react/src/types/instance.ts b/packages/react/src/types/instance.ts index fdece4dd..d001486a 100644 --- a/packages/react/src/types/instance.ts +++ b/packages/react/src/types/instance.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/no-namespace */ -import type { HandleConnection, HandleType, Rect, Viewport } from '@xyflow/system'; -import type { Node, Edge, ViewportHelperFunctions, InternalNode } from '.'; +import type { HandleConnection, HandleType, NodeConnection, Rect, Viewport } from '@xyflow/system'; +import type { Node, Edge, ViewportHelperFunctions, InternalNode, FitView } from '.'; export type ReactFlowJsonObject = { nodes: NodeType[]; @@ -13,6 +13,9 @@ export type DeleteElementsOptions = { edges?: (Edge | { id: Edge['id'] })[]; }; +/** + * @inline + */ export type GeneralHelpers = { /** * Returns nodes. @@ -21,13 +24,17 @@ export type GeneralHelpers NodeType[]; /** - * Sets nodes. + * Set your nodes array to something else by either overwriting it with a new array or by passing + * in a function to update the existing array. If using a function, it is important to make sure a + * new array is returned instead of mutating the existing array. Calling this function will + * trigger the `onNodesChange` handler in a controlled flow. * * @param payload - the nodes to set or a function that receives the current nodes and returns the new nodes */ setNodes: (payload: NodeType[] | ((nodes: NodeType[]) => NodeType[])) => void; /** - * Adds nodes. + * Add one or many nodes to your existing nodes array. Calling this function will trigger the + * `onNodesChange` handler in a controlled flow. * * @param payload - the nodes to add */ @@ -53,13 +60,17 @@ export type GeneralHelpers EdgeType[]; /** - * Sets edges. + * Set your edges array to something else by either overwriting it with a new array or by passing + * in a function to update the existing array. If using a function, it is important to make sure a + * new array is returned instead of mutating the existing array. Calling this function will + * trigger the `onEdgesChange` handler in a controlled flow. * * @param payload - the edges to set or a function that receives the current edges and returns the new edges */ setEdges: (payload: EdgeType[] | ((edges: EdgeType[]) => EdgeType[])) => void; /** - * Adds edges. + * Add one or many edges to your existing edges array. Calling this function will trigger the + * `onEdgesChange` handler in a controlled flow. * * @param payload - the edges to add */ @@ -90,7 +101,8 @@ export type GeneralHelpers; /** - * Returns all nodes that intersect with the given node or rect. + * Find all the nodes currently intersecting with a given node or rectangle. The `partially` + * parameter can be set to `true` to include nodes that are only partially intersecting. * * @param node - the node or rect to check for intersections * @param partially - if true, the node is considered to be intersecting if it partially overlaps with the passed node or rect @@ -104,7 +116,8 @@ export type GeneralHelpers NodeType[]; /** - * Checks if the given node or rect intersects with the passed rect. + * Determine if a given node or rectangle is intersecting with another rectangle. The `partially` + * parameter can be set to true return `true` even if the node is only partially intersecting. * * @param node - the node or rect to check for intersections * @param area - the rect to check for intersections @@ -182,8 +195,9 @@ export type GeneralHelpers Rect; /** - * Gets all connections for a given handle belonging to a specific node. - * + * Get all the connections of a handle belonging to a specific node. The type parameter be either + * `'source'` or `'target'`. + * @deprecated * @param type - handle type 'source' or 'target' * @param id - the handle id (this is only needed if you have multiple handles of the same type, meaning you have to provide a unique id for each handle) * @param nodeId - the node id the handle belongs to @@ -198,12 +212,50 @@ export type GeneralHelpers HandleConnection[]; + /** + * Gets all connections to a node. Can be filtered by handle type and id. + * @param type - handle type 'source' or 'target' + * @param handleId - the handle id (this is only needed if you have multiple handles of the same type, meaning you have to provide a unique id for each handle) + * @param nodeId - the node id the handle belongs to + * @returns an array with handle connections + */ + getNodeConnections: ({ + type, + handleId, + nodeId, + }: { + type?: HandleType; + nodeId: string; + handleId?: string | null; + }) => 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; }; - +/** + * The `ReactFlowInstance` provides a collection of methods to query and manipulate + * the internal state of your flow. You can get an instance by using the + * [`useReactFlow`](/api-reference/hooks/use-react-flow) hook or attaching a listener + * to the [`onInit`](/api-reference/react-flow#event-oninit) event. + * + * @public + */ export type ReactFlowInstance = GeneralHelpers< NodeType, EdgeType > & - Omit & { + ViewportHelperFunctions & { + /** + * React Flow needs to mount the viewport to the DOM and initialize its zoom and pan behavior. + * This property tells you when viewport is initialized. + */ viewportInitialized: boolean; }; diff --git a/packages/react/src/types/nodes.ts b/packages/react/src/types/nodes.ts index 4307742d..9214778c 100644 --- a/packages/react/src/types/nodes.ts +++ b/packages/react/src/types/nodes.ts @@ -4,7 +4,10 @@ import type { CoordinateExtent, NodeBase, OnError, NodeProps as NodePropsBase, I import { NodeTypes } from './general'; /** - * The node data structure that gets used for the nodes prop. + * The `Node` type represents everything React Flow needs to know about a given node. + * Whenever you want to update a certain attribute of a node, you need to create a new + * node object. + * * @public */ export type Node< @@ -18,9 +21,10 @@ export type Node< }; /** - * The node data structure that gets used for internal nodes. - * There are some data structures added under node.internal - * that are needed for tracking some properties + * The `InternalNode` type is identical to the base [`Node`](/api-references/types/node) + * type but is extended with some additional properties used internally. + * Some functions and callbacks that return nodes may return an `InternalNode`. + * * @public */ export type InternalNode = InternalNodeBase; @@ -56,6 +60,46 @@ export type NodeWrapperProps = { nodeClickDistance?: number; }; -export type BuiltInNode = Node<{ label: string }, 'input' | 'output' | 'default'>; +/** + * The `BuiltInNode` type represents the built-in node types that are available in React Flow. + * You can use this type to extend your custom node type if you still want ot use the built-in ones. + * + * @public + * @example + * ```ts + * type CustomNode = Node<{ value: number }, 'custom'>; + * type MyAppNode = CustomNode | BuiltInNode; + * ``` + */ +export type BuiltInNode = + | Node<{ label: string }, 'input' | 'output' | 'default'> + | Node, 'group'>; +/** + * When you implement a [custom node](/learn/customization/custom-nodes) it is + * wrapped in a component that enables basic functionality like selection and + * dragging. Your custom node receives `NodeProps` as props. + * + * @public + * @example + * ```tsx + *import { useState } from 'react'; + *import { NodeProps, Node } from '@xyflow/react'; + * + *export type CounterNode = Node<{ initialCount?: number }, 'counter'>; + * + *export default function CounterNode(props: NodeProps) { + * const [count, setCount] = useState(props.data?.initialCount ?? 0); + * + * return ( + *
+ *

Count: {count}

+ * + *
+ * ); + *} + *``` + */ export type NodeProps = NodePropsBase; diff --git a/packages/react/src/types/store.ts b/packages/react/src/types/store.ts index b060be36..1e10fe06 100644 --- a/packages/react/src/types/store.ts +++ b/packages/react/src/types/store.ts @@ -1,5 +1,6 @@ import { ConnectionMode, + withResolvers, type ConnectionState, type CoordinateExtent, type InternalNodeUpdate, @@ -53,6 +54,7 @@ export type ReactFlowStore>; parentLookup: ParentLookup>; edges: EdgeType[]; @@ -119,9 +121,9 @@ export type ReactFlowStore> | null; onNodesDelete?: OnNodesDelete; onEdgesDelete?: OnEdgesDelete; @@ -134,7 +136,7 @@ export type ReactFlowStore; - onSelectionChangeHandlers: OnSelectionChangeFunc[]; + onSelectionChangeHandlers: OnSelectionChangeFunc[]; ariaLiveMessage: string; autoPanOnConnect: boolean; @@ -155,7 +157,7 @@ export type ReactFlowActions = { updateNodeInternals: (updates: Map, params?: { triggerFitView: boolean }) => void; updateNodePositions: UpdateNodePositions; resetSelectedElements: () => void; - unselectNodesAndEdges: (params?: UnselectNodesAndEdgesParams) => void; + unselectNodesAndEdges: (params?: UnselectNodesAndEdgesParams) => void; addSelectedNodes: (nodeIds: string[]) => void; addSelectedEdges: (edgeIds: string[]) => void; setMinZoom: (minZoom: number) => void; @@ -168,8 +170,6 @@ export type ReactFlowActions = { triggerNodeChanges: (changes: NodeChange[]) => void; triggerEdgeChanges: (changes: EdgeChange[]) => void; panBy: PanBy; - fitView: (options?: FitViewOptions) => Promise; - fitViewSync: (options?: FitViewOptions) => boolean; setPaneClickDistance: (distance: number) => void; }; diff --git a/packages/react/src/utils/changes.ts b/packages/react/src/utils/changes.ts index 6de98e3a..f2d9bdd3 100644 --- a/packages/react/src/utils/changes.ts +++ b/packages/react/src/utils/changes.ts @@ -11,13 +11,17 @@ import { } from '@xyflow/system'; import type { Node, Edge, InternalNode } from '../types'; -// This function applies changes to nodes or edges that are triggered by React Flow internally. -// When you drag a node for example, React Flow will send a position change update. -// This function then applies the changes and returns the updated elements. +/* + * This function applies changes to nodes or edges that are triggered by React Flow internally. + * When you drag a node for example, React Flow will send a position change update. + * This function then applies the changes and returns the updated elements. + */ function applyChanges(changes: any[], elements: any[]): any[] { const updatedElements: any[] = []; - // By storing a map of changes for each element, we can a quick lookup as we - // iterate over the elements array! + /* + * By storing a map of changes for each element, we can a quick lookup as we + * iterate over the elements array! + */ const changesMap = new Map(); const addItemChanges: any[] = []; @@ -26,15 +30,19 @@ function applyChanges(changes: any[], elements: any[]): any[] { addItemChanges.push(change); continue; } else if (change.type === 'remove' || change.type === 'replace') { - // For a 'remove' change we can safely ignore any other changes queued for - // the same element, it's going to be removed anyway! + /* + * For a 'remove' change we can safely ignore any other changes queued for + * the same element, it's going to be removed anyway! + */ changesMap.set(change.id, [change]); } else { const elementChanges = changesMap.get(change.id); if (elementChanges) { - // If we have some changes queued already, we can do a mutable update of - // that array and save ourselves some copying. + /* + * If we have some changes queued already, we can do a mutable update of + * that array and save ourselves some copying. + */ elementChanges.push(change); } else { changesMap.set(change.id, [change]); @@ -45,8 +53,10 @@ function applyChanges(changes: any[], elements: any[]): any[] { for (const element of elements) { const changes = changesMap.get(element.id); - // When there are no changes for an element we can just push it unmodified, - // no need to copy it. + /* + * When there are no changes for an element we can just push it unmodified, + * no need to copy it. + */ if (!changes) { updatedElements.push(element); continue; @@ -62,9 +72,11 @@ function applyChanges(changes: any[], elements: any[]): any[] { continue; } - // For other types of changes, we want to start with a shallow copy of the - // object so React knows this element has changed. Sequential changes will - /// each _mutate_ this object, so there's only ever one copy. + /** + * For other types of changes, we want to start with a shallow copy of the + * object so React knows this element has changed. Sequential changes will + * each _mutate_ this object, so there's only ever one copy. + */ const updatedElement = { ...element }; for (const change of changes) { @@ -74,8 +86,10 @@ function applyChanges(changes: any[], elements: any[]): any[] { updatedElements.push(updatedElement); } - // we need to wait for all changes to be applied before adding new items - // to be able to add them at the correct index + /* + * we need to wait for all changes to be applied before adding new items + * to be able to add them at the correct index + */ if (addItemChanges.length) { addItemChanges.forEach((change) => { if (change.index !== undefined) { @@ -116,8 +130,12 @@ function applyChange(change: any, element: any): any { element.measured.height = change.dimensions.height; if (change.setAttributes) { - element.width = change.dimensions.width; - element.height = change.dimensions.height; + if (change.setAttributes === true || change.setAttributes === 'width') { + element.width = change.dimensions.width; + } + if (change.setAttributes === true || change.setAttributes === 'height') { + element.height = change.dimensions.height; + } } } @@ -133,22 +151,33 @@ function applyChange(change: any, element: any): any { /** * Drop in function that applies node changes to an array of nodes. * @public - * @remarks Various events on the component can produce an {@link NodeChange} that describes how to update the edges of your flow in some way. - If you don't need any custom behaviour, this util can be used to take an array of these changes and apply them to your edges. - * @param changes - Array of changes to apply - * @param nodes - Array of nodes to apply the changes to - * @returns Array of updated nodes + * @param changes - Array of changes to apply. + * @param nodes - Array of nodes to apply the changes to. + * @returns Array of updated nodes. * @example - * const onNodesChange = useCallback( - (changes) => { - setNodes((oldNodes) => applyNodeChanges(changes, oldNodes)); - }, - [setNodes], - ); - - return ( - - ); + *```tsx + *import { useState, useCallback } from 'react'; + *import { ReactFlow, applyNodeChanges, type Node, type Edge, type OnNodesChange } from '@xyflow/react'; + * + *export default function Flow() { + * const [nodes, setNodes] = useState([]); + * const [edges, setEdges] = useState([]); + * const onNodesChange: OnNodesChange = useCallback( + * (changes) => { + * setNodes((oldNodes) => applyNodeChanges(changes, oldNodes)); + * }, + * [setNodes], + * ); + * + * return ( + * + * ); + *} + *``` + * @remarks Various events on the component can produce an {@link NodeChange} + * that describes how to update the edges of your flow in some way. + * If you don't need any custom behaviour, this util can be used to take an array + * of these changes and apply them to your edges. */ export function applyNodeChanges( changes: NodeChange[], @@ -160,22 +189,33 @@ export function applyNodeChanges( /** * Drop in function that applies edge changes to an array of edges. * @public - * @remarks Various events on the component can produce an {@link EdgeChange} that describes how to update the edges of your flow in some way. - If you don't need any custom behaviour, this util can be used to take an array of these changes and apply them to your edges. - * @param changes - Array of changes to apply - * @param edges - Array of edge to apply the changes to - * @returns Array of updated edges + * @param changes - Array of changes to apply. + * @param edges - Array of edge to apply the changes to. + * @returns Array of updated edges. * @example + * ```tsx + *import { useState, useCallback } from 'react'; + *import { ReactFlow, applyEdgeChanges } from '@xyflow/react'; + * + *export default function Flow() { + * const [nodes, setNodes] = useState([]); + * const [edges, setEdges] = useState([]); * const onEdgesChange = useCallback( - (changes) => { - setEdges((oldEdges) => applyEdgeChanges(changes, oldEdges)); - }, - [setEdges], - ); - - return ( - - ); + * (changes) => { + * setEdges((oldEdges) => applyEdgeChanges(changes, oldEdges)); + * }, + * [setEdges], + * ); + * + * return ( + * + * ); + *} + *``` + * @remarks Various events on the component can produce an {@link EdgeChange} + * that describes how to update the edges of your flow in some way. + * If you don't need any custom behaviour, this util can be used to take an array + * of these changes and apply them to your edges. */ export function applyEdgeChanges( changes: EdgeChange[], @@ -205,9 +245,11 @@ export function getSelectionChanges( // we don't want to set all items to selected=false on the first selection if (!(item.selected === undefined && !willBeSelected) && item.selected !== willBeSelected) { if (mutateItem) { - // this hack is needed for nodes. When the user dragged a node, it's selected. - // When another node gets dragged, we need to deselect the previous one, - // in order to have only one selected node at a time - the onNodesChange callback comes too late here :/ + /* + * this hack is needed for nodes. When the user dragged a node, it's selected. + * When another node gets dragged, we need to deselect the previous one, + * in order to have only one selected node at a time - the onNodesChange callback comes too late here :/ + */ item.selected = willBeSelected; } changes.push(createSelectionChange(item.id, willBeSelected)); diff --git a/packages/react/src/utils/general.ts b/packages/react/src/utils/general.ts index 04608f74..c7c8a503 100644 --- a/packages/react/src/utils/general.ts +++ b/packages/react/src/utils/general.ts @@ -1,29 +1,57 @@ -import { type Ref, type RefAttributes, forwardRef } from 'react'; +import { type Ref, type RefAttributes, forwardRef, JSX } from 'react'; import { isNodeBase, isEdgeBase } from '@xyflow/system'; import type { Edge, Node } from '../types'; /** - * Test whether an object is useable as a Node + * Test whether an object is usable as an [`Node`](/api-reference/types/node). + * In TypeScript this is a type guard that will narrow the type of whatever you pass in to + * [`Node`](/api-reference/types/node) if it returns `true`. + * * @public * @remarks In TypeScript this is a type guard that will narrow the type of whatever you pass in to Node if it returns true - * @param element - The element to test - * @returns A boolean indicating whether the element is an Node + * @param element - The element to test. + * @returns Tests whether the provided value can be used as a `Node`. If you're using TypeScript, + * this function acts as a type guard and will narrow the type of the value to `Node` if it returns + * `true`. + * + * @example + * ```js + *import { isNode } from '@xyflow/react'; + * + *if (isNode(node)) { + * // ... + *} + *``` */ export const isNode = (element: unknown): element is NodeType => isNodeBase(element); /** - * Test whether an object is useable as an Edge + * Test whether an object is usable as an [`Edge`](/api-reference/types/edge). + * In TypeScript this is a type guard that will narrow the type of whatever you pass in to + * [`Edge`](/api-reference/types/edge) if it returns `true`. + * * @public * @remarks In TypeScript this is a type guard that will narrow the type of whatever you pass in to Edge if it returns true * @param element - The element to test - * @returns A boolean indicating whether the element is an Edge + * @returns Tests whether the provided value can be used as an `Edge`. If you're using TypeScript, + * this function acts as a type guard and will narrow the type of the value to `Edge` if it returns + * `true`. + * + * @example + * ```js + *import { isEdge } from '@xyflow/react'; + * + *if (isEdge(edge)) { + * // ... + *} + *``` */ export const isEdge = (element: unknown): element is EdgeType => isEdgeBase(element); -// eslint-disable-next-line @typescript-eslint/ban-types +// eslint-disable-next-line @typescript-eslint/no-empty-object-type export function fixedForwardRef( render: (props: P, ref: Ref) => JSX.Element ): (props: P & RefAttributes) => JSX.Element { diff --git a/packages/svelte/.eslintignore b/packages/svelte/.eslintignore deleted file mode 100644 index c4563de9..00000000 --- a/packages/svelte/.eslintignore +++ /dev/null @@ -1,14 +0,0 @@ -.DS_Store -node_modules -/build -/.svelte-kit -dist -/package -.env -.env.* -!.env.example - -# Ignore files for PNPM, NPM and YARN -pnpm-lock.yaml -package-lock.json -yarn.lock diff --git a/packages/svelte/.eslintrc.cjs b/packages/svelte/.eslintrc.cjs deleted file mode 100644 index 4b29a016..00000000 --- a/packages/svelte/.eslintrc.cjs +++ /dev/null @@ -1,30 +0,0 @@ -module.exports = { - root: true, - extends: [ - 'eslint:recommended', - 'plugin:@typescript-eslint/recommended', - 'plugin:svelte/recommended', - 'prettier' - ], - parser: '@typescript-eslint/parser', - plugins: ['@typescript-eslint'], - parserOptions: { - sourceType: 'module', - ecmaVersion: 2020, - extraFileExtensions: ['.svelte'] - }, - env: { - browser: true, - es2017: true, - node: true - }, - overrides: [ - { - files: ['*.svelte'], - parser: 'svelte-eslint-parser', - parserOptions: { - parser: '@typescript-eslint/parser' - } - } - ] -}; diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index e1c3cc73..4b7b5d7e 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,230 @@ # @xyflow/svelte +## 1.0.2 + +### Patch Changes + +- [#5266](https://github.com/xyflow/xyflow/pull/5266) [`77107453`](https://github.com/xyflow/xyflow/commit/77107453fa6f34cb08ef91640b8b02d58e31275e) Thanks [@peterkogo](https://github.com/peterkogo)! - Fix connection snapping for handles larger than connectionRadius + +- [#5267](https://github.com/xyflow/xyflow/pull/5267) [`d9f0ffbe`](https://github.com/xyflow/xyflow/commit/d9f0ffbe5cfa5e46197574456635fab7554d3b44) Thanks [@peterkogo](https://github.com/peterkogo)! - Make Background, Panel, Minimap work outside of SvelteFlow component + +- Updated dependencies [[`77107453`](https://github.com/xyflow/xyflow/commit/77107453fa6f34cb08ef91640b8b02d58e31275e)]: + - @xyflow/system@0.0.61 + +## 1.0.1 + +### Patch Changes + +- [#5258](https://github.com/xyflow/xyflow/pull/5258) [`47f7a0b6`](https://github.com/xyflow/xyflow/commit/47f7a0b6f2c347aa87d470ab53c9fdc6e02dc067) Thanks [@peterkogo](https://github.com/peterkogo)! - Fix fitView not working when accessed through the SvelteFlowProvider + +- [#5259](https://github.com/xyflow/xyflow/pull/5259) [`77bf79c4`](https://github.com/xyflow/xyflow/commit/77bf79c40e71e3da449ace3b1a1ed5bceff46b51) Thanks [@peterkogo](https://github.com/peterkogo)! - Fix background-color css variable fallback. + +- Updated dependencies [[`77bf79c4`](https://github.com/xyflow/xyflow/commit/77bf79c40e71e3da449ace3b1a1ed5bceff46b51)]: + - @xyflow/system@0.0.60 + +## 1.0.0 + +Yeah! Svelte Flow 1.0. is here 🚀 It is based on Svelte 5 and introduces a lot of new features and improvements. Please refer to the [migration guide](https://svelteflow.dev/learn/troubleshooting/migrate-to-v1) to see how to upgrade. + +### New features + +- **[Reconnect edges](https://svelteflow.dev/examples/edges/reconnect-edge)**: You can reconnect your edges by using the new `` component. It can be used to add custom reconnection points on custom edges. +- **Keyboard navigation & A11y**: We added support for keyboard navigation and improved accessibility for screen readers. You can now tab through nodes and edges and move nodes with the arrow keys. Can be disabled via [**disableKeyboardA11y**](https://svelteflow.dev/api-reference/svelte-flow#disablekeyboarda11y) +- **[Click connect](https://svelteflow.dev/examples/edges/click-connect)**: You can now create a new connection by clicking on a handle one by one. +- **[Enhanced ViewportPortal](https://svelteflow.dev/api-reference/components/viewport-portal)**: You can now decide if you want to render something below or above the nodes & edges in the viewport. +- **Improved [fitView](https://svelteflow.dev/api-reference/hooks/use-svelte-flow#fitview)**: We finetuned the `fitView` function to better work with dynamically added nodes. +- **colorModeSSR** prop: You can pass a fallback color mode for server side rendering when colorMode is set to 'system'. +- [**elevateNodesOnSelect**](https://svelteflow.dev/api-reference/svelte-flow#elevateNodesOnSelect) & [**elevateEdgesOnSelect**](https://svelteflow.dev/api-reference/svelte-flow#elevateEdgesOnSelect): Control if nodes & edges should be elevated via z-index when selected. +- [**noDragClass, noWheelClass, noPanClass**](https://svelteflow.dev/api-reference/svelte-flow#style-props): You can now modify the class name used to disable dragging, panning and zooming. +- [**onselectionchange**](https://svelteflow.dev/api-reference/svelte-flow#onselectionchange) & [**useOnSelectionChange**](https://svelteflow.dev/api-reference/hooks/use-on-selection-change): You can now listen to selection changes via a callback + +## 0.1.39 + +### Patch Changes + +- Updated dependencies [[`a95f0e2f`](https://github.com/xyflow/xyflow/commit/a95f0e2fbfc2d070d9bd70b753d1606a87332e3f)]: + - @xyflow/system@0.0.59 + +## 0.1.38 + +### Patch Changes + +- Updated dependencies [[`2a03213b`](https://github.com/xyflow/xyflow/commit/2a03213b0695d504f831579ec9df3f9de2d3e0bd)]: + - @xyflow/system@0.0.58 + +## 0.1.37 + +### Patch Changes + +- Updated dependencies [[`a7d10ffc`](https://github.com/xyflow/xyflow/commit/a7d10ffce5a0195471681980f97b1b5f6c448f35), [`4e681f9c`](https://github.com/xyflow/xyflow/commit/4e681f9c529c3f4f8b2ac5d25b4db7878c197e14)]: + - @xyflow/system@0.0.57 + +## 0.1.36 + +### Patch Changes + +- [#5212](https://github.com/xyflow/xyflow/pull/5212) [`0f43b8ea`](https://github.com/xyflow/xyflow/commit/0f43b8ea45bd293e50e4a86d83868074bb323f13) Thanks [@moklick](https://github.com/moklick)! - Add polyfill for `Promise.withResolvers` + +- [#5191](https://github.com/xyflow/xyflow/pull/5191) [`e5735b51`](https://github.com/xyflow/xyflow/commit/e5735b514a54d86ba0ca7bd725e8bfead89fc08e) Thanks [@peterkogo](https://github.com/peterkogo)! - Fix legacy padding being slightly larger than before + +- Updated dependencies [[`0f43b8ea`](https://github.com/xyflow/xyflow/commit/0f43b8ea45bd293e50e4a86d83868074bb323f13), [`fc241253`](https://github.com/xyflow/xyflow/commit/fc241253d5dba35f5febf411e77dbc5acb91d5d7), [`98fe23c7`](https://github.com/xyflow/xyflow/commit/98fe23c7c2b12972f1b7def866215ce82a86e2c0), [`e5735b51`](https://github.com/xyflow/xyflow/commit/e5735b514a54d86ba0ca7bd725e8bfead89fc08e)]: + - @xyflow/system@0.0.56 + +## 0.1.35 + +### Patch Changes + +- [#5158](https://github.com/xyflow/xyflow/pull/5158) [`06696060`](https://github.com/xyflow/xyflow/commit/0669606050bb2138a44a1591176ac8e16afeb0f1) Thanks [@dimaMachina](https://github.com/dimaMachina)! - Fix typo in TSDoc comments `React Flow` -> `Svelte Flow` + +- Updated dependencies [[`02a3b746`](https://github.com/xyflow/xyflow/commit/02a3b74645799a3f0ce670b69365fa86ecb0616e), [`cbe305e1`](https://github.com/xyflow/xyflow/commit/cbe305e15a5c5d3b92583e0ec12364b2509f49bd), [`1f671bd4`](https://github.com/xyflow/xyflow/commit/1f671bd48f06230da841fdd1d7a312413ef16d03), [`aaebc462`](https://github.com/xyflow/xyflow/commit/aaebc462951ded8e91374c3e084d77af5ed7380a), [`6ec942fc`](https://github.com/xyflow/xyflow/commit/6ec942fc6501f81009c278cc995764bef3e8d03b)]: + - @xyflow/system@0.0.55 + +## 0.1.34 + +### Patch Changes + +- [#5139](https://github.com/xyflow/xyflow/pull/5139) [`89de9ca8`](https://github.com/xyflow/xyflow/commit/89de9ca83fbf9263a687a0f5f915efb2beb31654) Thanks [@dimaMachina](https://github.com/dimaMachina)! - Use `rgba` for colors with opacity instead of `rgb` for `MiniMap` mask color + +- Updated dependencies [[`f819005b`](https://github.com/xyflow/xyflow/commit/f819005be362d044b16ce4c0b85432f3f300a13a), [`24a1bc89`](https://github.com/xyflow/xyflow/commit/24a1bc89348817ed9b5c87f74bf2519c705143be), [`36657cd6`](https://github.com/xyflow/xyflow/commit/36657cd66322c911e87eb37275c584a80025adfe), [`89de9ca8`](https://github.com/xyflow/xyflow/commit/89de9ca83fbf9263a687a0f5f915efb2beb31654), [`2ac6e155`](https://github.com/xyflow/xyflow/commit/2ac6e155e35256ca436281df16344366e7d05761), [`f0f378e5`](https://github.com/xyflow/xyflow/commit/f0f378e5b6918c2c30d9dc1e32587063cb942d4e)]: + - @xyflow/system@0.0.54 + +## 0.1.33 + +### Patch Changes + +- [#5124](https://github.com/xyflow/xyflow/pull/5124) [`b76f7f9e`](https://github.com/xyflow/xyflow/commit/b76f7f9eb4841f139b1468b8eda0430ddd19a1ae) Thanks [@bjornosal](https://github.com/bjornosal)! - Export NodeConnection type + +## 0.1.32 + +### Patch Changes + +- [#5059](https://github.com/xyflow/xyflow/pull/5059) [`065ff89d`](https://github.com/xyflow/xyflow/commit/065ff89d10488f9c76c56870511e45eaed299778) Thanks [@bcakmakoglu](https://github.com/bcakmakoglu)! - Prevent onPaneClick when connection is in progress. Closes [#5057](https://github.com/xyflow/xyflow/issues/5057) + +- [#5067](https://github.com/xyflow/xyflow/pull/5067) [`cb685281`](https://github.com/xyflow/xyflow/commit/cb685281d0eaf03e9833271c31f92b1d143af2fe) Thanks [@peterkogo](https://github.com/peterkogo)! - Fix fitView not working immediately after adding new nodes + +- [#5093](https://github.com/xyflow/xyflow/pull/5093) [`65825e89`](https://github.com/xyflow/xyflow/commit/65825e89a6e2e7591087eb41ac89da4da7095f8f) Thanks [@moklick](https://github.com/moklick)! - Hidden nodes are not displayed in the mini map anymore + +- Updated dependencies [[`5d15b01b`](https://github.com/xyflow/xyflow/commit/5d15b01ba8cb349d6397a6ed8162848b4dfec293), [`cb685281`](https://github.com/xyflow/xyflow/commit/cb685281d0eaf03e9833271c31f92b1d143af2fe), [`a79f30b3`](https://github.com/xyflow/xyflow/commit/a79f30b3dd7c8ff6400c8d22214b2c2282e5bac1)]: + - @xyflow/system@0.0.53 + +## 0.1.31 + +### Patch Changes + +- [#5019](https://github.com/xyflow/xyflow/pull/5019) [`3e80317c`](https://github.com/xyflow/xyflow/commit/3e80317cf6da0e9fdc111c3ade88f2a88a10dbd6) Thanks [@dimaMachina](https://github.com/dimaMachina)! - Add `"./package.json" to the `exports` field so that users can import it + +- Updated dependencies [[`99dd7d35`](https://github.com/xyflow/xyflow/commit/99dd7d3549e7423e7d103b2c956c8b37f5747b90)]: + - @xyflow/system@0.0.52 + +## 0.1.30 + +### Patch Changes + +- [#5010](https://github.com/xyflow/xyflow/pull/5010) [`6c121d42`](https://github.com/xyflow/xyflow/commit/6c121d427fea9a11e86a85f95d2c12ba8af34919) Thanks [@moklick](https://github.com/moklick)! - Add more TSDocs to components, hooks, utils funcs and types + +- Updated dependencies [[`6c121d42`](https://github.com/xyflow/xyflow/commit/6c121d427fea9a11e86a85f95d2c12ba8af34919), [`4947029c`](https://github.com/xyflow/xyflow/commit/4947029cd6cda0f695e1fb4815e4030adb232234), [`e8e0d684`](https://github.com/xyflow/xyflow/commit/e8e0d684957b95d53a6cc11598c8755ff02117c7)]: + - @xyflow/system@0.0.51 + +## 0.1.29 + +### Patch Changes + +- [#4957](https://github.com/xyflow/xyflow/pull/4957) [`fe843982`](https://github.com/xyflow/xyflow/commit/fe843982bfc7d7579d54772b201426b4c3f549c6) Thanks [@peterkogo](https://github.com/peterkogo)! - Narrow properties selected, selectable, deletable, draggable of NodeProps type to be required. + +- Updated dependencies [[`fe843982`](https://github.com/xyflow/xyflow/commit/fe843982bfc7d7579d54772b201426b4c3f549c6), [`e73ef09f`](https://github.com/xyflow/xyflow/commit/e73ef09fbc8d872b46cf52c9d6a32dbb388c220b)]: + - @xyflow/system@0.0.50 + +## 0.1.28 + +### Patch Changes + +- [#4949](https://github.com/xyflow/xyflow/pull/4949) [`592c7eaf`](https://github.com/xyflow/xyflow/commit/592c7eaf9574fc69df3123837da95f85877b23e8) Thanks [@peterkogo](https://github.com/peterkogo)! - Fix useNodeConnection hook not returning all connected edges. + +- Updated dependencies [[`592c7eaf`](https://github.com/xyflow/xyflow/commit/592c7eaf9574fc69df3123837da95f85877b23e8)]: + - @xyflow/system@0.0.49 + +## 0.1.27 + +### Patch Changes + +- [#4937](https://github.com/xyflow/xyflow/pull/4937) [`9d940300`](https://github.com/xyflow/xyflow/commit/9d940300441b10f4d9eef4b07fac49a3e69d3503) Thanks [@jrmoynihan](https://github.com/jrmoynihan)! - Expose props of Controls + +- [#4947](https://github.com/xyflow/xyflow/pull/4947) [`868aa3f3`](https://github.com/xyflow/xyflow/commit/868aa3f3db8223ea1b04a68aa027ea99fd1e91c8) Thanks [@moklick](https://github.com/moklick)! - Export ResizeControlVariant correctly as a value. + +- [#4880](https://github.com/xyflow/xyflow/pull/4880) [`e2d849dc`](https://github.com/xyflow/xyflow/commit/e2d849dca63aee5952f676aef1c675c6232bb69a) Thanks [@crimx](https://github.com/crimx)! - Add type check for all event targets + +- [#4725](https://github.com/xyflow/xyflow/pull/4725) [`e10f53cf`](https://github.com/xyflow/xyflow/commit/e10f53cf898a56f954783d6efcf6977a0d88f4a9) Thanks [@peterkogo](https://github.com/peterkogo)! - Add useNodeConnections hook to track all connections to a node. Can be filtered by handleType and handleId. + +- Updated dependencies [[`e2d849dc`](https://github.com/xyflow/xyflow/commit/e2d849dca63aee5952f676aef1c675c6232bb69a), [`e10f53cf`](https://github.com/xyflow/xyflow/commit/e10f53cf898a56f954783d6efcf6977a0d88f4a9), [`4947f683`](https://github.com/xyflow/xyflow/commit/4947f683b7530f8e6684865ab53ea38633de0f4d)]: + - @xyflow/system@0.0.48 + +## 0.1.26 + +### Patch Changes + +- [#4897](https://github.com/xyflow/xyflow/pull/4897) [`c6890512`](https://github.com/xyflow/xyflow/commit/c6890512a685c8ec87310b3b003e62b0b976eca4) Thanks [@peterkogo](https://github.com/peterkogo)! - Freeze required @svelte-put/shortcut version to 3.1.1 to prevent breaking of key inputs. + +## 0.1.25 + +### Patch Changes + +- [#4865](https://github.com/xyflow/xyflow/pull/4865) [`2c4acc2b`](https://github.com/xyflow/xyflow/commit/2c4acc2bd9ec271468bd4c904e19d7fca627d9e1) Thanks [@moklick](https://github.com/moklick)! - Add group node to BuiltInNode type. Thanks [@sjdemartini](https://github.com/sjdemartini)! + +- [#4877](https://github.com/xyflow/xyflow/pull/4877) [`9a8309da`](https://github.com/xyflow/xyflow/commit/9a8309dab892f047fce10d3c763466cf84f726b0) Thanks [@peterkogo](https://github.com/peterkogo)! - Fix intersections for nodes with origins other than [0,0]. Thanks [@gmvrpw](https://github.com/gmvrpw)! + +- Updated dependencies [[`d60331e6`](https://github.com/xyflow/xyflow/commit/d60331e6baa7931c46af219e35c1bedbd156187c)]: + - @xyflow/system@0.0.47 + +## 0.1.24 + +### Patch Changes + +- [#4790](https://github.com/xyflow/xyflow/pull/4790) [`2fa9a920`](https://github.com/xyflow/xyflow/commit/2fa9a92042ba11986abbababb7e8b294e208d6cb) Thanks [@peterkogo](https://github.com/peterkogo)! - Fix node dragging & resizing while zooming on flow that does not cover whole browser window. + +- [#4782](https://github.com/xyflow/xyflow/pull/4782) [`323e1b35`](https://github.com/xyflow/xyflow/commit/323e1b35c58bca80deb824bc8b136705593a5257) Thanks [@peterkogo](https://github.com/peterkogo)! - Fix node intersections in nested flow. + +- Updated dependencies [[`2fa9a920`](https://github.com/xyflow/xyflow/commit/2fa9a92042ba11986abbababb7e8b294e208d6cb), [`323e1b35`](https://github.com/xyflow/xyflow/commit/323e1b35c58bca80deb824bc8b136705593a5257)]: + - @xyflow/system@0.0.46 + +## 0.1.23 + +### Patch Changes + +- [#4772](https://github.com/xyflow/xyflow/pull/4772) [`7f670ab0`](https://github.com/xyflow/xyflow/commit/7f670ab0423b3848a50398027297f6ec11deeaa4) Thanks [@mistic](https://github.com/mistic)! - Splits exports field in package.json to support an explicit resolution for browser, node and default to resolve issues with webpack esm module resolution. + +- Updated dependencies [[`7f670ab0`](https://github.com/xyflow/xyflow/commit/7f670ab0423b3848a50398027297f6ec11deeaa4)]: + - @xyflow/system@0.0.45 + +## 0.1.22 + +### Patch Changes + +- [#4730](https://github.com/xyflow/xyflow/pull/4730) [`2c590b90`](https://github.com/xyflow/xyflow/commit/2c590b90787aabce42de2b4108174bdf31ad6155) Thanks [@peterkogo](https://github.com/peterkogo)! - Fixed rare crash while dragging + +- [#4758](https://github.com/xyflow/xyflow/pull/4758) [`e555bd1b`](https://github.com/xyflow/xyflow/commit/e555bd1b1c7fbe801ed46120ac51d3d9cee5a29a) Thanks [@peterkogo](https://github.com/peterkogo)! - Bump Svelte peer dependency to 5.0.0 + +- Updated dependencies [[`005ae1c0`](https://github.com/xyflow/xyflow/commit/005ae1c05f6a10c1f519cd789f4f3f2fdf293bc6), [`2c590b90`](https://github.com/xyflow/xyflow/commit/2c590b90787aabce42de2b4108174bdf31ad6155)]: + - @xyflow/system@0.0.44 + +## 0.1.21 + +### Patch Changes + +- [#4718](https://github.com/xyflow/xyflow/pull/4718) [`51f08aac`](https://github.com/xyflow/xyflow/commit/51f08aaca5ddfbaa3259f666005d687d0a83f3db) Thanks [@peterkogo](https://github.com/peterkogo)! - Fixed hook useNodesData unexpectedly returning undefined + +## 0.1.20 + +### Patch Changes + +- [#4670](https://github.com/xyflow/xyflow/pull/4670) [`b056564c`](https://github.com/xyflow/xyflow/commit/b056564c9658bb43b882eebfad5a7e224717ffb5) Thanks [@peterkogo](https://github.com/peterkogo)! - Fix initial `fitView` not working correctly for `nodeOrigin` other than [0,0] + +- [#4670](https://github.com/xyflow/xyflow/pull/4670) [`b056564c`](https://github.com/xyflow/xyflow/commit/b056564c9658bb43b882eebfad5a7e224717ffb5) Thanks [@peterkogo](https://github.com/peterkogo)! - Improve `fitView` to respect clamped node positions based on `nodeExtent` + +- [#4653](https://github.com/xyflow/xyflow/pull/4653) [`02390f99`](https://github.com/xyflow/xyflow/commit/02390f9966d51c80e4e1b488733b5bf7322ad710) Thanks [@bcakmakoglu](https://github.com/bcakmakoglu)! - Calculate viewport dimensions in `fitView` instead of using stored dimensions. Fixes [#4652](https://github.com/xyflow/xyflow/issues/4652) + +- Updated dependencies [[`99ba64ac`](https://github.com/xyflow/xyflow/commit/99ba64ac2e1ce9c5ac3cab85a3d574edc0ecf4cc), [`b056564c`](https://github.com/xyflow/xyflow/commit/b056564c9658bb43b882eebfad5a7e224717ffb5)]: + - @xyflow/system@0.0.43 + ## 0.1.19 ### Patch Changes diff --git a/packages/svelte/eslint.config.js b/packages/svelte/eslint.config.js new file mode 100644 index 00000000..56e30008 --- /dev/null +++ b/packages/svelte/eslint.config.js @@ -0,0 +1,39 @@ +import globals from 'globals'; +import js from '@eslint/js'; +import ts from 'typescript-eslint'; +import svelte from 'eslint-plugin-svelte'; +import eslintConfigPrettier from 'eslint-config-prettier'; +import svelteConfig from './svelte.config.js'; + +export default ts.config( + js.configs.recommended, + ...ts.configs.recommended, + ...svelte.configs.recommended, + eslintConfigPrettier, + { + languageOptions: { + ecmaVersion: 2020, + sourceType: 'module', + globals: { + ...globals.browser, + ...globals.es2017, + ...globals.node + } + } + }, + { + files: ['**/*.svelte', '**/*.svelte.ts', '**/*.svelte.js'], + ignores: ['eslint.config.js', 'svelte.config.js'], + languageOptions: { + parserOptions: { + projectService: true, + extraFileExtensions: ['.svelte'], + parser: ts.parser, + svelteConfig + } + } + }, + { + ignores: ['.svelte-kit/*', 'build/*', 'dist/*'] // Add common ignore patterns + } +); diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 0b8e09a4..b9693201 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -1,6 +1,6 @@ { "name": "@xyflow/svelte", - "version": "0.1.19", + "version": "1.0.2", "description": "Svelte Flow - A highly customizable Svelte library for building node-based editors, workflow systems, diagrams and more.", "keywords": [ "svelte", @@ -34,6 +34,7 @@ "type": "module", "module": "./dist/lib/index.js", "exports": { + "./package.json": "./package.json", ".": { "types": "./dist/lib/index.d.ts", "svelte": "./dist/lib/index.js", @@ -50,45 +51,48 @@ "access": "public" }, "dependencies": { - "@svelte-put/shortcut": "^3.1.0", - "@xyflow/system": "workspace:*", - "classcat": "^5.0.4" + "@svelte-put/shortcut": "^4.1.0", + "@xyflow/system": "workspace:*" }, "devDependencies": { - "@sveltejs/adapter-auto": "^3.1.1", - "@sveltejs/kit": "^2.5.4", - "@sveltejs/package": "^2.3.0", - "@typescript-eslint/eslint-plugin": "^7.2.0", - "@typescript-eslint/parser": "^7.2.0", - "autoprefixer": "^10.4.18", - "cssnano": "^6.1.0", - "dotenv": "^16.4.5", - "eslint": "^8.57.0", - "eslint-config-prettier": "^9.1.0", - "eslint-plugin-svelte": "^2.35.1", - "postcss": "^8.4.35", - "postcss-cli": "^11.0.0", + "@eslint/js": "^9.24.0", + "@sveltejs/adapter-auto": "^6.0.0", + "@sveltejs/kit": "^2.20.7", + "@sveltejs/package": "^2.3.11", + "@sveltejs/vite-plugin-svelte": "^5.0.3", + "@typescript-eslint/eslint-plugin": "^8.30.1", + "@typescript-eslint/parser": "^8.30.1", + "autoprefixer": "^10.4.21", + "cssnano": "^7.0.6", + "dotenv": "^16.5.0", + "eslint": "^9.24.0", + "eslint-config-prettier": "^10.1.2", + "eslint-plugin-svelte": "^3.5.1", + "globals": "^16.0.0", + "postcss": "^8.5.3", + "postcss-cli": "^11.0.1", "postcss-combine-duplicated-selectors": "^10.0.3", - "postcss-import": "^16.0.1", - "postcss-nested": "^6.0.1", + "postcss-import": "^16.1.0", + "postcss-nested": "^7.0.2", "postcss-rename": "^0.6.1", - "prettier": "^3.2.5", - "prettier-plugin-svelte": "^3.2.2", - "svelte": "^4.2.12", - "svelte-check": "^3.6.7", - "svelte-eslint-parser": "^0.33.1", - "svelte-preprocess": "^5.1.3", - "tslib": "^2.6.2", - "typescript": "5.4.2" + "prettier": "^3.5.3", + "prettier-plugin-svelte": "^3.3.3", + "svelte": "^5.27.0", + "svelte-check": "^4.1.6", + "svelte-eslint-parser": "^1.1.2", + "svelte-preprocess": "^6.0.3", + "tslib": "^2.8.1", + "typescript": "^5.8.3", + "typescript-eslint": "^8.30.1" }, "peerDependencies": { - "svelte": "^3.0.0 || ^4.0.0" + "svelte": "^5.25.0" }, "files": [ "dist" ], "typesVersions": { - ">4.0": { + ">5.0": { "index": [ "./dist/lib/index.d.ts" ] diff --git a/packages/svelte/src/lib/actions/drag/index.ts b/packages/svelte/src/lib/actions/drag/index.ts index 0d28f4e7..18aa2e3c 100644 --- a/packages/svelte/src/lib/actions/drag/index.ts +++ b/packages/svelte/src/lib/actions/drag/index.ts @@ -1,10 +1,10 @@ -import { get } from 'svelte/store'; -import { XYDrag, type OnDrag } from '@xyflow/system'; +import { XYDrag, type NodeBase, type OnDrag, type XYDragParams } from '@xyflow/system'; import type { SvelteFlowStore } from '$lib/store/types'; +import type { Node, Edge, NodeTargetEventWithPointer } from '$lib/types'; -export type UseDragParams = { - store: SvelteFlowStore; +export type UseDragParams = { + store: SvelteFlowStore; disabled?: boolean; noDragClass?: string; handleSelector?: string; @@ -17,7 +17,10 @@ export type UseDragParams = { onNodeMouseDown?: (id: string) => void; }; -export default function drag(domNode: Element, params: UseDragParams) { +export default function drag( + domNode: Element, + params: UseDragParams +) { const { store, onDrag, onDragStart, onDragStop, onNodeMouseDown } = params; const dragInstance = XYDrag({ onDrag, @@ -25,32 +28,34 @@ export default function drag(domNode: Element, params: UseDragParams) { onDragStop, onNodeMouseDown, getStoreItems: () => { - const snapGrid = get(store.snapGrid); - const vp = get(store.viewport); + const { snapGrid, viewport } = store; return { - nodes: get(store.nodes), - nodeLookup: get(store.nodeLookup), - edges: get(store.edges), - nodeExtent: get(store.nodeExtent), + nodes: store.nodes satisfies NodeBase[], + nodeLookup: store.nodeLookup, + edges: store.edges, + nodeExtent: store.nodeExtent, snapGrid: snapGrid ? snapGrid : [0, 0], snapToGrid: !!snapGrid, - nodeOrigin: get(store.nodeOrigin), - multiSelectionActive: get(store.multiselectionKeyPressed), - domNode: get(store.domNode), - transform: [vp.x, vp.y, vp.zoom], - autoPanOnNodeDrag: get(store.autoPanOnNodeDrag), - nodesDraggable: get(store.nodesDraggable), - selectNodesOnDrag: get(store.selectNodesOnDrag), - nodeDragThreshold: get(store.nodeDragThreshold), + nodeOrigin: store.nodeOrigin, + multiSelectionActive: store.multiselectionKeyPressed, + domNode: store.domNode, + transform: [viewport.x, viewport.y, viewport.zoom], + autoPanOnNodeDrag: store.autoPanOnNodeDrag, + nodesDraggable: store.nodesDraggable, + selectNodesOnDrag: store.selectNodesOnDrag, + nodeDragThreshold: store.nodeDragThreshold, unselectNodesAndEdges: store.unselectNodesAndEdges, updateNodePositions: store.updateNodePositions, + onSelectionDrag: store.onselectiondrag, + onSelectionDragStart: store.onselectiondragstart, + onSelectionDragStop: store.onselectiondragstop, panBy: store.panBy }; } - }); + } as XYDragParams>); - function updateDrag(domNode: Element, params: UseDragParams) { + function updateDrag(domNode: Element, params: UseDragParams) { if (params.disabled) { dragInstance.destroy(); return; @@ -69,7 +74,7 @@ export default function drag(domNode: Element, params: UseDragParams) { updateDrag(domNode, params); return { - update(params: UseDragParams) { + update(params: UseDragParams) { updateDrag(domNode, params); }, destroy() { diff --git a/packages/svelte/src/lib/actions/portal/index.ts b/packages/svelte/src/lib/actions/portal/index.ts index fba6562c..927ec7f0 100644 --- a/packages/svelte/src/lib/actions/portal/index.ts +++ b/packages/svelte/src/lib/actions/portal/index.ts @@ -1,31 +1 @@ -type PortalOptions = { - target?: string; - domNode: Element | null; -}; - -function tryToMount(node: Element, domNode: Element | null, target: string | undefined) { - if (!domNode) { - return; - } - - const targetEl = target ? domNode.querySelector(target) : domNode; - - if (targetEl) { - targetEl.appendChild(node); - } -} - -export default function (node: Element, { target, domNode }: PortalOptions) { - tryToMount(node, domNode, target); - - return { - async update({ target, domNode }: PortalOptions) { - tryToMount(node, domNode, target); - }, - destroy() { - if (node.parentNode) { - node.parentNode.removeChild(node); - } - } - }; -} +export { portal } from './portal.svelte'; diff --git a/packages/svelte/src/lib/actions/portal/portal.svelte.ts b/packages/svelte/src/lib/actions/portal/portal.svelte.ts new file mode 100644 index 00000000..40dea420 --- /dev/null +++ b/packages/svelte/src/lib/actions/portal/portal.svelte.ts @@ -0,0 +1,39 @@ +import { useStore } from '$lib/store'; + +type Portal = 'viewport-back' | 'viewport-front' | 'root' | 'edge-labels'; + +function tryToMount(node: Element, domNode: Element | null, target: Portal | undefined) { + if (!target || !domNode) { + return; + } + + const targetEl = target === 'root' ? domNode : domNode.querySelector(`.svelte-flow__${target}`); + + if (targetEl) { + targetEl.appendChild(node); + } +} + +export function portal(node: Element, target: Portal | undefined) { + // TODO: does this work if called outside of SvelteFlow + const store = useStore(); + + let previousTarget: Portal | undefined = target; + + tryToMount(node, store.domNode, target); + + return { + async update(target: Portal) { + if (target !== previousTarget) { + node.parentNode?.removeChild(node); + previousTarget = target; + } + tryToMount(node, store.domNode, target); + }, + destroy() { + if (node.parentNode) { + node.parentNode.removeChild(node); + } + } + }; +} diff --git a/packages/svelte/src/lib/actions/zoom/index.ts b/packages/svelte/src/lib/actions/zoom/index.ts index 85070d68..47cfa49e 100644 --- a/packages/svelte/src/lib/actions/zoom/index.ts +++ b/packages/svelte/src/lib/actions/zoom/index.ts @@ -1,4 +1,3 @@ -import type { Writable } from 'svelte/store'; import { PanOnScrollMode, XYPanZoom, @@ -10,17 +9,16 @@ import { } from '@xyflow/system'; type ZoomParams = { - viewport: Writable; + viewport: Viewport; initialViewport: Viewport; minZoom: number; maxZoom: number; - dragging: Writable; + setPanZoomInstance: (panZoomInstance: PanZoomInstance) => void; onPanZoomStart?: OnPanZoom; onPanZoom?: OnPanZoom; onPanZoomEnd?: OnPanZoom; onPaneContextMenu?: (event: MouseEvent) => void; translateExtent: CoordinateExtent; - panZoom: Writable; zoomOnScroll: boolean; zoomOnPinch: boolean; zoomOnDoubleClick: boolean; @@ -38,18 +36,19 @@ type ZoomParams = { lib: string; paneClickDistance: number; onTransformChange: (transform: Transform) => void; + onDraggingChange: (dragging: boolean) => void; }; export default function zoom(domNode: Element, params: ZoomParams) { const { - panZoom, minZoom, maxZoom, initialViewport, - viewport, - dragging, translateExtent, - paneClickDistance + paneClickDistance, + setPanZoomInstance, + onDraggingChange, + onTransformChange } = params; const panZoomInstance = XYPanZoom({ @@ -59,11 +58,19 @@ export default function zoom(domNode: Element, params: ZoomParams) { translateExtent, viewport: initialViewport, paneClickDistance, - onDraggingChange: dragging.set + onDraggingChange }); - const currentViewport = panZoomInstance.getViewport(); - viewport.set(currentViewport); - panZoom.set(panZoomInstance); + + const viewport = panZoomInstance.getViewport(); + if ( + initialViewport.x !== viewport.x || + initialViewport.y !== viewport.y || + initialViewport.zoom !== viewport.zoom + ) { + onTransformChange([viewport.x, viewport.y, viewport.zoom]); + } + + setPanZoomInstance(panZoomInstance); panZoomInstance.update(params); diff --git a/packages/svelte/src/lib/components/A11yDescriptions/A11yDescriptions.svelte b/packages/svelte/src/lib/components/A11yDescriptions/A11yDescriptions.svelte new file mode 100644 index 00000000..a26778c1 --- /dev/null +++ b/packages/svelte/src/lib/components/A11yDescriptions/A11yDescriptions.svelte @@ -0,0 +1,30 @@ + + + + + +{#if !store.disableKeyboardA11y} +
+ {store.ariaLiveMessage} +
+{/if} diff --git a/packages/svelte/src/lib/components/A11yDescriptions/index.ts b/packages/svelte/src/lib/components/A11yDescriptions/index.ts new file mode 100644 index 00000000..af58b941 --- /dev/null +++ b/packages/svelte/src/lib/components/A11yDescriptions/index.ts @@ -0,0 +1,5 @@ +export { default as A11yDescriptions } from './A11yDescriptions.svelte'; + +export const ARIA_NODE_DESC_KEY = 'svelte-flow__node-desc'; +export const ARIA_EDGE_DESC_KEY = 'svelte-flow__edge-desc'; +export const ARIA_LIVE_MESSAGE = 'svelte-flow__aria-live'; diff --git a/packages/svelte/src/lib/components/Attribution/Attribution.svelte b/packages/svelte/src/lib/components/Attribution/Attribution.svelte index bb7041d7..f860950d 100644 --- a/packages/svelte/src/lib/components/Attribution/Attribution.svelte +++ b/packages/svelte/src/lib/components/Attribution/Attribution.svelte @@ -2,10 +2,7 @@ import { Panel } from '$lib/container/Panel'; import type { AttributionProps } from './types'; - type $$Props = AttributionProps; - - export let proOptions: $$Props['proOptions'] = undefined; - export let position: $$Props['position'] = 'bottom-right'; + let { proOptions, position = 'bottom-right' }: AttributionProps = $props(); {#if !proOptions?.hideAttribution} diff --git a/packages/svelte/src/lib/components/BaseEdge/BaseEdge.svelte b/packages/svelte/src/lib/components/BaseEdge/BaseEdge.svelte deleted file mode 100644 index 36b3763e..00000000 --- a/packages/svelte/src/lib/components/BaseEdge/BaseEdge.svelte +++ /dev/null @@ -1,48 +0,0 @@ - - - - -{#if interactionWidthValue} - -{/if} - -{#if label} - - {label} - -{/if} diff --git a/packages/svelte/src/lib/components/BaseEdge/index.ts b/packages/svelte/src/lib/components/BaseEdge/index.ts deleted file mode 100644 index 65bb0fbc..00000000 --- a/packages/svelte/src/lib/components/BaseEdge/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as BaseEdge } from './BaseEdge.svelte'; diff --git a/packages/svelte/src/lib/components/BaseEdge/types.ts b/packages/svelte/src/lib/components/BaseEdge/types.ts deleted file mode 100644 index 41a1189b..00000000 --- a/packages/svelte/src/lib/components/BaseEdge/types.ts +++ /dev/null @@ -1,23 +0,0 @@ -import type { EdgeProps } from '$lib/types'; - -export type BaseEdgeProps = Pick< - EdgeProps, - 'interactionWidth' | 'label' | 'labelStyle' | 'style' -> & { - id?: string; - /** SVG path of the edge */ - path: string; - /** The x coordinate of the label */ - labelX?: number; - /** The y coordinate of the label */ - labelY?: number; - /** Marker at start of edge - * @example 'url(#arrow)' - */ - markerStart?: string; - /** Marker at end of edge - * @example 'url(#arrow)' - */ - markerEnd?: string; - class?: string; -}; diff --git a/packages/svelte/src/lib/components/CallOnMount/CallOnMount.svelte b/packages/svelte/src/lib/components/CallOnMount/CallOnMount.svelte deleted file mode 100644 index 4184da6f..00000000 --- a/packages/svelte/src/lib/components/CallOnMount/CallOnMount.svelte +++ /dev/null @@ -1,14 +0,0 @@ - diff --git a/packages/svelte/src/lib/components/CallOnMount/index.ts b/packages/svelte/src/lib/components/CallOnMount/index.ts deleted file mode 100644 index 3dee0d92..00000000 --- a/packages/svelte/src/lib/components/CallOnMount/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as CallOnMount } from './CallOnMount.svelte'; diff --git a/packages/svelte/src/lib/components/ConnectionLine/ConnectionLine.svelte b/packages/svelte/src/lib/components/ConnectionLine/ConnectionLine.svelte index 71e3f2c9..d8750cab 100644 --- a/packages/svelte/src/lib/components/ConnectionLine/ConnectionLine.svelte +++ b/packages/svelte/src/lib/components/ConnectionLine/ConnectionLine.svelte @@ -1,7 +1,5 @@ - -{#if $connection.inProgress} - - - - - {#if !isCustomComponent} +{#if store.connection.inProgress} + + + {#if LineComponent} + + {:else} {/if} diff --git a/packages/svelte/src/lib/components/EdgeLabel/EdgeLabel.svelte b/packages/svelte/src/lib/components/EdgeLabel/EdgeLabel.svelte index 9ab028c0..187567d7 100644 --- a/packages/svelte/src/lib/components/EdgeLabel/EdgeLabel.svelte +++ b/packages/svelte/src/lib/components/EdgeLabel/EdgeLabel.svelte @@ -1,31 +1,53 @@ - -
{}} - on:click={() => { - if (id) handleEdgeSelect(id); - }} - > - -
-
+
{ + if (selectEdgeOnClick && id) store.handleEdgeSelection(id); + }} + {...rest} +> + {@render children?.()} +
+ + diff --git a/packages/svelte/src/lib/components/EdgeLabel/index.ts b/packages/svelte/src/lib/components/EdgeLabel/index.ts index 2e0a457c..bd3bb47f 100644 --- a/packages/svelte/src/lib/components/EdgeLabel/index.ts +++ b/packages/svelte/src/lib/components/EdgeLabel/index.ts @@ -1 +1,2 @@ export { default as EdgeLabel } from './EdgeLabel.svelte'; +export * from './types'; diff --git a/packages/svelte/src/lib/components/EdgeLabel/types.ts b/packages/svelte/src/lib/components/EdgeLabel/types.ts new file mode 100644 index 00000000..0135cdf6 --- /dev/null +++ b/packages/svelte/src/lib/components/EdgeLabel/types.ts @@ -0,0 +1,12 @@ +import type { Snippet } from 'svelte'; +import type { HTMLAttributes } from 'svelte/elements'; + +export type EdgeLabelProps = { + x?: number; + y?: number; + width?: number; + height?: number; + selectEdgeOnClick?: boolean; + transparent?: boolean; + children?: Snippet; +} & HTMLAttributes; diff --git a/packages/svelte/src/lib/components/EdgeLabelRenderer/EdgeLabelRenderer.svelte b/packages/svelte/src/lib/components/EdgeLabelRenderer/EdgeLabelRenderer.svelte deleted file mode 100644 index 76bc8061..00000000 --- a/packages/svelte/src/lib/components/EdgeLabelRenderer/EdgeLabelRenderer.svelte +++ /dev/null @@ -1,10 +0,0 @@ - - -
- -
diff --git a/packages/svelte/src/lib/components/EdgeLabelRenderer/index.ts b/packages/svelte/src/lib/components/EdgeLabelRenderer/index.ts deleted file mode 100644 index 02f40786..00000000 --- a/packages/svelte/src/lib/components/EdgeLabelRenderer/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as EdgeLabelRenderer } from './EdgeLabelRenderer.svelte'; diff --git a/packages/svelte/src/lib/components/EdgeReconnectAnchor/EdgeReconnectAnchor.svelte b/packages/svelte/src/lib/components/EdgeReconnectAnchor/EdgeReconnectAnchor.svelte new file mode 100644 index 00000000..96842f26 --- /dev/null +++ b/packages/svelte/src/lib/components/EdgeReconnectAnchor/EdgeReconnectAnchor.svelte @@ -0,0 +1,123 @@ + + + + {#if !reconnecting && children} + {@render children()} + {/if} + diff --git a/packages/svelte/src/lib/components/EdgeReconnectAnchor/index.ts b/packages/svelte/src/lib/components/EdgeReconnectAnchor/index.ts new file mode 100644 index 00000000..5c3ad2c7 --- /dev/null +++ b/packages/svelte/src/lib/components/EdgeReconnectAnchor/index.ts @@ -0,0 +1,2 @@ +export { default as EdgeReconnectAnchor } from './EdgeReconnectAnchor.svelte'; +export * from './types'; diff --git a/packages/svelte/src/lib/components/EdgeReconnectAnchor/types.ts b/packages/svelte/src/lib/components/EdgeReconnectAnchor/types.ts new file mode 100644 index 00000000..74ed6d6d --- /dev/null +++ b/packages/svelte/src/lib/components/EdgeReconnectAnchor/types.ts @@ -0,0 +1,13 @@ +import type { HandleType, XYPosition } from '@xyflow/system'; +import type { Snippet } from 'svelte'; +import type { ClassValue, HTMLAttributes } from 'svelte/elements'; + +export type EdgeReconnectAnchorProps = { + type: HandleType; + reconnecting?: boolean; + style?: string; + class?: ClassValue; + position?: XYPosition; + size?: number; + children?: Snippet; +} & HTMLAttributes; diff --git a/packages/svelte/src/lib/components/EdgeWrapper/EdgeWrapper.svelte b/packages/svelte/src/lib/components/EdgeWrapper/EdgeWrapper.svelte index 02401f61..c1c988fd 100644 --- a/packages/svelte/src/lib/components/EdgeWrapper/EdgeWrapper.svelte +++ b/packages/svelte/src/lib/components/EdgeWrapper/EdgeWrapper.svelte @@ -1,115 +1,147 @@ - + - - + {#if !hidden} - + { - onMouseEvent(e, 'edgecontextmenu'); - }} - on:mouseenter={(e) => { - onMouseEvent(e, 'edgemouseenter'); - }} - on:mouseleave={(e) => { - onMouseEvent(e, 'edgemouseleave'); - }} + {onclick} + oncontextmenu={onedgecontextmenu + ? (e) => { + onmouseevent(e, onedgecontextmenu); + } + : undefined} + onpointerenter={onedgepointerenter + ? (e) => { + onmouseevent(e, onedgepointerenter); + } + : undefined} + onpointerleave={onedgepointerleave + ? (e) => { + onmouseevent(e, onedgepointerleave); + } + : undefined} aria-label={ariaLabel === null ? undefined : ariaLabel ? ariaLabel : `Edge from ${source} to ${target}`} - role="img" + aria-describedby={focusable ? `${ARIA_EDGE_DESC_KEY}-${store.flowId}` : undefined} + role={focusable ? 'button' : 'img'} + onkeydown={focusable ? onkeydown : undefined} + tabindex={focusable ? 0 : undefined} > - import { getContext } from 'svelte'; - import type { Writable } from 'svelte/store'; - import cc from 'classcat'; import { Position, XYHandle, isMouseEvent, - type HandleConnection, areConnectionMapsEqual, handleConnectionChange, - ConnectionMode + ConnectionMode, + getHostForElement, + type HandleConnection, + type Optional, + type ConnectionState, + type Connection } from '@xyflow/system'; import { useStore } from '$lib/store'; - import type { HandleProps } from '$lib/types'; - type $$Props = HandleProps; + import type { ConnectableContext } from '../NodeWrapper/types'; + import type { HandleProps } from './types'; - export let id: $$Props['id'] = undefined; - export let type: $$Props['type'] = 'source'; - export let position: $$Props['position'] = Position.Top; - export let style: $$Props['style'] = undefined; - export let isValidConnection: $$Props['isValidConnection'] = undefined; - export let onconnect: $$Props['onconnect'] = undefined; - export let ondisconnect: $$Props['ondisconnect'] = undefined; - // @todo implement connectablestart, connectableend - // export let isConnectableStart: $$Props['isConnectableStart'] = undefined; - // export let isConnectableEnd: $$Props['isConnectableEnd'] = undefined; + let { + id: handleId = null, + type = 'source', + position = Position.Top, + style, + class: className, + isConnectable: isConnectableProp, + isConnectableStart = true, + isConnectableEnd = true, + isValidConnection, + onconnect, + ondisconnect, + children, + ...rest + }: HandleProps = $props(); - let isConnectableProp: $$Props['isConnectable'] = undefined; - export { isConnectableProp as isConnectable }; - - let className: $$Props['class'] = undefined; - export { className as class }; - - $: isTarget = type === 'target'; const nodeId = getContext('svelteflow__node_id'); - const connectable = getContext>('svelteflow__node_connectable'); - $: isConnectable = isConnectableProp !== undefined ? isConnectableProp : $connectable; + const isConnectableContext = getContext('svelteflow__node_connectable'); - $: handleId = id || null; + let isTarget = $derived(type === 'target'); + let isConnectable = $derived( + isConnectableProp !== undefined ? isConnectableProp : isConnectableContext.value + ); - const store = useStore(); - const { - connectionMode, - domNode, - nodeLookup, - connectionRadius, - viewport, - isValidConnection: isValidConnectionStore, - lib, - addEdge, - onedgecreate, - panBy, - cancelConnection, - updateConnection, - autoPanOnConnect, - edges, - connectionLookup, - onconnect: onConnectAction, - onconnectstart: onConnectStartAction, - onconnectend: onConnectEndAction, - flowId, - connection - } = store; + let store = useStore(); - function onPointerDown(event: MouseEvent | TouchEvent) { + let prevConnections: Map | null = null; + $effect.pre(() => { + if (onconnect || ondisconnect) { + // connectionLookup is not reactive, so we use edges to get notified about updates + // eslint-disable-next-line @typescript-eslint/no-unused-expressions + store.edges; + let connections = store.connectionLookup.get( + `${nodeId}-${type}${handleId ? `-${handleId}` : ''}` + ); + + if (prevConnections && !areConnectionMapsEqual(connections, prevConnections)) { + const _connections = connections ?? new Map(); + + handleConnectionChange(prevConnections, _connections, ondisconnect); + handleConnectionChange(_connections, prevConnections, onconnect); + } + + prevConnections = new Map(connections); + } + }); + + let [connectionInProgress, connectingFrom, connectingTo, isPossibleTargetHandle, valid] = + $derived.by(() => { + if (!store.connection.inProgress) { + return [false, false, false, false, null]; + } + + const { fromHandle, toHandle, isValid } = store.connection; + + const connectingFrom = + fromHandle && + fromHandle.nodeId === nodeId && + fromHandle.type === type && + fromHandle.id === handleId; + + const connectingTo = + toHandle && + toHandle.nodeId === nodeId && + toHandle.type === type && + toHandle.id === handleId; + + const isPossibleTargetHandle = + store.connectionMode === ConnectionMode.Strict + ? fromHandle?.type !== type + : nodeId !== fromHandle?.nodeId || handleId !== fromHandle?.id; + + const valid = connectingTo && isValid; + + return [true, connectingFrom, connectingTo, isPossibleTargetHandle, valid]; + }); + + function onConnectExtended(connection: Connection) { + const edge = store.onbeforeconnect?.(connection) ?? connection; + + if (!edge) { + return; + } + + store.addEdge(edge); + store.onconnect?.(connection); + } + + function onpointerdown(event: MouseEvent | TouchEvent) { const isMouseTriggered = isMouseEvent(event); if ((isMouseTriggered && event.button === 0) || !isMouseTriggered) { @@ -73,77 +115,82 @@ handleId, nodeId, isTarget, - connectionRadius: $connectionRadius, - domNode: $domNode, - nodeLookup: $nodeLookup, - connectionMode: $connectionMode, - lib: $lib, - autoPanOnConnect: $autoPanOnConnect, - flowId: $flowId, - isValidConnection: isValidConnection ?? $isValidConnectionStore, - updateConnection, - cancelConnection, - panBy, - onConnect: (connection) => { - const edge = $onedgecreate ? $onedgecreate(connection) : connection; - - if (!edge) { - return; - } - - addEdge(edge); - $onConnectAction?.(connection); - }, + connectionRadius: store.connectionRadius, + domNode: store.domNode, + nodeLookup: store.nodeLookup, + connectionMode: store.connectionMode, + lib: 'svelte', + autoPanOnConnect: store.autoPanOnConnect, + flowId: store.flowId, + isValidConnection: isValidConnection ?? store.isValidConnection, + updateConnection: store.updateConnection, + cancelConnection: store.cancelConnection, + panBy: store.panBy, + onConnect: onConnectExtended, onConnectStart: (event, startParams) => { - $onConnectStartAction?.(event, { + store.onconnectstart?.(event, { nodeId: startParams.nodeId, handleId: startParams.handleId, handleType: startParams.handleType }); }, onConnectEnd: (event, connectionState) => { - $onConnectEndAction?.(event, connectionState); + store.onconnectend?.(event, connectionState); }, - getTransform: () => [$viewport.x, $viewport.y, $viewport.zoom], - getFromHandle: () => $connection.fromHandle + getTransform: () => [store.viewport.x, store.viewport.y, store.viewport.zoom], + getFromHandle: () => store.connection.fromHandle }); } } - let prevConnections: Map | null = null; - let connections: Map | undefined; - - $: if (onconnect || ondisconnect) { - // connectionLookup is not reactive, so we use edges to get notified about updates - $edges; - connections = $connectionLookup.get(`${nodeId}-${type}-${id || null}`); - } - - $: { - if (prevConnections && !areConnectionMapsEqual(connections, prevConnections)) { - const _connections = connections ?? new Map(); - - handleConnectionChange(prevConnections, _connections, ondisconnect); - handleConnectionChange(_connections, prevConnections, onconnect); + function onclick(event: MouseEvent) { + if (!nodeId || (!store.clickConnectStartHandle && !isConnectableStart)) { + return; } - prevConnections = connections ?? new Map(); - } + if (!store.clickConnectStartHandle) { + store.onclickconnectstart?.(event, { nodeId, handleId, handleType: type }); + store.clickConnectStartHandle = { nodeId, type, id: handleId }; + return; + } - $: connectionInProcess = !!$connection.fromHandle; - $: connectingFrom = - $connection.fromHandle?.nodeId === nodeId && - $connection.fromHandle?.type === type && - $connection.fromHandle?.id === handleId; - $: connectingTo = - $connection.toHandle?.nodeId === nodeId && - $connection.toHandle?.type === type && - $connection.toHandle?.id === handleId; - $: isPossibleEndHandle = - $connectionMode === ConnectionMode.Strict - ? $connection.fromHandle?.type !== type - : nodeId !== $connection.fromHandle?.nodeId || handleId !== $connection.fromHandle?.id; - $: valid = connectingTo && $connection.isValid; + const doc = getHostForElement(event.target); + const isValidConnectionHandler = isValidConnection ?? store.isValidConnection; + + const { connectionMode, clickConnectStartHandle, flowId, nodeLookup } = store; + const { connection, isValid } = XYHandle.isValid(event, { + handle: { + nodeId, + id: handleId, + type + }, + connectionMode, + fromNodeId: clickConnectStartHandle.nodeId, + fromHandleId: clickConnectStartHandle.id ?? null, + fromType: clickConnectStartHandle.type, + isValidConnection: isValidConnectionHandler, + flowId, + doc, + lib: 'svelte', + nodeLookup + }); + + if (isValid && connection) { + onConnectExtended(connection); + } + + const connectionClone = structuredClone($state.snapshot(store.connection)) as Optional< + ConnectionState, + 'inProgress' + >; + delete connectionClone.inProgress; + connectionClone.toPosition = connectionClone.toHandle + ? connectionClone.toHandle.position + : null; + store.onclickconnectend?.(event, connectionClone); + + store.clickConnectStartHandle = null; + } - {#if !hidden}
{ - dispatchNodeEvent('nodedrag', { event, targetNode, nodes }); + onnodedrag?.({ event, targetNode: targetNode as NodeType, nodes: nodes as NodeType[] }); }, onDragStart: (event, _, targetNode, nodes) => { - dispatchNodeEvent('nodedragstart', { event, targetNode, nodes }); + onnodedragstart?.({ + event, + targetNode: targetNode as NodeType, + nodes: nodes as NodeType[] + }); }, onDragStop: (event, _, targetNode, nodes) => { - dispatchNodeEvent('nodedragstop', { event, targetNode, nodes }); + onnodedragstop?.({ event, targetNode: targetNode as NodeType, nodes: nodes as NodeType[] }); }, store }} bind:this={nodeRef} data-id={id} - class={cc(['svelte-flow__node', `svelte-flow__node-${nodeType}`, className])} + class={['svelte-flow__node', `svelte-flow__node-${type}`, className]} class:dragging class:selected class:draggable @@ -183,15 +236,28 @@ style:z-index={zIndex} style:transform="translate({positionX}px, {positionY}px)" style:visibility={initialized ? 'visible' : 'hidden'} - style="{style ?? ''};{inlineStyleDimensions.width}{inlineStyleDimensions.height}" - on:click={onSelectNodeHandler} - on:mouseenter={(event) => dispatchNodeEvent('nodemouseenter', { node, event })} - on:mouseleave={(event) => dispatchNodeEvent('nodemouseleave', { node, event })} - on:mousemove={(event) => dispatchNodeEvent('nodemousemove', { node, event })} - on:contextmenu={(event) => dispatchNodeEvent('nodecontextmenu', { node, event })} + style={nodeStyle} + onclick={onSelectNodeHandler} + onpointerenter={onnodepointerenter + ? (event) => onnodepointerenter({ node: userNode, event }) + : undefined} + onpointerleave={onnodepointerleave + ? (event) => onnodepointerleave({ node: userNode, event }) + : undefined} + onpointermove={onnodepointermove + ? (event) => onnodepointermove({ node: userNode, event }) + : undefined} + oncontextmenu={onnodecontextmenu + ? (event) => onnodecontextmenu({ node: userNode, event }) + : undefined} + onkeydown={focusable ? onKeyDown : undefined} + tabIndex={focusable ? 0 : undefined} + role={focusable ? 'button' : undefined} + aria-describedby={store.disableKeyboardA11y + ? undefined + : `${ARIA_NODE_DESC_KEY}-${store.flowId}`} > - & { - measuredWidth?: number; - measuredHeight?: number; - type: string; - positionX: number; - positionY: number; - 'on:nodeclick'?: (event: MouseEvent) => void; +export type ConnectableContext = { + value: boolean; +}; + +export type NodeWrapperProps = { + node: InternalNode; + store: SvelteFlowStore; + nodeClickDistance?: number; resizeObserver?: ResizeObserver | null; - isParent?: boolean; - zIndex: number; - node: InternalNode; - initialized: boolean; - nodeClickDistance?: number; }; diff --git a/packages/svelte/src/lib/components/NodeWrapper/utils.ts b/packages/svelte/src/lib/components/NodeWrapper/utils.ts deleted file mode 100644 index f795e31d..00000000 --- a/packages/svelte/src/lib/components/NodeWrapper/utils.ts +++ /dev/null @@ -1,33 +0,0 @@ -export function getNodeInlineStyleDimensions({ - width, - height, - initialWidth, - initialHeight, - measuredWidth, - measuredHeight -}: { - width?: number; - height?: number; - initialWidth?: number; - initialHeight?: number; - measuredWidth?: number; - measuredHeight?: number; -}): { - width: string | undefined; - height: string | undefined; -} { - if (measuredWidth === undefined && measuredHeight === undefined) { - const styleWidth = width ?? initialWidth; - const styleHeight = height ?? initialHeight; - - return { - width: styleWidth ? `width:${styleWidth}px;` : '', - height: styleHeight ? `height:${styleHeight}px;` : '' - }; - } - - return { - width: width ? `width:${width}px;` : '', - height: height ? `height:${height}px;` : '' - }; -} diff --git a/packages/svelte/src/lib/components/Selection/Selection.svelte b/packages/svelte/src/lib/components/Selection/Selection.svelte index a9e0e7d6..25a7de2e 100644 --- a/packages/svelte/src/lib/components/Selection/Selection.svelte +++ b/packages/svelte/src/lib/components/Selection/Selection.svelte @@ -1,18 +1,28 @@ {#if isVisible}
+ >
{/if} diff --git a/packages/svelte/src/lib/container/NodeRenderer/types.ts b/packages/svelte/src/lib/container/NodeRenderer/types.ts deleted file mode 100644 index 93c78afe..00000000 --- a/packages/svelte/src/lib/container/NodeRenderer/types.ts +++ /dev/null @@ -1,3 +0,0 @@ -export type NodeRendererProps = { - nodeClickDistance?: number; -}; diff --git a/packages/svelte/src/lib/container/Pane/Pane.svelte b/packages/svelte/src/lib/container/Pane/Pane.svelte index 1d797af9..db11bd7d 100644 --- a/packages/svelte/src/lib/container/Pane/Pane.svelte +++ b/packages/svelte/src/lib/container/Pane/Pane.svelte @@ -1,4 +1,4 @@ - - - - + +
- + {@render children()}
- - diff --git a/packages/svelte/src/lib/container/Pane/index.ts b/packages/svelte/src/lib/container/Pane/index.ts index 2161528c..ddbb36e5 100644 --- a/packages/svelte/src/lib/container/Pane/index.ts +++ b/packages/svelte/src/lib/container/Pane/index.ts @@ -1 +1,2 @@ export { default as Pane } from './Pane.svelte'; +export * from './types'; diff --git a/packages/svelte/src/lib/container/Pane/types.ts b/packages/svelte/src/lib/container/Pane/types.ts index 96d97701..8442834e 100644 --- a/packages/svelte/src/lib/container/Pane/types.ts +++ b/packages/svelte/src/lib/container/Pane/types.ts @@ -1,4 +1,12 @@ -export type PaneProps = { +import type { Snippet } from 'svelte'; +import type { Node, Edge, PaneEvents } from '$lib/types'; +import type { SvelteFlowStore } from '$lib/store/types'; + +export type PaneProps = { + store: SvelteFlowStore; panOnDrag?: boolean | number[]; selectionOnDrag?: boolean; -}; + onselectionstart?: (event: PointerEvent) => void; + onselectionend?: (event: PointerEvent) => void; + children: Snippet; +} & PaneEvents; diff --git a/packages/svelte/src/lib/container/Panel/Panel.svelte b/packages/svelte/src/lib/container/Panel/Panel.svelte index 4ea6661b..314da233 100644 --- a/packages/svelte/src/lib/container/Panel/Panel.svelte +++ b/packages/svelte/src/lib/container/Panel/Panel.svelte @@ -1,26 +1,19 @@
- + {@render children?.()}
diff --git a/packages/svelte/src/lib/container/Panel/index.ts b/packages/svelte/src/lib/container/Panel/index.ts index aa07206d..bc926449 100644 --- a/packages/svelte/src/lib/container/Panel/index.ts +++ b/packages/svelte/src/lib/container/Panel/index.ts @@ -1,2 +1,2 @@ export { default as Panel } from './Panel.svelte'; -export type { PanelProps } from './types'; +export * from './types'; diff --git a/packages/svelte/src/lib/container/Panel/types.ts b/packages/svelte/src/lib/container/Panel/types.ts index b6e4dd0a..3b377a45 100644 --- a/packages/svelte/src/lib/container/Panel/types.ts +++ b/packages/svelte/src/lib/container/Panel/types.ts @@ -1,5 +1,5 @@ import type { PanelPosition } from '@xyflow/system'; -import type { HTMLAttributes } from 'svelte/elements'; +import type { ClassValue, HTMLAttributes } from 'svelte/elements'; export type PanelProps = HTMLAttributes & { 'data-testid'?: string; @@ -9,5 +9,5 @@ export type PanelProps = HTMLAttributes & { */ position?: PanelPosition; style?: string; - class?: string; + class?: ClassValue; }; diff --git a/packages/svelte/src/lib/container/SvelteFlow/SvelteFlow.svelte b/packages/svelte/src/lib/container/SvelteFlow/SvelteFlow.svelte index 4fc060a6..9b039558 100644 --- a/packages/svelte/src/lib/container/SvelteFlow/SvelteFlow.svelte +++ b/packages/svelte/src/lib/container/SvelteFlow/SvelteFlow.svelte @@ -1,218 +1,143 @@ - -
- + +
+
- - -
-
+ /> +
- + - -
- - + + {@render children?.()} + diff --git a/packages/svelte/src/lib/container/SvelteFlow/Wrapper.svelte b/packages/svelte/src/lib/container/SvelteFlow/Wrapper.svelte new file mode 100644 index 00000000..b1353a42 --- /dev/null +++ b/packages/svelte/src/lib/container/SvelteFlow/Wrapper.svelte @@ -0,0 +1,143 @@ + + +
} +> + {@render children?.()} +
+ + diff --git a/packages/svelte/src/lib/container/SvelteFlow/types.ts b/packages/svelte/src/lib/container/SvelteFlow/types.ts index 0963424e..3df0a991 100644 --- a/packages/svelte/src/lib/container/SvelteFlow/types.ts +++ b/packages/svelte/src/lib/container/SvelteFlow/types.ts @@ -1,4 +1,4 @@ -import type { DOMAttributes } from 'svelte/elements'; +import type { ClassValue } from 'svelte/elements'; import type { ConnectionLineType, NodeOrigin, @@ -17,7 +17,10 @@ import type { ColorMode, OnConnect, OnConnectStart, - OnConnectEnd + OnConnectEnd, + OnReconnect, + OnReconnectStart, + OnReconnectEnd } from '@xyflow/system'; import type { @@ -29,324 +32,443 @@ import type { DefaultEdgeOptions, FitViewOptions, OnDelete, - OnEdgeCreate, + OnBeforeConnect, OnBeforeDelete, - IsValidConnection + IsValidConnection, + OnBeforeReconnect, + OnSelectionChange } from '$lib/types'; -import type { Writable } from 'svelte/store'; -export type SvelteFlowProps = DOMAttributes & { - /** The id of the flow - * - * This is necessary if you want to render multiple flows. - * @optional - */ - id?: string; - /** An array of nodes to render in a controlled flow. - * @example - * const nodes = writable([ - * { - * id: 'node-1', - * type: 'input', - * data: { label: 'Node 1' }, - * position: { x: 250, y: 50 } - * } - * ]); - */ - nodes: Writable; - /** An array of edges to render in a controlled flow. - * @example - * const edges = writable([ - * { - * id: 'edge-1-2', - * source: 'node-1', - * target: 'node-2', - * } - * ]); - */ - edges: Writable; - /** Custom node types to be available in a flow. - * - * Svelte Flow matches a node's type to a component in the nodeTypes object. - * @example - * import CustomNode from './CustomNode.svelte'; - * - * const nodeTypes = { nameOfNodeType: CustomNode }; - */ - nodeTypes?: NodeTypes; - /** Custom edge types to be available in a flow. - * - * Svelte Flow matches an edge's type to a component in the edgeTypes object. - * @example - * import CustomEdge from './CustomEdge.svelte'; - * - * const edgeTypes = { nameOfEdgeType: CustomEdge }; - */ - edgeTypes?: EdgeTypes; - /** Pressing down this key you can select multiple elements with a selection box. - * @default 'Shift' - */ - selectionKey?: KeyDefinition | KeyDefinition[] | null; - /** If a key is set, you can pan the viewport while that key is held down even if panOnScroll is set to false. - * - * By setting this prop to null you can disable this functionality. - * @default 'Space' - */ - panActivationKey?: KeyDefinition | KeyDefinition[] | null; - /** Pressing down this key deletes all selected nodes & edges. - * @default 'Backspace' - */ - deleteKey?: KeyDefinition | KeyDefinition[] | null; - /** Pressing down this key you can select multiple elements by clicking. - * @default 'Meta' for macOS, "Ctrl" for other systems - */ - multiSelectionKey?: KeyDefinition | KeyDefinition[] | null; - /** If a key is set, you can zoom the viewport while that key is held down even if panOnScroll is set to false. - * - * By setting this prop to null you can disable this functionality. - * @default 'Meta' for macOS, "Ctrl" for other systems - * */ - zoomActivationKey?: KeyDefinition | KeyDefinition[] | null; - /** If set, initial viewport will show all nodes & edges */ - fitView?: boolean; - /** Options to be used in combination with fitView - * @example - * const fitViewOptions = { - * padding: 0.1, - * includeHiddenNodes: false, - * minZoom: 0.1, - * maxZoom: 1, - * duration: 200, - * nodes: [{id: 'node-1'}, {id: 'node-2'}], // nodes to fit - * }; - */ - fitViewOptions?: FitViewOptions; - /** Defines nodes relative position to its coordinates - * @example - * [0, 0] // default, top left - * [0.5, 0.5] // center - * [1, 1] // bottom right - */ - nodeOrigin?: NodeOrigin; - /** With a threshold greater than zero you can control the distinction between node drag and click events. - * - * If threshold equals 1, you need to drag the node 1 pixel before a drag event is fired. - * @default 1 - */ - nodeDragThreshold?: number; - /** Distance that the mouse can move between mousedown/up that will trigger a click - * @default 0 - */ - paneClickDistance?: number; - /** Distance that the mouse can move between mousedown/up that will trigger a click - * @default 0 - */ - nodeClickDistance?: number; - /** Minimum zoom level - * @default 0.5 - */ - minZoom?: number; - /** Maximum zoom level - * @default 2 - */ - maxZoom?: number; - /** Sets the initial position and zoom of the viewport. - * - * If a default viewport is provided but fitView is enabled, the default viewport will be ignored. - * @example - * const initialViewport = { - * zoom: 0.5, - * position: { x: 0, y: 0 } - * }; - */ - initialViewport?: Viewport; - /** Custom viewport writable to be used instead of internal one */ - viewport?: Writable; - /** The radius around a handle where you drop a connection line to create a new edge. - * @default 20 - */ - connectionRadius?: number; - /** 'strict' connection mode will only allow you to connect source handles to target handles. - * - * 'loose' connection mode will allow you to connect handles of any type to one another. - * @default 'strict' - */ - connectionMode?: ConnectionMode; - /** Styles to be applied to the connection line */ - connectionLineStyle?: string; - /** Styles to be applied to the container of the connection line */ - connectionLineContainerStyle?: string; - /** When set to "partial", when the user creates a selection box by click and dragging nodes that are only partially in the box are still selected. - * @default 'full' - */ - selectionMode?: SelectionMode; - /** Grid all nodes will snap to - * @example [20, 20] - */ - snapGrid?: SnapGrid; - /** Color of edge markers - * @example "#b1b1b7" - */ - defaultMarkerColor?: string; - /** Controls if all nodes should be draggable - * @default true - */ - nodesDraggable?: boolean; - /** Controls if all nodes should be connectable to each other - * @default true - */ - nodesConnectable?: boolean; - /** Controls if all elements should (nodes & edges) be selectable - * @default true - */ - elementsSelectable?: boolean; - /** By default the viewport extends infinitely. You can use this prop to set a boundary. - * - * The first pair of coordinates is the top left boundary and the second pair is the bottom right. - * @example [[-1000, -10000], [1000, 1000]] - */ - translateExtent?: CoordinateExtent; - /** By default the nodes can be placed anywhere. You can use this prop to set a boundary. - * - * The first pair of coordinates is the top left boundary and the second pair is the bottom right. - * @example [[-1000, -10000], [1000, 1000]] - */ - nodeExtent?: CoordinateExtent; - /** Disabling this prop will allow the user to scroll the page even when their pointer is over the flow. - * @default true - */ - preventScrolling?: boolean; - /** Controls if the viewport should zoom by scrolling inside the container */ - zoomOnScroll?: boolean; - /** Controls if the viewport should zoom by double clicking somewhere on the flow */ - zoomOnDoubleClick?: boolean; - /** Controls if the viewport should zoom by pinching on a touch screen */ - zoomOnPinch?: boolean; - /** Controls if the viewport should pan by scrolling inside the container - * - * Can be limited to a specific direction with panOnScrollMode - */ - panOnScroll?: boolean; - /** This prop is used to limit the direction of panning when panOnScroll is enabled. - * - * The "free" option allows panning in any direction. - * @default "free" - * @example "horizontal" | "vertical" - */ - panOnScrollMode?: PanOnScrollMode; - /** Enableing this prop allows users to pan the viewport by clicking and dragging. - * - * You can also set this prop to an array of numbers to limit which mouse buttons can activate panning. - * @example [0, 2] // allows panning with the left and right mouse buttons - * [0, 1, 2, 3, 4] // allows panning with all mouse buttons - */ - panOnDrag?: boolean | number[]; - /** Select multiple elements with a selection box, without pressing down selectionKey */ - selectionOnDrag?: boolean; - /** You can enable this optimisation to instruct Svelte Flow to only render nodes and edges that would be visible in the viewport. - * - * This might improve performance when you have a large number of nodes and edges but also adds an overhead. - * @default false - */ - onlyRenderVisibleElements?: boolean; - /** You can enable this prop to automatically pan the viewport while making a new connection. - * @default true - */ - autoPanOnConnect?: boolean; - /** You can enable this prop to automatically pan the viewport while dragging a node. - * @default true - */ - autoPanOnNodeDrag?: boolean; - /** Set position of the attribution - * @default 'bottom-right' - * @example 'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right' - */ - attributionPosition?: PanelPosition; - /** By default, we render a small attribution in the corner of your flows that links back to the project. - * - * Anyone is free to remove this attribution whether they're a Pro subscriber or not - * but we ask that you take a quick look at our {@link https://reactflow.dev/learn/troubleshooting/remove-attribution | removing attribution guide} - * before doing so. - */ - proOptions?: ProOptions; - /** Defaults to be applied to all new edges that are added to the flow. - * - * Properties on a new edge will override these defaults if they exist. - * @example - * const defaultEdgeOptions = { - * type: 'customEdgeType', - * animated: true, - * interactionWidth: 10, - * data: { label: 'custom label' }, - * hidden: false, - * deletable: true, - * selected: false, - * focusable: true, - * markerStart: EdgeMarker.ArrowClosed, - * markerEnd: EdgeMarker.ArrowClosed, - * zIndex: 12, - * ariaLabel: 'custom aria label' - * } - */ - defaultEdgeOptions?: DefaultEdgeOptions; - /** Sets a fixed width for the flow */ - width?: number; - /** Sets a fixed height for the flow */ - height?: number; - /** Controls color scheme used for styling the flow - * @default 'system' - * @example 'system' | 'light' | 'dark' - */ - colorMode?: ColorMode; - /** Class to be applied to the flow container */ - class?: string; - /** Styles to be applied to the flow container */ - style?: string; - /** Choose from the built-in edge types to be used for connections - * @default 'default' | ConnectionLineType.Bezier - * @example 'straight' | 'default' | 'step' | 'smoothstep' | 'bezier' - * @example ConnectionLineType.Straight | ConnectionLineType.Default | ConnectionLineType.Step | ConnectionLineType.SmoothStep | ConnectionLineType.Bezier - */ - connectionLineType?: ConnectionLineType; - /** This callback can be used to validate a new connection - * - * If you return false, the edge will not be added to your flow. - * If you have custom connection logic its preferred to use this callback over the isValidConnection prop on the handle component for performance reasons. - * @default (connection: Connection) => true - */ - isValidConnection?: IsValidConnection; - /** This event handler is called when the user begins to pan or zoom the viewport */ - onMoveStart?: OnMoveStart; - /** This event handler is called when the user pans or zooms the viewport */ - onMove?: OnMove; - /** This event handler is called when the user stops panning or zooming the viewport */ - onMoveEnd?: OnMoveEnd; - /** Ocassionally something may happen that causes Svelte Flow to throw an error. - * - * Instead of exploding your application, we log a message to the console and then call this event handler. - * You might use it for additional logging or to show a message to the user. - */ - onerror?: OnError; - /** This handler gets called when the user deletes nodes or edges. - * @example - * onDelete={({nodes, edges}) => { - * console.log('deleted nodes:', nodes); - * console.log('deleted edges:', edges); - * }} - */ - ondelete?: OnDelete; - /** This handler gets called before the user deletes nodes or edges and provides a way to abort the deletion by returning false. */ - onbeforedelete?: OnBeforeDelete; +import type { Component } from 'svelte'; +import type { + EdgeEvents, + NodeEvents, + NodeSelectionEvents, + OnSelectionDrag, + PaneEvents +} from '$lib/types/events'; - /** This handler gets called when a new edge is created. You can use it to modify the newly created edge. */ - onedgecreate?: OnEdgeCreate; - - /** This event gets fired when a connection successfully completes and an edge is created. */ - onconnect?: OnConnect; - /** When a user starts to drag a connection line, this event gets fired. */ - onconnectstart?: OnConnectStart; - /** When a user stops dragging a connection line, this event gets fired. */ - onconnectend?: OnConnectEnd; - /** This handler gets called when the flow is finished initializing */ - oninit?: () => void; -}; +export type SvelteFlowProps< + NodeType extends Node = Node, + EdgeType extends Edge = Edge +> = NodeEvents & + NodeSelectionEvents & + EdgeEvents & + PaneEvents & { + /** + * The id of the flow. This is necessary if you want to render multiple flows. + */ + id?: string; + /** Sets a fixed width for the flow */ + width?: number; + /** Sets a fixed height for the flow */ + height?: number; + /** + * An array of nodes to render in a flow. + * @example + * const nodes = $state.raw([ + * { + * id: 'node-1', + * type: 'input', + * data: { label: 'Node 1' }, + * position: { x: 250, y: 50 } + * } + * ]); + */ + nodes?: NodeType[]; + /** + * An array of edges to render in a flow. + * @example + * const edges = $state.raw([ + * { + * id: 'edge-1-2', + * source: 'node-1', + * target: 'node-2', + * } + * ]); + */ + edges?: EdgeType[]; + /** + * Custom node types to be available in a flow. + * Svelte Flow matches a node's type to a component in the nodeTypes object. + * @example + * import CustomNode from './CustomNode.svelte'; + * + * const nodeTypes = { nameOfNodeType: CustomNode }; + */ + nodeTypes?: NodeTypes; + /** + * Custom edge types to be available in a flow. + * Svelte Flow matches an edge's type to a component in the edgeTypes object. + * @example + * import CustomEdge from './CustomEdge.svelte'; + * + * const edgeTypes = { nameOfEdgeType: CustomEdge }; + */ + edgeTypes?: EdgeTypes; + /** Pressing down this key you can select multiple elements with a selection box. + * @default 'Shift' + */ + selectionKey?: KeyDefinition | KeyDefinition[] | null; + /** If a key is set, you can pan the viewport while that key is held down even if panOnScroll is set to false. + * + * By setting this prop to null you can disable this functionality. + * @default 'Space' + */ + panActivationKey?: KeyDefinition | KeyDefinition[] | null; + /** Pressing down this key deletes all selected nodes & edges. + * @default 'Backspace' + */ + deleteKey?: KeyDefinition | KeyDefinition[] | null; + /** Pressing down this key you can select multiple elements by clicking. + * @default 'Meta' for macOS, "Ctrl" for other systems + */ + multiSelectionKey?: KeyDefinition | KeyDefinition[] | null; + /** If a key is set, you can zoom the viewport while that key is held down even if panOnScroll is set to false. + * + * By setting this prop to null you can disable this functionality. + * @default 'Meta' for macOS, "Ctrl" for other systems + * */ + zoomActivationKey?: KeyDefinition | KeyDefinition[] | null; + /** If set, initial viewport will show all nodes & edges */ + fitView?: boolean; + /** + * Options to be used in combination with fitView + * @example + * const fitViewOptions = { + * padding: 0.1, + * includeHiddenNodes: false, + * minZoom: 0.1, + * maxZoom: 1, + * duration: 200, + * nodes: [{id: 'node-1'}, {id: 'node-2'}], // nodes to fit + * }; + */ + fitViewOptions?: FitViewOptions; + /** + * Defines nodes relative position to its coordinates + * @default [0, 0] + * @example + * [0, 0] // default, top left + * [0.5, 0.5] // center + * [1, 1] // bottom right + */ + nodeOrigin?: NodeOrigin; + /** + * With a threshold greater than zero you can control the distinction between node drag and click events. + * If threshold equals 1, you need to drag the node 1 pixel before a drag event is fired. + * @default 1 + */ + nodeDragThreshold?: number; + /** + * Distance that the mouse can move between mousedown/up that will trigger a click + * @default 0 + */ + paneClickDistance?: number; + /** Distance that the mouse can move between mousedown/up that will trigger a click + * @default 0 + */ + nodeClickDistance?: number; + /** Minimum zoom level + * @default 0.5 + */ + minZoom?: number; + /** Maximum zoom level + * @default 2 + */ + maxZoom?: number; + /** + * Sets the initial position and zoom of the viewport. + * If a default viewport is provided but fitView is enabled, the default viewport will be ignored. + * @default { zoom: 1, position: { x: 0, y: 0 } } + * @example + * const initialViewport = { + * zoom: 0.5, + * position: { x: 0, y: 0 } + * }; + */ + initialViewport?: Viewport; + /** Custom viewport to be used instead of internal one */ + viewport?: Viewport; + /** + * The radius around a handle where you drop a connection line to create a new edge. + * @default 20 + */ + connectionRadius?: number; + /** + * 'strict' connection mode will only allow you to connect source handles to target handles. + * 'loose' connection mode will allow you to connect handles of any type to one another. + * @default 'strict' + */ + connectionMode?: ConnectionMode; + /** Provide a custom snippet to be used insted of the default connection line */ + connectionLineComponent?: Component; + /** Styles to be applied to the connection line */ + connectionLineStyle?: string; + /** Styles to be applied to the container of the connection line */ + connectionLineContainerStyle?: string; + /** + * When set to "partial", when the user creates a selection box by click and dragging + * nodes that are only partially in the box are still selected. + * @default 'full' + */ + selectionMode?: SelectionMode; + /** + * Controls if nodes should be automatically selected when being dragged + */ + selectNodesOnDrag?: boolean; + /** + * Grid all nodes will snap to + * @example [20, 20] + */ + snapGrid?: SnapGrid; + /** Color of edge markers + * @example "#b1b1b7" + */ + defaultMarkerColor?: string; + /** + * Controls if all nodes should be draggable + * @default true + */ + nodesDraggable?: boolean; + /** + * Controls if all nodes should be connectable to each other + * @default true + */ + nodesConnectable?: boolean; + /** Controls if all elements should (nodes & edges) be selectable + * @default true + */ + elementsSelectable?: boolean; + /** + * When `true`, focus between nodes can be cycled with the `Tab` key and selected with the `Enter` + * key. This option can be overridden by individual nodes by setting their `focusable` prop. + * @default true + */ + nodesFocusable?: boolean; + /** + * When `true`, focus between edges can be cycled with the `Tab` key and selected with the `Enter` + * key. This option can be overridden by individual edges by setting their `focusable` prop. + * @default true + */ + edgesFocusable?: boolean; + /** + * By default the viewport extends infinitely. You can use this prop to set a boundary. + * The first pair of coordinates is the top left boundary and the second pair is the bottom right. + * @default @default [[-∞, -∞], [+∞, +∞]] + * @example [[-1000, -10000], [1000, 1000]] + */ + translateExtent?: CoordinateExtent; + /** + * By default the nodes can be placed anywhere. You can use this prop to set a boundary. + * The first pair of coordinates is the top left boundary and the second pair is the bottom right. + * @default [[-∞, -∞], [+∞, +∞]] + * @example [[-1000, -10000], [1000, 1000]] + */ + nodeExtent?: CoordinateExtent; + /** + * Disabling this prop will allow the user to scroll the page even when their pointer is over the flow. + * @default true + */ + preventScrolling?: boolean; + /** + * Controls if the viewport should zoom by scrolling inside the container. + * @default true + */ + zoomOnScroll?: boolean; + /** + * Controls if the viewport should zoom by double clicking somewhere on the flow + * @default true + */ + zoomOnDoubleClick?: boolean; + /** + * Controls if the viewport should zoom by pinching on a touch screen + * @default true + */ + zoomOnPinch?: boolean; + /** + * Controls if the viewport should pan by scrolling inside the container + * Can be limited to a specific direction with panOnScrollMode + * @default false + */ + panOnScroll?: boolean; + /** + * This prop is used to limit the direction of panning when panOnScroll is enabled. + * The "free" option allows panning in any direction. + * @default "free" + * @example "horizontal" | "vertical" + */ + panOnScrollMode?: PanOnScrollMode; + /** + * Enableing this prop allows users to pan the viewport by clicking and dragging. + * You can also set this prop to an array of numbers to limit which mouse buttons can activate panning. + * @default true + * @example [0, 2] // allows panning with the left and right mouse buttons + * [0, 1, 2, 3, 4] // allows panning with all mouse buttons + */ + panOnDrag?: boolean | number[]; + /** + * Select multiple elements with a selection box, without pressing down selectionKey. + * @default false + */ + selectionOnDrag?: boolean; + /** + * You can enable this optimisation to instruct Svelte Flow to only render nodes and edges that would be visible in the viewport. + * This might improve performance when you have a large number of nodes and edges but also adds an overhead. + * @default false + */ + onlyRenderVisibleElements?: boolean; + /** + * You can enable this prop to automatically pan the viewport while making a new connection. + * @default true + */ + autoPanOnConnect?: boolean; + /** + * You can enable this prop to automatically pan the viewport while dragging a node. + * @default true + */ + autoPanOnNodeDrag?: boolean; + /** + * Defaults to be applied to all new edges that are added to the flow. + * Properties on a new edge will override these defaults if they exist. + * @example + * const defaultEdgeOptions = { + * type: 'customEdgeType', + * animated: true + * } + */ + defaultEdgeOptions?: DefaultEdgeOptions; + /** + * Controls color scheme used for styling the flow + * @default 'system' + * @example 'system' | 'light' | 'dark' + */ + colorMode?: ColorMode; + /** Fallback color mode for SSR if colorMode is set to 'system' */ + colorModeSSR?: Omit; + /** Class to be applied to the flow container */ + class?: ClassValue; + /** Styles to be applied to the flow container */ + style?: string; + /** Choose from the built-in edge types to be used for connections + * @default 'default' | ConnectionLineType.Bezier + * @example 'straight' | 'default' | 'step' | 'smoothstep' | 'bezier' + * @example ConnectionLineType.Straight | ConnectionLineType.Default | ConnectionLineType.Step | ConnectionLineType.SmoothStep | ConnectionLineType.Bezier + */ + connectionLineType?: ConnectionLineType; + /** Enabling this option will raise the z-index of nodes when they are selected. + * @default true + */ + elevateNodesOnSelect?: boolean; + /** + * Enabling this option will raise the z-index of edges when they are selected, + * or when the connected nodes are selected. + * @default true + */ + elevateEdgesOnSelect?: boolean; + /** + * You can use this prop to disable keyboard accessibility features such as selecting nodes or + * moving selected nodes with the arrow keys. + * @default false + */ + disableKeyboardA11y?: boolean; + /** + * If a node is draggable, clicking and dragging that node will move it around the canvas. Adding + * the `"nodrag"` class prevents this behavior and this prop allows you to change the name of that + * class. + * @default "nodrag" + */ + noDragClass?: string; + /** + * Typically, scrolling the mouse wheel when the mouse is over the canvas will zoom the viewport. + * Adding the `"nowheel"` class to an element n the canvas will prevent this behavior and this prop + * allows you to change the name of that class. + * @default "nowheel" + */ + noWheelClass?: string; + /** + * If an element in the canvas does not stop mouse events from propagating, clicking and dragging + * that element will pan the viewport. Adding the `"nopan"` class prevents this behavior and this + * prop allows you to change the name of that class. + * @default "nopan" + */ + noPanClass?: string; + /** Toggles ability to make connections via clicking the handles */ + clickConnect?: boolean; + /** + * This callback can be used to validate a new connection. + * If you return `false`, the edge will not be added to your flow. + * If you have custom connection logic its preferred to use this callback over the + * `isValidConnection` prop on the handle component for performance reasons. + */ + /** + * Set position of the attribution + * @default 'bottom-right' + * @example 'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right' + */ + attributionPosition?: PanelPosition; + /** + * By default, we render a small attribution in the corner of your flows that links back to the project. + * You are free to remove this attribution but we ask that you take a quick look at our + * {@link https://svelteflow.dev/learn/troubleshooting/remove-attribution | removing attribution guide} + * before doing so. + */ + proOptions?: ProOptions; + isValidConnection?: IsValidConnection; + /** This event handler is called when the user begins to pan or zoom the viewport */ + onmovestart?: OnMoveStart; + /** This event handler is called when the user pans or zooms the viewport */ + onmove?: OnMove; + /** This event handler is called when the user stops panning or zooming the viewport */ + onmoveend?: OnMoveEnd; + /** + * Ocassionally something may happen that causes Svelte Flow to throw an error. + * Instead of exploding your application, we log a message to the console and then call this event handler. + * You might use it for additional logging or to show a message to the user. + */ + onflowerror?: OnError; + /** This handler gets called when the user deletes nodes or edges. + * @example + * onDelete={({nodes, edges}) => { + * console.log('deleted nodes:', nodes); + * console.log('deleted edges:', edges); + * }} + */ + ondelete?: OnDelete; + /** This handler gets called before the user deletes nodes or edges and provides a way to abort the deletion by returning false. */ + onbeforedelete?: OnBeforeDelete; + /** This handler gets called when a new edge is created. You can use it to modify the newly created edge. */ + onbeforeconnect?: OnBeforeConnect; + /** This event gets fired when a connection successfully completes and an edge is created. */ + onconnect?: OnConnect; + /** When a user starts to drag a connection line, this event gets fired. */ + onconnectstart?: OnConnectStart; + /** When a user stops dragging a connection line, this event gets fired. */ + onconnectend?: OnConnectEnd; + /** This event gets fired when after an edge was reconnected*/ + onreconnect?: OnReconnect; + /** This event gets fired when a user starts to reconnect an edge */ + onreconnectstart?: OnReconnectStart; + /** This event gets fired when a user stops reconnecting an edge */ + onreconnectend?: OnReconnectEnd; + /** This handler gets called when an edge is reconnected. You can use it to modify the edge before the update is applied. */ + onbeforereconnect?: OnBeforeReconnect; + /** A connection is started by clicking on a handle */ + onclickconnectstart?: OnConnectStart; + /** A connection is finished by clicking on a handle */ + onclickconnectend?: OnConnectEnd; + /** This handler gets called when the flow is finished initializing */ + oninit?: () => void; + /** This event handler gets called when the selected nodes & edges change */ + onselectionchange?: OnSelectionChange; + /** This event handler gets called when a user starts to drag a selection box. */ + onselectiondragstart?: OnSelectionDrag; + /** This event handler gets called when a user drags a selection box. */ + onselectiondrag?: OnSelectionDrag; + /** This event handler gets called when a user stops dragging a selection box. */ + onselectiondragstop?: OnSelectionDrag; + /** This event handler gets called when the user starts to drag a selection box */ + onselectionstart?: (event: PointerEvent) => void; + /** This event handler gets called when the user finishes dragging a selection box */ + onselectionend?: (event: PointerEvent) => void; + }; diff --git a/packages/svelte/src/lib/container/SvelteFlow/utils.ts b/packages/svelte/src/lib/container/SvelteFlow/utils.ts deleted file mode 100644 index d7a71a7c..00000000 --- a/packages/svelte/src/lib/container/SvelteFlow/utils.ts +++ /dev/null @@ -1,91 +0,0 @@ -import type { Writable } from 'svelte/store'; -import type { CoordinateExtent } from '@xyflow/system'; - -import type { SvelteFlowStore } from '$lib/store/types'; -import type { EdgeTypes, NodeTypes } from '$lib/types'; - -// this is helper function for updating the store -// for props where we need to call a specific store action -export function updateStore( - store: SvelteFlowStore, - { - nodeTypes, - edgeTypes, - minZoom, - maxZoom, - translateExtent, - paneClickDistance - }: { - nodeTypes?: NodeTypes; - edgeTypes?: EdgeTypes; - minZoom?: number; - maxZoom?: number; - translateExtent?: CoordinateExtent; - paneClickDistance?: number; - } -) { - if (nodeTypes !== undefined) { - store.setNodeTypes(nodeTypes); - } - - if (edgeTypes !== undefined) { - store.setEdgeTypes(edgeTypes); - } - - if (minZoom !== undefined) { - store.setMinZoom(minZoom); - } - - if (maxZoom !== undefined) { - store.setMaxZoom(maxZoom); - } - - if (translateExtent !== undefined) { - store.setTranslateExtent(translateExtent); - } - - if (paneClickDistance !== undefined) { - store.setPaneClickDistance(paneClickDistance); - } -} - -const getKeys = (obj: T) => Object.keys(obj) as Array; - -type UnwrapWritable = T extends Writable ? U : T; - -// @todo there must be a better way to define the types here.. -export type UpdatableStoreProps = { - flowId?: UnwrapWritable; - connectionLineType?: UnwrapWritable; - connectionRadius?: UnwrapWritable; - selectionMode?: UnwrapWritable; - snapGrid?: UnwrapWritable; - defaultMarkerColor?: UnwrapWritable; - nodesDraggable?: UnwrapWritable; - nodesConnectable?: UnwrapWritable; - elementsSelectable?: UnwrapWritable; - onlyRenderVisibleElements?: UnwrapWritable; - isValidConnection?: UnwrapWritable; - autoPanOnConnect?: UnwrapWritable; - autoPanOnNodeDrag?: UnwrapWritable; - connectionMode?: UnwrapWritable; - onerror?: UnwrapWritable; - ondelete?: UnwrapWritable; - onedgecreate?: UnwrapWritable; - nodeDragThreshold?: UnwrapWritable; - onconnect?: UnwrapWritable; - onconnectstart?: UnwrapWritable; - onconnectend?: UnwrapWritable; - onbeforedelete?: UnwrapWritable; - nodeOrigin?: UnwrapWritable; -}; - -export function updateStoreByKeys(store: SvelteFlowStore, keys: UpdatableStoreProps) { - getKeys(keys).forEach((prop) => { - const update = keys[prop]; - if (update !== undefined) { - // @ts-expect-error @todo: how to fix this TS error? - store[prop].set(update); - } - }); -} diff --git a/packages/svelte/src/lib/container/Viewport/Viewport.svelte b/packages/svelte/src/lib/container/Viewport/Viewport.svelte index a6578a87..c7c5b4cf 100644 --- a/packages/svelte/src/lib/container/Viewport/Viewport.svelte +++ b/packages/svelte/src/lib/container/Viewport/Viewport.svelte @@ -1,22 +1,18 @@ -
- + {@render children()}
- - diff --git a/packages/svelte/src/lib/container/Zoom/Zoom.svelte b/packages/svelte/src/lib/container/Zoom/Zoom.svelte index 4148d278..42a644a6 100644 --- a/packages/svelte/src/lib/container/Zoom/Zoom.svelte +++ b/packages/svelte/src/lib/container/Zoom/Zoom.svelte @@ -1,92 +1,77 @@ -
{ + store.dragging = dragging; + }, + setPanZoomInstance: (instance: PanZoomInstance) => { + store.panZoom = instance; + }, + onPanZoomStart: onmovestart, + onPanZoom: onmove, + onPanZoomEnd: onmoveend, zoomOnScroll, zoomOnDoubleClick, zoomOnPinch, - panOnScroll: _panOnScroll, - panOnDrag: _panOnDrag, + panOnScroll: panOnScrollActive, + panOnDrag: panOnDragActive, panOnScrollSpeed: 0.5, panOnScrollMode: panOnScrollMode || PanOnScrollMode.Free, - zoomActivationKeyPressed: $zoomActivationKeyPressed, + zoomActivationKeyPressed: store.zoomActivationKeyPressed, preventScrolling: typeof preventScrolling === 'boolean' ? preventScrolling : true, - noPanClassName: 'nopan', - noWheelClassName: 'nowheel', - userSelectionActive: !!$selectionRect, - translateExtent: $translateExtent, - lib: $lib, + noPanClassName: store.noPanClass, + noWheelClassName: store.noWheelClass, + userSelectionActive: !!store.selectionRect, + translateExtent: store.translateExtent, + lib: 'svelte', paneClickDistance, - onTransformChange + onTransformChange: (transform: Transform) => { + store.viewport = { x: transform[0], y: transform[1], zoom: transform[2] }; + } }} > - + {@render children()}
- - diff --git a/packages/svelte/src/lib/container/Zoom/types.ts b/packages/svelte/src/lib/container/Zoom/types.ts index b7b4f29b..247a16e9 100644 --- a/packages/svelte/src/lib/container/Zoom/types.ts +++ b/packages/svelte/src/lib/container/Zoom/types.ts @@ -1,11 +1,11 @@ -import type { OnMoveStart, OnMove, OnMoveEnd, PanOnScrollMode, Viewport } from '@xyflow/system'; +import type { SvelteFlowStore } from '$lib/store/types'; +import type { Edge, Node } from '$lib/types'; +import type { OnMoveStart, OnMove, OnMoveEnd, PanOnScrollMode } from '@xyflow/system'; +import type { Snippet } from 'svelte'; -export type ZoomProps = { - initialViewport?: Viewport; +export type ZoomProps = { + store: SvelteFlowStore; panOnScrollMode: PanOnScrollMode; - onMove?: OnMove; - onMoveStart?: OnMoveStart; - onMoveEnd?: OnMoveEnd; preventScrolling: boolean; zoomOnScroll: boolean; zoomOnDoubleClick: boolean; @@ -13,4 +13,9 @@ export type ZoomProps = { panOnScroll: boolean; panOnDrag: boolean | number[]; paneClickDistance: number; + onmove?: OnMove; + onmovestart?: OnMoveStart; + onmoveend?: OnMoveEnd; + oninit?: () => void; + children: Snippet; }; diff --git a/packages/svelte/src/lib/hooks/derivedWarning.svelte.ts b/packages/svelte/src/lib/hooks/derivedWarning.svelte.ts new file mode 100644 index 00000000..c676f4b1 --- /dev/null +++ b/packages/svelte/src/lib/hooks/derivedWarning.svelte.ts @@ -0,0 +1,24 @@ +import { key } from '$lib/store'; +import type { StoreContext } from '$lib/store/types'; +import { getContext } from 'svelte'; + +/** + * Warns the user that they should use $derived() when calling a hook. + * This is not neccessarry when the hook is called inside a child of , + * however exceptions can be made if you don't want to return a closure. + * @param functionName - The name of the function that is being called + * @param force - If true, the warning will be shown regardless if child of + */ +export function derivedWarning(functionName: string) { + const storeContext = getContext(key); + + if (!storeContext) { + throw new Error( + `In order to use ${functionName}() you need to wrap your component in a ` + ); + } + + if (storeContext.provider && typeof window === 'object' && !$effect.tracking()) { + throw new Error(`Use $derived(${functionName}()) to receive updates when values change.`); + } +} diff --git a/packages/svelte/src/lib/hooks/useColorMode.svelte.ts b/packages/svelte/src/lib/hooks/useColorMode.svelte.ts new file mode 100644 index 00000000..9ec2d65d --- /dev/null +++ b/packages/svelte/src/lib/hooks/useColorMode.svelte.ts @@ -0,0 +1,14 @@ +import type { ColorModeClass } from '@xyflow/system'; +import { useStore } from './useStore'; + +/** + * Hook for receiving the current color mode class 'dark' or 'light'. + * + */ +export function useColorMode(): { current: ColorModeClass } { + const { colorMode } = $derived(useStore()); + + return { + current: colorMode + }; +} diff --git a/packages/svelte/src/lib/hooks/useColorModeClass.ts b/packages/svelte/src/lib/hooks/useColorModeClass.ts deleted file mode 100644 index 07a9a824..00000000 --- a/packages/svelte/src/lib/hooks/useColorModeClass.ts +++ /dev/null @@ -1,38 +0,0 @@ -import type { ColorMode, ColorModeClass } from '@xyflow/system'; -import { readable, type Readable } from 'svelte/store'; - -function getMediaQuery() { - if (typeof window === 'undefined' || !window.matchMedia) { - return null; - } - - return window.matchMedia('(prefers-color-scheme: dark)'); -} - -/** - * Hook for receiving the current color mode class 'dark' or 'light'. - * - * @internal - * @param colorMode - The color mode to use ('dark', 'light' or 'system') - */ -export function useColorModeClass(colorMode: ColorMode = 'light'): Readable { - const colorModeClass = readable('light', (set) => { - if (colorMode !== 'system') { - set(colorMode); - - return; - } - - const mediaQuery = getMediaQuery(); - const updateColorModeClass = () => set(mediaQuery?.matches ? 'dark' : 'light'); - - set(mediaQuery?.matches ? 'dark' : 'light'); - mediaQuery?.addEventListener('change', updateColorModeClass); - - return () => { - mediaQuery?.removeEventListener('change', updateColorModeClass); - }; - }); - - return colorModeClass; -} diff --git a/packages/svelte/src/lib/hooks/useConnection.svelte.ts b/packages/svelte/src/lib/hooks/useConnection.svelte.ts new file mode 100644 index 00000000..da3688f4 --- /dev/null +++ b/packages/svelte/src/lib/hooks/useConnection.svelte.ts @@ -0,0 +1,19 @@ +import { useStore } from '$lib/store'; + +import type { ConnectionState } from '@xyflow/system'; + +/** + * Hook for receiving the current connection. + * + * @public + * @returns Current connection as a signal + */ +export function useConnection(): { current: ConnectionState } { + const { connection } = $derived(useStore()); + + return { + get current() { + return connection; + } + }; +} diff --git a/packages/svelte/src/lib/hooks/useConnection.ts b/packages/svelte/src/lib/hooks/useConnection.ts deleted file mode 100644 index 4402ab69..00000000 --- a/packages/svelte/src/lib/hooks/useConnection.ts +++ /dev/null @@ -1,16 +0,0 @@ -import type { Readable } from 'svelte/store'; -import { useStore } from '$lib/store'; - -import type { ConnectionState } from '@xyflow/system'; - -/** - * Hook for receiving the current connection. - * - * @public - * @returns current connection as a readable store - */ -export function useConnection(): Readable { - const { connection } = useStore(); - - return connection; -} diff --git a/packages/svelte/src/lib/hooks/useHandleConnections.ts b/packages/svelte/src/lib/hooks/useHandleConnections.ts deleted file mode 100644 index 7aec3858..00000000 --- a/packages/svelte/src/lib/hooks/useHandleConnections.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { derived } from 'svelte/store'; -import { areConnectionMapsEqual, type HandleConnection, type HandleType } from '@xyflow/system'; - -import { useStore } from '$lib/store'; -import { getContext } from 'svelte'; - -export type useHandleConnectionsParams = { - type: HandleType; - nodeId?: string; - id?: string | null; -}; - -const initialConnections: HandleConnection[] = []; - -/** - * Hook to check if a is connected to another and get the connections. - * - * @public - * @param param.nodeId - * @param param.type - handle type 'source' or 'target' - * @param param.id - the handle id (this is only needed if the node has multiple handles of the same type) - * @returns an array with connections - */ -export function useHandleConnections({ type, nodeId, id = null }: useHandleConnectionsParams) { - const { edges, connectionLookup } = useStore(); - - const _nodeId = getContext('svelteflow__node_id'); - const currentNodeId = nodeId ?? _nodeId; - - let prevConnections: Map | undefined = undefined; - - return derived( - [edges, connectionLookup], - ([, connectionLookup], set) => { - const nextConnections = connectionLookup.get(`${currentNodeId}-${type}-${id || null}`); - - if (!areConnectionMapsEqual(nextConnections, prevConnections)) { - prevConnections = nextConnections; - set(Array.from(prevConnections?.values() || [])); - } - }, - initialConnections - ); -} diff --git a/packages/svelte/src/lib/hooks/useHandleEdgeSelect.ts b/packages/svelte/src/lib/hooks/useHandleEdgeSelect.ts deleted file mode 100644 index 5e6ba5d3..00000000 --- a/packages/svelte/src/lib/hooks/useHandleEdgeSelect.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { get } from 'svelte/store'; -import { errorMessages } from '@xyflow/system'; - -import { useStore } from '$lib/store'; - -export function useHandleEdgeSelect() { - const { - edgeLookup, - selectionRect, - selectionRectMode, - multiselectionKeyPressed, - addSelectedEdges, - unselectNodesAndEdges, - elementsSelectable - } = useStore(); - - return (id: string) => { - const edge = get(edgeLookup).get(id); - - if (!edge) { - console.warn('012', errorMessages['error012'](id)); - return; - } - - const selectable = - edge.selectable || (get(elementsSelectable) && typeof edge.selectable === 'undefined'); - - if (selectable) { - selectionRect.set(null); - selectionRectMode.set(null); - - if (!edge.selected) { - addSelectedEdges([id]); - } else if (edge.selected && get(multiselectionKeyPressed)) { - unselectNodesAndEdges({ nodes: [], edges: [edge] }); - } - } - }; -} diff --git a/packages/svelte/src/lib/hooks/useInitialized.svelte.ts b/packages/svelte/src/lib/hooks/useInitialized.svelte.ts new file mode 100644 index 00000000..931c6cf1 --- /dev/null +++ b/packages/svelte/src/lib/hooks/useInitialized.svelte.ts @@ -0,0 +1,28 @@ +import { useStore } from '$lib/store'; + +/** + * Hook for seeing if nodes are initialized + * @returns A boolean that indicates if nodes are initialized + * @public + */ +export function useNodesInitialized() { + const { nodesInitialized } = $derived(useStore()); + return { + get current() { + return nodesInitialized; + } + }; +} + +/** + * Hook for seeing if the viewport is initialized + * @returns - reactive viewportInitialized + */ +export function useViewportInitialized() { + const { viewportInitialized } = $derived(useStore()); + return { + get current() { + return viewportInitialized; + } + }; +} diff --git a/packages/svelte/src/lib/hooks/useInitialized.ts b/packages/svelte/src/lib/hooks/useInitialized.ts deleted file mode 100644 index 0f486abf..00000000 --- a/packages/svelte/src/lib/hooks/useInitialized.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { useStore } from '$lib/store'; -import type { Readable } from 'svelte/store'; - -/** - * Hook for seeing if nodes are initialized - * @returns - nodesInitialized Writable - */ -export function useNodesInitialized() { - const { nodesInitialized } = useStore(); - return { - subscribe: nodesInitialized.subscribe - } as Readable; -} - -/** - * Hook for seeing if the flow is initialized - * @returns - initialized Writable - */ -export function useInitialized() { - const { initialized } = useStore(); - return { - subscribe: initialized.subscribe - } as Readable; -} diff --git a/packages/svelte/src/lib/hooks/useInternalNode.svelte.ts b/packages/svelte/src/lib/hooks/useInternalNode.svelte.ts new file mode 100644 index 00000000..46c1cc04 --- /dev/null +++ b/packages/svelte/src/lib/hooks/useInternalNode.svelte.ts @@ -0,0 +1,25 @@ +import { useStore } from '$lib/store'; +import type { InternalNode } from '$lib/types'; + +/** + * Hook to get an internal node by id. + * + * @public + * @param id - the node id + * @returns An internal node or undefined + */ +export function useInternalNode(id: string): { current: InternalNode | undefined } { + const { nodeLookup, nodes } = $derived(useStore()); + + const node = $derived.by(() => { + // eslint-disable-next-line @typescript-eslint/no-unused-expressions + nodes; + return nodeLookup.get(id); + }); + + return { + get current() { + return node; + } + }; +} diff --git a/packages/svelte/src/lib/hooks/useInternalNode.ts b/packages/svelte/src/lib/hooks/useInternalNode.ts deleted file mode 100644 index 16c83960..00000000 --- a/packages/svelte/src/lib/hooks/useInternalNode.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { derived, type Readable } from 'svelte/store'; - -import { useStore } from '$lib/store'; -import type { InternalNode } from '$lib/types'; - -/** - * Hook to get an internal node by id. - * - * @public - * @param id - the node id - * @returns a readable with an internal node or undefined - */ -export function useInternalNode(id: string): Readable { - const { nodeLookup, nodes } = useStore(); - - return derived([nodeLookup, nodes], ([nodeLookup]) => nodeLookup.get(id)); -} diff --git a/packages/svelte/src/lib/hooks/useNodeConnections.svelte.ts b/packages/svelte/src/lib/hooks/useNodeConnections.svelte.ts new file mode 100644 index 00000000..111fc715 --- /dev/null +++ b/packages/svelte/src/lib/hooks/useNodeConnections.svelte.ts @@ -0,0 +1,70 @@ +import { + areConnectionMapsEqual, + type NodeConnection, + type HandleType, + type Connection, + handleConnectionChange +} from '@xyflow/system'; + +import { useStore } from '$lib/store'; +import { getContext } from 'svelte'; + +type UseNodeConnectionsParams = { + id?: string; + handleType?: HandleType; + handleId?: string; + onConnect?: (connections: Connection[]) => void; + onDisconnect?: (connections: Connection[]) => void; +}; + +const initialConnections: NodeConnection[] = []; + +/** + * Hook to retrieve all edges connected to a node. Can be filtered by handle type and id. + * + * @public + * @param param.id - node id - optional if called inside a custom node + * @param param.handleType - filter by handle type 'source' or 'target' + * @param param.handleId - filter by handle id (this is only needed if the node has multiple handles of the same type) + * @param param.onConnect - gets called when a connection is established + * @param param.onDisconnect - gets called when a connection is removed + * @returns An array with connections + */ +export function useNodeConnections({ + id, + handleType, + handleId, + onConnect, + onDisconnect +}: UseNodeConnectionsParams = {}) { + const { edges, connectionLookup } = $derived(useStore()); + + const contextNodeId = getContext('svelteflow__node_id'); + const nodeId = id ?? contextNodeId; + + let prevConnections: Map = new Map(); + let connectionsArray: NodeConnection[] = initialConnections; + + const connections = $derived.by(() => { + // eslint-disable-next-line @typescript-eslint/no-unused-expressions + edges; + const nextConnections = + connectionLookup.get( + `${nodeId}${handleType ? (handleId ? `-${handleType}-${handleId}` : `-${handleType}`) : ''}` + ) ?? new Map(); + if (!areConnectionMapsEqual(nextConnections, prevConnections)) { + if (onConnect) handleConnectionChange(nextConnections, prevConnections, onConnect); + if (onDisconnect) handleConnectionChange(prevConnections, nextConnections, onDisconnect); + + prevConnections = nextConnections; + connectionsArray = Array.from(nextConnections.values() || initialConnections); + } + return connectionsArray; + }); + + return { + get current() { + return connections; + } + }; +} diff --git a/packages/svelte/src/lib/hooks/useNodesData.ts b/packages/svelte/src/lib/hooks/useNodesData.svelte.ts similarity index 64% rename from packages/svelte/src/lib/hooks/useNodesData.ts rename to packages/svelte/src/lib/hooks/useNodesData.svelte.ts index a13bdb65..71432ceb 100644 --- a/packages/svelte/src/lib/hooks/useNodesData.ts +++ b/packages/svelte/src/lib/hooks/useNodesData.svelte.ts @@ -1,4 +1,3 @@ -import { derived, type Readable } from 'svelte/store'; import { shallowNodeData } from '@xyflow/system'; import type { Node } from '$lib/types'; @@ -9,21 +8,24 @@ import { useStore } from '$lib/store'; * * @public * @param nodeId - The id (or ids) of the node to get the data from - * @returns A readable store with an array of data objects + * @returns An array of data objects */ export function useNodesData( nodeId: string -): Readable | null>; +): { current: Pick | null }; export function useNodesData( nodeIds: string[] -): Readable[]>; +): { current: Pick[] }; // eslint-disable-next-line @typescript-eslint/no-explicit-any export function useNodesData(nodeIds: any): any { - const { nodes, nodeLookup } = useStore(); + const { nodes, nodeLookup } = $derived(useStore()); // eslint-disable-next-line @typescript-eslint/no-explicit-any let prevNodesData: any[] = []; + let initialRun = true; - return derived([nodes, nodeLookup], ([, nodeLookup], set) => { + const nodeData = $derived.by(() => { + // eslint-disable-next-line @typescript-eslint/no-unused-expressions + nodes; const nextNodesData = []; const isArrayOfIds = Array.isArray(nodeIds); const _nodeIds = isArrayOfIds ? nodeIds : [nodeIds]; @@ -39,9 +41,17 @@ export function useNodesData(nodeIds: any): any { } } - if (!shallowNodeData(nextNodesData, prevNodesData)) { + if (!shallowNodeData(nextNodesData, prevNodesData) || initialRun) { prevNodesData = nextNodesData; - set(isArrayOfIds ? nextNodesData : nextNodesData[0] ?? null); + initialRun = false; } + + return isArrayOfIds ? prevNodesData : (prevNodesData[0] ?? null); }); + + return { + get current() { + return nodeData; + } + }; } diff --git a/packages/svelte/src/lib/hooks/useNodesEdges.ts b/packages/svelte/src/lib/hooks/useNodesEdges.ts deleted file mode 100644 index 062f596d..00000000 --- a/packages/svelte/src/lib/hooks/useNodesEdges.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { useStore } from '$lib/store'; - -/** - * Hook for getting the current nodes from the store. - * - * @public - * @returns store with an array of nodes - */ -export function useNodes() { - const { nodes } = useStore(); - return nodes; -} - -/** - * Hook for getting the current edges from the store. - * - * @public - * @returns store with an array of edges - */ -export function useEdges() { - const { edges } = useStore(); - return edges; -} diff --git a/packages/svelte/src/lib/hooks/useNodesEdgesViewport.svelte.ts b/packages/svelte/src/lib/hooks/useNodesEdgesViewport.svelte.ts new file mode 100644 index 00000000..aea6ab84 --- /dev/null +++ b/packages/svelte/src/lib/hooks/useNodesEdgesViewport.svelte.ts @@ -0,0 +1,75 @@ +import { useStore } from '$lib/store'; +import type { Edge, Node } from '$lib/types'; +import type { Viewport } from '@xyflow/system'; + +/** + * Hook for getting the current nodes from the store. + * + * @public + * @returns A reactive signal of the current nodes + */ +export function useNodes() { + const store = $derived(useStore()); + return { + get current() { + return store.nodes; + }, + set current(nodes) { + store.nodes = nodes; + }, + update(updateFn: (nodes: Node[]) => Node[]) { + store.nodes = updateFn(store.nodes); + }, + set(nodes: Node[]) { + store.nodes = nodes; + } + }; +} + +/** + * Hook for getting the current edges from the store. + * + * @public + * @returns A reactive signal of the current edges + */ +export function useEdges() { + const store = $derived(useStore()); + return { + get current() { + return store.edges; + }, + set current(edges) { + store.edges = edges; + }, + update(updateFn: (edges: Edge[]) => Edge[]) { + store.edges = updateFn(store.edges); + }, + set(edges: Edge[]) { + store.edges = edges; + } + }; +} + +/** + * Hook for getting the current viewport from the store. + * + * @public + * @returns A reactive signal of the current viewport + */ +export function useViewport() { + const store = $derived(useStore()); + return { + get current() { + return store.viewport; + }, + set current(viewport: Viewport) { + store.viewport = viewport; + }, + update(updateFn: (viewport: Viewport) => Viewport) { + store.viewport = updateFn(store.viewport); + }, + set(viewport: Viewport) { + store.viewport = viewport; + } + }; +} diff --git a/packages/svelte/src/lib/hooks/useOnSelectionChange.svelte.ts b/packages/svelte/src/lib/hooks/useOnSelectionChange.svelte.ts new file mode 100644 index 00000000..f827ddcc --- /dev/null +++ b/packages/svelte/src/lib/hooks/useOnSelectionChange.svelte.ts @@ -0,0 +1,15 @@ +import type { OnSelectionChange } from '$lib/types'; +import { useStore } from '$lib/hooks/useStore'; + +export function useOnSelectionChange(onselectionchange: OnSelectionChange) { + const store = $derived(useStore()); + const symbol = Symbol(); + + $effect(() => { + store.selectionChangeHandlers.set(symbol, onselectionchange); + + return () => { + store.selectionChangeHandlers.delete(symbol); + }; + }); +} diff --git a/packages/svelte/src/lib/hooks/useStore.ts b/packages/svelte/src/lib/hooks/useStore.ts new file mode 100644 index 00000000..11c89e7e --- /dev/null +++ b/packages/svelte/src/lib/hooks/useStore.ts @@ -0,0 +1,25 @@ +import { getContext } from 'svelte'; +import type { StoreContext, SvelteFlowStore } from '../store/types'; + +import { key } from '../store'; +import { derivedWarning } from './derivedWarning.svelte'; +import type { Node, Edge } from '$lib/types'; + +export function useStore< + NodeType extends Node = Node, + EdgeType extends Edge = Edge +>(): SvelteFlowStore { + const storeContext = getContext>(key); + + if (!storeContext) { + throw new Error( + 'To call useStore outside of you need to wrap your component in a ' + ); + } + + if (process.env.NODE_ENV === 'development') { + derivedWarning('useStore'); + } + + return storeContext.getStore(); +} diff --git a/packages/svelte/src/lib/hooks/useSvelteFlow.ts b/packages/svelte/src/lib/hooks/useSvelteFlow.svelte.ts similarity index 64% rename from packages/svelte/src/lib/hooks/useSvelteFlow.ts rename to packages/svelte/src/lib/hooks/useSvelteFlow.svelte.ts index 6ee27fb2..82c66064 100644 --- a/packages/svelte/src/lib/hooks/useSvelteFlow.ts +++ b/packages/svelte/src/lib/hooks/useSvelteFlow.svelte.ts @@ -1,4 +1,3 @@ -import { get, type Writable } from 'svelte/store'; import { getOverlappingArea, isRectObject, @@ -22,16 +21,16 @@ import { import { useStore } from '$lib/store'; import type { Edge, FitViewOptions, InternalNode, Node } from '$lib/types'; -import { isNode } from '$lib/utils'; +import { isEdge, isNode } from '$lib/utils'; +import { untrack } from 'svelte'; /** - * Hook for accessing the ReactFlow instance. + * Hook for accessing the SvelteFlow instance. * * @public - * - * @returns helper functions + * @returns A set of helper functions */ -export function useSvelteFlow(): { +export function useSvelteFlow(): { /** * Zooms viewport in by 1.2. * @@ -50,33 +49,33 @@ export function useSvelteFlow(): { * @param id - the node id * @returns the node or undefined if no node was found */ - getInternalNode: (id: string) => InternalNode | undefined; + getInternalNode: (id: string) => InternalNode | undefined; /** * Returns a node by id. * * @param id - the node id * @returns the node or undefined if no node was found */ - getNode: (id: string) => Node | undefined; + getNode: (id: string) => NodeType | undefined; /** * Returns nodes. * * @returns nodes array */ - getNodes: (ids?: string[]) => Node[]; + getNodes: (ids?: string[]) => NodeType[]; /** * Returns an edge by id. * * @param id - the edge id * @returns the edge or undefined if no edge was found */ - getEdge: (id: string) => Edge | undefined; + getEdge: (id: string) => EdgeType | undefined; /** * Returns edges. * * @returns edges array */ - getEdges: (ids?: string[]) => Edge[]; + getEdges: (ids?: string[]) => EdgeType[]; /** * Sets the current zoom level. * @@ -132,10 +131,10 @@ export function useSvelteFlow(): { * @returns an array of intersecting nodes */ getIntersectingNodes: ( - nodeOrRect: Node | { id: Node['id'] } | Rect, + nodeOrRect: NodeType | { id: NodeType['id'] } | Rect, partially?: boolean, - nodesToIntersect?: Node[] - ) => Node[]; + nodesToIntersect?: NodeType[] + ) => NodeType[]; /** * Checks if the given node or rect intersects with the passed rect. * @@ -146,7 +145,7 @@ export function useSvelteFlow(): { * @returns true if the node or rect intersects with the given area */ isNodeIntersecting: ( - nodeOrRect: Node | { id: Node['id'] } | Rect, + nodeOrRect: NodeType | { id: NodeType['id'] } | Rect, area: Rect, partially?: boolean ) => boolean; @@ -169,9 +168,9 @@ export function useSvelteFlow(): { nodes, edges }: { - nodes?: (Node | { id: Node['id'] })[]; - edges?: (Edge | { id: Edge['id'] })[]; - }) => Promise<{ deletedNodes: Node[]; deletedEdges: Edge[] }>; + nodes?: (Partial & { id: string })[]; + edges?: (Partial & { id: string })[]; + }) => Promise<{ deletedNodes: NodeType[]; deletedEdges: EdgeType[] }>; /** * Converts a screen / client position to a flow position. * @@ -196,7 +195,6 @@ export function useSvelteFlow(): { * const clientPosition = flowToScreenPosition({ x: node.position.x, y: node.position.y }) */ flowToScreenPosition: (flowPosition: XYPosition) => XYPosition; - viewport: Writable; /** * Updates a node. * @@ -207,9 +205,14 @@ export function useSvelteFlow(): { * @example * updateNode('node-1', (node) => ({ position: { x: node.position.x + 10, y: node.position.y } })); */ + // updateNode: ( + // id: string, + // nodeUpdate: Partial | ((node: NodeType) => Partial), + // options?: { replace: boolean } + // ) => void; updateNode: ( id: string, - nodeUpdate: Partial | ((node: Node) => Partial), + nodeUpdate: Partial | ((node: NodeType) => Partial), options?: { replace: boolean } ) => void; /** @@ -224,7 +227,7 @@ export function useSvelteFlow(): { */ updateNodeData: ( id: string, - dataUpdate: object | ((node: Node) => object), + dataUpdate: Partial | ((node: NodeType) => Partial), options?: { replace: boolean } ) => void; /** @@ -232,7 +235,22 @@ export function useSvelteFlow(): { * * @returns the nodes, edges and the viewport as a JSON object */ - toObject: () => { nodes: Node[]; edges: Edge[]; viewport: Viewport }; + /** + * Updates an edge. + * + * @param id - id of the edge to update + * @param edgeUpdate - the edge update as an object or a function that receives the current edge and returns the edge update + * @param options.replace - if true, the edge is replaced with the edge update, otherwise the changes get merged + * + * @example + * updateNode('node-1', (node) => ({ position: { x: node.position.x + 10, y: node.position.y } })); + */ + updateEdge: ( + id: string, + edgeUpdate: Partial | ((edge: EdgeType) => Partial), + options?: { replace: boolean } + ) => void; + toObject: () => { nodes: NodeType[]; edges: EdgeType[]; viewport: Viewport }; /** * Returns the bounds of the given nodes or node ids. * @@ -240,7 +258,7 @@ export function useSvelteFlow(): { * * @returns the bounds of the given nodes */ - getNodesBounds: (nodes: (Node | InternalNode | string)[]) => Rect; + getNodesBounds: (nodes: (NodeType | InternalNode | string)[]) => Rect; /** Gets all connections for a given handle belonging to a specific node. * * @param type - handle type 'source' or 'target' @@ -258,120 +276,99 @@ export function useSvelteFlow(): { id?: string | null; }) => HandleConnection[]; } { - const { - zoomIn, - zoomOut, - fitView, - onbeforedelete, - snapGrid, - viewport, - width, - height, - minZoom, - maxZoom, - panZoom, - nodes, - edges, - domNode, - nodeLookup, - nodeOrigin, - edgeLookup, - connectionLookup - } = useStore(); + const store = $derived(useStore()); - const getNodeRect = (node: Node | { id: Node['id'] }): Rect | null => { - const $nodeLookup = get(nodeLookup); - const nodeToUse = isNode(node) ? node : $nodeLookup.get(node.id)!; + const getNodeRect = (node: NodeType | { id: NodeType['id'] }): Rect | null => { + const nodeToUse = isNode(node) ? node : store.nodeLookup.get(node.id)!; const position = nodeToUse.parentId ? evaluateAbsolutePosition( nodeToUse.position, nodeToUse.measured, nodeToUse.parentId, - $nodeLookup, - get(nodeOrigin) + store.nodeLookup, + store.nodeOrigin ) : nodeToUse.position; const nodeWithPosition = { - id: nodeToUse.id, + ...nodeToUse, position, width: nodeToUse.measured?.width ?? nodeToUse.width, - height: nodeToUse.measured?.height ?? nodeToUse.height, - data: nodeToUse.data + height: nodeToUse.measured?.height ?? nodeToUse.height }; return nodeToRect(nodeWithPosition); }; - const updateNode = ( + function updateNode( id: string, - nodeUpdate: Partial | ((node: Node) => Partial), + nodeUpdate: Partial | ((node: NodeType) => Partial), options: { replace: boolean } = { replace: false } - ) => { - const node = get(nodeLookup).get(id)?.internals.userNode; + ) { + store.nodes = untrack(() => store.nodes).map((node) => { + if (node.id === id) { + const nextNode = typeof nodeUpdate === 'function' ? nodeUpdate(node) : nodeUpdate; + return options?.replace && isNode(nextNode) ? nextNode : { ...node, ...nextNode }; + } - if (!node) { - return; - } + return node; + }); + } - const nextNode = typeof nodeUpdate === 'function' ? nodeUpdate(node as Node) : nodeUpdate; + function updateEdge( + id: string, + edgeUpdate: Partial | ((edge: EdgeType) => Partial), + options: { replace: boolean } = { replace: false } + ) { + store.edges = untrack(() => store.edges).map((edge) => { + if (edge.id === id) { + const nextEdge = typeof edgeUpdate === 'function' ? edgeUpdate(edge) : edgeUpdate; + return options.replace && isEdge(nextEdge) ? nextEdge : { ...edge, ...nextEdge }; + } - if (options.replace) { - nodes.update((nds) => - nds.map((node) => { - if (node.id === id) { - return isNode(nextNode) ? nextNode : { ...node, ...nextNode }; - } + return edge; + }); + } - return node; - }) - ); - } else { - Object.assign(node, nextNode); - nodes.update((nds) => nds); - } - }; - - const getInternalNode = (id: string) => get(nodeLookup).get(id); + const getInternalNode = (id: string) => store.nodeLookup.get(id); return { - zoomIn, - zoomOut, + zoomIn: store.zoomIn, + zoomOut: store.zoomOut, getInternalNode, getNode: (id) => getInternalNode(id)?.internals.userNode, - getNodes: (ids) => (ids === undefined ? get(nodes) : getElements(get(nodeLookup), ids)), - getEdge: (id) => get(edgeLookup).get(id), - getEdges: (ids) => (ids === undefined ? get(edges) : getElements(get(edgeLookup), ids)), + getNodes: (ids) => (ids === undefined ? store.nodes : getElements(store.nodeLookup, ids)), + getEdge: (id) => store.edgeLookup.get(id), + getEdges: (ids) => (ids === undefined ? store.edges : getElements(store.edgeLookup, ids)), setZoom: (zoomLevel, options) => { - const currentPanZoom = get(panZoom); - return currentPanZoom - ? currentPanZoom.scaleTo(zoomLevel, { duration: options?.duration }) + const panZoom = store.panZoom; + return panZoom + ? panZoom.scaleTo(zoomLevel, { duration: options?.duration }) : Promise.resolve(false); }, - getZoom: () => get(viewport).zoom, + getZoom: () => store.viewport.zoom, setViewport: async (nextViewport, options) => { - const currentViewport = get(viewport); - const currentPanZoom = get(panZoom); + const currentViewport = store.viewport; - if (!currentPanZoom) { + if (!store.panZoom) { return Promise.resolve(false); } - await currentPanZoom.setViewport( + await store.panZoom.setViewport( { x: nextViewport.x ?? currentViewport.x, y: nextViewport.y ?? currentViewport.y, zoom: nextViewport.zoom ?? currentViewport.zoom }, - { duration: options?.duration } + options ); return Promise.resolve(true); }, - getViewport: () => get(viewport), + getViewport: () => $state.snapshot(store.viewport), setCenter: async (x, y, options) => { - const nextZoom = typeof options?.zoom !== 'undefined' ? options.zoom : get(maxZoom); - const currentPanZoom = get(panZoom); + const nextZoom = typeof options?.zoom !== 'undefined' ? options.zoom : store.maxZoom; + const currentPanZoom = store.panZoom; if (!currentPanZoom) { return Promise.resolve(false); @@ -379,40 +376,44 @@ export function useSvelteFlow(): { await currentPanZoom.setViewport( { - x: get(width) / 2 - x * nextZoom, - y: get(height) / 2 - y * nextZoom, + x: store.width / 2 - x * nextZoom, + y: store.height / 2 - y * nextZoom, zoom: nextZoom }, - { duration: options?.duration } + { duration: options?.duration, ease: options?.ease, interpolate: options?.interpolate } ); return Promise.resolve(true); }, - fitView, + fitView: (options?: FitViewOptions) => { + return store.fitView(options); + }, fitBounds: async (bounds: Rect, options?: FitBoundsOptions) => { - const currentPanZoom = get(panZoom); - - if (!currentPanZoom) { + if (!store.panZoom) { return Promise.resolve(false); } const viewport = getViewportForBounds( bounds, - get(width), - get(height), - get(minZoom), - get(maxZoom), + store.width, + store.height, + store.minZoom, + store.maxZoom, options?.padding ?? 0.1 ); - await currentPanZoom.setViewport(viewport, { duration: options?.duration }); + await store.panZoom.setViewport(viewport, { + duration: options?.duration, + ease: options?.ease, + interpolate: options?.interpolate + }); return Promise.resolve(true); }, getIntersectingNodes: ( - nodeOrRect: Node | { id: Node['id'] } | Rect, + nodeOrRect: NodeType | { id: NodeType['id'] } | Rect, partially = true, - nodesToIntersect?: Node[] + nodesToIntersect?: NodeType[] ) => { const isRect = isRectObject(nodeOrRect); const nodeRect = isRect ? nodeOrRect : getNodeRect(nodeOrRect); @@ -421,8 +422,8 @@ export function useSvelteFlow(): { return []; } - return (nodesToIntersect || get(nodes)).filter((n) => { - const internalNode = get(nodeLookup).get(n.id); + return (nodesToIntersect || store.nodes).filter((n) => { + const internalNode = store.nodeLookup.get(n.id); if (!internalNode || (!isRect && n.id === nodeOrRect.id)) { return false; } @@ -435,7 +436,7 @@ export function useSvelteFlow(): { }); }, isNodeIntersecting: ( - nodeOrRect: Node | { id: Node['id'] } | Rect, + nodeOrRect: NodeType | { id: NodeType['id'] } | Rect, area: Rect, partially = true ) => { @@ -452,23 +453,26 @@ export function useSvelteFlow(): { return partiallyVisible || overlappingArea >= nodeRect.width * nodeRect.height; }, deleteElements: async ({ nodes: nodesToRemove = [], edges: edgesToRemove = [] }) => { - const { nodes: matchingNodes, edges: matchingEdges } = await getElementsToRemove({ + const { nodes: matchingNodes, edges: matchingEdges } = await getElementsToRemove< + NodeType, + EdgeType + >({ nodesToRemove, edgesToRemove, - nodes: get(nodes), - edges: get(edges), - onBeforeDelete: get(onbeforedelete) + nodes: store.nodes, + edges: store.edges, + onBeforeDelete: store.onbeforedelete }); if (matchingNodes) { - nodes.update((nds) => - nds.filter((node) => !matchingNodes.some(({ id }) => id === node.id)) + store.nodes = untrack(() => store.nodes).filter( + (node) => !matchingNodes.some(({ id }) => id === node.id) ); } if (matchingEdges) { - edges.update((eds) => - eds.filter((edge) => !matchingEdges.some(({ id }) => id === edge.id)) + store.edges = untrack(() => store.edges).filter( + (edge) => !matchingEdges.some(({ id }) => id === edge.id) ); } @@ -481,15 +485,13 @@ export function useSvelteFlow(): { position: XYPosition, options: { snapToGrid: boolean } = { snapToGrid: true } ) => { - const _domNode = get(domNode); - - if (!_domNode) { + if (!store.domNode) { return position; } - const _snapGrid = options.snapToGrid ? get(snapGrid) : false; - const { x, y, zoom } = get(viewport); - const { x: domX, y: domY } = _domNode.getBoundingClientRect(); + const _snapGrid = options.snapToGrid ? store.snapGrid : false; + const { x, y, zoom } = store.viewport; + const { x: domX, y: domY } = store.domNode.getBoundingClientRect(); const correctedPosition = { x: position.x - domX, y: position.y - domY @@ -508,14 +510,12 @@ export function useSvelteFlow(): { * @returns */ flowToScreenPosition: (position: XYPosition) => { - const _domNode = get(domNode); - - if (!_domNode) { + if (!store.domNode) { return position; } - const { x, y, zoom } = get(viewport); - const { x: domX, y: domY } = _domNode.getBoundingClientRect(); + const { x, y, zoom } = store.viewport; + const { x: domX, y: domY } = store.domNode.getBoundingClientRect(); const rendererPosition = rendererPointToPoint(position, [x, y, zoom]); return { @@ -525,49 +525,43 @@ export function useSvelteFlow(): { }, toObject: () => { - return { - nodes: get(nodes).map((node) => ({ - ...node, - // we want to make sure that changes to the nodes object that gets returned by toObject - // do not affect the nodes object - position: { ...node.position }, - data: { ...node.data } - })), - edges: get(edges).map((edge) => ({ ...edge })), - viewport: { ...get(viewport) } - }; + return structuredClone({ + nodes: [...store.nodes], + edges: [...store.edges], + viewport: { ...store.viewport } + }); }, updateNode, updateNodeData: (id, dataUpdate, options) => { - const node = get(nodeLookup).get(id)?.internals.userNode; + const node = store.nodeLookup.get(id)?.internals.userNode; if (!node) { return; } const nextData = typeof dataUpdate === 'function' ? dataUpdate(node) : dataUpdate; - - node.data = options?.replace ? nextData : { ...node.data, ...nextData }; - - nodes.update((nds) => nds); + updateNode(id, (node) => ({ + ...node, + data: options?.replace ? nextData : { ...node.data, ...nextData } + })); }, + updateEdge, getNodesBounds: (nodes) => { - const _nodeLookup = get(nodeLookup); - const _nodeOrigin = get(nodeOrigin); - - return getNodesBounds(nodes, { nodeLookup: _nodeLookup, nodeOrigin: _nodeOrigin }); + return getNodesBounds(nodes, { nodeLookup: store.nodeLookup, nodeOrigin: store.nodeOrigin }); }, getHandleConnections: ({ type, id, nodeId }) => - Array.from( - get(connectionLookup) - .get(`${nodeId}-${type}-${id ?? null}`) - ?.values() ?? [] - ), - viewport + Array.from(store.connectionLookup.get(`${nodeId}-${type}-${id ?? null}`)?.values() ?? []) }; } -function getElements(lookup: Map, ids: string[]): Node[]; -function getElements(lookup: Map, ids: string[]): Edge[]; + +function getElements( + lookup: Map>, + ids: string[] +): NodeType[]; +function getElements( + lookup: Map, + ids: string[] +): EdgeType[]; // eslint-disable-next-line @typescript-eslint/no-explicit-any function getElements(lookup: Map, ids: string[]): any[] { const result = []; diff --git a/packages/svelte/src/lib/hooks/useUpdateNodeInternals.svelte.ts b/packages/svelte/src/lib/hooks/useUpdateNodeInternals.svelte.ts new file mode 100644 index 00000000..f6124312 --- /dev/null +++ b/packages/svelte/src/lib/hooks/useUpdateNodeInternals.svelte.ts @@ -0,0 +1,40 @@ +import { useStore } from '$lib/store'; +import { getContext } from 'svelte'; + +/** + * When you programmatically add or remove handles to a node or update a node's + * handle position, you need to let Svelte Flow know about it using this hook. This + * will update the internal dimensions of the node and properly reposition handles + * on the canvas if necessary. + * + * @public + * @returns A function for telling Svelte Flow to update the internal state of one or more nodes + * that you have changed programmatically. + */ +export function useUpdateNodeInternals(): (nodeId?: string | string[]) => void { + const { domNode, updateNodeInternals } = $derived(useStore()); + const nodeId = getContext('svelteflow__node_id') as string | undefined; + + // @todo: do we want to add this to system? + const updateInternals = (id?: string | string[]) => { + if (!id && !nodeId) { + throw new Error('When using outside of a node, you must provide an id.'); + } + const updateIds = id ? (Array.isArray(id) ? id : [id]) : [nodeId]; + const updates = new Map(); + + updateIds.forEach((updateId) => { + const nodeElement = domNode?.querySelector( + `.svelte-flow__node[data-id="${updateId}"]` + ) as HTMLDivElement; + + if (nodeElement) { + updates.set(updateId, { id: updateId, nodeElement, force: true }); + } + }); + + requestAnimationFrame(() => updateNodeInternals(updates)); + }; + + return updateInternals; +} diff --git a/packages/svelte/src/lib/hooks/useUpdateNodeInternals.ts b/packages/svelte/src/lib/hooks/useUpdateNodeInternals.ts deleted file mode 100644 index 489dec0b..00000000 --- a/packages/svelte/src/lib/hooks/useUpdateNodeInternals.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { get } from 'svelte/store'; -import type { UpdateNodeInternals } from '@xyflow/system'; - -import { useStore } from '$lib/store'; - -/** - * Hook for updating node internals. - * - * @public - * @returns function for updating node internals - */ -export function useUpdateNodeInternals(): UpdateNodeInternals { - const { domNode, updateNodeInternals } = useStore(); - - // @todo: do we want to add this to system? - const updateInternals = (id: string | string[]) => { - const updateIds = Array.isArray(id) ? id : [id]; - const updates = new Map(); - - updateIds.forEach((updateId) => { - const nodeElement = get(domNode)?.querySelector( - `.svelte-flow__node[data-id="${updateId}"]` - ) as HTMLDivElement; - - if (nodeElement) { - updates.set(updateId, { id: updateId, nodeElement, force: true }); - } - }); - - requestAnimationFrame(() => updateNodeInternals(updates)); - }; - - return updateInternals; -} diff --git a/packages/svelte/src/lib/index.ts b/packages/svelte/src/lib/index.ts index 112af9fa..158c78fd 100644 --- a/packages/svelte/src/lib/index.ts +++ b/packages/svelte/src/lib/index.ts @@ -5,12 +5,17 @@ export * from '$lib/container/SvelteFlow/types'; // components export * from '$lib/container/Panel'; export * from '$lib/components/SvelteFlowProvider'; -export * from '$lib/components/EdgeLabelRenderer'; export * from '$lib/components/ViewportPortal'; -export * from '$lib/components/BaseEdge'; -export { BezierEdge, StepEdge, SmoothStepEdge, StraightEdge } from '$lib/components/edges'; +export { + BezierEdge, + StepEdge, + SmoothStepEdge, + StraightEdge, + BaseEdge +} from '$lib/components/edges'; export * from '$lib/components/Handle'; export * from '$lib/components/EdgeLabel'; +export * from '$lib/components/EdgeReconnectAnchor'; // plugins export * from '$lib/plugins/Controls'; @@ -26,14 +31,18 @@ export { useStore } from '$lib/store'; export * from '$lib/utils'; //hooks -export * from '$lib/hooks/useSvelteFlow'; -export * from '$lib/hooks/useUpdateNodeInternals'; -export * from '$lib/hooks/useConnection'; -export * from '$lib/hooks/useNodesEdges'; -export * from '$lib/hooks/useHandleConnections'; -export * from '$lib/hooks/useNodesData'; -export * from '$lib/hooks/useInternalNode'; -export { useInitialized, useNodesInitialized } from '$lib/hooks/useInitialized'; +export * from '$lib/hooks/useSvelteFlow.svelte'; +export * from '$lib/hooks/useUpdateNodeInternals.svelte'; +export * from '$lib/hooks/useConnection.svelte'; +export * from '$lib/hooks/useNodesEdgesViewport.svelte'; +export * from '$lib/hooks/useNodeConnections.svelte'; +export * from '$lib/hooks/useNodesData.svelte'; +export * from '$lib/hooks/useInternalNode.svelte'; +export * from '$lib/hooks/useInitialized.svelte'; +export * from '$lib/hooks/useOnSelectionChange.svelte'; + +//actions +export * from '$lib/actions/portal'; // types export type { @@ -46,16 +55,10 @@ export type { EdgeTypes, DefaultEdgeOptions } from '$lib/types/edges'; -export type { HandleProps, FitViewOptions } from '$lib/types/general'; -export type { - Node, - NodeTypes, - DefaultNodeOptions, - BuiltInNode, - NodeProps, - InternalNode -} from '$lib/types/nodes'; +export type { FitViewOptions, OnBeforeDelete } from '$lib/types/general'; +export type { Node, NodeTypes, BuiltInNode, NodeProps, InternalNode } from '$lib/types/nodes'; export type { SvelteFlowStore } from '$lib/store/types'; +export * from '$lib/types/events'; // system types export { @@ -104,11 +107,12 @@ export { type OnResizeEnd, type ControlPosition, type ControlLinePosition, - type ResizeControlVariant, + ResizeControlVariant, type ResizeParams, type ResizeParamsWithDirection, type ResizeDragEvent, - type IsValidConnection + type IsValidConnection, + type NodeConnection } from '@xyflow/system'; // system utils diff --git a/packages/svelte/src/lib/plugins/Background/Background.svelte b/packages/svelte/src/lib/plugins/Background/Background.svelte index c6a67d48..971f6ac4 100644 --- a/packages/svelte/src/lib/plugins/Background/Background.svelte +++ b/packages/svelte/src/lib/plugins/Background/Background.svelte @@ -1,4 +1,4 @@ - - - diff --git a/packages/svelte/src/lib/plugins/Background/DotPattern.svelte b/packages/svelte/src/lib/plugins/Background/DotPattern.svelte index 62c0077a..3972ecab 100644 --- a/packages/svelte/src/lib/plugins/Background/DotPattern.svelte +++ b/packages/svelte/src/lib/plugins/Background/DotPattern.svelte @@ -1,14 +1,12 @@ diff --git a/packages/svelte/src/lib/plugins/Background/LinePattern.svelte b/packages/svelte/src/lib/plugins/Background/LinePattern.svelte index 6ef75599..a57f05e2 100644 --- a/packages/svelte/src/lib/plugins/Background/LinePattern.svelte +++ b/packages/svelte/src/lib/plugins/Background/LinePattern.svelte @@ -1,16 +1,22 @@ diff --git a/packages/svelte/src/lib/plugins/Background/types.ts b/packages/svelte/src/lib/plugins/Background/types.ts index dba56b97..2e74491f 100644 --- a/packages/svelte/src/lib/plugins/Background/types.ts +++ b/packages/svelte/src/lib/plugins/Background/types.ts @@ -1,3 +1,5 @@ +import type { ClassValue } from 'svelte/elements'; + export enum BackgroundVariant { Lines = 'lines', Dots = 'dots', @@ -5,6 +7,7 @@ export enum BackgroundVariant { } export type BackgroundProps = { + /** When multiple backgrounds are present on the page, each one should have a unique id. */ id?: string; /** Color of the background */ bgColor?: string; @@ -13,14 +16,27 @@ export type BackgroundProps = { /** Class applied to the pattern */ patternClass?: string; /** Class applied to the container */ - class?: string; - /** Gap between repetitions of the pattern */ + class?: ClassValue; + /** + * The gap between patterns. Passing in a tuple allows you to control the x and y gap + * independently. + * @default 20 + */ gap?: number | [number, number]; - /** Size of a single pattern element */ + /** + * The radius of each dot or the size of each rectangle if `BackgroundVariant.Dots` or + * `BackgroundVariant.Cross` is used. This defaults to 1 or 6 respectively, or ignored if + * `BackgroundVariant.Lines` is used. + */ size?: number; - /** Line width of the Line pattern */ + /** + * The stroke thickness used when drawing the pattern. + * @default 1 + */ lineWidth?: number; - /** Variant of the pattern + /** + * Variant of the pattern. + * @default BackgroundVariant.Dots * @example BackgroundVariant.Lines, BackgroundVariant.Dots, BackgroundVariant.Cross * 'lines', 'dots', 'cross' */ diff --git a/packages/svelte/src/lib/plugins/Controls/ControlButton.svelte b/packages/svelte/src/lib/plugins/Controls/ControlButton.svelte index f2c05810..03fa34bf 100644 --- a/packages/svelte/src/lib/plugins/Controls/ControlButton.svelte +++ b/packages/svelte/src/lib/plugins/Controls/ControlButton.svelte @@ -1,30 +1,29 @@ diff --git a/packages/svelte/src/lib/plugins/Controls/Controls.svelte b/packages/svelte/src/lib/plugins/Controls/Controls.svelte index 2984ad2c..d91588f9 100644 --- a/packages/svelte/src/lib/plugins/Controls/Controls.svelte +++ b/packages/svelte/src/lib/plugins/Controls/Controls.svelte @@ -1,8 +1,6 @@ - + {#if before} + {@render before()} + {/if} {#if showZoom} {:else}{/if} {/if} - - + {#if children} + {@render children()} + {/if} + {#if after} + {@render after()} + {/if} diff --git a/packages/svelte/src/lib/plugins/Controls/types.ts b/packages/svelte/src/lib/plugins/Controls/types.ts index a4e5f9d7..7b011a22 100644 --- a/packages/svelte/src/lib/plugins/Controls/types.ts +++ b/packages/svelte/src/lib/plugins/Controls/types.ts @@ -1,4 +1,5 @@ -import type { HTMLButtonAttributes } from 'svelte/elements'; +import type { Snippet } from 'svelte'; +import type { ClassValue, HTMLAttributes, HTMLButtonAttributes } from 'svelte/elements'; import type { PanelPosition } from '@xyflow/system'; import type { FitViewOptions } from '$lib/types'; @@ -19,18 +20,23 @@ export type ControlsProps = { buttonBgColorHover?: string; buttonColor?: string; buttonColorHover?: string; + buttonBorderColor?: string; 'aria-label'?: string; style?: string; - class?: string; + class?: ClassValue; orientation?: 'horizontal' | 'vertical'; + children?: Snippet; + before?: Snippet; + after?: Snippet; fitViewOptions?: FitViewOptions; -}; +} & HTMLAttributes; export type ControlButtonProps = HTMLButtonAttributes & { - class?: string; + class?: ClassValue; bgColor?: string; bgColorHover?: string; color?: string; colorHover?: string; borderColor?: string; + children?: Snippet; }; diff --git a/packages/svelte/src/lib/plugins/Minimap/Minimap.svelte b/packages/svelte/src/lib/plugins/Minimap/Minimap.svelte index 789dc476..5dcb5b3f 100644 --- a/packages/svelte/src/lib/plugins/Minimap/Minimap.svelte +++ b/packages/svelte/src/lib/plugins/Minimap/Minimap.svelte @@ -1,4 +1,4 @@ - - {#if $panZoom} + {#if store.panZoom} {#if ariaLabel}{ariaLabel}{/if} - {#each $nodes as userNode (userNode.id)} - {@const node = $nodeLookup.get(userNode.id)} + {#each store.nodes as userNode (userNode.id)} + {@const node = store.nodeLookup.get(userNode.id)} {#if node && nodeHasDimensions(node)} {@const nodeDimesions = getNodeDimensions(node)} - import cc from 'classcat'; + import type { ClassValue } from 'svelte/elements'; - export let x: number; - export let y: number; - export let width: number = 0; - export let height: number = 0; - export let borderRadius: number = 5; - export let color: string | undefined = undefined; - export let shapeRendering: string; - export let strokeColor: string | undefined = undefined; - export let strokeWidth: number = 2; - export let selected: boolean = false; - let className: string = ''; - export { className as class }; + let { + x, + y, + width, + height, + borderRadius = 5, + color, + shapeRendering, + strokeColor, + strokeWidth = 2, + selected, + class: className + }: { + x: number; + y: number; + width: number; + height: number; + borderRadius?: number; + color?: string; + shapeRendering: string; + strokeColor?: string; + strokeWidth?: number; + selected?: boolean; + class?: ClassValue; + } = $props(); diff --git a/packages/svelte/src/lib/plugins/Minimap/interactive.ts b/packages/svelte/src/lib/plugins/Minimap/interactive.ts index 064f0d27..49084f6d 100644 --- a/packages/svelte/src/lib/plugins/Minimap/interactive.ts +++ b/packages/svelte/src/lib/plugins/Minimap/interactive.ts @@ -1,14 +1,9 @@ -import { get, type Writable } from 'svelte/store'; -import { - XYMinimap, - type PanZoomInstance, - type Viewport, - type XYMinimapUpdate -} from '@xyflow/system'; +import { XYMinimap, type PanZoomInstance, type XYMinimapUpdate } from '@xyflow/system'; +import type { SvelteFlowStore } from '$lib/store/types'; export type UseInteractiveParams = { panZoom: PanZoomInstance; - viewport: Writable; + store: SvelteFlowStore; getViewScale: () => number; } & XYMinimapUpdate; @@ -17,12 +12,22 @@ export default function interactive(domNode: Element, params: UseInteractivePara domNode, panZoom: params.panZoom, getTransform: () => { - const viewport = get(params.viewport); + const { viewport } = params.store; return [viewport.x, viewport.y, viewport.zoom]; }, getViewScale: params.getViewScale }); + minimap.update({ + translateExtent: params.translateExtent, + width: params.width, + height: params.height, + inversePan: params.inversePan, + zoomStep: params.zoomStep, + pannable: params.pannable, + zoomable: params.zoomable + }); + function update(params: UseInteractiveParams) { minimap.update({ translateExtent: params.translateExtent, diff --git a/packages/svelte/src/lib/plugins/Minimap/types.ts b/packages/svelte/src/lib/plugins/Minimap/types.ts index d66577a8..dd6903de 100644 --- a/packages/svelte/src/lib/plugins/Minimap/types.ts +++ b/packages/svelte/src/lib/plugins/Minimap/types.ts @@ -1,5 +1,5 @@ import type { PanelPosition } from '@xyflow/system'; - +import type { ClassValue, HTMLAttributes } from 'svelte/elements'; import type { Node } from '$lib/types'; export type GetMiniMapNodeAttribute = (node: Node) => string; @@ -29,7 +29,7 @@ export type MiniMapProps = { */ position?: PanelPosition; /** Class applied to container */ - class?: string; + class?: ClassValue; /** Style applied to container */ style?: string; /** The aria-label applied to container */ @@ -46,4 +46,4 @@ export type MiniMapProps = { inversePan?: boolean; /** Step size for zooming in/out */ zoomStep?: number; -}; +} & HTMLAttributes; diff --git a/packages/svelte/src/lib/plugins/NodeResizer/NodeResizer.svelte b/packages/svelte/src/lib/plugins/NodeResizer/NodeResizer.svelte index b57afcd1..8ced5ba3 100644 --- a/packages/svelte/src/lib/plugins/NodeResizer/NodeResizer.svelte +++ b/packages/svelte/src/lib/plugins/NodeResizer/NodeResizer.svelte @@ -7,29 +7,15 @@ XY_RESIZER_LINE_POSITIONS } from '@xyflow/system'; - type $$Props = NodeResizerProps; - - export let nodeId: $$Props['nodeId'] = undefined; - export let isVisible: $$Props['isVisible'] = true; - export let handleClass: $$Props['handleClass'] = undefined; - export let handleStyle: $$Props['handleStyle'] = undefined; - export let lineClass: $$Props['lineClass'] = undefined; - export let lineStyle: $$Props['lineStyle'] = undefined; - export let color: $$Props['color'] = undefined; - export let minWidth: $$Props['minWidth'] = 10; - export let minHeight: $$Props['minHeight'] = 10; - export let maxWidth: $$Props['maxWidth'] = Number.MAX_VALUE; - export let maxHeight: $$Props['maxHeight'] = Number.MAX_VALUE; - export let keepAspectRatio: $$Props['keepAspectRatio'] = false; - export let shouldResize: $$Props['shouldResize'] = undefined; - export let onResizeStart: $$Props['onResizeStart'] = undefined; - export let onResize: $$Props['onResize'] = undefined; - export let onResizeEnd: $$Props['onResizeEnd'] = undefined; - - let _minWidth = minWidth || 10; - let _minHeight = minHeight || 10; - let _maxWidth = maxWidth || Number.MAX_VALUE; - let _maxHeight = maxHeight || Number.MAX_VALUE; + let { + isVisible = true, + nodeId, + handleClass, + handleStyle, + lineClass, + lineStyle, + ...rest + }: NodeResizerProps = $props(); {#if isVisible} @@ -40,34 +26,10 @@ {nodeId} {position} variant={ResizeControlVariant.Line} - {color} - minWidth={_minWidth} - minHeight={_minHeight} - maxWidth={_maxWidth} - maxHeight={_maxHeight} - {onResizeStart} - {keepAspectRatio} - {shouldResize} - {onResize} - {onResizeEnd} + {...rest} /> {/each} {#each XY_RESIZER_HANDLE_POSITIONS as position (position)} - + {/each} {/if} diff --git a/packages/svelte/src/lib/plugins/NodeResizer/ResizeControl.svelte b/packages/svelte/src/lib/plugins/NodeResizer/ResizeControl.svelte index 1e5f39e2..b30ecc47 100644 --- a/packages/svelte/src/lib/plugins/NodeResizer/ResizeControl.svelte +++ b/packages/svelte/src/lib/plugins/NodeResizer/ResizeControl.svelte @@ -1,6 +1,5 @@
- + {@render children?.()}
diff --git a/packages/svelte/src/lib/plugins/NodeResizer/types.ts b/packages/svelte/src/lib/plugins/NodeResizer/types.ts index ebe44b8b..23140c52 100644 --- a/packages/svelte/src/lib/plugins/NodeResizer/types.ts +++ b/packages/svelte/src/lib/plugins/NodeResizer/types.ts @@ -1,3 +1,5 @@ +import type { Snippet } from 'svelte'; +import type { HTMLAttributes } from 'svelte/elements'; import type { ControlPosition, ResizeControlVariant, @@ -42,7 +44,7 @@ export type NodeResizerProps = { onResize?: OnResize; /** Callback called when resizing ends */ onResizeEnd?: OnResizeEnd; -}; +} & HTMLAttributes; export type ResizeControlProps = Pick< NodeResizerProps, @@ -67,6 +69,5 @@ export type ResizeControlProps = Pick< * @example ResizeControlVariant.Handle, ResizeControlVariant.Line */ variant?: ResizeControlVariant; - class?: string; - style?: string; -}; + children?: Snippet; +} & HTMLAttributes; diff --git a/packages/svelte/src/lib/plugins/NodeToolbar/NodeToolbar.svelte b/packages/svelte/src/lib/plugins/NodeToolbar/NodeToolbar.svelte index 58d73db5..65a5c2d3 100644 --- a/packages/svelte/src/lib/plugins/NodeToolbar/NodeToolbar.svelte +++ b/packages/svelte/src/lib/plugins/NodeToolbar/NodeToolbar.svelte @@ -1,39 +1,36 @@ -{#if $domNode && isActive && toolbarNodes} +{#if store.domNode && isActive && toolbarNodes}
`${acc}${node.id} `, '').trim()} + use:portal={'root'} class="svelte-flow__node-toolbar" - use:portal={{ domNode: $domNode }} + data-id={toolbarNodes.reduce((acc, node) => `${acc}${node.id} `, '').trim()} style:position="absolute" style:transform style:z-index={zIndex} + {...rest} > - + {@render children?.()}
{/if} diff --git a/packages/svelte/src/lib/plugins/NodeToolbar/types.ts b/packages/svelte/src/lib/plugins/NodeToolbar/types.ts index aec506a3..704a0d83 100644 --- a/packages/svelte/src/lib/plugins/NodeToolbar/types.ts +++ b/packages/svelte/src/lib/plugins/NodeToolbar/types.ts @@ -1,4 +1,6 @@ import type { Position, Align } from '@xyflow/system'; +import type { Snippet } from 'svelte'; +import type { HTMLAttributes } from 'svelte/elements'; export type NodeToolbarProps = { /** The id of the node, or array of ids the toolbar should be displayed at */ @@ -16,4 +18,5 @@ export type NodeToolbarProps = { offset?: number; /** If true, node toolbar is visible even if node is not selected */ isVisible?: boolean; -}; + children?: Snippet; +} & HTMLAttributes; diff --git a/packages/svelte/src/lib/store/index.ts b/packages/svelte/src/lib/store/index.ts index 24279a45..657fdce2 100644 --- a/packages/svelte/src/lib/store/index.ts +++ b/packages/svelte/src/lib/store/index.ts @@ -1,15 +1,9 @@ -import { getContext, setContext } from 'svelte'; -import { derived, get, writable } from 'svelte/store'; import { - createMarkerIds, - fitView as fitViewSystem, - getElementsToRemove, panBy as panBySystem, updateNodeInternals as updateNodeInternalsSystem, addEdge as addEdgeUtil, initialConnection, errorMessages, - pointToRendererPoint, type UpdateNodePositions, type InternalNodeUpdate, type ViewportHelperFunctionOptions, @@ -18,112 +12,81 @@ import { type CoordinateExtent, type UpdateConnection, type ConnectionState, - type NodeOrigin, - getFitViewNodes + updateAbsolutePositions, + snapPosition, + calculateNodePosition } from '@xyflow/system'; import type { EdgeTypes, NodeTypes, Node, Edge, FitViewOptions } from '$lib/types'; -import { initialEdgeTypes, initialNodeTypes, getInitialStore } from './initial-store'; -import type { SvelteFlowStore } from './types'; -import { syncNodeStores, syncEdgeStores, syncViewportStores } from './utils'; -import { getVisibleEdges } from './visible-edges'; -import { getVisibleNodes } from './visible-nodes'; +import { initialEdgeTypes, initialNodeTypes, getInitialStore } from './initial-store.svelte'; +import { type StoreSignals, type SvelteFlowStore, type SvelteFlowStoreActions } from './types'; export const key = Symbol(); -export function createStore({ - nodes, - edges, - width, - height, - fitView: fitViewOnCreate, - nodeOrigin, - nodeExtent -}: { - nodes?: Node[]; - edges?: Edge[]; - width?: number; - height?: number; - fitView?: boolean; - nodeOrigin?: NodeOrigin; - nodeExtent?: CoordinateExtent; -}): SvelteFlowStore { - const store = getInitialStore({ - nodes, - edges, - width, - height, - fitView: fitViewOnCreate, - nodeOrigin, - nodeExtent - }); +export { useStore } from '../hooks/useStore'; + +export function createStore( + signals: StoreSignals +): SvelteFlowStore { + const store = getInitialStore(signals); function setNodeTypes(nodeTypes: NodeTypes) { - store.nodeTypes.set({ + store.nodeTypes = { ...initialNodeTypes, ...nodeTypes - }); + }; } function setEdgeTypes(edgeTypes: EdgeTypes) { - store.edgeTypes.set({ + store.edgeTypes = { ...initialEdgeTypes, ...edgeTypes - }); + }; } - function addEdge(edgeParams: Edge | Connection) { - const edges = get(store.edges); - store.edges.set(addEdgeUtil(edgeParams, edges)); + function addEdge(edgeParams: EdgeType | Connection) { + store.edges = addEdgeUtil(edgeParams, store.edges); } const updateNodePositions: UpdateNodePositions = (nodeDragItems, dragging = false) => { - const nodeLookup = get(store.nodeLookup); - - for (const [id, dragItem] of nodeDragItems) { - const node = nodeLookup.get(id)?.internals.userNode; - - if (!node) { - continue; - } - - node.position = dragItem.position; - node.dragging = dragging; - } - - store.nodes.update((nds) => nds); + store.nodes = store.nodes.map((node) => { + const dragItem = nodeDragItems.get(node.id); + return dragItem ? { ...node, position: dragItem.position, dragging } : node; + }); }; function updateNodeInternals(updates: Map) { - const nodeLookup = get(store.nodeLookup); const { changes, updatedInternals } = updateNodeInternalsSystem( updates, - nodeLookup, - get(store.parentLookup), - get(store.domNode), - get(store.nodeOrigin) + store.nodeLookup, + store.parentLookup, + store.domNode, + store.nodeOrigin ); if (!updatedInternals) { return; } - if (!get(store.fitViewOnInitDone) && get(store.fitViewOnInit)) { - const fitViewOptions = get(store.fitViewOptions); - const fitViewOnInitDone = fitViewSync({ - ...fitViewOptions, - nodes: fitViewOptions?.nodes - }); - store.fitViewOnInitDone.set(fitViewOnInitDone); + updateAbsolutePositions(store.nodeLookup, store.parentLookup, { + nodeOrigin: store.nodeOrigin, + nodeExtent: store.nodeExtent + }); + + if (store.fitViewQueued) { + store.resolveFitView(); } + const newNodes = new Map(); for (const change of changes) { - const node = nodeLookup.get(change.id)?.internals.userNode; + const userNode = store.nodeLookup.get(change.id)?.internals.userNode; - if (!node) { + if (!userNode) { continue; } + const node = { ...userNode }; + switch (change.type) { case 'dimensions': { const measured = { ...node.measured, ...change.dimensions }; @@ -138,66 +101,33 @@ export function createStore({ } case 'position': node.position = change.position ?? node.position; - break; } + + newNodes.set(change.id, node); } - store.nodes.update((nds) => nds); - - if (!get(store.nodesInitialized)) { - store.nodesInitialized.set(true); - } + store.nodes = store.nodes.map((node) => newNodes.get(node.id) ?? node); } function fitView(options?: FitViewOptions) { - const panZoom = get(store.panZoom); + // 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.fitViewResolver ?? Promise.withResolvers(); - if (!panZoom) { - return Promise.resolve(false); - } + // We schedule a fitView by setting fitViewQueued and triggering a setNodes + store.fitViewQueued = true; + store.fitViewOptions = options; + store.fitViewResolver = fitViewResolver; - const fitViewNodes = getFitViewNodes(get(store.nodeLookup), options); + // We need to update the nodes so that adoptUserNodes is triggered + store.nodes = [...store.nodes]; - return fitViewSystem( - { - nodes: fitViewNodes, - width: get(store.width), - height: get(store.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; + return fitViewResolver.promise; } function zoomBy(factor: number, options?: ViewportHelperFunctionOptions) { - const panZoom = get(store.panZoom); + const panZoom = store.panZoom; if (!panZoom) { return Promise.resolve(false); } @@ -214,236 +144,226 @@ export function createStore({ } function setMinZoom(minZoom: number) { - const panZoom = get(store.panZoom); + const panZoom = store.panZoom; if (panZoom) { - panZoom.setScaleExtent([minZoom, get(store.maxZoom)]); - store.minZoom.set(minZoom); + panZoom.setScaleExtent([minZoom, store.maxZoom]); + store.minZoom = minZoom; } } function setMaxZoom(maxZoom: number) { - const panZoom = get(store.panZoom); + const panZoom = store.panZoom; if (panZoom) { - panZoom.setScaleExtent([get(store.minZoom), maxZoom]); - store.maxZoom.set(maxZoom); + panZoom.setScaleExtent([store.minZoom, maxZoom]); + store.maxZoom = maxZoom; } } function setTranslateExtent(extent: CoordinateExtent) { - const panZoom = get(store.panZoom); + const panZoom = store.panZoom; if (panZoom) { panZoom.setTranslateExtent(extent); - store.translateExtent.set(extent); + store.translateExtent = extent; } } - function resetSelectedElements(elements: Node[] | Edge[]) { - let elementsChanged = false; - elements.forEach((element) => { - if (element.selected) { - element.selected = false; - elementsChanged = true; - } - }); - return elementsChanged; - } - function setPaneClickDistance(distance: number) { - get(store.panZoom)?.setClickDistance(distance); + store.panZoom?.setClickDistance(distance); + } + + function deselect( + elements: T[], + elementsToDeselect: Set | null = null + ): [boolean, T[]] { + let deselected = false; + + const newElements = elements.map((element) => { + const shouldDeselect = elementsToDeselect ? elementsToDeselect.has(element.id) : true; + + if (shouldDeselect && element.selected) { + deselected = true; + return { ...element, selected: false }; + } + return element; + }); + + return [deselected, newElements]; } function unselectNodesAndEdges(params?: { nodes?: Node[]; edges?: Edge[] }) { - const resetNodes = resetSelectedElements(params?.nodes || get(store.nodes)); - if (resetNodes) store.nodes.set(get(store.nodes)); + const nodesToDeselect = params?.nodes ? new Set(params.nodes.map((node) => node.id)) : null; + const [nodesDeselected, newNodes] = deselect(store.nodes, nodesToDeselect); + if (nodesDeselected) { + store.nodes = newNodes; + } - const resetEdges = resetSelectedElements(params?.edges || get(store.edges)); - if (resetEdges) store.edges.set(get(store.edges)); + const edgesToDeselect = params?.edges ? new Set(params.edges.map((node) => node.id)) : null; + const [edgesDeselected, newEdges] = deselect(store.edges, edgesToDeselect); + if (edgesDeselected) { + store.edges = newEdges; + } } - store.deleteKeyPressed.subscribe(async (deleteKeyPressed) => { - if (deleteKeyPressed) { - const nodes = get(store.nodes); - const edges = get(store.edges); - const selectedNodes = nodes.filter((node) => node.selected); - const selectedEdges = edges.filter((edge) => edge.selected); - - const { nodes: matchingNodes, edges: matchingEdges } = await getElementsToRemove({ - nodesToRemove: selectedNodes, - edgesToRemove: selectedEdges, - nodes, - edges, - onBeforeDelete: get(store.onbeforedelete) - }); - - if (matchingNodes.length || matchingEdges.length) { - store.nodes.update((nds) => - nds.filter((node) => !matchingNodes.some((mN) => mN.id === node.id)) - ); - store.edges.update((eds) => - eds.filter((edge) => !matchingEdges.some((mE) => mE.id === edge.id)) - ); - - get(store.ondelete)?.({ - nodes: matchingNodes, - edges: matchingEdges - }); - } - } - }); - function addSelectedNodes(ids: string[]) { - const isMultiSelection = get(store.multiselectionKeyPressed); + const isMultiSelection = store.multiselectionKeyPressed; - store.nodes.update((ns) => - ns.map((node) => { - const nodeWillBeSelected = ids.includes(node.id); - const selected = isMultiSelection - ? node.selected || nodeWillBeSelected - : nodeWillBeSelected; + store.nodes = store.nodes.map((node) => { + const nodeWillBeSelected = ids.includes(node.id); + const selected = isMultiSelection ? node.selected || nodeWillBeSelected : nodeWillBeSelected; + if (node.selected !== selected) { // we need to mutate the node here in order to have the correct selected state in the drag handler + const internalNode = store.nodeLookup.get(node.id); + if (internalNode) internalNode.selected = selected; node.selected = selected; - - return node; - }) - ); + return { ...node }; + } + return node; + }); if (!isMultiSelection) { - store.edges.update((es) => - es.map((edge) => { - edge.selected = false; - return edge; - }) - ); + unselectNodesAndEdges({ nodes: [] }); } } function addSelectedEdges(ids: string[]) { - const isMultiSelection = get(store.multiselectionKeyPressed); + const isMultiSelection = store.multiselectionKeyPressed; - store.edges.update((edges) => - edges.map((edge) => { - const edgeWillBeSelected = ids.includes(edge.id); - const selected = isMultiSelection - ? edge.selected || edgeWillBeSelected - : edgeWillBeSelected; + store.edges = store.edges.map((edge) => { + const edgeWillBeSelected = ids.includes(edge.id); + const selected = isMultiSelection ? edge.selected || edgeWillBeSelected : edgeWillBeSelected; - edge.selected = selected; - - return edge; - }) - ); + if (edge.selected !== selected) { + return { ...edge, selected }; + } + return edge; + }); if (!isMultiSelection) { - store.nodes.update((ns) => - ns.map((node) => { - node.selected = false; - return node; - }) - ); + unselectNodesAndEdges({ edges: [] }); } } - function handleNodeSelection(id: string) { - const node = get(store.nodes)?.find((n) => n.id === id); + function handleNodeSelection(id: string, unselect?: boolean, nodeRef?: HTMLDivElement | null) { + const node = store.nodeLookup.get(id); if (!node) { console.warn('012', errorMessages['error012'](id)); return; } - store.selectionRect.set(null); - store.selectionRectMode.set(null); + store.selectionRect = null; + store.selectionRectMode = null; if (!node.selected) { addSelectedNodes([id]); - } else if (node.selected && get(store.multiselectionKeyPressed)) { + } else if (unselect || (node.selected && store.multiselectionKeyPressed)) { unselectNodesAndEdges({ nodes: [node], edges: [] }); + + requestAnimationFrame(() => nodeRef?.blur()); } } + function handleEdgeSelection(id: string) { + const edge = store.edgeLookup.get(id); + + if (!edge) { + console.warn('012', errorMessages['error012'](id)); + return; + } + + const selectable = + edge.selectable || (store.elementsSelectable && typeof edge.selectable === 'undefined'); + + if (selectable) { + store.selectionRect = null; + store.selectionRectMode = null; + + if (!edge.selected) { + addSelectedEdges([id]); + } else if (edge.selected && store.multiselectionKeyPressed) { + unselectNodesAndEdges({ nodes: [], edges: [edge] }); + } + } + } + + function moveSelectedNodes(direction: XYPosition, factor: number) { + const { nodeExtent, snapGrid, nodeOrigin, nodeLookup, nodesDraggable, onerror } = store; + + const nodeUpdates = new Map(); + /* + * by default a node moves 5px on each key press + * if snap grid is enabled, we use that for the velocity + */ + const xVelo = snapGrid?.[0] ?? 5; + const yVelo = snapGrid?.[1] ?? 5; + + const xDiff = direction.x * xVelo * factor; + const yDiff = direction.y * yVelo * factor; + + for (const node of nodeLookup.values()) { + const isSelected = + node.selected && + (node.draggable || (nodesDraggable && typeof node.draggable === 'undefined')); + + if (!isSelected) { + continue; + } + + let nextPosition = { + x: node.internals.positionAbsolute.x + xDiff, + y: node.internals.positionAbsolute.y + yDiff + }; + + if (snapGrid) { + nextPosition = snapPosition(nextPosition, snapGrid); + } + + const { position, positionAbsolute } = calculateNodePosition({ + nodeId: node.id, + nextPosition, + nodeLookup, + nodeExtent, + nodeOrigin, + onError: onerror + }); + + node.position = position; + node.internals.positionAbsolute = positionAbsolute; + + nodeUpdates.set(node.id, node); + } + updateNodePositions(nodeUpdates); + } + function panBy(delta: XYPosition) { - const viewport = get(store.viewport); return panBySystem({ delta, - panZoom: get(store.panZoom), - transform: [viewport.x, viewport.y, viewport.zoom], - translateExtent: get(store.translateExtent), - width: get(store.width), - height: get(store.height) + panZoom: store.panZoom, + transform: [store.viewport.x, store.viewport.y, store.viewport.zoom], + translateExtent: store.translateExtent, + width: store.width, + height: store.height }); } - const _connection = writable(initialConnection); const updateConnection: UpdateConnection = (newConnection: ConnectionState) => { - _connection.set({ ...newConnection }); + store._connection = { ...newConnection }; }; function cancelConnection() { - _connection.set(initialConnection); + store._connection = initialConnection; } function reset() { - store.fitViewOnInitDone.set(false); - store.selectionRect.set(null); - store.selectionRectMode.set(null); - store.snapGrid.set(null); - store.isValidConnection.set(() => true); - + store.resetStoreValues(); unselectNodesAndEdges(); - cancelConnection(); } - return { - // state - ...store, - - // derived state - visibleEdges: getVisibleEdges(store), - visibleNodes: getVisibleNodes(store), - connection: derived([_connection, store.viewport], ([connection, viewport]) => { - return connection.inProgress - ? { - ...connection, - to: pointToRendererPoint(connection.to, [viewport.x, viewport.y, viewport.zoom]) - } - : { ...connection }; - }), - markers: derived( - [store.edges, store.defaultMarkerColor, store.flowId], - ([edges, defaultColor, id]) => createMarkerIds(edges, { defaultColor, id }) - ), - initialized: (() => { - let initialized = false; - const initialNodesLength = get(store.nodes).length; - const initialEdgesLength = get(store.edges).length; - return derived( - [store.nodesInitialized, store.edgesInitialized, store.viewportInitialized], - ([nodesInitialized, edgesInitialized, viewportInitialized]) => { - // If it was already initialized, return true from then on - if (initialized) return initialized; - - // if it hasn't been initialised check if it's now - if (initialNodesLength === 0) { - initialized = viewportInitialized; - } else if (initialEdgesLength === 0) { - initialized = viewportInitialized && nodesInitialized; - } else { - initialized = viewportInitialized && nodesInitialized && edgesInitialized; - } - - return initialized; - } - ); - })(), - - // actions - syncNodeStores: (nodes) => syncNodeStores(store.nodes, nodes), - syncEdgeStores: (edges) => syncEdgeStores(store.edges, edges), - syncViewport: (viewport) => syncViewportStores(store.panZoom, store.viewport, viewport), + const storeWithActions = Object.assign(store, { setNodeTypes, setEdgeTypes, addEdge, @@ -451,7 +371,7 @@ export function createStore({ updateNodeInternals, zoomIn, zoomOut, - fitView: (options?: FitViewOptions) => fitView(options), + fitView, setMinZoom, setMaxZoom, setTranslateExtent, @@ -460,47 +380,13 @@ export function createStore({ addSelectedNodes, addSelectedEdges, handleNodeSelection, + handleEdgeSelection, + moveSelectedNodes, panBy, updateConnection, cancelConnection, reset - }; -} + } satisfies SvelteFlowStoreActions); -export function useStore(): SvelteFlowStore { - const store = getContext<{ getStore: () => SvelteFlowStore }>(key); - - if (!store) { - throw new Error( - 'In order to use useStore you need to wrap your component in a ' - ); - } - - return store.getStore(); -} - -export function createStoreContext({ - nodes, - edges, - width, - height, - fitView, - nodeOrigin, - nodeExtent -}: { - nodes?: Node[]; - edges?: Edge[]; - width?: number; - height?: number; - fitView?: boolean; - nodeOrigin?: NodeOrigin; - nodeExtent?: CoordinateExtent; -}) { - const store = createStore({ nodes, edges, width, height, fitView, nodeOrigin, nodeExtent }); - - setContext(key, { - getStore: () => store - }); - - return store; + return storeWithActions; } diff --git a/packages/svelte/src/lib/store/initial-store.svelte.ts b/packages/svelte/src/lib/store/initial-store.svelte.ts new file mode 100644 index 00000000..8ba4efa4 --- /dev/null +++ b/packages/svelte/src/lib/store/initial-store.svelte.ts @@ -0,0 +1,450 @@ +import { + infiniteExtent, + SelectionMode, + ConnectionMode, + devWarn, + adoptUserNodes, + getViewportForBounds, + updateConnectionLookup, + initialConnection, + type SelectionRect, + type SnapGrid, + type MarkerProps, + type PanZoomInstance, + type CoordinateExtent, + type NodeOrigin, + type OnError, + type Viewport, + type OnConnect, + type OnConnectStart, + type OnConnectEnd, + type NodeLookup, + type ConnectionState, + getInternalNodesBounds, + createMarkerIds, + type EdgeLookup, + type ConnectionLookup, + type ParentLookup, + pointToRendererPoint, + type ColorModeClass, + type Transform, + fitViewport, + type Handle, + type OnReconnect, + type OnReconnectStart, + type OnReconnectEnd +} from '@xyflow/system'; + +import DefaultNode from '$lib/components/nodes/DefaultNode.svelte'; +import InputNode from '$lib/components/nodes/InputNode.svelte'; +import OutputNode from '$lib/components/nodes/OutputNode.svelte'; +import GroupNode from '$lib/components/nodes/GroupNode.svelte'; + +import { + BezierEdgeInternal, + SmoothStepEdgeInternal, + StraightEdgeInternal, + StepEdgeInternal +} from '$lib/components/edges'; + +import type { + NodeTypes, + EdgeTypes, + FitViewOptions, + OnDelete, + OnBeforeConnect, + OnBeforeDelete, + IsValidConnection, + Edge, + Node, + EdgeLayouted, + InternalNode, + OnBeforeReconnect, + OnSelectionChange, + OnSelectionDrag +} from '$lib/types'; + +import type { StoreSignals } from './types'; +import { MediaQuery } from 'svelte/reactivity'; +import { getLayoutedEdges, getVisibleNodes, type EdgeLayoutAllOptions } from './visibleElements'; + +export const initialNodeTypes = { + input: InputNode, + output: OutputNode, + default: DefaultNode, + group: GroupNode +}; + +export const initialEdgeTypes = { + straight: StraightEdgeInternal, + smoothstep: SmoothStepEdgeInternal, + default: BezierEdgeInternal, + step: StepEdgeInternal +}; + +export function getInitialStore( + signals: StoreSignals +) { + // We use a class here, because Svelte adds getters & setter for us. + // Inline classes have some performance implications but we just call it once (max twice). + class SvelteFlowStore { + flowId: string = $derived(signals.props.id ?? '1'); + domNode = $state(null); + panZoom: PanZoomInstance | null = $state(null); + width = $state(signals.width ?? 0); + height = $state(signals.height ?? 0); + + nodesInitialized: boolean = $derived.by(() => { + const nodesInitialized = adoptUserNodes(signals.nodes, this.nodeLookup, this.parentLookup, { + nodeExtent: this.nodeExtent, + nodeOrigin: this.nodeOrigin, + elevateNodesOnSelect: signals.props.elevateNodesOnSelect ?? true, + checkEquality: true + }); + + if (this.fitViewQueued && nodesInitialized) { + if (this.fitViewOptions?.duration) { + this.resolveFitView(); + } else { + /** + * When no duration is set, viewport is set immediately which prevents an update + * I do not understand why, however we are setting state in a derived which is a no-go + */ + queueMicrotask(() => { + this.resolveFitView(); + }); + } + } + + return nodesInitialized; + }); + viewportInitialized: boolean = $derived(this.panZoom !== null); + + _edges: EdgeType[] = $derived.by(() => { + updateConnectionLookup(this.connectionLookup, this.edgeLookup, signals.edges); + return signals.edges; + }); + + get nodes() { + // eslint-disable-next-line @typescript-eslint/no-unused-expressions + this.nodesInitialized; + return signals.nodes; + } + set nodes(nodes) { + signals.nodes = nodes; + } + get edges() { + return this._edges; + } + set edges(edges) { + signals.edges = edges; + } + + _prevSelectedNodes: NodeType[] = []; + _prevSelectedNodeIds = new Set(); + selectedNodes = $derived.by(() => { + const selectedNodesCount = this._prevSelectedNodeIds.size; + const selectedNodeIds = new Set(); + const selectedNodes = this.nodes.filter((node) => { + if (node.selected) { + selectedNodeIds.add(node.id); + this._prevSelectedNodeIds.delete(node.id); + } + return node.selected; + }); + + // Either the number of selected nodes has changed or two nodes changed their selection state + // at the same time. However then the previously selected node will be inside _prevSelectedNodeIds + if (selectedNodesCount !== selectedNodeIds.size || this._prevSelectedNodeIds.size > 0) { + this._prevSelectedNodes = selectedNodes; + } + + this._prevSelectedNodeIds = selectedNodeIds; + return this._prevSelectedNodes; + }); + + _prevSelectedEdges: EdgeType[] = []; + _prevSelectedEdgeIds = new Set(); + selectedEdges = $derived.by(() => { + const selectedEdgesCount = this._prevSelectedEdgeIds.size; + const selectedEdgeIds = new Set(); + const selectedEdges = this.edges.filter((edge) => { + if (edge.selected) { + selectedEdgeIds.add(edge.id); + this._prevSelectedEdgeIds.delete(edge.id); + } + return edge.selected; + }); + // Either the number of selected edges has changed or two edges changed their selection state + // at the same time. However then the previously selected edge will be inside _prevSelectedEdgeIds + if (selectedEdgesCount !== selectedEdgeIds.size || this._prevSelectedEdgeIds.size > 0) { + this._prevSelectedEdges = selectedEdges; + } + this._prevSelectedEdgeIds = selectedEdgeIds; + return this._prevSelectedEdges; + }); + + selectionChangeHandlers = new Map>(); + + nodeLookup: NodeLookup> = new Map(); + parentLookup: ParentLookup> = new Map(); + connectionLookup: ConnectionLookup = new Map(); + edgeLookup: EdgeLookup = new Map(); + + _prevVisibleEdges = new Map>(); + visible = $derived.by(() => { + const { + // We need to access this._nodes to trigger on changes + // eslint-disable-next-line @typescript-eslint/no-unused-vars + nodes, + _edges: edges, + _prevVisibleEdges: previousEdges, + nodeLookup, + connectionMode, + onerror, + onlyRenderVisibleElements, + defaultEdgeOptions + } = this; + + let visibleNodes: Map>; + let visibleEdges: Map>; + + const options = { + edges, + defaultEdgeOptions, + previousEdges, + nodeLookup, + connectionMode, + elevateEdgesOnSelect: signals.props.elevateEdgesOnSelect ?? true, + onerror + }; + + if (onlyRenderVisibleElements) { + // We only subscribe to viewport, width, height if onlyRenderVisibleElements is true + const { viewport, width, height } = this; + const transform: Transform = [viewport.x, viewport.y, viewport.zoom]; + + visibleNodes = getVisibleNodes(nodeLookup, transform, width, height); + visibleEdges = getLayoutedEdges({ + ...options, + onlyRenderVisible: true, + visibleNodes, + transform, + width, + height + }); + } else { + visibleNodes = this.nodeLookup; + visibleEdges = getLayoutedEdges(options as EdgeLayoutAllOptions); + } + + return { + nodes: visibleNodes, + edges: visibleEdges + }; + }); + + nodesDraggable: boolean = $derived(signals.props.nodesDraggable ?? true); + nodesConnectable: boolean = $derived(signals.props.nodesConnectable ?? true); + elementsSelectable: boolean = $derived(signals.props.elementsSelectable ?? true); + nodesFocusable: boolean = $derived(signals.props.nodesFocusable ?? true); + edgesFocusable: boolean = $derived(signals.props.edgesFocusable ?? true); + + disableKeyboardA11y: boolean = $derived(signals.props.disableKeyboardA11y ?? false); + + minZoom: number = $derived(signals.props.minZoom ?? 0.5); + maxZoom: number = $derived(signals.props.maxZoom ?? 2); + + nodeOrigin: NodeOrigin = $derived(signals.props.nodeOrigin ?? [0, 0]); + nodeExtent: CoordinateExtent = $derived(signals.props.nodeExtent ?? infiniteExtent); + translateExtent: CoordinateExtent = $derived(signals.props.translateExtent ?? infiniteExtent); + + defaultEdgeOptions: Partial = $derived(signals.props.defaultEdgeOptions ?? {}); + + nodeDragThreshold: number = $derived(signals.props.nodeDragThreshold ?? 1); + autoPanOnNodeDrag: boolean = $derived(signals.props.autoPanOnNodeDrag ?? true); + autoPanOnConnect: boolean = $derived(signals.props.autoPanOnConnect ?? true); + + fitViewQueued: boolean = signals.props.fitView ?? false; + fitViewOptions: FitViewOptions | undefined = signals.props.fitViewOptions; + fitViewResolver: PromiseWithResolvers | null = null; + + snapGrid: SnapGrid | null = $derived(signals.props.snapGrid ?? null); + + dragging: boolean = $state(false); + selectionRect: SelectionRect | null = $state(null); + + selectionKeyPressed: boolean = $state(false); + multiselectionKeyPressed: boolean = $state(false); + deleteKeyPressed: boolean = $state(false); + panActivationKeyPressed: boolean = $state(false); + zoomActivationKeyPressed: boolean = $state(false); + selectionRectMode: string | null = $state(null); + ariaLiveMessage = $state(''); + selectionMode: SelectionMode = $derived(signals.props.selectionMode ?? SelectionMode.Partial); + + nodeTypes: NodeTypes = $derived({ ...initialNodeTypes, ...signals.props.nodeTypes }); + edgeTypes: EdgeTypes = $derived({ ...initialEdgeTypes, ...signals.props.edgeTypes }); + + noPanClass: string = $derived(signals.props.noPanClass ?? 'nopan'); + noDragClass: string = $derived(signals.props.noDragClass ?? 'nodrag'); + noWheelClass: string = $derived(signals.props.noWheelClass ?? 'nowheel'); + + // _viewport is the internal viewport. + // when binding to viewport, we operate on signals.viewport instead + _viewport: Viewport = $state(signals.props.initialViewport ?? { x: 0, y: 0, zoom: 1 }); + get viewport() { + return signals.viewport ?? this._viewport; + } + set viewport(newViewport: Viewport) { + if (signals.viewport) { + signals.viewport = newViewport; + } + this._viewport = newViewport; + } + + // _connection is viewport independent and originating from XYHandle + _connection: ConnectionState = $state(initialConnection); + // We derive a viewport dependent connection here + connection: ConnectionState = $derived.by(() => { + if (this._connection.inProgress) { + return { + ...this._connection, + to: pointToRendererPoint(this._connection.to, [ + this.viewport.x, + this.viewport.y, + this.viewport.zoom + ]) + }; + } else { + return this._connection; + } + }); + connectionMode: ConnectionMode = $derived( + signals.props.connectionMode ?? ConnectionMode.Strict + ); + connectionRadius: number = $derived(signals.props.connectionRadius ?? 20); + isValidConnection: IsValidConnection = $derived( + signals.props.isValidConnection ?? (() => true) + ); + + selectNodesOnDrag: boolean = $derived(signals.props.selectNodesOnDrag ?? true); + + defaultMarkerColor: string = $derived(signals.props.defaultMarkerColor ?? '#b1b1b7'); + markers: MarkerProps[] = $derived.by(() => { + return createMarkerIds(signals.edges, { + defaultColor: this.defaultMarkerColor, + id: this.flowId, + defaultMarkerStart: this.defaultEdgeOptions.markerStart, + defaultMarkerEnd: this.defaultEdgeOptions.markerEnd + }); + }); + onlyRenderVisibleElements: boolean = $derived(signals.props.onlyRenderVisibleElements ?? false); + onerror: OnError = $derived(signals.props.onflowerror ?? devWarn); + + ondelete?: OnDelete = $derived(signals.props.ondelete); + onbeforedelete?: OnBeforeDelete = $derived(signals.props.onbeforedelete); + + onbeforeconnect?: OnBeforeConnect = $derived(signals.props.onbeforeconnect); + onconnect?: OnConnect = $derived(signals.props.onconnect); + onconnectstart?: OnConnectStart = $derived(signals.props.onconnectstart); + onconnectend?: OnConnectEnd = $derived(signals.props.onconnectend); + + onbeforereconnect?: OnBeforeReconnect = $derived(signals.props.onbeforereconnect); + onreconnect?: OnReconnect = $derived(signals.props.onreconnect); + onreconnectstart?: OnReconnectStart = $derived(signals.props.onreconnectstart); + onreconnectend?: OnReconnectEnd = $derived(signals.props.onreconnectend); + + clickConnect?: boolean = $derived(signals.props.clickConnect ?? true); + onclickconnectstart?: OnConnectStart = $derived(signals.props.onclickconnectstart); + onclickconnectend?: OnConnectEnd = $derived(signals.props.onclickconnectend); + clickConnectStartHandle: Pick | null = $state(null); + + onselectiondrag?: OnSelectionDrag = $derived(signals.props.onselectiondrag); + onselectiondragstart?: OnSelectionDrag = $derived(signals.props.onselectiondragstart); + onselectiondragstop?: OnSelectionDrag = $derived(signals.props.onselectiondragstop); + + resolveFitView = async () => { + if (!this.panZoom) { + return; + } + + await fitViewport( + { + nodes: this.nodeLookup, + width: this.width, + height: this.height, + panZoom: this.panZoom, + minZoom: this.minZoom, + maxZoom: this.maxZoom + }, + this.fitViewOptions + ); + + this.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 + */ + this.fitViewQueued = false; + this.fitViewOptions = undefined; + this.fitViewResolver = null; + }; + + _prefersDark = new MediaQuery( + '(prefers-color-scheme: dark)', + signals.props.colorModeSSR === 'dark' + ); + colorMode: ColorModeClass = $derived( + signals.props.colorMode === 'system' + ? this._prefersDark.current + ? 'dark' + : 'light' + : (signals.props.colorMode ?? 'light') + ); + + constructor() { + // Process intial fitView here + if (signals.props.fitView && !signals.props.initialViewport && this.width && this.height) { + const bounds = getInternalNodesBounds(this.nodeLookup, { + filter: (node) => + !!((node.width || node.initialWidth) && (node.height || node.initialHeight)) + }); + this.viewport = getViewportForBounds(bounds, this.width, this.height, 0.5, 2, 0.1); + } + + if (process.env.NODE_ENV === 'development') { + warnIfDeeplyReactive(signals.nodes, 'nodes'); + warnIfDeeplyReactive(signals.edges, 'edges'); + } + } + + resetStoreValues() { + this.dragging = false; + this.selectionRect = null; + this.selectionRectMode = null; + this.selectionKeyPressed = false; + this.multiselectionKeyPressed = false; + this.deleteKeyPressed = false; + this.panActivationKeyPressed = false; + this.zoomActivationKeyPressed = false; + this._connection = initialConnection; + this.clickConnectStartHandle = null; + this.viewport = signals.props.initialViewport ?? { x: 0, y: 0, zoom: 1 }; + this.ariaLiveMessage = ''; + } + } + return new SvelteFlowStore(); +} + +// Only way to check if an object is a proxy +// is to see if is failes to perform a structured clone +function warnIfDeeplyReactive(array: unknown[] | undefined, name: string) { + try { + if (array && array.length > 0) { + structuredClone(array[0]); + } + } catch { + console.warn(`Use $state.raw for ${name} to prevent performance issues.`); + } +} diff --git a/packages/svelte/src/lib/store/initial-store.ts b/packages/svelte/src/lib/store/initial-store.ts deleted file mode 100644 index 142460a7..00000000 --- a/packages/svelte/src/lib/store/initial-store.ts +++ /dev/null @@ -1,178 +0,0 @@ -import { readable, writable } from 'svelte/store'; -import { - infiniteExtent, - SelectionMode, - ConnectionMode, - ConnectionLineType, - devWarn, - adoptUserNodes, - getViewportForBounds, - updateConnectionLookup, - initialConnection, - type SelectionRect, - type SnapGrid, - type MarkerProps, - type PanZoomInstance, - type CoordinateExtent, - type NodeOrigin, - type OnError, - type Viewport, - type ConnectionLookup, - type OnConnect, - type OnConnectStart, - type OnConnectEnd, - type NodeLookup, - type EdgeLookup, - type ConnectionState, - type ParentLookup, - getInternalNodesBounds -} from '@xyflow/system'; - -import DefaultNode from '$lib/components/nodes/DefaultNode.svelte'; -import InputNode from '$lib/components/nodes/InputNode.svelte'; -import OutputNode from '$lib/components/nodes/OutputNode.svelte'; -import GroupNode from '$lib/components/nodes/GroupNode.svelte'; - -import { - BezierEdgeInternal, - SmoothStepEdgeInternal, - StraightEdgeInternal, - StepEdgeInternal -} from '$lib/components/edges'; - -import type { - NodeTypes, - EdgeTypes, - EdgeLayouted, - Node, - Edge, - FitViewOptions, - OnDelete, - OnEdgeCreate, - OnBeforeDelete, - IsValidConnection, - InternalNode -} from '$lib/types'; -import { createNodesStore, createEdgesStore } from './utils'; - -export const initialNodeTypes = { - input: InputNode, - output: OutputNode, - default: DefaultNode, - group: GroupNode -}; - -export const initialEdgeTypes = { - straight: StraightEdgeInternal, - smoothstep: SmoothStepEdgeInternal, - default: BezierEdgeInternal, - step: StepEdgeInternal -}; - -export const getInitialStore = ({ - nodes = [], - edges = [], - width, - height, - fitView, - nodeOrigin, - nodeExtent -}: { - nodes?: Node[]; - edges?: Edge[]; - width?: number; - height?: number; - fitView?: boolean; - nodeOrigin?: NodeOrigin; - nodeExtent?: CoordinateExtent; -}) => { - const nodeLookup: NodeLookup = new Map(); - const parentLookup = new Map(); - const connectionLookup = new Map(); - const edgeLookup = new Map(); - - const storeNodeOrigin = nodeOrigin ?? [0, 0]; - const storeNodeExtent = nodeExtent ?? infiniteExtent; - - adoptUserNodes(nodes, nodeLookup, parentLookup, { - nodeExtent: storeNodeExtent, - nodeOrigin: storeNodeOrigin, - elevateNodesOnSelect: false, - checkEquality: false - }); - - updateConnectionLookup(connectionLookup, edgeLookup, edges); - - let viewport: Viewport = { x: 0, y: 0, zoom: 1 }; - - if (fitView && width && height) { - const bounds = getInternalNodesBounds(nodeLookup, { - filter: (node) => !!((node.width || node.initialWidth) && (node.height || node.initialHeight)) - }); - viewport = getViewportForBounds(bounds, width, height, 0.5, 2, 0.1); - } - - return { - flowId: writable(null), - nodes: createNodesStore(nodes, nodeLookup, parentLookup, storeNodeOrigin, storeNodeExtent), - nodeLookup: readable>(nodeLookup), - parentLookup: readable>(parentLookup), - edgeLookup: readable>(edgeLookup), - visibleNodes: readable([]), - edges: createEdgesStore(edges, connectionLookup, edgeLookup), - visibleEdges: readable([]), - connectionLookup: readable(connectionLookup), - height: writable(500), - width: writable(500), - minZoom: writable(0.5), - maxZoom: writable(2), - nodeOrigin: writable(storeNodeOrigin), - nodeDragThreshold: writable(1), - nodeExtent: writable(storeNodeExtent), - translateExtent: writable(infiniteExtent), - autoPanOnNodeDrag: writable(true), - autoPanOnConnect: writable(true), - fitViewOnInit: writable(false), - fitViewOnInitDone: writable(false), - fitViewOptions: writable(undefined), - panZoom: writable(null), - snapGrid: writable(null), - dragging: writable(false), - selectionRect: writable(null), - selectionKeyPressed: writable(false), - multiselectionKeyPressed: writable(false), - deleteKeyPressed: writable(false), - panActivationKeyPressed: writable(false), - zoomActivationKeyPressed: writable(false), - selectionRectMode: writable(null), - selectionMode: writable(SelectionMode.Partial), - nodeTypes: writable(initialNodeTypes), - edgeTypes: writable(initialEdgeTypes), - viewport: writable(viewport), - connectionMode: writable(ConnectionMode.Strict), - domNode: writable(null), - connection: readable(initialConnection), - connectionLineType: writable(ConnectionLineType.Bezier), - connectionRadius: writable(20), - isValidConnection: writable(() => true), - nodesDraggable: writable(true), - nodesConnectable: writable(true), - elementsSelectable: writable(true), - selectNodesOnDrag: writable(true), - markers: readable([]), - defaultMarkerColor: writable('#b1b1b7'), - lib: readable('svelte'), - onlyRenderVisibleElements: writable(false), - onerror: writable(devWarn), - ondelete: writable(undefined), - onedgecreate: writable(undefined), - onconnect: writable(undefined), - onconnectstart: writable(undefined), - onconnectend: writable(undefined), - onbeforedelete: writable(undefined), - nodesInitialized: writable(false), - edgesInitialized: writable(false), - viewportInitialized: writable(false), - initialized: readable(false) - }; -}; diff --git a/packages/svelte/src/lib/store/types.ts b/packages/svelte/src/lib/store/types.ts index 88ee8be0..fd345c61 100644 --- a/packages/svelte/src/lib/store/types.ts +++ b/packages/svelte/src/lib/store/types.ts @@ -1,4 +1,3 @@ -import type { Writable } from 'svelte/store'; import type { InternalNodeUpdate, XYPosition, @@ -10,16 +9,14 @@ import type { Viewport } from '@xyflow/system'; -import type { getInitialStore } from './initial-store'; +import type { getInitialStore } from './initial-store.svelte'; import type { Node, Edge, NodeTypes, EdgeTypes, FitViewOptions } from '$lib/types'; +import type { SvelteFlowProps } from '$lib/container/SvelteFlow'; -export type SvelteFlowStoreActions = { - syncNodeStores: (nodesStore: Writable) => void; - syncEdgeStores: (edgeStore: Writable) => void; - syncViewport: (viewportStore?: Writable) => void; +export type SvelteFlowStoreActions = { setNodeTypes: (nodeTypes: NodeTypes) => void; setEdgeTypes: (edgeTypes: EdgeTypes) => void; - addEdge: (edge: Edge | Connection) => void; + addEdge: (edge: EdgeType | Connection) => void; zoomIn: (options?: ViewportHelperFunctionOptions) => Promise; zoomOut: (options?: ViewportHelperFunctionOptions) => Promise; setMinZoom: (minZoom: number) => void; @@ -29,16 +26,96 @@ export type SvelteFlowStoreActions = { fitView: (options?: FitViewOptions) => Promise; updateNodePositions: UpdateNodePositions; updateNodeInternals: (updates: Map) => void; - unselectNodesAndEdges: (params?: { nodes?: Node[]; edges?: Edge[] }) => void; + unselectNodesAndEdges: (params?: { nodes?: NodeType[]; edges?: EdgeType[] }) => void; addSelectedNodes: (ids: string[]) => void; addSelectedEdges: (ids: string[]) => void; - handleNodeSelection: (id: string) => void; + handleNodeSelection: (id: string, unselect?: boolean, nodeRef?: HTMLDivElement | null) => void; + handleEdgeSelection: (id: string) => void; + moveSelectedNodes: (direction: XYPosition, factor: number) => void; panBy: (delta: XYPosition) => Promise; updateConnection: UpdateConnection; cancelConnection: () => void; reset(): void; }; -export type SvelteFlowStoreState = ReturnType; +export type SvelteFlowRestProps = Omit< + SvelteFlowProps, + | 'width' + | 'height' + | 'class' + | 'proOptions' + | 'selectionKey' + | 'deleteKey' + | 'panActivationKey' + | 'multiSelectionKey' + | 'zoomActivationKey' + | 'paneClickDistance' + | 'nodeClickDistance' + | 'onMoveStart' + | 'onMoveEnd' + | 'onMove' + | 'onnodeclick' + | 'onnodecontextmenu' + | 'onnodedrag' + | 'onnodedragstart' + | 'onnodedragstop' + | 'onnodepointerenter' + | 'onnodepointermove' + | 'onnodepointerleave' + | 'onselectionclick' + | 'onselectioncontextmenu' + | 'onedgeclick' + | 'onedgecontextmenu' + | 'onedgepointerenter' + | 'onedgepointerleave' + | 'onpaneclick' + | 'onpanecontextmenu' + | 'panOnScrollMode' + | 'preventScrolling' + | 'zoomOnScroll' + | 'zoomOnDoubleClick' + | 'zoomOnPinch' + | 'panOnScroll' + | 'panOnDrag' + | 'selectionOnDrag' + | 'connectionLineComponent' + | 'connectionLineStyle' + | 'connectionLineContainerStyle' + | 'connectionLineType' + | 'attributionPosition' + | 'children' + | 'nodes' + | 'edges' + | 'viewport' +>; -export type SvelteFlowStore = SvelteFlowStoreState & SvelteFlowStoreActions; +export type StoreSignals = { + props: SvelteFlowRestProps; + width?: number; + height?: number; + nodes: NodeType[]; + edges: EdgeType[]; + viewport?: Viewport; +}; + +export type SvelteFlowStoreState< + NodeType extends Node = Node, + EdgeType extends Edge = Edge +> = ReturnType>; + +export type SvelteFlowStore< + NodeType extends Node = Node, + EdgeType extends Edge = Edge +> = SvelteFlowStoreState & SvelteFlowStoreActions; + +export type StoreContext = { + getStore: () => SvelteFlowStore; + provider: boolean; +}; + +export type ProviderContext< + NodeType extends Node = Node, + EdgeType extends Edge = Edge +> = StoreContext & { + setStore: (store: SvelteFlowStore) => void; +}; diff --git a/packages/svelte/src/lib/store/utils.ts b/packages/svelte/src/lib/store/utils.ts deleted file mode 100644 index 30af8f73..00000000 --- a/packages/svelte/src/lib/store/utils.ts +++ /dev/null @@ -1,220 +0,0 @@ -import { - writable, - get, - type Unsubscriber, - type Subscriber, - type Updater, - type Writable -} from 'svelte/store'; -import { - adoptUserNodes, - updateConnectionLookup, - type Viewport, - type PanZoomInstance, - type ConnectionLookup, - type EdgeLookup, - type NodeLookup, - type ParentLookup, - type NodeOrigin, - infiniteExtent, - type CoordinateExtent -} from '@xyflow/system'; - -import type { DefaultEdgeOptions, DefaultNodeOptions, Edge, InternalNode, Node } from '$lib/types'; - -// 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). -export function syncNodeStores( - nodesStore: ReturnType, - userNodesStore: Writable -) { - const nodesStoreSetter = nodesStore.set; - const userNodesStoreSetter = userNodesStore.set; - const currentNodesStore = get(nodesStore); - const currentUserNodesStore = get(userNodesStore); - // depending how the user initializes the nodes, we need to decide if we want to use - // the user nodes or the internal nodes for initialization. A user can use a SvelteFlowProvider - // without providing any nodes, in that case we want to use the nodes passed by the user. - // By default we are using the store nodes, because they already have the absolute positions. - const initWithUserNodes = currentNodesStore.length === 0 && currentUserNodesStore.length > 0; - - let val = initWithUserNodes ? currentUserNodesStore : currentNodesStore; - nodesStore.set(val); - - const _set = (nds: Node[]) => { - const updatedNodes = nodesStoreSetter(nds); - val = updatedNodes; - - userNodesStoreSetter(val); - - return updatedNodes; - }; - - nodesStore.set = userNodesStore.set = _set; - nodesStore.update = userNodesStore.update = (fn: (nds: Node[]) => Node[]) => _set(fn(val)); -} - -// same for edges -export function syncEdgeStores( - edgesStore: ReturnType, - userEdgesStore: Writable -) { - const nodesStoreSetter = edgesStore.set; - const userEdgesStoreSetter = userEdgesStore.set; - - let val = get(userEdgesStore); - edgesStore.set(val); - - const _set = (eds: Edge[]) => { - nodesStoreSetter(eds); - userEdgesStoreSetter(eds); - val = eds; - }; - - edgesStore.set = userEdgesStore.set = _set; - edgesStore.update = userEdgesStore.update = (fn: (nds: Edge[]) => Edge[]) => _set(fn(val)); -} - -// it is possible to pass a viewport store to SvelteFlow for having more control -// if that's the case we need to sync the internal viewport with the user viewport -export const syncViewportStores = ( - panZoomStore: Writable, - viewportStore: Writable, - userViewportStore?: Writable -) => { - if (!userViewportStore) { - return; - } - - const panZoom = get(panZoomStore); - - const viewportStoreSetter = viewportStore.set; - const userViewportStoreSetter = userViewportStore.set; - - let val = userViewportStore ? get(userViewportStore) : { x: 0, y: 0, zoom: 1 }; - viewportStore.set(val); - - viewportStore.set = (vp: Viewport) => { - viewportStoreSetter(vp); - userViewportStoreSetter(vp); - - val = vp; - - return vp; - }; - - userViewportStore.set = (vp: Viewport) => { - panZoom?.syncViewport(vp); - - viewportStoreSetter(vp); - userViewportStoreSetter(vp); - - val = vp; - - return vp; - }; - - viewportStore.update = (fn: (vp: Viewport) => Viewport) => { - viewportStore.set(fn(val)); - }; - - userViewportStore.update = (fn: (vp: Viewport) => Viewport) => { - userViewportStore.set(fn(val)); - }; -}; - -export type NodeStoreOptions = { - elevateNodesOnSelect?: boolean; -}; - -// we are creating a custom store for the internals nodes in order to update the zIndex and positionAbsolute. -// The user only passes in relative positions, so we need to calculate the absolute positions based on the parent nodes. -export const createNodesStore = ( - nodes: Node[], - nodeLookup: NodeLookup, - parentLookup: ParentLookup, - nodeOrigin: NodeOrigin = [0, 0], - nodeExtent: CoordinateExtent = infiniteExtent -): { - subscribe: (this: void, run: Subscriber) => Unsubscriber; - update: (this: void, updater: Updater) => void; - set: (this: void, value: Node[]) => Node[]; - setDefaultOptions: (opts: DefaultNodeOptions) => void; - setOptions: (opts: NodeStoreOptions) => void; -} => { - const { subscribe, set, update } = writable([]); - let value = nodes; - let defaults = {}; - let elevateNodesOnSelect = true; - - const _set = (nds: Node[]): Node[] => { - adoptUserNodes(nds, nodeLookup, parentLookup, { - elevateNodesOnSelect, - nodeOrigin, - nodeExtent, - defaults, - checkEquality: false - }); - - value = nds; - - set(value); - - return value; - }; - - const _update: typeof update = (fn: (nds: Node[]) => Node[]) => _set(fn(value)); - - const setDefaultOptions = (options: DefaultNodeOptions) => { - defaults = options; - }; - - const setOptions = (options: NodeStoreOptions) => { - elevateNodesOnSelect = options.elevateNodesOnSelect ?? elevateNodesOnSelect; - }; - - _set(value); - - return { - subscribe, - set: _set, - update: _update, - setDefaultOptions, - setOptions - }; -}; - -export const createEdgesStore = ( - edges: Edge[], - connectionLookup: ConnectionLookup, - edgeLookup: EdgeLookup, - defaultOptions?: DefaultEdgeOptions -): Writable & { setDefaultOptions: (opts: DefaultEdgeOptions) => void } => { - const { subscribe, set, update } = writable([]); - let value = edges; - let defaults = defaultOptions || {}; - - const _set: typeof set = (eds: Edge[]) => { - const nextEdges = defaults ? eds.map((edge) => ({ ...defaults, ...edge })) : eds; - - updateConnectionLookup(connectionLookup, edgeLookup, nextEdges); - - value = nextEdges; - set(value); - }; - - const _update: typeof update = (fn: (eds: Edge[]) => Edge[]) => _set(fn(value)); - - const setDefaultOptions = (options: DefaultEdgeOptions) => { - defaults = options; - }; - - _set(value); - - return { - subscribe, - set: _set, - update: _update, - setDefaultOptions - }; -}; diff --git a/packages/svelte/src/lib/store/visible-edges.ts b/packages/svelte/src/lib/store/visible-edges.ts deleted file mode 100644 index d1b4a467..00000000 --- a/packages/svelte/src/lib/store/visible-edges.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { derived } from 'svelte/store'; -import { isEdgeVisible, getEdgePosition, getElevatedEdgeZIndex } from '@xyflow/system'; - -import type { EdgeLayouted } from '$lib/types'; -import type { SvelteFlowStoreState } from './types'; - -export function getVisibleEdges(store: SvelteFlowStoreState) { - const visibleEdges = derived( - [ - store.edges, - store.nodes, - store.nodeLookup, - store.onlyRenderVisibleElements, - store.viewport, - store.width, - store.height - ], - ([edges, , nodeLookup, onlyRenderVisibleElements, viewport, width, height]) => { - const visibleEdges = - onlyRenderVisibleElements && width && height - ? edges.filter((edge) => { - const sourceNode = nodeLookup.get(edge.source); - const targetNode = nodeLookup.get(edge.target); - - return ( - sourceNode && - targetNode && - isEdgeVisible({ - sourceNode, - targetNode, - width, - height, - transform: [viewport.x, viewport.y, viewport.zoom] - }) - ); - }) - : edges; - - return visibleEdges; - } - ); - - return derived( - [visibleEdges, store.nodes, store.nodeLookup, store.connectionMode, store.onerror], - ([visibleEdges, , nodeLookup, connectionMode, onerror]) => { - const layoutedEdges = visibleEdges.reduce((res, edge) => { - const sourceNode = nodeLookup.get(edge.source); - const targetNode = nodeLookup.get(edge.target); - - if (!sourceNode || !targetNode) { - return res; - } - - const edgePosition = getEdgePosition({ - id: edge.id, - sourceNode, - targetNode, - sourceHandle: edge.sourceHandle || null, - targetHandle: edge.targetHandle || null, - connectionMode, - onError: onerror - }); - - if (edgePosition) { - res.push({ - ...edge, - zIndex: getElevatedEdgeZIndex({ - selected: edge.selected, - zIndex: edge.zIndex, - sourceNode, - targetNode, - elevateOnSelect: false - }), - ...edgePosition - }); - } - - return res; - }, []); - - return layoutedEdges; - } - ); -} diff --git a/packages/svelte/src/lib/store/visible-nodes.ts b/packages/svelte/src/lib/store/visible-nodes.ts deleted file mode 100644 index e615439f..00000000 --- a/packages/svelte/src/lib/store/visible-nodes.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { derived } from 'svelte/store'; -import { getNodesInside, type Transform } from '@xyflow/system'; -import type { SvelteFlowStoreState } from './types'; - -export function getVisibleNodes(store: SvelteFlowStoreState) { - return derived( - [ - store.nodeLookup, - store.onlyRenderVisibleElements, - store.width, - store.height, - store.viewport, - store.nodes - ], - ([nodeLookup, onlyRenderVisibleElements, width, height, viewport]) => { - const transform: Transform = [viewport.x, viewport.y, viewport.zoom]; - - return onlyRenderVisibleElements - ? getNodesInside(nodeLookup, { x: 0, y: 0, width, height }, transform, true) - : Array.from(nodeLookup.values()); - } - ); -} diff --git a/packages/svelte/src/lib/store/visibleElements.ts b/packages/svelte/src/lib/store/visibleElements.ts new file mode 100644 index 00000000..e8ff8aca --- /dev/null +++ b/packages/svelte/src/lib/store/visibleElements.ts @@ -0,0 +1,147 @@ +import type { DefaultEdgeOptions, Node, Edge, EdgeLayouted, InternalNode } from '$lib/types'; +import { + ConnectionMode, + getEdgePosition, + getElevatedEdgeZIndex, + getNodesInside, + isEdgeVisible, + type NodeLookup, + type OnError, + type Transform +} from '@xyflow/system'; + +export function getVisibleNodes( + nodeLookup: NodeLookup>, + transform: Transform, + width: number, + height: number +) { + const visibleNodes = new Map>(); + getNodesInside(nodeLookup, { x: 0, y: 0, width: width, height: height }, transform, true).forEach( + (node) => { + visibleNodes.set(node.id, node); + } + ); + return visibleNodes; +} + +export interface EdgeLayoutBaseOptions { + edges: EdgeType[]; + defaultEdgeOptions: DefaultEdgeOptions; + elevateEdgesOnSelect: boolean; + previousEdges: Map>; + nodeLookup: NodeLookup>; + connectionMode: ConnectionMode; + onerror: OnError; +} + +export interface EdgeLayoutAllOptions + extends EdgeLayoutBaseOptions { + onlyRenderVisible: never; + visibleNodes: never; + transform: never; + width: never; + height: never; +} + +export interface EdgeLayoutOnlyVisibleOptions< + NodeType extends Node = Node, + EdgeType extends Edge = Edge +> extends EdgeLayoutBaseOptions { + visibleNodes: Map; + transform: Transform; + width: number; + height: number; + onlyRenderVisible: true; +} + +export type EdgeLayoutOptions = + | EdgeLayoutAllOptions + | EdgeLayoutOnlyVisibleOptions; + +export function getLayoutedEdges( + options: EdgeLayoutOptions +): Map> { + const { + edges, + defaultEdgeOptions, + nodeLookup, + previousEdges, + connectionMode, + onerror, + onlyRenderVisible, + elevateEdgesOnSelect + } = options; + const layoutedEdges = new Map>(); + for (const edge of edges) { + const sourceNode = nodeLookup.get(edge.source); + const targetNode = nodeLookup.get(edge.target); + + if (!sourceNode || !targetNode) { + continue; + } + + if (onlyRenderVisible) { + const { visibleNodes, transform, width, height } = options; + if ( + isEdgeVisible({ + sourceNode, + targetNode, + width: width!, + height: height!, + transform: transform! + }) + ) { + visibleNodes!.set(sourceNode.id, sourceNode); + visibleNodes!.set(targetNode.id, targetNode); + } else { + continue; + } + } + + // we reuse the previous edge object if + // the current and previous edge are the same + // and the source and target node are the same + // and references to internalNodes are the same + const previous = previousEdges.get(edge.id); + if ( + previous && + edge === previous.edge && + sourceNode == previous.sourceNode && + targetNode == previous.targetNode + ) { + layoutedEdges.set(edge.id, previous); + continue; + } + + const edgePosition = getEdgePosition({ + id: edge.id, + sourceNode, + targetNode, + sourceHandle: edge.sourceHandle || null, + targetHandle: edge.targetHandle || null, + connectionMode, + onError: onerror + }); + + if (edgePosition) { + layoutedEdges.set(edge.id, { + ...defaultEdgeOptions, + ...edge, + ...edgePosition, + zIndex: getElevatedEdgeZIndex({ + selected: edge.selected, + zIndex: edge.zIndex ?? defaultEdgeOptions.zIndex, + sourceNode, + targetNode, + elevateOnSelect: elevateEdgesOnSelect + }), + sourceNode, + targetNode, + edge + }); + } + } + + return layoutedEdges; +} diff --git a/packages/svelte/src/lib/types/edges.ts b/packages/svelte/src/lib/types/edges.ts index de1def91..c903512a 100644 --- a/packages/svelte/src/lib/types/edges.ts +++ b/packages/svelte/src/lib/types/edges.ts @@ -1,4 +1,4 @@ -import type { SvelteComponent, ComponentType } from 'svelte'; +import type { Component } from 'svelte'; import type { EdgeBase, BezierPathOptions, @@ -9,9 +9,12 @@ import type { } from '@xyflow/system'; import type { Node } from '$lib/types'; +import type { ClassValue, HTMLAttributes } from 'svelte/elements'; /** - * The Edge type is mainly used for the `edges` that get passed to the SvelteFlow component. + * An `Edge` is the complete description with everything Svelte Flow needs to know in order to + * render it. + * @public */ export type Edge< EdgeData extends Record = Record, @@ -20,9 +23,32 @@ export type Edge< label?: string; labelStyle?: string; style?: string; - class?: string; + class?: ClassValue; + focusable?: boolean; }; +export type BaseEdgeProps = Pick< + EdgeProps, + 'interactionWidth' | 'label' | 'labelStyle' | 'style' +> & { + id?: string; + /** SVG path of the edge */ + path: string; + /** The x coordinate of the label */ + labelX?: number; + /** The y coordinate of the label */ + labelY?: number; + /** Marker at start of edge + * @example 'url(#arrow)' + */ + markerStart?: string; + /** Marker at end of edge + * @example 'url(#arrow)' + */ + markerEnd?: string; + class?: ClassValue; +} & HTMLAttributes; + type SmoothStepEdge = Record> = Edge< EdgeData, 'smoothstep' @@ -113,47 +139,23 @@ export type StraightEdgeProps = Omit + Component< + EdgeProps & { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + data?: any; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + type: any; + } > >; export type DefaultEdgeOptions = DefaultEdgeOptionsBase; -export type EdgeLayouted = Pick< - Edge, - | 'type' - | 'id' - | 'data' - | 'style' - | 'source' - | 'target' - | 'animated' - | 'selected' - | 'selectable' - | 'deletable' - | 'label' - | 'labelStyle' - | 'interactionWidth' - | 'markerStart' - | 'markerEnd' - | 'sourceHandle' - | 'targetHandle' - | 'ariaLabel' - | 'hidden' - | 'class' - | 'zIndex' -> & +export type EdgeLayouted = EdgeType & EdgePosition & { sourceNode?: Node; targetNode?: Node; sourceHandleId?: string | null; targetHandleId?: string | null; + edge: EdgeType; }; diff --git a/packages/svelte/src/lib/types/events.ts b/packages/svelte/src/lib/types/events.ts new file mode 100644 index 00000000..24db8e1e --- /dev/null +++ b/packages/svelte/src/lib/types/events.ts @@ -0,0 +1,77 @@ +import type { Edge } from './edges'; +import type { Node } from './nodes'; + +export type NodeEventWithPointer = ({ + node, + event +}: { + node: NodeType; + event: T; +}) => void; + +export type NodesEventWithPointer = ({ + nodes, + event +}: { + nodes: NodeType[]; + event: T; +}) => void; + +export type NodeTargetEventWithPointer = ({ + targetNode, + nodes, + event +}: { + targetNode: NodeType | null; + nodes: NodeType[]; + event: T; +}) => void; + +export type NodeEvents = { + /** This event handler is called when a user clicks on a node. */ + onnodeclick?: NodeEventWithPointer; + /** This event handler is called when a user right-clicks on a node. */ + onnodecontextmenu?: NodeEventWithPointer; + /** This event handler is called when a user drags a node. */ + onnodedrag?: NodeTargetEventWithPointer; + /** This event handler is called when a user starts to drag a node. */ + onnodedragstart?: NodeTargetEventWithPointer; + /** This event handler is called when a user stops dragging a node. */ + onnodedragstop?: NodeTargetEventWithPointer; + /** This event handler is called when the pointer of a user enters a node. */ + onnodepointerenter?: NodeEventWithPointer; + /** This event handler is called when the pointer of a user leaves a node. */ + onnodepointerleave?: NodeEventWithPointer; + /** This event handler is called when the pointer of a user moves over a node. */ + onnodepointermove?: NodeEventWithPointer; +}; + +export type NodeSelectionEvents = { + /** This event handler is called when a user right-clicks the selection box. */ + onselectioncontextmenu?: NodesEventWithPointer; + /** This event handler is called when a user clicks the selection box. */ + onselectionclick?: NodesEventWithPointer; +}; + +export type PaneEvents = { + /** This event handler is called when a user clicks the pane. */ + onpaneclick?: ({ event }: { event: MouseEvent }) => void; + /** This event handler is called when a user right-clicks the pane. */ + onpanecontextmenu?: ({ event }: { event: MouseEvent }) => void; +}; + +export type EdgeEvents = { + /** This event handler is called when a user clicks an edge. */ + onedgeclick?: ({ edge, event }: { edge: EdgeType; event: MouseEvent }) => void; + /** This event handler is called when a user right-clicks an edge. */ + onedgecontextmenu?: ({ edge, event }: { edge: EdgeType; event: MouseEvent }) => void; + /** This event handler is called when the pointer of a user enters an edge. */ + onedgepointerenter?: ({ edge, event }: { edge: EdgeType; event: PointerEvent }) => void; + /** This event handler is called when the pointer of a user enters an edge. */ + onedgepointerleave?: ({ edge, event }: { edge: EdgeType; event: PointerEvent }) => void; +}; + +export type OnSelectionDrag = ( + event: MouseEvent, + nodes: NodeType[] +) => void; diff --git a/packages/svelte/src/lib/types/general.ts b/packages/svelte/src/lib/types/general.ts index 3e15ecee..34474408 100644 --- a/packages/svelte/src/lib/types/general.ts +++ b/packages/svelte/src/lib/types/general.ts @@ -4,8 +4,7 @@ import type { XYPosition, Handle, Connection, - OnBeforeDeleteBase, - HandleProps as HandlePropsSystem + OnBeforeDeleteBase } from '@xyflow/system'; import type { Node } from './nodes'; @@ -22,17 +21,20 @@ export type ConnectionData = { connectionStatus: string | null; }; -export type HandleProps = HandlePropsSystem & { - class?: string; - style?: string; - onconnect?: (connections: Connection[]) => void; - ondisconnect?: (connections: Connection[]) => void; -}; - export type FitViewOptions = FitViewOptionsBase; -export type OnDelete = (params: { nodes: Node[]; edges: Edge[] }) => void; -export type OnEdgeCreate = (connection: Connection) => Edge | Connection | void; +export type OnDelete = (params: { + nodes: NodeType[]; + edges: EdgeType[]; +}) => void; + +export type OnBeforeConnect = ( + connection: Connection +) => EdgeType | Connection | void; +export type OnBeforeReconnect = ( + newEdge: EdgeType, + oldEdge: EdgeType +) => EdgeType | void; export type OnBeforeDelete< NodeType extends Node = Node, EdgeType extends Edge = Edge @@ -41,3 +43,8 @@ export type OnBeforeDelete< export type IsValidConnection = ( edge: EdgeType | Connection ) => boolean; + +export type OnSelectionChange< + NodeType extends Node = Node, + EdgeType extends Edge = Edge +> = (params: { nodes: NodeType[]; edges: EdgeType[] }) => void; diff --git a/packages/svelte/src/lib/types/index.ts b/packages/svelte/src/lib/types/index.ts index 16df96cc..2796d0eb 100644 --- a/packages/svelte/src/lib/types/index.ts +++ b/packages/svelte/src/lib/types/index.ts @@ -1,3 +1,4 @@ export * from './nodes'; export * from './edges'; export * from './general'; +export * from './events'; diff --git a/packages/svelte/src/lib/types/nodes.ts b/packages/svelte/src/lib/types/nodes.ts index 021dad70..266f7cc3 100644 --- a/packages/svelte/src/lib/types/nodes.ts +++ b/packages/svelte/src/lib/types/nodes.ts @@ -1,4 +1,5 @@ -import type { ComponentType, SvelteComponent } from 'svelte'; +import type { Component } from 'svelte'; +import type { ClassValue } from 'svelte/elements'; import type { InternalNodeBase, NodeBase, NodeProps as NodePropsBase } from '@xyflow/system'; /** @@ -17,8 +18,9 @@ export type Node< NodeData extends Record = Record, NodeType extends string | undefined = undefined > = NodeBase & { - class?: string; + class?: ClassValue; style?: string; + focusable?: boolean; }; // @todo: currently generics for nodes are not really supported @@ -30,29 +32,16 @@ export type NodeProps = NodePropsBase & export type NodeTypes = Record< string, - ComponentType< - SvelteComponent< - NodeProps & { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - data: any; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - type: any; - } - > + Component< + NodeProps & { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + data: any; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + type: any; + } > >; -export type DefaultNodeOptions = Partial>; - -export type BuiltInNode = Node<{ label: string }, 'input' | 'output' | 'default'>; - -export type NodeEventMap = { - nodeclick: { node: Node; event: MouseEvent | TouchEvent }; - nodecontextmenu: { node: Node; event: MouseEvent | TouchEvent }; - nodedrag: { targetNode: Node | null; nodes: Node[]; event: MouseEvent | TouchEvent }; - nodedragstart: { targetNode: Node | null; nodes: Node[]; event: MouseEvent | TouchEvent }; - nodedragstop: { targetNode: Node | null; nodes: Node[]; event: MouseEvent | TouchEvent }; - nodemouseenter: { node: Node; event: MouseEvent | TouchEvent }; - nodemouseleave: { node: Node; event: MouseEvent | TouchEvent }; - nodemousemove: { node: Node; event: MouseEvent | TouchEvent }; -}; +export type BuiltInNode = + | Node<{ label: string }, 'input' | 'output' | 'default'> + | Node, 'group'>; diff --git a/packages/svelte/src/lib/utils/index.ts b/packages/svelte/src/lib/utils/index.ts index 10f205ca..122f8091 100644 --- a/packages/svelte/src/lib/utils/index.ts +++ b/packages/svelte/src/lib/utils/index.ts @@ -1,9 +1,9 @@ -import { isNodeBase, isEdgeBase } from '@xyflow/system'; +import { isNodeBase, isEdgeBase, type XYPosition } from '@xyflow/system'; import type { Edge, Node } from '$lib/types'; /** - * Test whether an object is useable as a Node + * Test whether an object is usable as a Node * @public * @remarks In TypeScript this is a type guard that will narrow the type of whatever you pass in to Node if it returns true * @param element - The element to test @@ -13,7 +13,7 @@ export const isNode = (element: unknown): element isNodeBase(element); /** - * Test whether an object is useable as an Edge + * Test whether an object is usable as an Edge * @public * @remarks In TypeScript this is a type guard that will narrow the type of whatever you pass in to Edge if it returns true * @param element - The element to test @@ -21,3 +21,14 @@ export const isNode = (element: unknown): element */ export const isEdge = (element: unknown): element is EdgeType => isEdgeBase(element); + +export function toPxString(value: number | undefined): string | undefined { + return value === undefined ? undefined : `${value}px`; +} + +export const arrowKeyDiffs: Record = { + ArrowUp: { x: 0, y: -1 }, + ArrowDown: { x: 0, y: 1 }, + ArrowLeft: { x: -1, y: 0 }, + ArrowRight: { x: 1, y: 0 } +}; diff --git a/packages/svelte/src/routes/+page.svelte.d.ts b/packages/svelte/src/routes/+page.svelte.d.ts new file mode 100644 index 00000000..7097772f --- /dev/null +++ b/packages/svelte/src/routes/+page.svelte.d.ts @@ -0,0 +1,23 @@ +/** @typedef {typeof __propDef.props} PageProps */ +/** @typedef {typeof __propDef.events} PageEvents */ +/** @typedef {typeof __propDef.slots} PageSlots */ +// export default class Page extends SvelteComponentTyped<{ +// [x: string]: never; +// }, { +// [evt: string]: CustomEvent; +// }, {}> { +// } +export type PageProps = typeof __propDef.props; +export type PageEvents = typeof __propDef.events; +export type PageSlots = typeof __propDef.slots; +// import { SvelteComponentTyped } from "svelte"; +// declare const __propDef: { +// props: { +// [x: string]: never; +// }; +// events: { +// [evt: string]: CustomEvent; +// }; +// slots: {}; +// }; +export {}; diff --git a/packages/svelte/src/styles/base.css b/packages/svelte/src/styles/base.css index a0cf3040..56bfb526 100644 --- a/packages/svelte/src/styles/base.css +++ b/packages/svelte/src/styles/base.css @@ -7,3 +7,7 @@ text-align: center; position: absolute; } + +.svelte-flow__container { + user-select: none; +} diff --git a/packages/svelte/src/styles/style.css b/packages/svelte/src/styles/style.css index 981bc92e..dc35ccc6 100644 --- a/packages/svelte/src/styles/style.css +++ b/packages/svelte/src/styles/style.css @@ -8,15 +8,10 @@ position: absolute; padding: 2px; font-size: 10px; - cursor: pointer; color: var(--xy-edge-label-color, var(--xy-edge-label-color-default)); background: var(--xy-edge-label-background-color, var(--xy-edge-label-background-color-default)); } -.svelte-flow__nodes { - z-index: 0; -} - -.svelte-flow__edgelabel-renderer { - z-index: 0; +.svelte-flow__container { + user-select: none; } diff --git a/packages/svelte/svelte.config.js b/packages/svelte/svelte.config.js index 60749928..cfe265ab 100644 --- a/packages/svelte/svelte.config.js +++ b/packages/svelte/svelte.config.js @@ -1,9 +1,12 @@ import adapter from '@sveltejs/adapter-auto'; -import preprocess from 'svelte-preprocess'; +import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; /** @type {import('@sveltejs/kit').Config} */ const config = { - preprocess: preprocess(), + preprocess: vitePreprocess(), + compilerOptions: { + runes: true + }, kit: { adapter: adapter() } diff --git a/packages/svelte/tsconfig.json b/packages/svelte/tsconfig.json index d9cfe5d6..8fbe9e82 100644 --- a/packages/svelte/tsconfig.json +++ b/packages/svelte/tsconfig.json @@ -8,8 +8,7 @@ "resolveJsonModule": true, "skipLibCheck": true, "sourceMap": true, - "strict": true, - "noErrorTruncation": true + "strict": true } // Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias diff --git a/packages/system/CHANGELOG.md b/packages/system/CHANGELOG.md index 6add95b1..a7eb7ee9 100644 --- a/packages/system/CHANGELOG.md +++ b/packages/system/CHANGELOG.md @@ -1,5 +1,165 @@ # @xyflow/system +## 0.0.61 + +### Patch Changes + +- [#5266](https://github.com/xyflow/xyflow/pull/5266) [`77107453`](https://github.com/xyflow/xyflow/commit/77107453fa6f34cb08ef91640b8b02d58e31275e) Thanks [@peterkogo](https://github.com/peterkogo)! - Fix connection snapping for handles larger than connectionRadius + +## 0.0.60 + +### Patch Changes + +- [#5259](https://github.com/xyflow/xyflow/pull/5259) [`77bf79c4`](https://github.com/xyflow/xyflow/commit/77bf79c40e71e3da449ace3b1a1ed5bceff46b51) Thanks [@peterkogo](https://github.com/peterkogo)! - Fix background-color css variable fallback. + +## 0.0.59 + +### Patch Changes + +- [#5255](https://github.com/xyflow/xyflow/pull/5255) [`a95f0e2f`](https://github.com/xyflow/xyflow/commit/a95f0e2fbfc2d070d9bd70b753d1606a87332e3f) Thanks [@peterkogo](https://github.com/peterkogo)! - Added OnReconnect types + +## 0.0.58 + +### Patch Changes + +- [#5252](https://github.com/xyflow/xyflow/pull/5252) [`2a03213b`](https://github.com/xyflow/xyflow/commit/2a03213b0695d504f831579ec9df3f9de2d3e0bd) Thanks [@moklick](https://github.com/moklick)! - Center panel correctly when bottom-center or top-center position is used + +## 0.0.57 + +### Patch Changes + +- [#5227](https://github.com/xyflow/xyflow/pull/5227) [`a7d10ffc`](https://github.com/xyflow/xyflow/commit/a7d10ffce5a0195471681980f97b1b5f6c448f35) Thanks [@moklick](https://github.com/moklick)! - add resizeDirection for XYResizer + +- [#5221](https://github.com/xyflow/xyflow/pull/5221) [`4e681f9c`](https://github.com/xyflow/xyflow/commit/4e681f9c529c3f4f8b2ac5d25b4db7878c197e14) Thanks [@moklick](https://github.com/moklick)! - Allow zero as a valid node width/height value + +## 0.0.56 + +### Patch Changes + +- [#5212](https://github.com/xyflow/xyflow/pull/5212) [`0f43b8ea`](https://github.com/xyflow/xyflow/commit/0f43b8ea45bd293e50e4a86d83868074bb323f13) Thanks [@moklick](https://github.com/moklick)! - Add polyfill for `Promise.withResolvers` + +- [#5192](https://github.com/xyflow/xyflow/pull/5192) [`fc241253`](https://github.com/xyflow/xyflow/commit/fc241253d5dba35f5febf411e77dbc5acb91d5d7) Thanks [@peterkogo](https://github.com/peterkogo)! - Optimize performance of nodesInitialized + +- [#5153](https://github.com/xyflow/xyflow/pull/5153) [`98fe23c7`](https://github.com/xyflow/xyflow/commit/98fe23c7c2b12972f1b7def866215ce82a86e2c0) Thanks [@nick-lawrence-ctm](https://github.com/nick-lawrence-ctm)! - Add separators to horizontal control buttons + +- [#5191](https://github.com/xyflow/xyflow/pull/5191) [`e5735b51`](https://github.com/xyflow/xyflow/commit/e5735b514a54d86ba0ca7bd725e8bfead89fc08e) Thanks [@peterkogo](https://github.com/peterkogo)! - Fix legacy padding being slightly larger than before + +## 0.0.55 + +### Patch Changes + +- [#5156](https://github.com/xyflow/xyflow/pull/5156) [`02a3b746`](https://github.com/xyflow/xyflow/commit/02a3b74645799a3f0ce670b69365fa86ecb0616e) Thanks [@dimaMachina](https://github.com/dimaMachina)! - Improve TSDoc comments for `type GetSmoothStepPathParams` and `getSmoothStepPath` function + +- [#5168](https://github.com/xyflow/xyflow/pull/5168) [`cbe305e1`](https://github.com/xyflow/xyflow/commit/cbe305e15a5c5d3b92583e0ec12364b2509f49bd) Thanks [@dimaMachina](https://github.com/dimaMachina)! - Improve TSDoc comments for `getOutgoers`, `getIncomers` and `type GetNodesBoundsParams` + +- [#5169](https://github.com/xyflow/xyflow/pull/5169) [`1f671bd4`](https://github.com/xyflow/xyflow/commit/1f671bd48f06230da841fdd1d7a312413ef16d03) Thanks [@dimaMachina](https://github.com/dimaMachina)! - Improve TSDoc comments for `type ReconnectEdgeOptions` and `getViewportForBounds` + +- [#5155](https://github.com/xyflow/xyflow/pull/5155) [`aaebc462`](https://github.com/xyflow/xyflow/commit/aaebc462951ded8e91374c3e084d77af5ed7380a) Thanks [@dimaMachina](https://github.com/dimaMachina)! - Improve TSDoc comments for `type GetStraightPathParams` and `getStraightPath` function + +- [#5157](https://github.com/xyflow/xyflow/pull/5157) [`6ec942fc`](https://github.com/xyflow/xyflow/commit/6ec942fc6501f81009c278cc995764bef3e8d03b) Thanks [@dimaMachina](https://github.com/dimaMachina)! - Improve TSDoc comments for `type GetBezierPathParams` and `getBezierPath` function + +## 0.0.54 + +### Patch Changes + +- [#5147](https://github.com/xyflow/xyflow/pull/5147) [`f819005b`](https://github.com/xyflow/xyflow/commit/f819005be362d044b16ce4c0b85432f3f300a13a) Thanks [@moklick](https://github.com/moklick)! - Pass dimensions to final resize change event + +- [#5142](https://github.com/xyflow/xyflow/pull/5142) [`24a1bc89`](https://github.com/xyflow/xyflow/commit/24a1bc89348817ed9b5c87f74bf2519c705143be) Thanks [@dimaMachina](https://github.com/dimaMachina)! - Improve TSDoc comments for `HandleProps`, `NodeBase` and `InternalNodeBase` + +- [#5136](https://github.com/xyflow/xyflow/pull/5136) [`36657cd6`](https://github.com/xyflow/xyflow/commit/36657cd66322c911e87eb37275c584a80025adfe) Thanks [@dimaMachina](https://github.com/dimaMachina)! - Improve TSDoc comments for `EdgeBase` + +- [#5139](https://github.com/xyflow/xyflow/pull/5139) [`89de9ca8`](https://github.com/xyflow/xyflow/commit/89de9ca83fbf9263a687a0f5f915efb2beb31654) Thanks [@dimaMachina](https://github.com/dimaMachina)! - Use `rgba` for colors with opacity instead of `rgb` for `MiniMap` mask color + +- [#5148](https://github.com/xyflow/xyflow/pull/5148) [`2ac6e155`](https://github.com/xyflow/xyflow/commit/2ac6e155e35256ca436281df16344366e7d05761) Thanks [@moklick](https://github.com/moklick)! - Prevent browser zoom for pinch zoom gestures on nowheel elements + +- [#5137](https://github.com/xyflow/xyflow/pull/5137) [`f0f378e5`](https://github.com/xyflow/xyflow/commit/f0f378e5b6918c2c30d9dc1e32587063cb942d4e) Thanks [@dimaMachina](https://github.com/dimaMachina)! - Improve TSDoc comments for `Connection` and `ConnectionInProgress` + +## 0.0.53 + +### Patch Changes + +- [#5118](https://github.com/xyflow/xyflow/pull/5118) [`5d15b01b`](https://github.com/xyflow/xyflow/commit/5d15b01ba8cb349d6397a6ed8162848b4dfec293) Thanks [@moklick](https://github.com/moklick)! - Do not swallow key events when a button is focused + +- [#5067](https://github.com/xyflow/xyflow/pull/5067) [`cb685281`](https://github.com/xyflow/xyflow/commit/cb685281d0eaf03e9833271c31f92b1d143af2fe) Thanks [@peterkogo](https://github.com/peterkogo)! - Fix fitView not working immediately after adding new nodes + +- [#5091](https://github.com/xyflow/xyflow/pull/5091) [`a79f30b3`](https://github.com/xyflow/xyflow/commit/a79f30b3dd7c8ff6400c8d22214b2c2282e5bac1) Thanks [@moklick](https://github.com/moklick)! - Add center-left and center-right as a panel position + +## 0.0.52 + +### Patch Changes + +- [#5052](https://github.com/xyflow/xyflow/pull/5052) [`99dd7d35`](https://github.com/xyflow/xyflow/commit/99dd7d3549e7423e7d103b2c956c8b37f5747b90) Thanks [@moklick](https://github.com/moklick)! - Show an error if user drags uninitialized node + +## 0.0.51 + +### Patch Changes + +- [#5010](https://github.com/xyflow/xyflow/pull/5010) [`6c121d42`](https://github.com/xyflow/xyflow/commit/6c121d427fea9a11e86a85f95d2c12ba8af34919) Thanks [@moklick](https://github.com/moklick)! - Add more TSDocs to components, hooks, utils funcs and types + +- [#4990](https://github.com/xyflow/xyflow/pull/4990) [`4947029c`](https://github.com/xyflow/xyflow/commit/4947029cd6cda0f695e1fb4815e4030adb232234) Thanks [@damianstasik](https://github.com/damianstasik)! - Make it possible to stop autoPanOnDrag by setting it to false + +- [#5003](https://github.com/xyflow/xyflow/pull/5003) [`e8e0d684`](https://github.com/xyflow/xyflow/commit/e8e0d684957b95d53a6cc11598c8755ff02117c7) Thanks [@dimaMachina](https://github.com/dimaMachina)! - repair lint command + +## 0.0.50 + +### Patch Changes + +- [#4957](https://github.com/xyflow/xyflow/pull/4957) [`fe843982`](https://github.com/xyflow/xyflow/commit/fe843982bfc7d7579d54772b201426b4c3f549c6) Thanks [@peterkogo](https://github.com/peterkogo)! - Narrow properties selected, selectable, deletable, draggable of NodeProps type to be required. + +- [#4968](https://github.com/xyflow/xyflow/pull/4968) [`e73ef09f`](https://github.com/xyflow/xyflow/commit/e73ef09fbc8d872b46cf52c9d6a32dbb388c220b) Thanks [@peterkogo](https://github.com/peterkogo)! - Make internal nodes immutable + +## 0.0.49 + +### Patch Changes + +- [#4949](https://github.com/xyflow/xyflow/pull/4949) [`592c7eaf`](https://github.com/xyflow/xyflow/commit/592c7eaf9574fc69df3123837da95f85877b23e8) Thanks [@peterkogo](https://github.com/peterkogo)! - Fix useNodeConnection hook not returning all connected edges. + +## 0.0.48 + +### Patch Changes + +- [#4880](https://github.com/xyflow/xyflow/pull/4880) [`e2d849dc`](https://github.com/xyflow/xyflow/commit/e2d849dca63aee5952f676aef1c675c6232bb69a) Thanks [@crimx](https://github.com/crimx)! - Add type check for all event targets + +- [#4725](https://github.com/xyflow/xyflow/pull/4725) [`e10f53cf`](https://github.com/xyflow/xyflow/commit/e10f53cf898a56f954783d6efcf6977a0d88f4a9) Thanks [@peterkogo](https://github.com/peterkogo)! - Add useNodeConnections hook to track all connections to a node. Can be filtered by handleType and handleId. + +- [#4929](https://github.com/xyflow/xyflow/pull/4929) [`4947f683`](https://github.com/xyflow/xyflow/commit/4947f683b7530f8e6684865ab53ea38633de0f4d) Thanks [@peterkogo](https://github.com/peterkogo)! - Optimize selections and take into account if edges connected to selected nodes are actually selectable. + +## 0.0.47 + +### Patch Changes + +- [#4874](https://github.com/xyflow/xyflow/pull/4874) [`d60331e6`](https://github.com/xyflow/xyflow/commit/d60331e6baa7931c46af219e35c1bedbd156187c) Thanks [@crimx](https://github.com/crimx)! - Refactor isInputDOMNode so that it handles non-html objects + +## 0.0.46 + +### Patch Changes + +- [#4790](https://github.com/xyflow/xyflow/pull/4790) [`2fa9a920`](https://github.com/xyflow/xyflow/commit/2fa9a92042ba11986abbababb7e8b294e208d6cb) Thanks [@peterkogo](https://github.com/peterkogo)! - Fix node dragging & resizing while zooming on flow that does not cover whole browser window. + +- [#4782](https://github.com/xyflow/xyflow/pull/4782) [`323e1b35`](https://github.com/xyflow/xyflow/commit/323e1b35c58bca80deb824bc8b136705593a5257) Thanks [@peterkogo](https://github.com/peterkogo)! - Fix node intersections in nested flow. + +## 0.0.45 + +### Patch Changes + +- [#4772](https://github.com/xyflow/xyflow/pull/4772) [`7f670ab0`](https://github.com/xyflow/xyflow/commit/7f670ab0423b3848a50398027297f6ec11deeaa4) Thanks [@mistic](https://github.com/mistic)! - Splits exports field in package.json to support an explicit resolution for browser, node and default to resolve issues with webpack esm module resolution. + +## 0.0.44 + +### Patch Changes + +- [#4755](https://github.com/xyflow/xyflow/pull/4755) [`005ae1c0`](https://github.com/xyflow/xyflow/commit/005ae1c05f6a10c1f519cd789f4f3f2fdf293bc6) Thanks [@peterkogo](https://github.com/peterkogo)! - Add module to exports in package.json. This should resolve possible issues with Webpack ESM Module Resolution. + +- [#4730](https://github.com/xyflow/xyflow/pull/4730) [`2c590b90`](https://github.com/xyflow/xyflow/commit/2c590b90787aabce42de2b4108174bdf31ad6155) Thanks [@peterkogo](https://github.com/peterkogo)! - Fixed rare crash while dragging + +## 0.0.43 + +### Patch Changes + +- [#4681](https://github.com/xyflow/xyflow/pull/4681) [`99ba64ac`](https://github.com/xyflow/xyflow/commit/99ba64ac2e1ce9c5ac3cab85a3d574edc0ecf4cc) Thanks [@moklick](https://github.com/moklick)! - Fix an issue that appeared when user deleted node while dragging. + +- [#4670](https://github.com/xyflow/xyflow/pull/4670) [`b056564c`](https://github.com/xyflow/xyflow/commit/b056564c9658bb43b882eebfad5a7e224717ffb5) Thanks [@peterkogo](https://github.com/peterkogo)! - Fix initial `fitView` not working correctly for `nodeOrigin` other than [0,0] + ## 0.0.42 ### Patch Changes diff --git a/packages/system/package.json b/packages/system/package.json index 8b072334..3ed062f2 100644 --- a/packages/system/package.json +++ b/packages/system/package.json @@ -1,6 +1,6 @@ { "name": "@xyflow/system", - "version": "0.0.42", + "version": "0.0.61", "description": "xyflow core system that powers React Flow and Svelte Flow.", "keywords": [ "node-based UI", @@ -20,9 +20,17 @@ "types": "dist/esm/index.d.ts", "exports": { ".": { - "types": "./dist/esm/index.d.ts", - "import": "./dist/esm/index.mjs", - "require": "./dist/umd/index.js" + "node": { + "types": "./dist/esm/index.d.ts", + "module": "./dist/esm/index.js", + "require": "./dist/umd/index.js", + "import": "./dist/esm/index.mjs" + }, + "browser": { + "import": "./dist/esm/index.js", + "require": "./dist/umd/index.js" + }, + "default": "./dist/esm/index.js" } }, "sideEffects": false, @@ -43,10 +51,12 @@ }, "dependencies": { "@types/d3-drag": "^3.0.7", + "@types/d3-interpolate": "^3.0.4", "@types/d3-selection": "^3.0.10", "@types/d3-transition": "^3.0.8", "@types/d3-zoom": "^3.0.8", "d3-drag": "^3.0.0", + "d3-interpolate": "^3.0.1", "d3-selection": "^3.0.0", "d3-zoom": "^3.0.0" }, @@ -55,7 +65,7 @@ "@xyflow/eslint-config": "workspace:*", "@xyflow/rollup-config": "workspace:*", "@xyflow/tsconfig": "workspace:*", - "typescript": "5.1.3" + "typescript": "5.4.5" }, "rollup": { "globals": { diff --git a/packages/system/src/constants.ts b/packages/system/src/constants.ts index e108b761..9bb65656 100644 --- a/packages/system/src/constants.ts +++ b/packages/system/src/constants.ts @@ -24,6 +24,10 @@ export const errorMessages = { `Node with id "${id}" does not exist, it may have been removed. This can happen when a node is deleted before the "onNodeClick" handler is called.`, error013: (lib: string = 'react') => `It seems that you haven't loaded the styles. Please import '@xyflow/${lib}/dist/style.css' or base.css to make sure everything is working properly.`, + error014: () => + 'useNodeConnections: No node ID found. Call useNodeConnections inside a custom Node or provide a node ID.', + error015: () => + 'It seems that you are trying to drag a node that is not initialized. Please use onNodesChange as explained in the docs.', }; export const infiniteExtent: CoordinateExtent = [ diff --git a/packages/system/src/styles/init.css b/packages/system/src/styles/init.css index e167c53f..7b4b92be 100644 --- a/packages/system/src/styles/init.css +++ b/packages/system/src/styles/init.css @@ -13,7 +13,7 @@ --xy-attribution-background-color-default: rgba(255, 255, 255, 0.5); --xy-minimap-background-color-default: #fff; - --xy-minimap-mask-background-color-default: rgb(240, 240, 240, 0.6); + --xy-minimap-mask-background-color-default: rgba(240, 240, 240, 0.6); --xy-minimap-mask-stroke-color-default: transparent; --xy-minimap-mask-stroke-width-default: 1; --xy-minimap-node-background-color-default: #e2e2e2; @@ -37,7 +37,7 @@ --xy-attribution-background-color-default: rgba(150, 150, 150, 0.25); --xy-minimap-background-color-default: #141414; - --xy-minimap-mask-background-color-default: rgb(60, 60, 60, 0.6); + --xy-minimap-mask-background-color-default: rgba(60, 60, 60, 0.6); --xy-minimap-mask-stroke-color-default: transparent; --xy-minimap-mask-stroke-width-default: 1; --xy-minimap-node-background-color-default: #2b2b2b; @@ -55,7 +55,7 @@ } .xy-flow__background { - background-color: var(--xy-background-color, var(--xy-background-color-props, var(--xy-background-color-default))); + background-color: var(--xy-background-color-props, var(--xy-background-color, var(--xy-background-color-default))); } .xy-flow__container { @@ -278,6 +278,14 @@ svg.xy-flow__connectionline { bottom: 0; } + &.top, + &.bottom { + &.center { + left: 50%; + transform: translateX(-15px) translateX(-50%); + } + } + &.left { left: 0; } @@ -286,9 +294,12 @@ svg.xy-flow__connectionline { right: 0; } - &.center { - left: 50%; - transform: translateX(-50%); + &.left, + &.right { + &.center { + top: 50%; + transform: translateY(-15px) translateY(-50%); + } } } diff --git a/packages/system/src/styles/style.css b/packages/system/src/styles/style.css index cb6f2b7c..a6e5f9da 100644 --- a/packages/system/src/styles/style.css +++ b/packages/system/src/styles/style.css @@ -158,4 +158,17 @@ &-button:last-child { border-bottom: none; } + + &.horizontal &-button { + border-bottom: none; + border-right: 1px solid + var( + --xy-controls-button-border-color-props, + var(--xy-controls-button-border-color, var(--xy-controls-button-border-color-default)) + ); + } + + &.horizontal &-button:last-child { + border-right: none; + } } diff --git a/packages/system/src/types/changes.ts b/packages/system/src/types/changes.ts index 9b232733..999f2cf2 100644 --- a/packages/system/src/types/changes.ts +++ b/packages/system/src/types/changes.ts @@ -7,7 +7,7 @@ export type NodeDimensionChange = { /* if this is true, the node is currently being resized via the NodeResizer */ resizing?: boolean; /* if this is true, we will set width and height of the node and not just the measured dimensions */ - setAttributes?: boolean; + setAttributes?: boolean | 'width' | 'height'; }; export type NodePositionChange = { @@ -42,7 +42,10 @@ export type NodeReplaceChange = { }; /** - * Union type of all possible node changes. + * The [`onNodesChange`](/api-reference/react-flow#on-nodes-change) callback takes + *an array of `NodeChange` objects that you should use to update your flow's state. + *The `NodeChange` type is a union of six different object types that represent that + *various ways an node can change in a flow. * @public */ export type NodeChange = @@ -67,6 +70,14 @@ export type EdgeReplaceChange = { type: 'replace'; }; +/** + * The [`onEdgesChange`](/api-reference/react-flow#on-edges-change) callback takes + *an array of `EdgeChange` objects that you should use to update your flow's state. + *The `EdgeChange` type is a union of four different object types that represent that + *various ways an edge can change in a flow. + * + * @public + */ export type EdgeChange = | EdgeSelectionChange | EdgeRemoveChange diff --git a/packages/system/src/types/edges.ts b/packages/system/src/types/edges.ts index 39dc97bd..c0568200 100644 --- a/packages/system/src/types/edges.ts +++ b/packages/system/src/types/edges.ts @@ -4,40 +4,41 @@ export type EdgeBase< EdgeData extends Record = Record, EdgeType extends string | undefined = string | undefined > = { - /** Unique id of an edge */ + /** Unique id of an edge. */ id: string; - /** Type of an edge defined in edgeTypes */ + /** Type of edge defined in `edgeTypes`. */ type?: EdgeType; - /** Id of source node */ + /** Id of source node. */ source: string; - /** Id of target node */ + /** Id of target node. */ target: string; - /** Id of source handle - * only needed if there are multiple handles per node - */ + /** Id of source handle, only needed if there are multiple handles per node. */ sourceHandle?: string | null; - /** Id of target handle - * only needed if there are multiple handles per node - */ + /** Id of target handle, only needed if there are multiple handles per node. */ targetHandle?: string | null; animated?: boolean; hidden?: boolean; deletable?: boolean; selectable?: boolean; - /** Arbitrary data passed to an edge */ + /** Arbitrary data passed to an edge. */ data?: EdgeData; selected?: boolean; - /** Set the marker on the beginning of an edge + /** + * Set the marker on the beginning of an edge. * @example 'arrow', 'arrowclosed' or custom marker */ markerStart?: EdgeMarkerType; - /** Set the marker on the end of an edge + /** + * Set the marker on the end of an edge. * @example 'arrow', 'arrowclosed' or custom marker */ markerEnd?: EdgeMarkerType; zIndex?: number; ariaLabel?: string; - /** Padding around the edge where interaction is still possible */ + /** + * ReactFlow renders an invisible path around each edge to make them easier to click or tap on. + * This property sets the width of that invisible path. + */ interactionWidth?: number; }; @@ -54,11 +55,24 @@ export type BezierPathOptions = { curvature?: number; }; +/** + * @inline + */ export type DefaultEdgeOptionsBase = Omit< EdgeType, 'id' | 'source' | 'target' | 'sourceHandle' | 'targetHandle' | 'selected' >; +/** + * If you set the `connectionLineType` prop on your [``](/api-reference/react-flow#connection-connectionLineType) + *component, it will dictate the style of connection line rendered when creating + *new edges. + * + * @public + * + * @remarks If you choose to render a custom connection line component, this value will be + *passed to your component as part of its [`ConnectionLineComponentProps`](/api-reference/types/connection-line-component-props). + */ export enum ConnectionLineType { Bezier = 'default', Straight = 'straight', @@ -67,6 +81,13 @@ export enum ConnectionLineType { SimpleBezier = 'simplebezier', } +/** + * Edges can optionally have markers at the start and end of an edge. The `EdgeMarker` + *type is used to configure those markers! Check the docs for [`MarkerType`](/api-reference/types/marker-type) + *for details on what types of edge marker are available. + * + * @public + */ export type EdgeMarker = { type: MarkerType; color?: string; @@ -79,6 +100,12 @@ export type EdgeMarker = { export type EdgeMarkerType = string | EdgeMarker; +/** + * Edges may optionally have a marker on either end. The MarkerType type enumerates + * the options available to you when configuring a given marker. + * + * @public + */ export enum MarkerType { Arrow = 'arrow', ArrowClosed = 'arrowclosed', @@ -88,6 +115,9 @@ export type MarkerProps = EdgeMarker & { id: string; }; +/** + * @inline + */ export type EdgePosition = { sourceX: number; sourceY: number; diff --git a/packages/system/src/types/general.ts b/packages/system/src/types/general.ts index 98d8afb6..4e7cf910 100644 --- a/packages/system/src/types/general.ts +++ b/packages/system/src/types/general.ts @@ -2,8 +2,7 @@ import type { Selection as D3Selection } from 'd3-selection'; import type { D3DragEvent, SubjectPosition } from 'd3-drag'; import type { ZoomBehavior } from 'd3-zoom'; -// this is needed for the Selection type to include the transition function :/ -// eslint-disable-next-line @typescript-eslint/no-unused-vars +// eslint-disable-next-line @typescript-eslint/no-unused-vars -- this is needed for the Selection type to include the transition function :/ import type { Transition } from 'd3-transition'; import type { XYPosition, Rect, Position } from './utils'; @@ -26,17 +25,46 @@ export type SetViewport = (viewport: Viewport, options?: ViewportHelperFunctionO export type SetCenter = (x: number, y: number, options?: SetCenterOptions) => Promise; export type FitBounds = (bounds: Rect, options?: FitBoundsOptions) => Promise; +/** + * The `Connection` type is the basic minimal description of an [`Edge`](/api-reference/types/edge) + * between two nodes. The [`addEdge`](/api-reference/utils/add-edge) util can be used to upgrade + * a `Connection` to an [`Edge`](/api-reference/types/edge). + * + * @public + */ export type Connection = { + /** The id of the node this connection originates from. */ source: string; + /** The id of the node this connection terminates at. */ target: string; + /** When not `null`, the id of the handle on the source node that this connection originates from. */ sourceHandle: string | null; + /** When not `null`, the id of the handle on the target node that this connection terminates at. */ targetHandle: string | null; }; +/** + * The `HandleConnection` type is an extension of a basic [Connection](/api-reference/types/connection) that includes the `edgeId`. + */ export type HandleConnection = Connection & { edgeId: string; }; +/** + * The `NodeConnection` type is an extension of a basic [Connection](/api-reference/types/connection) that includes the `edgeId`. + * + */ +export type NodeConnection = Connection & { + edgeId: string; +}; + +/** + * The `ConnectionMode` is used to set the mode of connection between nodes. + * The `Strict` mode is the default one and only allows source to target edges. + * `Loose` mode allows source to source and target to target edges as well. + * + * @public + */ export enum ConnectionMode { Strict = 'strict', Loose = 'loose', @@ -52,8 +80,24 @@ export type OnConnectStart = (event: MouseEvent | TouchEvent, params: OnConnectS export type OnConnect = (connection: Connection) => void; export type OnConnectEnd = (event: MouseEvent | TouchEvent, connectionState: FinalConnectionState) => void; +export type OnReconnect = (oldEdge: EdgeType, newConnection: Connection) => void; +export type OnReconnectStart = ( + event: MouseEvent | TouchEvent, + edge: EdgeType, + handleType: HandleType +) => void; +export type OnReconnectEnd = ( + event: MouseEvent | TouchEvent, + edge: EdgeType, + handleType: HandleType, + connectionState: FinalConnectionState +) => void; + export type IsValidConnection = (edge: EdgeBase | Connection) => boolean; +/** + * @inline + */ export type FitViewParamsBase = { nodes: Map>; width: number; @@ -63,15 +107,45 @@ export type FitViewParamsBase = { 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 + */ export type FitViewOptionsBase = { - padding?: number; + padding?: Padding; includeHiddenNodes?: boolean; minZoom?: number; maxZoom?: number; duration?: number; + ease?: (t: number) => number; + interpolate?: 'smooth' | 'linear'; nodes?: (NodeType | { id: string })[]; }; +/** + * Internally, React Flow maintains a coordinate system that is independent of the + * rest of the page. The `Viewport` type tells you where in that system your flow + * is currently being display at and how zoomed in or out it is. + * + * @public + * @remarks A `Transform` has the same properties as the viewport, but they represent + * different things. Make sure you don't get them muddled up or things will start + * to look weird! + * + */ export type Viewport = { x: number; y: number; @@ -82,14 +156,27 @@ export type KeyCode = string | Array; export type SnapGrid = [number, number]; +/** + * This enum is used to set the different modes of panning the viewport when the + * user scrolls. The `Free` mode allows the user to pan in any direction by scrolling + * with a device like a trackpad. The `Vertical` and `Horizontal` modes restrict + * scroll panning to only the vertical or horizontal axis, respectively. + * + * @public + */ export enum PanOnScrollMode { Free = 'free', Vertical = 'vertical', Horizontal = 'horizontal', } +/** + * @inline + */ export type ViewportHelperFunctionOptions = { duration?: number; + ease?: (t: number) => number; + interpolate?: 'smooth' | 'linear'; }; export type SetCenterOptions = ViewportHelperFunctionOptions & { @@ -108,7 +195,23 @@ export type D3ZoomHandler = (this: Element, event: any, d: unknown) => void; export type UpdateNodeInternals = (nodeId: string | string[]) => void; -export type PanelPosition = 'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right'; +/** + * This type is mostly used to help position things on top of the flow viewport. For + * example both the [``](/api-reference/components/minimap) and + * [``](/api-reference/components/controls) components take a `position` + * prop of this type. + * + * @public + */ +export type PanelPosition = + | 'top-left' + | 'top-center' + | 'top-right' + | 'bottom-left' + | 'bottom-center' + | 'bottom-right' + | 'center-left' + | 'center-right'; export type ProOptions = { account?: string; @@ -158,17 +261,37 @@ export type NoConnection = { toNode: null; }; export type ConnectionInProgress = { + /** Indicates whether a connection is currently in progress. */ inProgress: true; + /** + * If an ongoing connection is above a handle or inside the connection radius, this will be `true` + * or `false`, otherwise `null`. + */ isValid: boolean | null; + /** Returns the xy start position or `null` if no connection is in progress. */ from: XYPosition; + /** Returns the start handle or `null` if no connection is in progress. */ fromHandle: Handle; + /** Returns the side (called position) of the start handle or `null` if no connection is in progress. */ fromPosition: Position; + /** Returns the start node or `null` if no connection is in progress. */ fromNode: NodeType; + /** Returns the xy end position or `null` if no connection is in progress. */ to: XYPosition; + /** Returns the end handle or `null` if no connection is in progress. */ toHandle: Handle | null; + /** Returns the side (called position) of the end handle or `null` if no connection is in progress. */ toPosition: Position; + /** Returns the end node or `null` if no connection is in progress. */ toNode: NodeType | null; }; + +/** + * The `ConnectionState` type bundles all information about an ongoing connection. + * It is returned by the [`useConnection`](/api-reference/hooks/use-connection) hook. + * + * @public + */ export type ConnectionState = | ConnectionInProgress | NoConnection; diff --git a/packages/system/src/types/handles.ts b/packages/system/src/types/handles.ts index f9828dd5..a8b51aa3 100644 --- a/packages/system/src/types/handles.ts +++ b/packages/system/src/types/handles.ts @@ -14,27 +14,45 @@ export type Handle = { }; export type HandleProps = { - /** Type of the handle + /** + * Type of the handle. + * @default "source" * @example HandleType.Source, HandleType.Target */ type: HandleType; - /** Position of the handle - * @example Position.TopLeft, Position.TopRight, - * Position.BottomLeft, Position.BottomRight + /** + * The position of the handle relative to the node. In a horizontal flow source handles are + * typically `Position.Right` and in a vertical flow they are typically `Position.Top`. + * @default Position.Top + * @example Position.TopLeft, Position.TopRight, Position.BottomLeft, Position.BottomRight */ position: Position; - /** Should you be able to connect to/from this handle */ + /** + * Should you be able to connect to/from this handle. + * @default true + */ isConnectable?: boolean; - /** Should you be able to connect from this handle */ + /** + * Dictates whether a connection can start from this handle. + * @default true + */ isConnectableStart?: boolean; - /** Should you be able to connect to this handle */ + /** + * Dictates whether a connection can end on this handle. + * @default true + */ isConnectableEnd?: boolean; - /** Callback if connection is valid + /** + * Called when a connection is dragged to this handle. You can use this callback to perform some + * custom validation logic based on the connection target and source, for example. Where possible, + * we recommend you move this logic to the `isValidConnection` prop on the main ReactFlow + * component for performance reasons. * @remarks connection becomes an edge if isValidConnection returns true */ isValidConnection?: IsValidConnection; - /** Id of the handle + /** + * Id of the handle. * @remarks optional if there is only one handle of this type */ - id?: string; + id?: string | null; }; diff --git a/packages/system/src/types/nodes.ts b/packages/system/src/types/nodes.ts index b1d93369..687f81ba 100644 --- a/packages/system/src/types/nodes.ts +++ b/packages/system/src/types/nodes.ts @@ -4,6 +4,7 @@ import { Optional } from '../utils/types'; /** * Framework independent node data structure. * + * @inline * @typeParam NodeData - type of the node data * @typeParam NodeType - type of the node */ @@ -11,45 +12,60 @@ export type NodeBase< NodeData extends Record = Record, NodeType extends string | undefined = string | undefined > = { - /** Unique id of a node */ + /** Unique id of a node. */ id: string; - /** Position of a node on the pane + /** + * Position of a node on the pane. * @example { x: 0, y: 0 } */ position: XYPosition; - /** Arbitrary data passed to a node */ + /** Arbitrary data passed to a node. */ data: NodeData; - /** 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' */ sourcePosition?: Position; - /** Only relevant for default, source, target nodeType. controls target position + /** + * Only relevant for default, source, target nodeType. Controls target position. * @example 'right', 'left', 'top', 'bottom' */ targetPosition?: Position; + /** Whether or not the node should be visible on the canvas. */ hidden?: boolean; selected?: boolean; - /** True, if node is being dragged */ + /** Whether or not the node is currently being dragged. */ dragging?: boolean; + /** Whether or not the node is able to be dragged. */ draggable?: boolean; selectable?: boolean; connectable?: boolean; deletable?: boolean; + /** + * A class name that can be applied to elements inside the node that allows those elements to act + * as drag handles, letting the user drag the node by clicking and dragging on those elements. + */ dragHandle?: string; width?: number; height?: number; initialWidth?: number; initialHeight?: number; - /** Parent node id, used for creating sub-flows */ + /** Parent node id, used for creating sub-flows. */ parentId?: string; zIndex?: number; - /** Boundary a node can be moved in + /** + * Boundary a node can be moved in. * @example 'parent' or [[0, 0], [100, 100]] */ extent?: 'parent' | CoordinateExtent; + /** + * When `true`, the parent node will automatically expand if this node is dragged to the edge of + * the parent node's bounds. + */ expandParent?: boolean; ariaLabel?: string; - /** Origin of the node relative to it's position + /** + * Origin of the node relative to its position. * @example * [0.5, 0.5] // centers the node * [0, 0] // top left @@ -71,7 +87,7 @@ export type NodeBase< type: NodeType; }); -export type InternalNodeBase = NodeType & { +export type InternalNodeBase = Omit & { measured: { width?: number; height?: number; @@ -79,8 +95,10 @@ export type InternalNodeBase = NodeType & internals: { positionAbsolute: XYPosition; z: number; - /** Holds a reference to the original node object provided by the user. - * Used as an optimization to avoid certain operations. */ + /** + * Holds a reference to the original node object provided by the user. + * Used as an optimization to avoid certain operations. + */ userNode: NodeType; handleBounds?: NodeHandleBounds; bounds?: NodeBounds; @@ -88,31 +106,20 @@ export type InternalNodeBase = NodeType & }; /** - * The node data structure that gets used for the nodes prop. + * The node data structure that gets used for the custom nodes props. * * @public */ export type NodeProps = Pick< NodeType, - | 'id' - | 'data' - | 'width' - | 'height' - | 'sourcePosition' - | 'targetPosition' - | 'selected' - | 'dragHandle' - | 'selectable' - | 'deletable' - | 'draggable' - | 'parentId' + 'id' | 'data' | 'width' | 'height' | 'sourcePosition' | 'targetPosition' | 'dragHandle' | 'parentId' > & - Required> & { - /** whether a node is connectable or not */ + Required> & { + /** Whether a node is connectable or not. */ isConnectable: boolean; - /** position absolute x value */ + /** Position absolute x value. */ positionAbsoluteX: number; - /** position absolute x value */ + /** Position absolute y value. */ positionAbsoluteY: number; }; @@ -151,10 +158,22 @@ export type NodeDragItem = { expandParent?: boolean; }; +/** + * The origin of a Node determines how it is placed relative to its own coordinates. + * `[0, 0]` places it at the top left corner, `[0.5, 0.5]` right in the center and + * `[1, 1]` at the bottom right of its position. + * + * @public + */ export type NodeOrigin = [number, number]; export type OnSelectionDrag = (event: MouseEvent, nodes: NodeBase[]) => void; +/** + * Type for the handles of a node + * + * @public + */ export type NodeHandle = Omit, 'nodeId'>; export type Align = 'center' | 'start' | 'end'; diff --git a/packages/system/src/types/panzoom.ts b/packages/system/src/types/panzoom.ts index a2577505..ae968fc8 100644 --- a/packages/system/src/types/panzoom.ts +++ b/packages/system/src/types/panzoom.ts @@ -20,6 +20,8 @@ export type PanZoomParams = { export type PanZoomTransformOptions = { duration?: number; + ease?: (t: number) => number; + interpolate?: 'smooth' | 'linear'; }; export type OnPanZoom = (event: MouseEvent | TouchEvent | null, viewport: Viewport) => void; diff --git a/packages/system/src/types/utils.ts b/packages/system/src/types/utils.ts index b06786a7..274bfe55 100644 --- a/packages/system/src/types/utils.ts +++ b/packages/system/src/types/utils.ts @@ -1,3 +1,10 @@ +/** + * While [`PanelPosition`](/api-reference/types/panel-position) can be used to place a + * component in the corners of a container, the `Position` enum is less precise and used + * primarily in relation to edges and handles. + * + * @public + */ export enum Position { Left = 'left', Top = 'top', @@ -12,6 +19,11 @@ export const oppositePosition = { [Position.Bottom]: Position.Top, }; +/** + * All positions are stored in an object with x and y coordinates. + * + * @public + */ export type XYPosition = { x: number; y: number; @@ -33,4 +45,14 @@ export type Box = XYPosition & { export type Transform = [number, number, number]; +/** + * A coordinate extent represents two points in a coordinate system: one in the top + * left corner and one in the bottom right corner. It is used to represent the + * bounds of nodes in the flow or the bounds of the viewport. + * + * @public + * + * @remarks Props that expect a `CoordinateExtent` usually default to `[[-∞, -∞], [+∞, +∞]]` + * to represent an unbounded extent. + */ export type CoordinateExtent = [[number, number], [number, number]]; diff --git a/packages/system/src/utils/dom.ts b/packages/system/src/utils/dom.ts index 2cd2bf5c..2311153f 100644 --- a/packages/system/src/utils/dom.ts +++ b/packages/system/src/utils/dom.ts @@ -5,14 +5,18 @@ export type GetPointerPositionParams = { transform: Transform; snapGrid?: SnapGrid; snapToGrid?: boolean; + containerBounds: DOMRect | null; }; export function getPointerPosition( event: MouseEvent | TouchEvent, - { snapGrid = [0, 0], snapToGrid = false, transform }: GetPointerPositionParams + { snapGrid = [0, 0], snapToGrid = false, transform, containerBounds }: GetPointerPositionParams ): XYPosition & { xSnapped: number; ySnapped: number } { const { x, y } = getEventPosition(event); - const pointerPos = pointToRendererPoint({ x, y }, transform); + const pointerPos = pointToRendererPoint( + { x: x - (containerBounds?.left ?? 0), y: y - (containerBounds?.top ?? 0) }, + transform + ); const { x: xSnapped, y: ySnapped } = snapToGrid ? snapPosition(pointerPos, snapGrid) : pointerPos; // we need the snapped position in order to be able to skip unnecessary drag events @@ -28,18 +32,20 @@ export const getDimensions = (node: HTMLDivElement): Dimensions => ({ height: node.offsetHeight, }); -export const getHostForElement = (element: HTMLElement): Document | ShadowRoot => - (element.getRootNode?.() as Document | ShadowRoot) || window?.document; +export const getHostForElement = (element: HTMLElement | EventTarget | null): Document | ShadowRoot => + ((element as Partial | null)?.getRootNode?.() as Document | ShadowRoot) || window?.document; const inputTags = ['INPUT', 'SELECT', 'TEXTAREA']; export function isInputDOMNode(event: KeyboardEvent): boolean { // using composed path for handling shadow dom - const target = (event.composedPath?.()?.[0] || event.target) as HTMLElement; - const isInput = inputTags.includes(target?.nodeName) || target?.hasAttribute('contenteditable'); + const target = (event.composedPath?.()?.[0] || event.target) as Element | null; + if (target?.nodeType !== 1 /* Node.ELEMENT_NODE */) return false; + + const isInput = inputTags.includes(target.nodeName) || target.hasAttribute('contenteditable'); // when an input field is focused we don't want to trigger deletion or movement of nodes - return isInput || !!target?.closest('.nokey'); + return isInput || !!target.closest('.nokey'); } export const isMouseEvent = (event: MouseEvent | TouchEvent): event is MouseEvent => 'clientX' in event; @@ -55,9 +61,11 @@ export const getEventPosition = (event: MouseEvent | TouchEvent, bounds?: DOMRec }; }; -// The handle bounds are calculated relative to the node element. -// We store them in the internals object of the node in order to avoid -// unnecessary recalculations. +/* + * The handle bounds are calculated relative to the node element. + * We store them in the internals object of the node in order to avoid + * unnecessary recalculations. + */ export const getHandleBounds = ( type: 'source' | 'target', nodeElement: HTMLDivElement, diff --git a/packages/system/src/utils/edges/bezier-edge.ts b/packages/system/src/utils/edges/bezier-edge.ts index 640deffa..d5c0b6bb 100644 --- a/packages/system/src/utils/edges/bezier-edge.ts +++ b/packages/system/src/utils/edges/bezier-edge.ts @@ -1,12 +1,28 @@ import { Position } from '../../types'; export type GetBezierPathParams = { + /** The `x` position of the source handle. */ sourceX: number; + /** The `y` position of the source handle. */ sourceY: number; + /** + * The position of the source handle. + * @default Position.Bottom + */ sourcePosition?: Position; + /** The `x` position of the target handle. */ targetX: number; + /** The `y` position of the target handle. */ targetY: number; + /** + * The position of the target handle. + * @default Position.Top + */ targetPosition?: Position; + /** + * The curvature of the bezier edge. + * @default 0.25 + */ curvature?: number; }; @@ -38,8 +54,10 @@ export function getBezierEdgeCenter({ targetControlX: number; targetControlY: number; }): [number, number, number, number] { - // cubic bezier t=0.5 mid point, not the actual mid point, but easy to calculate - // https://stackoverflow.com/questions/67516101/how-to-find-distance-mid-point-of-bezier-curve + /* + * cubic bezier t=0.5 mid point, not the actual mid point, but easy to calculate + * https://stackoverflow.com/questions/67516101/how-to-find-distance-mid-point-of-bezier-curve + */ const centerX = sourceX * 0.125 + sourceControlX * 0.375 + targetControlX * 0.375 + targetX * 0.125; const centerY = sourceY * 0.125 + sourceControlY * 0.375 + targetControlY * 0.375 + targetY * 0.125; const offsetX = Math.abs(centerX - sourceX); @@ -70,27 +88,35 @@ function getControlWithCurvature({ pos, x1, y1, x2, y2, c }: GetControlWithCurva } /** - * Get a bezier path from source to target handle - * @param params.sourceX - The x position of the source handle - * @param params.sourceY - The y position of the source handle - * @param params.sourcePosition - The position of the source handle (default: Position.Bottom) - * @param params.targetX - The x position of the target handle - * @param params.targetY - The y position of the target handle - * @param params.targetPosition - The position of the target handle (default: Position.Top) - * @param params.curvature - The curvature of the bezier edge - * @returns A path string you can use in an SVG, the labelX and labelY position (center of path) and offsetX, offsetY between source handle and label + * The `getBezierPath` util returns everything you need to render a bezier edge + *between two nodes. + * @public + * @returns A path string you can use in an SVG, the `labelX` and `labelY` position (center of path) + * and `offsetX`, `offsetY` between source handle and label. + * - `path`: the path to use in an SVG `` element. + * - `labelX`: the `x` position you can use to render a label for this edge. + * - `labelY`: the `y` position you can use to render a label for this edge. + * - `offsetX`: the absolute difference between the source `x` position and the `x` position of the + * middle of this path. + * - `offsetY`: the absolute difference between the source `y` position and the `y` position of the + * middle of this path. * @example + * ```js * const source = { x: 0, y: 20 }; - const target = { x: 150, y: 100 }; - - const [path, labelX, labelY, offsetX, offsetY] = getBezierPath({ - sourceX: source.x, - sourceY: source.y, - sourcePosition: Position.Right, - targetX: target.x, - targetY: target.y, - targetPosition: Position.Left, -}); + * const target = { x: 150, y: 100 }; + * + * const [path, labelX, labelY, offsetX, offsetY] = getBezierPath({ + * sourceX: source.x, + * sourceY: source.y, + * sourcePosition: Position.Right, + * targetX: target.x, + * targetY: target.y, + * targetPosition: Position.Left, + *}); + *``` + * + * @remarks This function returns a tuple (aka a fixed-size array) to make it easier to + *work with multiple edge paths at once. */ export function getBezierPath({ sourceX, diff --git a/packages/system/src/utils/edges/general.ts b/packages/system/src/utils/edges/general.ts index 7dfa4b44..c094ab0b 100644 --- a/packages/system/src/utils/edges/general.ts +++ b/packages/system/src/utils/edges/general.ts @@ -90,12 +90,16 @@ const connectionExists = (edge: EdgeBase, edges: EdgeBase[]) => { }; /** - * This util is a convenience function to add a new Edge to an array of edges - * @remarks It also performs some validation to make sure you don't add an invalid edge or duplicate an existing one. + * This util is a convenience function to add a new Edge to an array of edges. It also performs some validation to make sure you don't add an invalid edge or duplicate an existing one. * @public - * @param edgeParams - Either an Edge or a Connection you want to add - * @param edges - The array of all current edges - * @returns A new array of edges with the new edge added + * @param edgeParams - Either an `Edge` or a `Connection` you want to add. + * @param edges - The array of all current edges. + * @returns A new array of edges with the new edge added. + * + * @remarks If an edge with the same `target` and `source` already exists (and the same + *`targetHandle` and `sourceHandle` if those are set), then this util won't add + *a new edge even if the `id` property is different. + * */ export const addEdge = ( edgeParams: EdgeType | Connection, @@ -133,16 +137,28 @@ export const addEdge = ( }; export type ReconnectEdgeOptions = { + /** + * Should the id of the old edge be replaced with the new connection id. + * @default true + */ shouldReplaceId?: boolean; }; /** - * A handy utility to reconnect an existing edge with new properties - * @param oldEdge - The edge you want to update - * @param newConnection - The new connection you want to update the edge with - * @param edges - The array of all current edges - * @param options.shouldReplaceId - should the id of the old edge be replaced with the new connection id - * @returns the updated edges array + * A handy utility to update an existing [`Edge`](/api-reference/types/edge) with new properties. + *This searches your edge array for an edge with a matching `id` and updates its + *properties with the connection you provide. + * @public + * @param oldEdge - The edge you want to update. + * @param newConnection - The new connection you want to update the edge with. + * @param edges - The array of all current edges. + * @returns The updated edges array. + * + * @example + * ```js + *const onReconnect = useCallback( + * (oldEdge: Edge, newConnection: Connection) => setEdges((els) => reconnectEdge(oldEdge, newConnection, els)),[]); + *``` */ export const reconnectEdge = ( oldEdge: EdgeType, diff --git a/packages/system/src/utils/edges/smoothstep-edge.ts b/packages/system/src/utils/edges/smoothstep-edge.ts index 5e57efcd..b10c5a07 100644 --- a/packages/system/src/utils/edges/smoothstep-edge.ts +++ b/packages/system/src/utils/edges/smoothstep-edge.ts @@ -2,15 +2,29 @@ import { getEdgeCenter } from './general'; import { Position, type XYPosition } from '../../types'; export interface GetSmoothStepPathParams { + /** The `x` position of the source handle. */ sourceX: number; + /** The `y` position of the source handle. */ sourceY: number; + /** + * The position of the source handle. + * @default Position.Bottom + */ sourcePosition?: Position; + /** The `x` position of the target handle. */ targetX: number; + /** The `y` position of the target handle. */ targetY: number; + /** + * The position of the target handle. + * @default Position.Top + */ targetPosition?: Position; + /** @default 5 */ borderRadius?: number; centerX?: number; centerY?: number; + /** @default 20 */ offset?: number; } @@ -38,8 +52,10 @@ const getDirection = ({ const distance = (a: XYPosition, b: XYPosition) => Math.sqrt(Math.pow(b.x - a.x, 2) + Math.pow(b.y - a.y, 2)); -// ith this function we try to mimic a orthogonal edge routing behaviour -// It's not as good as a real orthogonal edge routing but it's faster and good enough as a default for step and smooth step edges +/* + * With this function we try to mimic an orthogonal edge routing behaviour + * It's not as good as a real orthogonal edge routing, but it's faster and good enough as a default for step and smooth step edges + */ function getPoints({ source, sourcePosition = Position.Bottom, @@ -83,16 +99,20 @@ function getPoints({ if (sourceDir[dirAccessor] * targetDir[dirAccessor] === -1) { centerX = center.x ?? defaultCenterX; centerY = center.y ?? defaultCenterY; - // ---> - // | - // >--- + /* + * ---> + * | + * >--- + */ const verticalSplit: XYPosition[] = [ { x: centerX, y: sourceGapped.y }, { x: centerX, y: targetGapped.y }, ]; - // | - // --- - // | + /* + * | + * --- + * | + */ const horizontalSplit: XYPosition[] = [ { x: sourceGapped.x, y: centerY }, { x: targetGapped.x, y: centerY }, @@ -191,26 +211,35 @@ function getBend(a: XYPosition, b: XYPosition, c: XYPosition, size: number): str } /** - * Get a smooth step path from source to target handle - * @param params.sourceX - The x position of the source handle - * @param params.sourceY - The y position of the source handle - * @param params.sourcePosition - The position of the source handle (default: Position.Bottom) - * @param params.targetX - The x position of the target handle - * @param params.targetY - The y position of the target handle - * @param params.targetPosition - The position of the target handle (default: Position.Top) - * @returns A path string you can use in an SVG, the labelX and labelY position (center of path) and offsetX, offsetY between source handle and label + * The `getSmoothStepPath` util returns everything you need to render a stepped path + * between two nodes. The `borderRadius` property can be used to choose how rounded + * the corners of those steps are. + * @public + * @returns A path string you can use in an SVG, the `labelX` and `labelY` position (center of path) + * and `offsetX`, `offsetY` between source handle and label. + * + * - `path`: the path to use in an SVG `` element. + * - `labelX`: the `x` position you can use to render a label for this edge. + * - `labelY`: the `y` position you can use to render a label for this edge. + * - `offsetX`: the absolute difference between the source `x` position and the `x` position of the + * middle of this path. + * - `offsetY`: the absolute difference between the source `y` position and the `y` position of the + * middle of this path. * @example + * ```js * const source = { x: 0, y: 20 }; - const target = { x: 150, y: 100 }; - - const [path, labelX, labelY, offsetX, offsetY] = getSmoothStepPath({ - sourceX: source.x, - sourceY: source.y, - sourcePosition: Position.Right, - targetX: target.x, - targetY: target.y, - targetPosition: Position.Left, - }); + * const target = { x: 150, y: 100 }; + * + * const [path, labelX, labelY, offsetX, offsetY] = getSmoothStepPath({ + * sourceX: source.x, + * sourceY: source.y, + * sourcePosition: Position.Right, + * targetX: target.x, + * targetY: target.y, + * targetPosition: Position.Left, + * }); + * ``` + * @remarks This function returns a tuple (aka a fixed-size array) to make it easier to work with multiple edge paths at once. */ export function getSmoothStepPath({ sourceX, diff --git a/packages/system/src/utils/edges/straight-edge.ts b/packages/system/src/utils/edges/straight-edge.ts index 9940bfea..c5bc3db2 100644 --- a/packages/system/src/utils/edges/straight-edge.ts +++ b/packages/system/src/utils/edges/straight-edge.ts @@ -1,31 +1,44 @@ import { getEdgeCenter } from './general'; export type GetStraightPathParams = { + /** The `x` position of the source handle. */ sourceX: number; + /** The `y` position of the source handle. */ sourceY: number; + /** The `x` position of the target handle. */ targetX: number; + /** The `y` position of the target handle. */ targetY: number; }; /** - * Get a straight path from source to target handle - * @param params.sourceX - The x position of the source handle - * @param params.sourceY - The y position of the source handle - * @param params.targetX - The x position of the target handle - * @param params.targetY - The y position of the target handle - * @returns A path string you can use in an SVG, the labelX and labelY position (center of path) and offsetX, offsetY between source handle and label + * Calculates the straight line path between two points. + * @public + * @returns A path string you can use in an SVG, the `labelX` and `labelY` position (center of path) + * and `offsetX`, `offsetY` between source handle and label. + * + * - `path`: the path to use in an SVG `` element. + * - `labelX`: the `x` position you can use to render a label for this edge. + * - `labelY`: the `y` position you can use to render a label for this edge. + * - `offsetX`: the absolute difference between the source `x` position and the `x` position of the + * middle of this path. + * - `offsetY`: the absolute difference between the source `y` position and the `y` position of the + * middle of this path. * @example + * ```js * const source = { x: 0, y: 20 }; - const target = { x: 150, y: 100 }; - - const [path, labelX, labelY, offsetX, offsetY] = getStraightPath({ - sourceX: source.x, - sourceY: source.y, - sourcePosition: Position.Right, - targetX: target.x, - targetY: target.y, - targetPosition: Position.Left, - }); + * const target = { x: 150, y: 100 }; + * + * const [path, labelX, labelY, offsetX, offsetY] = getStraightPath({ + * sourceX: source.x, + * sourceY: source.y, + * sourcePosition: Position.Right, + * targetX: target.x, + * targetY: target.y, + * targetPosition: Position.Left, + * }); + * ``` + * @remarks This function returns a tuple (aka a fixed-size array) to make it easier to work with multiple edge paths at once. */ export function getStraightPath({ sourceX, diff --git a/packages/system/src/utils/general.ts b/packages/system/src/utils/general.ts index 533c83ee..7fa4a3d8 100644 --- a/packages/system/src/utils/general.ts +++ b/packages/system/src/utils/general.ts @@ -10,6 +10,8 @@ import type { Transform, InternalNodeBase, NodeLookup, + Padding, + PaddingWithUnit, } from '../types'; import { type Viewport } from '../types'; import { getNodePositionWithOrigin, isInternalNodeBase } from './graph'; @@ -174,20 +176,119 @@ export const rendererPointToPoint = ({ x, y }: XYPosition, [tx, ty, tScale]: Tra }; /** - * Returns a viewport that encloses the given bounds with optional padding. + * 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)) * 0.5); + } + + 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 padding. * @public * @remarks You can determine bounds of nodes with {@link getNodesBounds} and {@link getBoundsOfRects} - * @param bounds - Bounds to fit inside viewport - * @param width - Width of the viewport - * @param height - Height of the viewport - * @param minZoom - Minimum zoom level of the resulting viewport - * @param maxZoom - Maximum zoom level of the resulting viewport - * @param padding - Optional padding around the bounds - * @returns A transforned {@link Viewport} that encloses the given bounds which you can pass to e.g. {@link setViewport} + * @param bounds - Bounds to fit inside viewport. + * @param width - Width of the viewport. + * @param height - Height of the viewport. + * @param minZoom - Minimum zoom level of the resulting viewport. + * @param maxZoom - Maximum zoom level of the resulting viewport. + * @param padding - Padding around the bounds. + * @returns A transformed {@link Viewport} that encloses the given bounds which you can pass to e.g. {@link setViewport}. * @example * const { x, y, zoom } = getViewportForBounds( - { x: 0, y: 0, width: 100, height: 100}, - 1200, 800, 0.5, 2); + * { x: 0, y: 0, width: 100, height: 100}, + * 1200, 800, 0.5, 2); */ export const getViewportForBounds = ( bounds: Rect, @@ -195,18 +296,39 @@ export const getViewportForBounds = ( height: number, minZoom: number, maxZoom: number, - padding: number + padding: Padding ): Viewport => { - const xZoom = width / (bounds.width * (1 + padding)); - const yZoom = height / (bounds.height * (1 + padding)); + // First we resolve all the paddings to actual pixel values + 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 clampedZoom = clamp(zoom, minZoom, maxZoom); + const boundsCenterX = bounds.x + bounds.width / 2; const boundsCenterY = bounds.y + bounds.height / 2; const x = width / 2 - boundsCenterX * 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; @@ -252,19 +374,47 @@ export function evaluateAbsolutePosition( nodeLookup: NodeLookup, nodeOrigin: NodeOrigin ): XYPosition { - let nextParentId: string | undefined = parentId; const positionAbsolute = { ...position }; - while (nextParentId) { - const parent = nodeLookup.get(nextParentId); - nextParentId = parent?.parentId; - - if (parent) { - const origin = parent.origin || nodeOrigin; - positionAbsolute.x += parent.internals.positionAbsolute.x - (dimensions.width ?? 0) * origin[0]; - positionAbsolute.y += parent.internals.positionAbsolute.y - (dimensions.height ?? 0) * origin[1]; - } + const parent = nodeLookup.get(parentId); + if (parent) { + const origin = parent.origin || nodeOrigin; + positionAbsolute.x += parent.internals.positionAbsolute.x - (dimensions.width ?? 0) * origin[0]; + positionAbsolute.y += parent.internals.positionAbsolute.y - (dimensions.height ?? 0) * origin[1]; } return positionAbsolute; } + +export function areSetsEqual(a: Set, b: Set) { + if (a.size !== b.size) { + return false; + } + + for (const item of a) { + if (!b.has(item)) { + return false; + } + } + + return true; +} + +/** + * Polyfill for Promise.withResolvers until we can use it in all browsers + * @internal + */ +export function withResolvers(): { + promise: Promise; + resolve: (value: T | PromiseLike) => void; + reject: (reason?: unknown) => void; +} { + let resolve!: (value: T | PromiseLike) => void; + let reject!: (reason?: unknown) => void; + + const promise = new Promise((res, rej) => { + resolve = res; + reject = rej; + }); + return { promise, resolve, reject }; +} diff --git a/packages/system/src/utils/graph.ts b/packages/system/src/utils/graph.ts index ddec917c..402067d2 100644 --- a/packages/system/src/utils/graph.ts +++ b/packages/system/src/utils/graph.ts @@ -30,7 +30,7 @@ import { import { errorMessages } from '../constants'; /** - * Test whether an object is useable as an Edge + * Test whether an object is usable as an Edge * @public * @remarks In TypeScript this is a type guard that will narrow the type of whatever you pass in to Edge if it returns true * @param element - The element to test @@ -40,7 +40,7 @@ export const isEdgeBase = (element: any): 'id' in element && 'source' in element && 'target' in element; /** - * Test whether an object is useable as a Node + * Test whether an object is usable as a Node * @public * @remarks In TypeScript this is a type guard that will narrow the type of whatever you pass in to Node if it returns true * @param element - The element to test @@ -54,12 +54,27 @@ export const isInternalNodeBase = 'id' in element && 'internals' in element && !('source' in element) && !('target' in element); /** - * Pass in a node, and get connected nodes where edge.source === node.id + * This util is used to tell you what nodes, if any, are connected to the given node + * as the _target_ of an edge. * @public - * @param node - The node to get the connected nodes from - * @param nodes - The array of all nodes - * @param edges - The array of all edges - * @returns An array of nodes that are connected over eges where the source is the given node + * @param node - The node to get the connected nodes from. + * @param nodes - The array of all nodes. + * @param edges - The array of all edges. + * @returns An array of nodes that are connected over edges where the source is the given node. + * + * @example + * ```ts + *import { getOutgoers } from '@xyflow/react'; + * + *const nodes = []; + *const edges = []; + * + *const outgoers = getOutgoers( + * { id: '1', position: { x: 0, y: 0 }, data: { label: 'node' } }, + * nodes, + * edges, + *); + *``` */ export const getOutgoers = ( node: NodeType | { id: string }, @@ -81,12 +96,27 @@ export const getOutgoers = ( node: NodeType | { id: string }, @@ -119,21 +149,52 @@ export const getNodePositionWithOrigin = (node: NodeBase, nodeOrigin: NodeOrigin }; export type GetNodesBoundsParams = { + /** + * Origin of the nodes: `[0, 0]` for top-left, `[0.5, 0.5]` for center. + * @default [0, 0] + */ nodeOrigin?: NodeOrigin; nodeLookup?: NodeLookup>; }; /** - * Internal function for determining a bounding box that contains all given nodes in an array. + * Returns the bounding box that contains all the given nodes in an array. This can + * be useful when combined with [`getViewportForBounds`](/api-reference/utils/get-viewport-for-bounds) + * to calculate the correct transform to fit the given nodes in a viewport. * @public * @remarks Useful when combined with {@link getViewportForBounds} to calculate the correct transform to fit the given nodes in a viewport. - * @param nodes - Nodes to calculate the bounds for - * @param params.nodeOrigin - Origin of the nodes: [0, 0] - top left, [0.5, 0.5] - center - * @returns Bounding box enclosing all nodes + * @param nodes - Nodes to calculate the bounds for. + * @returns Bounding box enclosing all nodes. + * + * @remarks This function was previously called `getRectOfNodes` + * + * @example + * ```js + *import { getNodesBounds } from '@xyflow/react'; + * + *const nodes = [ + * { + * id: 'a', + * position: { x: 0, y: 0 }, + * data: { label: 'a' }, + * width: 50, + * height: 25, + * }, + * { + * id: 'b', + * position: { x: 100, y: 100 }, + * data: { label: 'b' }, + * width: 50, + * height: 25, + * }, + *]; + * + *const bounds = getNodesBounds(nodes); + *``` */ export const getNodesBounds = ( nodes: (NodeType | InternalNodeBase | string)[], - params: GetNodesBoundsParams = { nodeOrigin: [0, 0], nodeLookup: undefined } + params: GetNodesBoundsParams = { nodeOrigin: [0, 0] } ): Rect => { if (process.env.NODE_ENV === 'development' && !params.nodeLookup) { console.warn( @@ -238,10 +299,30 @@ export const getNodesInside = ( }; /** - * Get all connecting edges for a given set of nodes - * @param nodes - Nodes you want to get the connected edges for - * @param edges - All edges - * @returns Array of edges that connect any of the given nodes with each other + * This utility filters an array of edges, keeping only those where either the source or target + * node is present in the given array of nodes. + * @public + * @param nodes - Nodes you want to get the connected edges for. + * @param edges - All edges. + * @returns Array of edges that connect any of the given nodes with each other. + * + * @example + * ```js + *import { getConnectedEdges } from '@xyflow/react'; + * + *const nodes = [ + * { id: 'a', position: { x: 0, y: 0 } }, + * { id: 'b', position: { x: 100, y: 0 } }, + *]; + * + *const edges = [ + * { id: 'a->c', source: 'a', target: 'c' }, + * { id: 'c->d', source: 'c', target: 'd' }, + *]; + * + *const connectedEdges = getConnectedEdges(nodes, edges); + * // => [{ id: 'a->c', source: 'a', target: 'c' }] + *``` */ export const getConnectedEdges = ( nodes: NodeType[], @@ -255,10 +336,10 @@ export const getConnectedEdges = nodeIds.has(edge.source) || nodeIds.has(edge.target)); }; -export function getFitViewNodes< +function getFitViewNodes< Params extends NodeLookup>, Options extends FitViewOptionsBase ->(nodeLookup: Params, options?: Pick) { +>(nodeLookup: Params, options?: Options) { const fitViewNodes: NodeLookup = new Map(); const optionNodeIds = options?.nodes ? new Set(options.nodes.map((node) => node.id)) : null; @@ -273,15 +354,20 @@ export function getFitViewNodes< return fitViewNodes; } -export async function fitView, Options extends FitViewOptionsBase>( +export async function fitViewport< + Params extends FitViewParamsBase, + Options extends FitViewOptionsBase +>( { nodes, width, height, panZoom, minZoom, maxZoom }: Params, options?: Omit ): Promise { 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( bounds, @@ -292,7 +378,11 @@ export async function fitView, Option 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); } @@ -350,10 +440,14 @@ export function calculateNodePosition({ ? clampPosition(nextPosition, extent, node.measured) : nextPosition; + if (node.measured.width === undefined || node.measured.height === undefined) { + onError?.('015', errorMessages['error015']()); + } + return { position: { - x: positionAbsolute.x - parentX + node.measured.width! * origin[0], - y: positionAbsolute.y - parentY + node.measured.height! * origin[1], + x: positionAbsolute.x - parentX + (node.measured.width ?? 0) * origin[0], + y: positionAbsolute.y - parentY + (node.measured.height ?? 0) * origin[1], }, positionAbsolute, }; diff --git a/packages/system/src/utils/node-toolbar.ts b/packages/system/src/utils/node-toolbar.ts index 1813513c..f5246bcc 100644 --- a/packages/system/src/utils/node-toolbar.ts +++ b/packages/system/src/utils/node-toolbar.ts @@ -15,8 +15,10 @@ export function getNodeToolbarTransform( alignmentOffset = 1; } - // position === Position.Top - // we set the x any y position of the toolbar based on the nodes position + /* + * position === Position.Top + * we set the x any y position of the toolbar based on the nodes position + */ let pos = [ (nodeRect.x + nodeRect.width * alignmentOffset) * viewport.zoom + viewport.x, nodeRect.y * viewport.zoom + viewport.y - offset, diff --git a/packages/system/src/utils/store.ts b/packages/system/src/utils/store.ts index 73b6afc9..d94a7dff 100644 --- a/packages/system/src/utils/store.ts +++ b/packages/system/src/utils/store.ts @@ -1,4 +1,4 @@ -import { infiniteExtent } from '..'; +import { HandleConnection, infiniteExtent } from '..'; import { NodeBase, CoordinateExtent, @@ -42,7 +42,7 @@ const adoptUserNodesDefaultOptions = { checkEquality: true, }; -function mergeObjects>(base: T, incoming?: Partial): T { +function mergeObjects>(base: T, incoming?: Partial): T { const result = { ...base }; for (const key in incoming) { if (incoming[key] !== undefined) { @@ -61,11 +61,14 @@ export function updateAbsolutePositions( ) { const _options = mergeObjects(defaultOptions, options); for (const node of nodeLookup.values()) { - if (!node.parentId) { - continue; + if (node.parentId) { + updateChildNode(node, nodeLookup, parentLookup, _options); + } else { + const positionWithOrigin = getNodePositionWithOrigin(node, _options.nodeOrigin); + const extent = isCoordinateExtent(node.extent) ? node.extent : _options.nodeExtent; + const clampedPosition = clampPosition(positionWithOrigin, extent, getNodeDimensions(node)); + node.internals.positionAbsolute = clampedPosition; } - - updateChildNode(node, nodeLookup, parentLookup, _options); } } @@ -82,8 +85,10 @@ export function adoptUserNodes( nodeLookup: NodeLookup>, parentLookup: ParentLookup>, options?: UpdateNodesOptions -) { +): boolean { const _options = mergeObjects(adoptUserNodesDefaultOptions, options); + + let nodesInitialized = nodes.length > 0; const tmpLookup = new Map(nodeLookup); const selectedNodeZ: number = _options?.elevateNodesOnSelect ? 1000 : 0; @@ -119,10 +124,21 @@ export function adoptUserNodes( nodeLookup.set(userNode.id, internalNode); } + if ( + (internalNode.measured === undefined || + internalNode.measured.width === undefined || + internalNode.measured.height === undefined) && + !internalNode.hidden + ) { + nodesInitialized = false; + } + if (userNode.parentId) { updateChildNode(internalNode, nodeLookup, parentLookup, options); } } + + return nodesInitialized; } function updateParentLookup( @@ -170,11 +186,15 @@ function updateChildNode( const positionChanged = x !== positionAbsolute.x || y !== positionAbsolute.y; if (positionChanged || z !== node.internals.z) { - node.internals = { - ...node.internals, - positionAbsolute: positionChanged ? { x, y } : positionAbsolute, - z, - }; + // we create a new object to mark the node as updated + nodeLookup.set(node.id, { + ...node, + internals: { + ...node.internals, + positionAbsolute: positionChanged ? { x, y } : positionAbsolute, + z, + }, + }); } } @@ -268,8 +288,10 @@ export function handleExpandParent( }, }); - // We move all child nodes in the oppsite direction - // so the x,y changes of the parent do not move the children + /* + * We move all child nodes in the oppsite direction + * so the x,y changes of the parent do not move the children + */ parentLookup.get(parentId)?.forEach((childNode) => { if (!children.some((child) => child.id === childNode.id)) { changes.push({ @@ -330,60 +352,70 @@ export function updateNodeInternals( } if (node.hidden) { - node.internals = { - ...node.internals, - handleBounds: undefined, - }; + nodeLookup.set(node.id, { + ...node, + internals: { + ...node.internals, + handleBounds: undefined, + }, + }); updatedInternals = true; - } else { - const dimensions = getDimensions(update.nodeElement); - const dimensionChanged = node.measured.width !== dimensions.width || node.measured.height !== dimensions.height; - const doUpdate = !!( - dimensions.width && - dimensions.height && - (dimensionChanged || !node.internals.handleBounds || update.force) - ); + continue; + } - if (doUpdate) { - const nodeBounds = update.nodeElement.getBoundingClientRect(); - const extent = isCoordinateExtent(node.extent) ? node.extent : nodeExtent; - let { positionAbsolute } = node.internals; + const dimensions = getDimensions(update.nodeElement); + const dimensionChanged = node.measured.width !== dimensions.width || node.measured.height !== dimensions.height; + const doUpdate = !!( + dimensions.width && + dimensions.height && + (dimensionChanged || !node.internals.handleBounds || update.force) + ); - if (node.parentId && node.extent === 'parent') { - positionAbsolute = clampPositionToParent(positionAbsolute, dimensions, nodeLookup.get(node.parentId)!); - } else if (extent) { - positionAbsolute = clampPosition(positionAbsolute, extent, dimensions); - } + if (doUpdate) { + const nodeBounds = update.nodeElement.getBoundingClientRect(); + const extent = isCoordinateExtent(node.extent) ? node.extent : nodeExtent; + let { positionAbsolute } = node.internals; - node.measured = dimensions; - node.internals = { + if (node.parentId && node.extent === 'parent') { + positionAbsolute = clampPositionToParent(positionAbsolute, dimensions, nodeLookup.get(node.parentId)!); + } else if (extent) { + positionAbsolute = clampPosition(positionAbsolute, extent, dimensions); + } + + const newNode = { + ...node, + measured: dimensions, + internals: { ...node.internals, positionAbsolute, handleBounds: { source: getHandleBounds('source', update.nodeElement, nodeBounds, zoom, node.id), target: getHandleBounds('target', update.nodeElement, nodeBounds, zoom, node.id), }, - }; - if (node.parentId) { - updateChildNode(node, nodeLookup, parentLookup, { nodeOrigin }); - } + }, + }; - updatedInternals = true; + nodeLookup.set(node.id, newNode); - if (dimensionChanged) { - changes.push({ + if (node.parentId) { + updateChildNode(newNode, nodeLookup, parentLookup, { nodeOrigin }); + } + + updatedInternals = true; + + if (dimensionChanged) { + changes.push({ + id: node.id, + type: 'dimensions', + dimensions, + }); + + if (node.expandParent && node.parentId) { + parentExpandChildren.push({ id: node.id, - type: 'dimensions', - dimensions, + parentId: node.parentId, + rect: nodeToRect(newNode, nodeOrigin), }); - - if (node.expandParent && node.parentId) { - parentExpandChildren.push({ - id: node.id, - parentId: node.parentId, - rect: nodeToRect(node, nodeOrigin), - }); - } } } } @@ -436,22 +468,58 @@ export async function panBy({ return Promise.resolve(transformChanged); } +/** + * this function adds the connection to the connectionLookup + * at the following keys: nodeId-type-handleId, nodeId-type and nodeId + * @param type type of the connection + * @param connection connection that should be added to the lookup + * @param connectionKey at which key the connection should be added + * @param connectionLookup reference to the connection lookup + * @param nodeId nodeId of the connection + * @param handleId handleId of the conneciton + */ +function addConnectionToLookup( + type: 'source' | 'target', + connection: HandleConnection, + connectionKey: string, + connectionLookup: ConnectionLookup, + nodeId: string, + handleId: string | null +) { + /* + * We add the connection to the connectionLookup at the following keys + * 1. nodeId, 2. nodeId-type, 3. nodeId-type-handleId + * If the key already exists, we add the connection to the existing map + */ + let key = nodeId; + const nodeMap = connectionLookup.get(key) || new Map(); + connectionLookup.set(key, nodeMap.set(connectionKey, connection)); + + key = `${nodeId}-${type}`; + const typeMap = connectionLookup.get(key) || new Map(); + connectionLookup.set(key, typeMap.set(connectionKey, connection)); + + if (handleId) { + key = `${nodeId}-${type}-${handleId}`; + const handleMap = connectionLookup.get(key) || new Map(); + connectionLookup.set(key, handleMap.set(connectionKey, connection)); + } +} + export function updateConnectionLookup(connectionLookup: ConnectionLookup, edgeLookup: EdgeLookup, edges: EdgeBase[]) { connectionLookup.clear(); edgeLookup.clear(); for (const edge of edges) { - const { source, target, sourceHandle = null, targetHandle = null } = edge; + const { source: sourceNode, target: targetNode, sourceHandle = null, targetHandle = null } = edge; - const sourceKey = `${source}-source-${sourceHandle}`; - const targetKey = `${target}-target-${targetHandle}`; + const connection = { edgeId: edge.id, source: sourceNode, target: targetNode, sourceHandle, targetHandle }; + const sourceKey = `${sourceNode}-${sourceHandle}--${targetNode}-${targetHandle}`; + const targetKey = `${targetNode}-${targetHandle}--${sourceNode}-${sourceHandle}`; - const prevSource = connectionLookup.get(sourceKey) || new Map(); - const prevTarget = connectionLookup.get(targetKey) || new Map(); - const connection = { edgeId: edge.id, source, target, sourceHandle, targetHandle }; + addConnectionToLookup('source', connection, targetKey, connectionLookup, sourceNode, sourceHandle); + addConnectionToLookup('target', connection, sourceKey, connectionLookup, targetNode, targetHandle); edgeLookup.set(edge.id, edge); - connectionLookup.set(sourceKey, prevSource.set(`${target}-${targetHandle}`, connection)); - connectionLookup.set(targetKey, prevTarget.set(`${source}-${sourceHandle}`, connection)); } } diff --git a/packages/system/src/xydrag/XYDrag.ts b/packages/system/src/xydrag/XYDrag.ts index c4c5600f..33658aad 100644 --- a/packages/system/src/xydrag/XYDrag.ts +++ b/packages/system/src/xydrag/XYDrag.ts @@ -139,13 +139,23 @@ export function XYDrag voi } for (const [id, dragItem] of dragItems) { + if (!nodeLookup.has(id)) { + /* + * if the node is not in the nodeLookup anymore, it was probably deleted while dragging + * and we don't need to update it anymore + */ + continue; + } + let nextPosition = { x: x - dragItem.distance.x, y: y - dragItem.distance.y }; if (snapToGrid) { nextPosition = snapPosition(nextPosition, snapGrid); } - // if there is selection with multiple nodes and a node extent is set, we need to adjust the node extent for each node - // based on its position so that the node stays at it's position relative to the selection. + /* + * if there is selection with multiple nodes and a node extent is set, we need to adjust the node extent for each node + * based on its position so that the node stays at it's position relative to the selection. + */ let adjustedNodeExtent: CoordinateExtent = [ [nodeExtent[0][0], nodeExtent[0][1]], [nodeExtent[1][0], nodeExtent[1][1]], @@ -208,7 +218,13 @@ export function XYDrag voi return; } - const { transform, panBy, autoPanSpeed } = getStoreItems(); + const { transform, panBy, autoPanSpeed, autoPanOnNodeDrag } = getStoreItems(); + + if (!autoPanOnNodeDrag) { + autoPanStarted = false; + cancelAnimationFrame(autoPanId); + return; + } const [xMovement, yMovement] = calcAutoPan(mousePosition, containerBounds, autoPanSpeed); @@ -251,7 +267,7 @@ export function XYDrag voi onNodeMouseDown?.(nodeId); } - const pointerPos = getPointerPosition(event.sourceEvent, { transform, snapGrid, snapToGrid }); + const pointerPos = getPointerPosition(event.sourceEvent, { transform, snapGrid, snapToGrid, containerBounds }); lastPos = pointerPos; dragItems = getDragItems(nodeLookup, nodesDraggable, pointerPos, nodeId); @@ -275,6 +291,7 @@ export function XYDrag voi .clickDistance(nodeClickDistance) .on('start', (event: UseDragEvent) => { const { domNode, nodeDragThreshold, transform, snapGrid, snapToGrid } = getStoreItems(); + containerBounds = domNode?.getBoundingClientRect() || null; abortDrag = false; @@ -282,16 +299,19 @@ export function XYDrag voi startDrag(event); } - const pointerPos = getPointerPosition(event.sourceEvent, { transform, snapGrid, snapToGrid }); + const pointerPos = getPointerPosition(event.sourceEvent, { transform, snapGrid, snapToGrid, containerBounds }); lastPos = pointerPos; - containerBounds = domNode?.getBoundingClientRect() || null; mousePosition = getEventPosition(event.sourceEvent, containerBounds!); }) .on('drag', (event: UseDragEvent) => { - const { autoPanOnNodeDrag, transform, snapGrid, snapToGrid, nodeDragThreshold } = getStoreItems(); - const pointerPos = getPointerPosition(event.sourceEvent, { transform, snapGrid, snapToGrid }); + const { autoPanOnNodeDrag, transform, snapGrid, snapToGrid, nodeDragThreshold, nodeLookup } = getStoreItems(); + const pointerPos = getPointerPosition(event.sourceEvent, { transform, snapGrid, snapToGrid, containerBounds }); - if (event.sourceEvent.type === 'touchmove' && event.sourceEvent.touches.length > 1) { + if ( + (event.sourceEvent.type === 'touchmove' && event.sourceEvent.touches.length > 1) || + // if user deletes a node while dragging, we need to abort the drag to prevent errors + (nodeId && !nodeLookup.has(nodeId)) + ) { abortDrag = true; } @@ -354,7 +374,7 @@ export function XYDrag voi } }) .filter((event: MouseEvent) => { - const target = event.target as HTMLDivElement; + const target = event.target; const isDraggable = !event.button && (!noDragClassName || !hasSelector(target, `.${noDragClassName}`, domNode)) && diff --git a/packages/system/src/xydrag/utils.ts b/packages/system/src/xydrag/utils.ts index a668746a..dc980281 100644 --- a/packages/system/src/xydrag/utils.ts +++ b/packages/system/src/xydrag/utils.ts @@ -18,13 +18,13 @@ export function isParentSelected(node: NodeType, node return isParentSelected(parentNode, nodeLookup); } -export function hasSelector(target: Element, selector: string, domNode: Element): boolean { - let current = target; +export function hasSelector(target: Element | EventTarget | null, selector: string, domNode: Element): boolean { + let current = target as Partial | null | undefined; do { - if (current?.matches(selector)) return true; + if (current?.matches?.(selector)) return true; if (current === domNode) return false; - current = current.parentElement as Element; + current = current?.parentElement; } while (current); return false; @@ -74,9 +74,11 @@ export function getDragItems( return dragItems; } -// returns two params: -// 1. the dragged node (or the first of the list, if we are dragging a node selection) -// 2. array of selected nodes (for multi selections) +/* + * returns two params: + * 1. the dragged node (or the first of the list, if we are dragging a node selection) + * 2. array of selected nodes (for multi selections) + */ export function getEventHandlerParams({ nodeId, dragItems, @@ -106,14 +108,16 @@ export function getEventHandlerParams({ return [nodesFromDragItems[0], nodesFromDragItems]; } - const node = nodeLookup.get(nodeId)!.internals.userNode; + const node = nodeLookup.get(nodeId)?.internals.userNode; return [ - { - ...node, - position: dragItems.get(nodeId)?.position || node.position, - dragging, - }, + !node + ? nodesFromDragItems[0] + : { + ...node, + position: dragItems.get(nodeId)?.position || node.position, + dragging, + }, nodesFromDragItems, ]; } diff --git a/packages/system/src/xyhandle/XYHandle.ts b/packages/system/src/xyhandle/XYHandle.ts index b15befc9..b8a5b4d2 100644 --- a/packages/system/src/xyhandle/XYHandle.ts +++ b/packages/system/src/xyhandle/XYHandle.ts @@ -48,7 +48,7 @@ function onPointerDown( }: OnPointerDownParams ) { // when xyflow is used inside a shadow root we can't use document - const doc = getHostForElement(event.target as HTMLElement); + const doc = getHostForElement(event.target); let autoPanId = 0; let closestHandle: Handle | null; @@ -157,16 +157,18 @@ function onPointerDown( ...previousConnection, isValid, to: - closestHandle && isValid - ? rendererPointToPoint({ x: closestHandle.x, y: closestHandle.y }, transform) + result.toHandle && isValid + ? rendererPointToPoint({ x: result.toHandle.x, y: result.toHandle.y }, transform) : position, toHandle: result.toHandle, toPosition: isValid && result.toHandle ? result.toHandle.position : oppositePosition[fromHandle.position], toNode: result.toHandle ? nodeLookup.get(result.toHandle.nodeId)! : null, }; - // we don't want to trigger an update when the connection - // is snapped to the same handle as before + /* + * we don't want to trigger an update when the connection + * is snapped to the same handle as before + */ if ( isValid && closestHandle && @@ -190,8 +192,10 @@ function onPointerDown( onConnect?.(connection); } - // it's important to get a fresh reference from the store here - // in order to get the latest state of onConnectEnd + /* + * it's important to get a fresh reference from the store here + * in order to get the latest state of onConnectEnd + */ // eslint-disable-next-line @typescript-eslint/no-unused-vars const { inProgress, ...connectionState } = previousConnection; const finalConnectionState = { @@ -248,8 +252,10 @@ function isValidHandle( const { x, y } = getEventPosition(event); const handleBelow = doc.elementFromPoint(x, y); - // we always want to prioritize the handle below the mouse cursor over the closest distance handle, - // because it could be that the center of another handle is closer to the mouse pointer than the handle below the cursor + /* + * we always want to prioritize the handle below the mouse cursor over the closest distance handle, + * because it could be that the center of another handle is closer to the mouse pointer than the handle below the cursor + */ const handleToCheck = handleBelow?.classList.contains(`${lib}-flow__handle`) ? handleBelow : handleDomNode; const result: Result = { @@ -289,7 +295,7 @@ function isValidHandle( result.isValid = isValid && isValidConnection(connection); - result.toHandle = getHandle(handleNodeId, handleType, handleId, nodeLookup, connectionMode, false); + result.toHandle = getHandle(handleNodeId, handleType, handleId, nodeLookup, connectionMode, true); } return result; diff --git a/packages/system/src/xyhandle/types.ts b/packages/system/src/xyhandle/types.ts index 6e468b0a..d3625c9b 100644 --- a/packages/system/src/xyhandle/types.ts +++ b/packages/system/src/xyhandle/types.ts @@ -11,7 +11,6 @@ import { type UpdateConnection, type IsValidConnection, NodeLookup, - ConnectionState, FinalConnectionState, } from '../types'; diff --git a/packages/system/src/xyhandle/utils.ts b/packages/system/src/xyhandle/utils.ts index ecc9b1cc..4650f192 100644 --- a/packages/system/src/xyhandle/utils.ts +++ b/packages/system/src/xyhandle/utils.ts @@ -19,8 +19,10 @@ function getNodesWithinDistance(position: XYPosition, nodeLookup: NodeLookup, di return nodes; } -// this distance is used for the area around the user pointer -// while doing a connection for finding the closest nodes +/* + * this distance is used for the area around the user pointer + * while doing a connection for finding the closest nodes + */ const ADDITIONAL_DISTANCE = 250; export function getClosestHandle( diff --git a/packages/system/src/xypanzoom/XYPanZoom.ts b/packages/system/src/xypanzoom/XYPanZoom.ts index 54b97e30..0c1a8e12 100644 --- a/packages/system/src/xypanzoom/XYPanZoom.ts +++ b/packages/system/src/xypanzoom/XYPanZoom.ts @@ -1,5 +1,6 @@ import { type ZoomTransform, zoom, zoomTransform } from 'd3-zoom'; import { select } from 'd3-selection'; +import { interpolateZoom, interpolate } from 'd3-interpolate'; import { type CoordinateExtent, @@ -19,6 +20,7 @@ import { createZoomOnScrollHandler, } from './eventhandler'; import { createFilter } from './filter'; +import { transition } from 'd3-transition'; export type ZoomPanValues = { isZoomingOrPanning: boolean; @@ -78,8 +80,8 @@ export function XYPanZoom({ function setTransform(transform: ZoomTransform, options?: PanZoomTransformOptions) { if (d3Selection) { return new Promise((resolve) => { - d3ZoomInstance?.transform( - getD3Transition(d3Selection, options?.duration, () => resolve(true)), + d3ZoomInstance?.interpolate(options?.interpolate === 'linear' ? interpolate : interpolateZoom).transform( + getD3Transition(d3Selection, options?.duration, options?.ease, () => resolve(true)), transform ); }); @@ -178,9 +180,11 @@ export function XYPanZoom({ }); d3ZoomInstance.filter(filter); - // We cannot add zoomOnDoubleClick to the filter above because - // double tapping on touch screens circumvents the filter and - // dblclick.zoom is fired on the selection directly + /* + * We cannot add zoomOnDoubleClick to the filter above because + * double tapping on touch screens circumvents the filter and + * dblclick.zoom is fired on the selection directly + */ if (zoomOnDoubleClick) { d3Selection.on('dblclick.zoom', d3DblClickZoomHandler); } else { @@ -240,8 +244,8 @@ export function XYPanZoom({ function scaleTo(zoom: number, options?: PanZoomTransformOptions) { if (d3Selection) { return new Promise((resolve) => { - d3ZoomInstance?.scaleTo( - getD3Transition(d3Selection, options?.duration, () => resolve(true)), + d3ZoomInstance?.interpolate(options?.interpolate === 'linear' ? interpolate : interpolateZoom).scaleTo( + getD3Transition(d3Selection, options?.duration, options?.ease, () => resolve(true)), zoom ); }); @@ -253,8 +257,8 @@ export function XYPanZoom({ function scaleBy(factor: number, options?: PanZoomTransformOptions) { if (d3Selection) { return new Promise((resolve) => { - d3ZoomInstance?.scaleBy( - getD3Transition(d3Selection, options?.duration, () => resolve(true)), + d3ZoomInstance?.interpolate(options?.interpolate === 'linear' ? interpolate : interpolateZoom).scaleBy( + getD3Transition(d3Selection, options?.duration, options?.ease, () => resolve(true)), factor ); }); diff --git a/packages/system/src/xypanzoom/eventhandler.ts b/packages/system/src/xypanzoom/eventhandler.ts index 2e78b90a..e6dda425 100644 --- a/packages/system/src/xypanzoom/eventhandler.ts +++ b/packages/system/src/xypanzoom/eventhandler.ts @@ -90,8 +90,10 @@ export function createPanOnScrollHandler({ return; } - // increase scroll speed in firefox - // firefox: deltaMode === 1; chrome: deltaMode === 0 + /* + * increase scroll speed in firefox + * firefox: deltaMode === 1; chrome: deltaMode === 0 + */ const deltaNormalize = event.deltaMode === 1 ? 20 : 1; let deltaX = panOnScrollMode === PanOnScrollMode.Vertical ? 0 : event.deltaX * deltaNormalize; let deltaY = panOnScrollMode === PanOnScrollMode.Horizontal ? 0 : event.deltaY * deltaNormalize; @@ -114,9 +116,11 @@ export function createPanOnScrollHandler({ clearTimeout(zoomPanValues.panScrollTimeout); - // for pan on scroll we need to handle the event calls on our own - // we can't use the start, zoom and end events from d3-zoom - // because start and move gets called on every scroll event and not once at the beginning + /* + * for pan on scroll we need to handle the event calls on our own + * we can't use the start, zoom and end events from d3-zoom + * because start and move gets called on every scroll event and not once at the beginning + */ if (!zoomPanValues.isPanScrolling) { zoomPanValues.isPanScrolling = true; @@ -137,10 +141,17 @@ export function createPanOnScrollHandler({ export function createZoomOnScrollHandler({ noWheelClassName, preventScrolling, d3ZoomHandler }: ZoomOnScrollParams) { return function (this: Element, event: any, d: unknown) { + const isWheel = event.type === 'wheel'; // we still want to enable pinch zooming even if preventScrolling is set to false - const preventZoom = !preventScrolling && event.type === 'wheel' && !event.ctrlKey; + const preventZoom = !preventScrolling && isWheel && !event.ctrlKey; + const hasNoWheelClass = isWrappedWithClass(event, noWheelClassName); - if (preventZoom || isWrappedWithClass(event, noWheelClassName)) { + // if user is pinch zooming above a nowheel element, we don't want the browser to zoom + if (event.ctrlKey && isWheel && hasNoWheelClass) { + event.preventDefault(); + } + + if (preventZoom || hasNoWheelClass) { return null; } diff --git a/packages/system/src/xypanzoom/utils.ts b/packages/system/src/xypanzoom/utils.ts index fbd0324d..22382bd5 100644 --- a/packages/system/src/xypanzoom/utils.ts +++ b/packages/system/src/xypanzoom/utils.ts @@ -1,5 +1,6 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { type ZoomTransform, zoomIdentity } from 'd3-zoom'; +import { transition } from 'd3-transition'; import { type D3SelectionInstance, type Viewport } from '../types'; import { isMacOs } from '../utils'; @@ -21,14 +22,17 @@ export const isWrappedWithClass = (event: any, className: string | undefined) => export const isRightClickPan = (panOnDrag: boolean | number[], usedButton: number) => usedButton === 2 && Array.isArray(panOnDrag) && panOnDrag.includes(2); -export const getD3Transition = (selection: D3SelectionInstance, duration = 0, onEnd = () => {}) => { +// taken from d3-ease: https://github.com/d3/d3-ease/blob/main/src/cubic.js +const defaultEase = (t: number) => ((t *= 2) <= 1 ? t * t * t : (t -= 2) * t * t + 2) / 2; + +export const getD3Transition = (selection: D3SelectionInstance, duration = 0, ease = defaultEase, onEnd = () => {}) => { const hasDuration = typeof duration === 'number' && duration > 0; if (!hasDuration) { onEnd(); } - return hasDuration ? selection.transition().duration(duration).on('end', onEnd) : selection; + return hasDuration ? selection.transition().duration(duration).ease(ease).on('end', onEnd) : selection; }; export const wheelDelta = (event: any) => { diff --git a/packages/system/src/xyresizer/XYResizer.ts b/packages/system/src/xyresizer/XYResizer.ts index c9dae4aa..03a0675e 100644 --- a/packages/system/src/xyresizer/XYResizer.ts +++ b/packages/system/src/xyresizer/XYResizer.ts @@ -12,7 +12,15 @@ import type { Transform, XYPosition, } from '../types'; -import type { OnResize, OnResizeEnd, OnResizeStart, ResizeDragEvent, ShouldResize, ControlPosition } from './types'; +import type { + OnResize, + OnResizeEnd, + OnResizeStart, + ResizeDragEvent, + ShouldResize, + ControlPosition, + ResizeControlDirection, +} from './types'; const initPrevValues = { width: 0, height: 0, x: 0, y: 0 }; @@ -45,9 +53,10 @@ type XYResizerParams = { snapGrid?: [number, number]; snapToGrid: boolean; nodeOrigin: NodeOrigin; + paneDomNode: HTMLDivElement | null; }; onChange: (changes: XYResizerChange, childChanges: XYResizerChildChange[]) => void; - onEnd?: () => void; + onEnd?: (change: Required) => void; }; type XYResizerUpdateParams = { @@ -59,6 +68,7 @@ type XYResizerUpdateParams = { maxHeight: number; }; keepAspectRatio: boolean; + resizeDirection?: ResizeControlDirection; onResizeStart: OnResizeStart | undefined; onResize: OnResize | undefined; onResizeEnd: OnResizeEnd | undefined; @@ -98,6 +108,7 @@ export function XYResizer({ domNode, nodeId, getStoreItems, onChange, onEnd }: X controlPosition, boundaries, keepAspectRatio, + resizeDirection, onResizeStart, onResize, onResizeEnd, @@ -109,6 +120,7 @@ export function XYResizer({ domNode, nodeId, getStoreItems, onChange, onEnd }: X const controlDirection = getControlDirection(controlPosition); let node: InternalNodeBase | undefined = undefined; + let containerBounds: DOMRect | null = null; let childNodes: XYResizerChildChange[] = []; let parentNode: InternalNodeBase | undefined = undefined; // Needed to fix expandParent let parentExtent: CoordinateExtent | undefined = undefined; @@ -116,14 +128,20 @@ export function XYResizer({ domNode, nodeId, getStoreItems, onChange, onEnd }: X const dragHandler = drag() .on('start', (event: ResizeDragEvent) => { - const { nodeLookup, transform, snapGrid, snapToGrid, nodeOrigin } = getStoreItems(); + const { nodeLookup, transform, snapGrid, snapToGrid, nodeOrigin, paneDomNode } = getStoreItems(); node = nodeLookup.get(nodeId); if (!node) { return; } - const { xSnapped, ySnapped } = getPointerPosition(event.sourceEvent, { transform, snapGrid, snapToGrid }); + containerBounds = paneDomNode?.getBoundingClientRect() ?? null; + const { xSnapped, ySnapped } = getPointerPosition(event.sourceEvent, { + transform, + snapGrid, + snapToGrid, + containerBounds, + }); prevValues = { width: node.measured.width ?? 0, @@ -146,8 +164,10 @@ export function XYResizer({ domNode, nodeId, getStoreItems, onChange, onEnd }: X parentExtent = parentNode && node.extent === 'parent' ? nodeToParentExtent(parentNode) : undefined; } - // Collect all child nodes to correct their relative positions when top/left changes - // Determine largest minimal extent the parent node is allowed to resize to + /* + * Collect all child nodes to correct their relative positions when top/left changes + * Determine largest minimal extent the parent node is allowed to resize to + */ childNodes = []; childExtent = undefined; @@ -178,7 +198,13 @@ export function XYResizer({ domNode, nodeId, getStoreItems, onChange, onEnd }: X }) .on('drag', (event: ResizeDragEvent) => { const { transform, snapGrid, snapToGrid, nodeOrigin: storeNodeOrigin } = getStoreItems(); - const pointerPosition = getPointerPosition(event.sourceEvent, { transform, snapGrid, snapToGrid }); + const pointerPosition = getPointerPosition(event.sourceEvent, { + transform, + snapGrid, + snapToGrid, + containerBounds, + }); + const childChanges: XYResizerChildChange[] = []; if (!node) { @@ -216,8 +242,10 @@ export function XYResizer({ domNode, nodeId, getStoreItems, onChange, onEnd }: X prevValues.x = change.x; prevValues.y = change.y; - // when top/left changes, correct the relative positions of child nodes - // so that they stay in the same position + /* + * when top/left changes, correct the relative positions of child nodes + * so that they stay in the same position + */ if (childNodes.length > 0) { const xChange = x - prevX; const yChange = y - prevY; @@ -233,8 +261,10 @@ export function XYResizer({ domNode, nodeId, getStoreItems, onChange, onEnd }: X } if (isWidthChange || isHeightChange) { - change.width = isWidthChange ? width : prevValues.width; - change.height = isHeightChange ? height : prevValues.height; + change.width = + isWidthChange && (!resizeDirection || resizeDirection === 'horizontal') ? width : prevValues.width; + change.height = + isHeightChange && (!resizeDirection || resizeDirection === 'vertical') ? height : prevValues.height; prevValues.width = change.width; prevValues.height = change.height; } @@ -276,7 +306,7 @@ export function XYResizer({ domNode, nodeId, getStoreItems, onChange, onEnd }: X }) .on('end', (event: ResizeDragEvent) => { onResizeEnd?.(event, { ...prevValues }); - onEnd?.(); + onEnd?.({ ...prevValues }); }); selection.call(dragHandler); } diff --git a/packages/system/src/xyresizer/types.ts b/packages/system/src/xyresizer/types.ts index 99480512..1d16eb3a 100644 --- a/packages/system/src/xyresizer/types.ts +++ b/packages/system/src/xyresizer/types.ts @@ -11,15 +11,36 @@ export type ResizeParamsWithDirection = ResizeParams & { direction: number[]; }; +/** + * Used to determine the control line position of the NodeResizer + * + * @public + */ export type ControlLinePosition = 'top' | 'bottom' | 'left' | 'right'; +/** + * Used to determine the control position of the NodeResizer + * + * @public + */ export type ControlPosition = ControlLinePosition | 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'; +/** + * Used to determine the variant of the resize control + * + * @public + */ export enum ResizeControlVariant { Line = 'line', Handle = 'handle', } +/** + * The direction the user can resize the node. + * @public + */ +export type ResizeControlDirection = 'horizontal' | 'vertical'; + export const XY_RESIZER_HANDLE_POSITIONS: ControlPosition[] = ['top-left', 'top-right', 'bottom-left', 'bottom-right']; export const XY_RESIZER_LINE_POSITIONS: ControlLinePosition[] = ['top', 'right', 'bottom', 'left']; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 918e9fd6..d47c4673 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -17,27 +17,12 @@ importers: '@playwright/test': specifier: ^1.44.1 version: 1.44.1 - '@typescript-eslint/eslint-plugin': - specifier: latest - version: 8.0.0(@typescript-eslint/parser@8.0.0(eslint@8.43.0)(typescript@5.1.3))(eslint@8.43.0)(typescript@5.1.3) - '@typescript-eslint/parser': - specifier: latest - version: 8.0.0(eslint@8.43.0)(typescript@5.1.3) concurrently: specifier: ^7.6.0 version: 7.6.0 eslint: specifier: ^8.22.0 version: 8.43.0 - eslint-config-prettier: - specifier: ^8.5.0 - version: 8.8.0(eslint@8.43.0) - eslint-plugin-prettier: - specifier: ^4.2.1 - version: 4.2.1(eslint-config-prettier@8.8.0(eslint@8.43.0))(eslint@8.43.0)(prettier@2.8.8) - eslint-plugin-react: - specifier: latest - version: 7.35.0(eslint@8.43.0) prettier: specifier: ^2.7.1 version: 2.8.8 @@ -57,8 +42,8 @@ importers: specifier: ^2.0.3 version: 2.0.3 typescript: - specifier: 5.1.3 - version: 5.1.3 + specifier: 5.4.5 + version: 5.4.5 examples/astro-xyflow: dependencies: @@ -67,7 +52,7 @@ importers: version: 3.0.2(@types/react-dom@18.2.8)(@types/react@18.2.24)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(vite@4.5.3(@types/node@20.14.6)(terser@5.31.0)) '@astrojs/svelte': specifier: ^4.0.2 - version: 4.0.2(astro@3.2.2(@types/node@20.14.6)(terser@5.31.0))(svelte@4.2.1)(typescript@5.4.5)(vite@4.5.3(@types/node@20.14.6)(terser@5.31.0)) + version: 4.0.2(astro@3.2.2(@types/node@20.14.6)(terser@5.31.0))(svelte@4.2.1)(typescript@5.8.3)(vite@4.5.3(@types/node@20.14.6)(terser@5.31.0)) '@types/react': specifier: ^18.2.24 version: 18.2.24 @@ -160,8 +145,8 @@ importers: specifier: ^2.0.2 version: 2.0.2 typescript: - specifier: 5.2.2 - version: 5.2.2 + specifier: 5.4.5 + version: 5.4.5 vite: specifier: 4.5.0 version: 4.5.0(@types/node@20.14.6)(terser@5.31.0) @@ -169,57 +154,57 @@ importers: examples/svelte: dependencies: '@dagrejs/dagre': - specifier: ^1.0.4 - version: 1.0.4 + specifier: ^1.1.4 + version: 1.1.4 '@sveltejs/vite-plugin-svelte': - specifier: ^3.1.1 - version: 3.1.1(svelte@4.2.12)(vite@5.2.12(@types/node@20.14.6)(terser@5.31.0)) + specifier: ^5.0.3 + version: 5.0.3(svelte@5.27.0)(vite@6.3.0(@types/node@20.14.6)(terser@5.31.0)) '@xyflow/svelte': specifier: workspace:^ version: link:../../packages/svelte devDependencies: '@sveltejs/adapter-auto': - specifier: ^3.2.1 - version: 3.2.1(@sveltejs/kit@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))) + specifier: ^6.0.0 + version: 6.0.0(@sveltejs/kit@2.20.7(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.27.0)(vite@6.3.0(@types/node@20.14.6)(terser@5.31.0)))(svelte@5.27.0)(vite@6.3.0(@types/node@20.14.6)(terser@5.31.0))) '@sveltejs/kit': - specifier: ^2.5.10 - 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)) + specifier: ^2.20.7 + version: 2.20.7(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.27.0)(vite@6.3.0(@types/node@20.14.6)(terser@5.31.0)))(svelte@5.27.0)(vite@6.3.0(@types/node@20.14.6)(terser@5.31.0)) '@typescript-eslint/eslint-plugin': - 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) + specifier: ^8.30.1 + version: 8.30.1(@typescript-eslint/parser@8.30.1(eslint@9.24.0)(typescript@5.8.3))(eslint@9.24.0)(typescript@5.8.3) '@typescript-eslint/parser': - specifier: ^6.10.0 - version: 6.10.0(eslint@8.53.0)(typescript@5.2.2) + specifier: ^8.30.1 + version: 8.30.1(eslint@9.24.0)(typescript@5.8.3) eslint: - specifier: ^8.53.0 - version: 8.53.0 + specifier: ^9.24.0 + version: 9.24.0 eslint-config-prettier: - specifier: ^9.0.0 - version: 9.0.0(eslint@8.53.0) + specifier: ^10.1.2 + version: 10.1.2(eslint@9.24.0) eslint-plugin-svelte: - specifier: ^2.35.0 - version: 2.35.0(eslint@8.53.0)(svelte@4.2.12) + specifier: ^3.5.1 + version: 3.5.1(eslint@9.24.0)(svelte@5.27.0) prettier: - specifier: ^3.0.3 - version: 3.0.3 + specifier: ^3.5.3 + version: 3.5.3 prettier-plugin-svelte: - specifier: ^3.0.3 - version: 3.0.3(prettier@3.0.3)(svelte@4.2.12) + specifier: ^3.3.3 + version: 3.3.3(prettier@3.5.3)(svelte@5.27.0) svelte: - specifier: ^4.2.12 - version: 4.2.12 + specifier: ^5.27.0 + version: 5.27.0 svelte-check: - specifier: ^3.6.6 - version: 3.6.6(@babel/core@7.24.7)(postcss-load-config@5.0.2(postcss@8.4.35))(postcss@8.4.35)(svelte@4.2.12) + specifier: ^4.1.6 + version: 4.1.6(picomatch@4.0.2)(svelte@5.27.0)(typescript@5.8.3) tslib: - specifier: ^2.6.2 - version: 2.6.2 + specifier: ^2.8.1 + version: 2.8.1 typescript: - specifier: ^5.2.2 - version: 5.2.2 + specifier: ^5.8.3 + version: 5.8.3 vite: - specifier: ^5.2.12 - version: 5.2.12(@types/node@20.14.6)(terser@5.31.0) + specifier: ^6.3.0 + version: 6.3.0(@types/node@20.14.6)(terser@5.31.0) packages/react: dependencies: @@ -282,102 +267,114 @@ importers: specifier: ^18.2.0 version: 18.2.0 typescript: - specifier: 5.1.3 - version: 5.1.3 + specifier: 5.4.5 + version: 5.4.5 packages/svelte: dependencies: '@svelte-put/shortcut': - specifier: ^3.1.0 - version: 3.1.0 + specifier: ^4.1.0 + version: 4.1.0(svelte@5.27.0) '@xyflow/system': specifier: workspace:* version: link:../system - classcat: - specifier: ^5.0.4 - version: 5.0.4 devDependencies: + '@eslint/js': + specifier: ^9.24.0 + version: 9.24.0 '@sveltejs/adapter-auto': - specifier: ^3.1.1 - version: 3.1.1(@sveltejs/kit@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))) + specifier: ^6.0.0 + version: 6.0.0(@sveltejs/kit@2.20.7(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.27.0)(vite@6.3.0(@types/node@20.14.6)(terser@5.31.0)))(svelte@5.27.0)(vite@6.3.0(@types/node@20.14.6)(terser@5.31.0))) '@sveltejs/kit': - specifier: ^2.5.4 - 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)) + specifier: ^2.20.7 + version: 2.20.7(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.27.0)(vite@6.3.0(@types/node@20.14.6)(terser@5.31.0)))(svelte@5.27.0)(vite@6.3.0(@types/node@20.14.6)(terser@5.31.0)) '@sveltejs/package': - specifier: ^2.3.0 - version: 2.3.0(svelte@4.2.12)(typescript@5.4.2) + specifier: ^2.3.11 + version: 2.3.11(svelte@5.27.0)(typescript@5.8.3) + '@sveltejs/vite-plugin-svelte': + specifier: ^5.0.3 + version: 5.0.3(svelte@5.27.0)(vite@6.3.0(@types/node@20.14.6)(terser@5.31.0)) '@typescript-eslint/eslint-plugin': - 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) + specifier: ^8.30.1 + version: 8.30.1(@typescript-eslint/parser@8.30.1(eslint@9.24.0)(typescript@5.8.3))(eslint@9.24.0)(typescript@5.8.3) '@typescript-eslint/parser': - specifier: ^7.2.0 - version: 7.2.0(eslint@8.57.0)(typescript@5.4.2) + specifier: ^8.30.1 + version: 8.30.1(eslint@9.24.0)(typescript@5.8.3) autoprefixer: - specifier: ^10.4.18 - version: 10.4.18(postcss@8.4.35) + specifier: ^10.4.21 + version: 10.4.21(postcss@8.5.3) cssnano: - specifier: ^6.1.0 - version: 6.1.0(postcss@8.4.35) + specifier: ^7.0.6 + version: 7.0.6(postcss@8.5.3) dotenv: - specifier: ^16.4.5 - version: 16.4.5 + specifier: ^16.5.0 + version: 16.5.0 eslint: - specifier: ^8.57.0 - version: 8.57.0 + specifier: ^9.24.0 + version: 9.24.0 eslint-config-prettier: - specifier: ^9.1.0 - version: 9.1.0(eslint@8.57.0) + specifier: ^10.1.2 + version: 10.1.2(eslint@9.24.0) eslint-plugin-svelte: - specifier: ^2.35.1 - version: 2.35.1(eslint@8.57.0)(svelte@4.2.12) + specifier: ^3.5.1 + version: 3.5.1(eslint@9.24.0)(svelte@5.27.0) + globals: + specifier: ^16.0.0 + version: 16.0.0 postcss: - specifier: ^8.4.35 - version: 8.4.35 + specifier: ^8.5.3 + version: 8.5.3 postcss-cli: - specifier: ^11.0.0 - version: 11.0.0(postcss@8.4.35) + specifier: ^11.0.1 + version: 11.0.1(postcss@8.5.3) postcss-combine-duplicated-selectors: specifier: ^10.0.3 - version: 10.0.3(postcss@8.4.35) + version: 10.0.3(postcss@8.5.3) postcss-import: - specifier: ^16.0.1 - version: 16.0.1(postcss@8.4.35) + specifier: ^16.1.0 + version: 16.1.0(postcss@8.5.3) postcss-nested: - specifier: ^6.0.1 - version: 6.0.1(postcss@8.4.35) + specifier: ^7.0.2 + version: 7.0.2(postcss@8.5.3) postcss-rename: specifier: ^0.6.1 - version: 0.6.1(postcss@8.4.35) + version: 0.6.1(postcss@8.5.3) prettier: - specifier: ^3.2.5 - version: 3.2.5 + specifier: ^3.5.3 + version: 3.5.3 prettier-plugin-svelte: - specifier: ^3.2.2 - version: 3.2.2(prettier@3.2.5)(svelte@4.2.12) + specifier: ^3.3.3 + version: 3.3.3(prettier@3.5.3)(svelte@5.27.0) svelte: - specifier: ^4.2.12 - version: 4.2.12 + specifier: ^5.27.0 + version: 5.27.0 svelte-check: - specifier: ^3.6.7 - version: 3.6.7(@babel/core@7.24.7)(postcss-load-config@5.0.2(postcss@8.4.35))(postcss@8.4.35)(svelte@4.2.12) + specifier: ^4.1.6 + version: 4.1.6(picomatch@4.0.2)(svelte@5.27.0)(typescript@5.8.3) svelte-eslint-parser: - specifier: ^0.33.1 - version: 0.33.1(svelte@4.2.12) + specifier: ^1.1.2 + version: 1.1.2(svelte@5.27.0) svelte-preprocess: - 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) + specifier: ^6.0.3 + version: 6.0.3(@babel/core@7.24.7)(postcss-load-config@5.0.2(postcss@8.5.3))(postcss@8.5.3)(svelte@5.27.0)(typescript@5.8.3) tslib: - specifier: ^2.6.2 - version: 2.6.2 + specifier: ^2.8.1 + version: 2.8.1 typescript: - specifier: 5.4.2 - version: 5.4.2 + specifier: ^5.8.3 + version: 5.8.3 + typescript-eslint: + specifier: ^8.30.1 + version: 8.30.1(eslint@9.24.0)(typescript@5.8.3) packages/system: dependencies: '@types/d3-drag': specifier: ^3.0.7 version: 3.0.7 + '@types/d3-interpolate': + specifier: ^3.0.4 + version: 3.0.4 '@types/d3-selection': specifier: ^3.0.10 version: 3.0.10 @@ -390,6 +387,9 @@ importers: d3-drag: specifier: ^3.0.0 version: 3.0.0 + d3-interpolate: + specifier: ^3.0.1 + version: 3.0.1 d3-selection: specifier: ^3.0.0 version: 3.0.0 @@ -410,14 +410,14 @@ importers: specifier: workspace:* version: link:../../tooling/tsconfig typescript: - specifier: 5.1.3 - version: 5.1.3 + specifier: 5.4.5 + version: 5.4.5 tests/playwright: dependencies: '@playwright/experimental-ct-react': - specifier: ^1.45.0 - version: 1.45.0(@types/node@20.14.6)(terser@5.31.0)(vite@5.3.2(@types/node@20.14.6)(terser@5.31.0)) + specifier: ^1.51.1 + version: 1.51.1(@types/node@20.14.6)(terser@5.31.0)(vite@6.3.0(@types/node@20.14.6)(terser@5.31.0)) '@types/react': specifier: ^18.3.3 version: 18.3.3 @@ -431,30 +431,36 @@ importers: specifier: ^18.3.1 version: 18.3.1(react@18.3.1) typescript: - specifier: ^5.4.5 - version: 5.4.5 + specifier: ^5.7.3 + version: 5.7.3 devDependencies: '@playwright/test': - specifier: ^1.45.0 - version: 1.45.0 + specifier: ^1.51.1 + version: 1.51.1 '@types/node': specifier: ^20.14.6 version: 20.14.6 tooling/eslint-config: devDependencies: - eslint: - specifier: ^8.22.0 - version: 8.43.0 + '@typescript-eslint/eslint-plugin': + specifier: ^8.23.0 + version: 8.29.1(@typescript-eslint/parser@8.29.1(eslint@9.24.0)(typescript@5.8.3))(eslint@9.24.0)(typescript@5.8.3) + '@typescript-eslint/parser': + specifier: ^8.23.0 + version: 8.29.1(eslint@9.24.0)(typescript@5.8.3) eslint-config-prettier: - specifier: ^8.5.0 - version: 8.8.0(eslint@8.43.0) + specifier: ^10.0.1 + version: 10.1.1(eslint@9.24.0) eslint-config-turbo: - specifier: ^2.0.3 - version: 2.0.3(eslint@8.43.0) + specifier: ^2.4.0 + version: 2.5.0(eslint@9.24.0)(turbo@2.0.3) + eslint-plugin-prettier: + specifier: ^4.2.1 + version: 4.2.1(eslint-config-prettier@10.1.1(eslint@9.24.0))(eslint@9.24.0)(prettier@3.5.3) eslint-plugin-react: - specifier: ^7.33.2 - version: 7.33.2(eslint@8.43.0) + specifier: ^7.37.4 + version: 7.37.5(eslint@9.24.0) tooling/rollup-config: devDependencies: @@ -472,7 +478,7 @@ importers: version: 0.4.4(rollup@4.18.0) '@rollup/plugin-typescript': 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.8.1)(typescript@5.7.3) rollup: specifier: ^4.18.0 version: 4.18.0 @@ -480,8 +486,8 @@ importers: specifier: ^2.2.4 version: 2.2.4(rollup@4.18.0) typescript: - specifier: ^5.1.3 - version: 5.4.2 + specifier: ^5.4.5 + version: 5.7.3 tooling/tsconfig: {} @@ -856,22 +862,16 @@ packages: '@cypress/xvfb@1.2.4': resolution: {integrity: sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q==} - '@dagrejs/dagre@1.0.4': - resolution: {integrity: sha512-jrEore+HhW1yg1Rsd9H1PPMcoEOD4bVh0WCXc6GqzyzubnJj4GaWGg8ETOrskTd/3n/g5LOzumGM4CCgpNLJNw==} + '@dagrejs/dagre@1.1.4': + resolution: {integrity: sha512-QUTc54Cg/wvmlEUxB+uvoPVKFazM1H18kVHBQNmK2NbrDR5ihOCR6CXLnDSZzMcSQKJtabPUWridBOlJM3WkDg==} - '@dagrejs/graphlib@2.1.13': - resolution: {integrity: sha512-calbMa7Gcyo+/t23XBaqQqon8LlgE9regey4UVoikoenKBXvUnCUL3s9RP6USCxttfr0XWVICtYUuKMdehKqMw==} + '@dagrejs/graphlib@2.2.4': + resolution: {integrity: sha512-mepCf/e9+SKYy1d02/UkvSy6+6MoyXhVxP8lLDfA7BPE1X1d4dR0sZznmbM8/XVJ1GPM+Svnx7Xj6ZweByWUkw==} engines: {node: '>17.0.0'} - '@esbuild/aix-ppc64@0.20.2': - resolution: {integrity: sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==} - engines: {node: '>=12'} - cpu: [ppc64] - os: [aix] - - '@esbuild/aix-ppc64@0.21.5': - resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} - engines: {node: '>=12'} + '@esbuild/aix-ppc64@0.25.2': + resolution: {integrity: sha512-wCIboOL2yXZym2cgm6mlA742s9QeJ8DjGVaL39dLN4rRwrOgOyYSnOaFPhKZGLb2ngj4EyfAFjsNJwPXZvseag==} + engines: {node: '>=18'} cpu: [ppc64] os: [aix] @@ -887,15 +887,9 @@ packages: cpu: [arm64] os: [android] - '@esbuild/android-arm64@0.20.2': - resolution: {integrity: sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==} - engines: {node: '>=12'} - cpu: [arm64] - os: [android] - - '@esbuild/android-arm64@0.21.5': - resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} - engines: {node: '>=12'} + '@esbuild/android-arm64@0.25.2': + resolution: {integrity: sha512-5ZAX5xOmTligeBaeNEPnPaeEuah53Id2tX4c2CVP3JaROTH+j4fnfHCkr1PjXMd78hMst+TlkfKcW/DlTq0i4w==} + engines: {node: '>=18'} cpu: [arm64] os: [android] @@ -911,15 +905,9 @@ packages: cpu: [arm] os: [android] - '@esbuild/android-arm@0.20.2': - resolution: {integrity: sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==} - engines: {node: '>=12'} - cpu: [arm] - os: [android] - - '@esbuild/android-arm@0.21.5': - resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} - engines: {node: '>=12'} + '@esbuild/android-arm@0.25.2': + resolution: {integrity: sha512-NQhH7jFstVY5x8CKbcfa166GoV0EFkaPkCKBQkdPJFvo5u+nGXLEH/ooniLb3QI8Fk58YAx7nsPLozUWfCBOJA==} + engines: {node: '>=18'} cpu: [arm] os: [android] @@ -935,15 +923,9 @@ packages: cpu: [x64] os: [android] - '@esbuild/android-x64@0.20.2': - resolution: {integrity: sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==} - engines: {node: '>=12'} - cpu: [x64] - os: [android] - - '@esbuild/android-x64@0.21.5': - resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} - engines: {node: '>=12'} + '@esbuild/android-x64@0.25.2': + resolution: {integrity: sha512-Ffcx+nnma8Sge4jzddPHCZVRvIfQ0kMsUsCMcJRHkGJ1cDmhe4SsrYIjLUKn1xpHZybmOqCWwB0zQvsjdEHtkg==} + engines: {node: '>=18'} cpu: [x64] os: [android] @@ -959,15 +941,9 @@ packages: cpu: [arm64] os: [darwin] - '@esbuild/darwin-arm64@0.20.2': - resolution: {integrity: sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==} - engines: {node: '>=12'} - cpu: [arm64] - os: [darwin] - - '@esbuild/darwin-arm64@0.21.5': - resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} - engines: {node: '>=12'} + '@esbuild/darwin-arm64@0.25.2': + resolution: {integrity: sha512-MpM6LUVTXAzOvN4KbjzU/q5smzryuoNjlriAIx+06RpecwCkL9JpenNzpKd2YMzLJFOdPqBpuub6eVRP5IgiSA==} + engines: {node: '>=18'} cpu: [arm64] os: [darwin] @@ -983,15 +959,9 @@ packages: cpu: [x64] os: [darwin] - '@esbuild/darwin-x64@0.20.2': - resolution: {integrity: sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==} - engines: {node: '>=12'} - cpu: [x64] - os: [darwin] - - '@esbuild/darwin-x64@0.21.5': - resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} - engines: {node: '>=12'} + '@esbuild/darwin-x64@0.25.2': + resolution: {integrity: sha512-5eRPrTX7wFyuWe8FqEFPG2cU0+butQQVNcT4sVipqjLYQjjh8a8+vUTfgBKM88ObB85ahsnTwF7PSIt6PG+QkA==} + engines: {node: '>=18'} cpu: [x64] os: [darwin] @@ -1007,15 +977,9 @@ packages: cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-arm64@0.20.2': - resolution: {integrity: sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==} - engines: {node: '>=12'} - cpu: [arm64] - os: [freebsd] - - '@esbuild/freebsd-arm64@0.21.5': - resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} - engines: {node: '>=12'} + '@esbuild/freebsd-arm64@0.25.2': + resolution: {integrity: sha512-mLwm4vXKiQ2UTSX4+ImyiPdiHjiZhIaE9QvC7sw0tZ6HoNMjYAqQpGyui5VRIi5sGd+uWq940gdCbY3VLvsO1w==} + engines: {node: '>=18'} cpu: [arm64] os: [freebsd] @@ -1031,15 +995,9 @@ packages: cpu: [x64] os: [freebsd] - '@esbuild/freebsd-x64@0.20.2': - resolution: {integrity: sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==} - engines: {node: '>=12'} - cpu: [x64] - os: [freebsd] - - '@esbuild/freebsd-x64@0.21.5': - resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} - engines: {node: '>=12'} + '@esbuild/freebsd-x64@0.25.2': + resolution: {integrity: sha512-6qyyn6TjayJSwGpm8J9QYYGQcRgc90nmfdUb0O7pp1s4lTY+9D0H9O02v5JqGApUyiHOtkz6+1hZNvNtEhbwRQ==} + engines: {node: '>=18'} cpu: [x64] os: [freebsd] @@ -1055,15 +1013,9 @@ packages: cpu: [arm64] os: [linux] - '@esbuild/linux-arm64@0.20.2': - resolution: {integrity: sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==} - engines: {node: '>=12'} - cpu: [arm64] - os: [linux] - - '@esbuild/linux-arm64@0.21.5': - resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} - engines: {node: '>=12'} + '@esbuild/linux-arm64@0.25.2': + resolution: {integrity: sha512-gq/sjLsOyMT19I8obBISvhoYiZIAaGF8JpeXu1u8yPv8BE5HlWYobmlsfijFIZ9hIVGYkbdFhEqC0NvM4kNO0g==} + engines: {node: '>=18'} cpu: [arm64] os: [linux] @@ -1079,15 +1031,9 @@ packages: cpu: [arm] os: [linux] - '@esbuild/linux-arm@0.20.2': - resolution: {integrity: sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==} - engines: {node: '>=12'} - cpu: [arm] - os: [linux] - - '@esbuild/linux-arm@0.21.5': - resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} - engines: {node: '>=12'} + '@esbuild/linux-arm@0.25.2': + resolution: {integrity: sha512-UHBRgJcmjJv5oeQF8EpTRZs/1knq6loLxTsjc3nxO9eXAPDLcWW55flrMVc97qFPbmZP31ta1AZVUKQzKTzb0g==} + engines: {node: '>=18'} cpu: [arm] os: [linux] @@ -1103,15 +1049,9 @@ packages: cpu: [ia32] os: [linux] - '@esbuild/linux-ia32@0.20.2': - resolution: {integrity: sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==} - engines: {node: '>=12'} - cpu: [ia32] - os: [linux] - - '@esbuild/linux-ia32@0.21.5': - resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} - engines: {node: '>=12'} + '@esbuild/linux-ia32@0.25.2': + resolution: {integrity: sha512-bBYCv9obgW2cBP+2ZWfjYTU+f5cxRoGGQ5SeDbYdFCAZpYWrfjjfYwvUpP8MlKbP0nwZ5gyOU/0aUzZ5HWPuvQ==} + engines: {node: '>=18'} cpu: [ia32] os: [linux] @@ -1127,15 +1067,9 @@ packages: cpu: [loong64] os: [linux] - '@esbuild/linux-loong64@0.20.2': - resolution: {integrity: sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==} - engines: {node: '>=12'} - cpu: [loong64] - os: [linux] - - '@esbuild/linux-loong64@0.21.5': - resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} - engines: {node: '>=12'} + '@esbuild/linux-loong64@0.25.2': + resolution: {integrity: sha512-SHNGiKtvnU2dBlM5D8CXRFdd+6etgZ9dXfaPCeJtz+37PIUlixvlIhI23L5khKXs3DIzAn9V8v+qb1TRKrgT5w==} + engines: {node: '>=18'} cpu: [loong64] os: [linux] @@ -1151,15 +1085,9 @@ packages: cpu: [mips64el] os: [linux] - '@esbuild/linux-mips64el@0.20.2': - resolution: {integrity: sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==} - engines: {node: '>=12'} - cpu: [mips64el] - os: [linux] - - '@esbuild/linux-mips64el@0.21.5': - resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} - engines: {node: '>=12'} + '@esbuild/linux-mips64el@0.25.2': + resolution: {integrity: sha512-hDDRlzE6rPeoj+5fsADqdUZl1OzqDYow4TB4Y/3PlKBD0ph1e6uPHzIQcv2Z65u2K0kpeByIyAjCmjn1hJgG0Q==} + engines: {node: '>=18'} cpu: [mips64el] os: [linux] @@ -1175,15 +1103,9 @@ packages: cpu: [ppc64] os: [linux] - '@esbuild/linux-ppc64@0.20.2': - resolution: {integrity: sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==} - engines: {node: '>=12'} - cpu: [ppc64] - os: [linux] - - '@esbuild/linux-ppc64@0.21.5': - resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} - engines: {node: '>=12'} + '@esbuild/linux-ppc64@0.25.2': + resolution: {integrity: sha512-tsHu2RRSWzipmUi9UBDEzc0nLc4HtpZEI5Ba+Omms5456x5WaNuiG3u7xh5AO6sipnJ9r4cRWQB2tUjPyIkc6g==} + engines: {node: '>=18'} cpu: [ppc64] os: [linux] @@ -1199,15 +1121,9 @@ packages: cpu: [riscv64] os: [linux] - '@esbuild/linux-riscv64@0.20.2': - resolution: {integrity: sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==} - engines: {node: '>=12'} - cpu: [riscv64] - os: [linux] - - '@esbuild/linux-riscv64@0.21.5': - resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} - engines: {node: '>=12'} + '@esbuild/linux-riscv64@0.25.2': + resolution: {integrity: sha512-k4LtpgV7NJQOml/10uPU0s4SAXGnowi5qBSjaLWMojNCUICNu7TshqHLAEbkBdAszL5TabfvQ48kK84hyFzjnw==} + engines: {node: '>=18'} cpu: [riscv64] os: [linux] @@ -1223,15 +1139,9 @@ packages: cpu: [s390x] os: [linux] - '@esbuild/linux-s390x@0.20.2': - resolution: {integrity: sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==} - engines: {node: '>=12'} - cpu: [s390x] - os: [linux] - - '@esbuild/linux-s390x@0.21.5': - resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} - engines: {node: '>=12'} + '@esbuild/linux-s390x@0.25.2': + resolution: {integrity: sha512-GRa4IshOdvKY7M/rDpRR3gkiTNp34M0eLTaC1a08gNrh4u488aPhuZOCpkF6+2wl3zAN7L7XIpOFBhnaE3/Q8Q==} + engines: {node: '>=18'} cpu: [s390x] os: [linux] @@ -1247,17 +1157,17 @@ packages: cpu: [x64] os: [linux] - '@esbuild/linux-x64@0.20.2': - resolution: {integrity: sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==} - engines: {node: '>=12'} + '@esbuild/linux-x64@0.25.2': + resolution: {integrity: sha512-QInHERlqpTTZ4FRB0fROQWXcYRD64lAoiegezDunLpalZMjcUcld3YzZmVJ2H/Cp0wJRZ8Xtjtj0cEHhYc/uUg==} + engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/linux-x64@0.21.5': - resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} - engines: {node: '>=12'} - cpu: [x64] - os: [linux] + '@esbuild/netbsd-arm64@0.25.2': + resolution: {integrity: sha512-talAIBoY5M8vHc6EeI2WW9d/CkiO9MQJ0IOWX8hrLhxGbro/vBXJvaQXefW2cP0z0nQVTdQ/eNyGFV1GSKrxfw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] '@esbuild/netbsd-x64@0.18.20': resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==} @@ -1271,17 +1181,17 @@ packages: cpu: [x64] os: [netbsd] - '@esbuild/netbsd-x64@0.20.2': - resolution: {integrity: sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==} - engines: {node: '>=12'} + '@esbuild/netbsd-x64@0.25.2': + resolution: {integrity: sha512-voZT9Z+tpOxrvfKFyfDYPc4DO4rk06qamv1a/fkuzHpiVBMOhpjK+vBmWM8J1eiB3OLSMFYNaOaBNLXGChf5tg==} + engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/netbsd-x64@0.21.5': - resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} - engines: {node: '>=12'} - cpu: [x64] - os: [netbsd] + '@esbuild/openbsd-arm64@0.25.2': + resolution: {integrity: sha512-dcXYOC6NXOqcykeDlwId9kB6OkPUxOEqU+rkrYVqJbK2hagWOMrsTGsMr8+rW02M+d5Op5NNlgMmjzecaRf7Tg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] '@esbuild/openbsd-x64@0.18.20': resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==} @@ -1295,15 +1205,9 @@ packages: cpu: [x64] os: [openbsd] - '@esbuild/openbsd-x64@0.20.2': - resolution: {integrity: sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==} - engines: {node: '>=12'} - cpu: [x64] - os: [openbsd] - - '@esbuild/openbsd-x64@0.21.5': - resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} - engines: {node: '>=12'} + '@esbuild/openbsd-x64@0.25.2': + resolution: {integrity: sha512-t/TkWwahkH0Tsgoq1Ju7QfgGhArkGLkF1uYz8nQS/PPFlXbP5YgRpqQR3ARRiC2iXoLTWFxc6DJMSK10dVXluw==} + engines: {node: '>=18'} cpu: [x64] os: [openbsd] @@ -1319,15 +1223,9 @@ packages: cpu: [x64] os: [sunos] - '@esbuild/sunos-x64@0.20.2': - resolution: {integrity: sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==} - engines: {node: '>=12'} - cpu: [x64] - os: [sunos] - - '@esbuild/sunos-x64@0.21.5': - resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} - engines: {node: '>=12'} + '@esbuild/sunos-x64@0.25.2': + resolution: {integrity: sha512-cfZH1co2+imVdWCjd+D1gf9NjkchVhhdpgb1q5y6Hcv9TP6Zi9ZG/beI3ig8TvwT9lH9dlxLq5MQBBgwuj4xvA==} + engines: {node: '>=18'} cpu: [x64] os: [sunos] @@ -1343,15 +1241,9 @@ packages: cpu: [arm64] os: [win32] - '@esbuild/win32-arm64@0.20.2': - resolution: {integrity: sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==} - engines: {node: '>=12'} - cpu: [arm64] - os: [win32] - - '@esbuild/win32-arm64@0.21.5': - resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} - engines: {node: '>=12'} + '@esbuild/win32-arm64@0.25.2': + resolution: {integrity: sha512-7Loyjh+D/Nx/sOTzV8vfbB3GJuHdOQyrOryFdZvPHLf42Tk9ivBU5Aedi7iyX+x6rbn2Mh68T4qq1SDqJBQO5Q==} + engines: {node: '>=18'} cpu: [arm64] os: [win32] @@ -1367,15 +1259,9 @@ packages: cpu: [ia32] os: [win32] - '@esbuild/win32-ia32@0.20.2': - resolution: {integrity: sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==} - engines: {node: '>=12'} - cpu: [ia32] - os: [win32] - - '@esbuild/win32-ia32@0.21.5': - resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} - engines: {node: '>=12'} + '@esbuild/win32-ia32@0.25.2': + resolution: {integrity: sha512-WRJgsz9un0nqZJ4MfhabxaD9Ft8KioqU3JMinOTvobbX6MOSUigSBlogP8QB3uxpJDsFS6yN+3FDBdqE5lg9kg==} + engines: {node: '>=18'} cpu: [ia32] os: [win32] @@ -1391,15 +1277,9 @@ packages: cpu: [x64] os: [win32] - '@esbuild/win32-x64@0.20.2': - resolution: {integrity: sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==} - engines: {node: '>=12'} - cpu: [x64] - os: [win32] - - '@esbuild/win32-x64@0.21.5': - resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} - engines: {node: '>=12'} + '@esbuild/win32-x64@0.25.2': + resolution: {integrity: sha512-kM3HKb16VIXZyIeVrM1ygYmZBKybX8N4p754bw390wGO3Tf2j4L2/WYL+4suWujpgf6GBYs3jv7TyUivdd05JA==} + engines: {node: '>=18'} cpu: [x64] os: [win32] @@ -1409,37 +1289,63 @@ packages: peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + '@eslint-community/eslint-utils@4.5.1': + resolution: {integrity: sha512-soEIOALTfTK6EjmKMMoLugwaP0rzkad90iIWd1hMO9ARkSAyjfMfkRRhLvD5qH7vvM0Cg72pieUfR6yh6XxC4w==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + '@eslint-community/regexpp@4.10.0': resolution: {integrity: sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + '@eslint-community/regexpp@4.12.1': + resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + '@eslint-community/regexpp@4.9.1': resolution: {integrity: sha512-Y27x+MBLjXa+0JWDhykM3+JE+il3kHKAEqabfEWq3SDhZjLYb6/BHL/JKFnH3fe207JaXkyDo685Oc2Glt6ifA==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + '@eslint/config-array@0.20.0': + resolution: {integrity: sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/config-helpers@0.2.1': + resolution: {integrity: sha512-RI17tsD2frtDu/3dmI7QRrD4bedNKPM08ziRYaC5AhkGrzIAJelm9kJU1TznK+apx6V+cqRz8tfpEeG3oIyjxw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.12.0': + resolution: {integrity: sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.13.0': + resolution: {integrity: sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/eslintrc@2.1.2': resolution: {integrity: sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - '@eslint/eslintrc@2.1.3': - resolution: {integrity: sha512-yZzuIG+jnVu6hNSzFEN07e8BxF3uAzYtQb6uDkaYZLo6oYZDCq454c5kB8zxnzfCYyP4MIuyBn10L0DqwujTmA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - - '@eslint/eslintrc@2.1.4': - resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@eslint/eslintrc@3.3.1': + resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/js@8.43.0': resolution: {integrity: sha512-s2UHCoiXfxMvmfzqoN+vrQ84ahUSYde9qNO1MdxmoEhyHWsfmwOpFlwYV+ePJEVc7gFnATGUi376WowX1N7tFg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - '@eslint/js@8.53.0': - resolution: {integrity: sha512-Kn7K8dx/5U6+cT1yEhpX1w4PCSg0M+XyRILPgvwcEBjerFWCwQj5sbr3/VmxqV0JGHCBCzyd6LxypEuehypY1w==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@eslint/js@9.24.0': + resolution: {integrity: sha512-uIY/y3z0uvOGX8cp1C2fiC4+ZmBhp6yZWkojtHL1YEMnRt1Y63HB9TM17proGEmeG7HeUY+UP36F0aknKYTpYA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/js@8.57.0': - resolution: {integrity: sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@eslint/object-schema@2.1.6': + resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/plugin-kit@0.2.8': + resolution: {integrity: sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@fastify/busboy@2.0.0': resolution: {integrity: sha512-JUFJad5lv7jxj926GPgymrWQxxjPYuJNiNjNMzqT+HiuP6Vl3dk5xzG+8sTX96np0ZAluvaMzPsjhHZ5rNuNQQ==} @@ -1451,16 +1357,19 @@ packages: '@hapi/topo@5.1.0': resolution: {integrity: sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==} + '@humanfs/core@0.19.1': + resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.6': + resolution: {integrity: sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==} + engines: {node: '>=18.18.0'} + '@humanwhocodes/config-array@0.11.13': resolution: {integrity: sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==} engines: {node: '>=10.10.0'} deprecated: Use @eslint/config-array instead - '@humanwhocodes/config-array@0.11.14': - resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==} - engines: {node: '>=10.10.0'} - deprecated: Use @eslint/config-array instead - '@humanwhocodes/module-importer@1.0.1': resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} engines: {node: '>=12.22'} @@ -1469,9 +1378,13 @@ packages: resolution: {integrity: sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==} deprecated: Use @eslint/object-schema instead - '@humanwhocodes/object-schema@2.0.2': - resolution: {integrity: sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==} - deprecated: Use @eslint/object-schema instead + '@humanwhocodes/retry@0.3.1': + resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==} + engines: {node: '>=18.18'} + + '@humanwhocodes/retry@0.4.2': + resolution: {integrity: sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==} + engines: {node: '>=18.18'} '@jridgewell/gen-mapping@0.3.3': resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==} @@ -1503,6 +1416,9 @@ packages: '@jridgewell/sourcemap-codec@1.4.15': resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + '@jridgewell/sourcemap-codec@1.5.0': + resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} + '@jridgewell/trace-mapping@0.3.20': resolution: {integrity: sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==} @@ -1527,12 +1443,12 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} - '@playwright/experimental-ct-core@1.45.0': - resolution: {integrity: sha512-XxAZW3TnkkRlqRRiAnN/q8wzuspqDA2o8912TQkhbH2BKD3Ko4RLMVy0J7QJ+quG1fCp1/rlbg96/fB15wOemg==} + '@playwright/experimental-ct-core@1.51.1': + resolution: {integrity: sha512-kpRZWBT3SMukL1fx8BwEj385Pkgtp86bBKzmrmJU30lWlQiIDFNaIHosgxQC68c8y2mg3Una/lBSHNc2Fotgkw==} engines: {node: '>=18'} - '@playwright/experimental-ct-react@1.45.0': - resolution: {integrity: sha512-GoT7eZMR5wLPFjGwQbGywiy2Z+9jwx5F+GJAZE1xbqMJPzSB5bv8zd5g09mWN4aKOHpc/kzkCBv0bxzW/es51A==} + '@playwright/experimental-ct-react@1.51.1': + resolution: {integrity: sha512-Lu14QiemO2TWfiL6TQPWBH2n+4IvPYH/W6cfblzYpLu/TZdJ7e608l3GwqAGRN5FJym3YOonC7x1Q5EAwcBarg==} engines: {node: '>=18'} hasBin: true @@ -1541,8 +1457,8 @@ packages: engines: {node: '>=16'} hasBin: true - '@playwright/test@1.45.0': - resolution: {integrity: sha512-TVYsfMlGAaxeUllNkywbwek67Ncf8FRGn8ZlRdO291OL3NjG9oMbfVhyP82HQF0CZLMrYsvesqoUekxdWuF9Qw==} + '@playwright/test@1.51.1': + resolution: {integrity: sha512-nM+kEaTSAoVlXmMPH10017vn3FSiFqr/bh4fKg9vmAdMfd9SDqRZNvPSiAHADc/itWak+qPvMPZQOPwCBW7k7Q==} engines: {node: '>=18'} hasBin: true @@ -1627,81 +1543,181 @@ packages: cpu: [arm] os: [android] + '@rollup/rollup-android-arm-eabi@4.39.0': + resolution: {integrity: sha512-lGVys55Qb00Wvh8DMAocp5kIcaNzEFTmGhfFd88LfaogYTRKrdxgtlO5H6S49v2Nd8R2C6wLOal0qv6/kCkOwA==} + cpu: [arm] + os: [android] + '@rollup/rollup-android-arm64@4.18.0': resolution: {integrity: sha512-avCea0RAP03lTsDhEyfy+hpfr85KfyTctMADqHVhLAF3MlIkq83CP8UfAHUssgXTYd+6er6PaAhx/QGv4L1EiA==} cpu: [arm64] os: [android] + '@rollup/rollup-android-arm64@4.39.0': + resolution: {integrity: sha512-It9+M1zE31KWfqh/0cJLrrsCPiF72PoJjIChLX+rEcujVRCb4NLQ5QzFkzIZW8Kn8FTbvGQBY5TkKBau3S8cCQ==} + cpu: [arm64] + os: [android] + '@rollup/rollup-darwin-arm64@4.18.0': resolution: {integrity: sha512-IWfdwU7KDSm07Ty0PuA/W2JYoZ4iTj3TUQjkVsO/6U+4I1jN5lcR71ZEvRh52sDOERdnNhhHU57UITXz5jC1/w==} cpu: [arm64] os: [darwin] + '@rollup/rollup-darwin-arm64@4.39.0': + resolution: {integrity: sha512-lXQnhpFDOKDXiGxsU9/l8UEGGM65comrQuZ+lDcGUx+9YQ9dKpF3rSEGepyeR5AHZ0b5RgiligsBhWZfSSQh8Q==} + cpu: [arm64] + os: [darwin] + '@rollup/rollup-darwin-x64@4.18.0': resolution: {integrity: sha512-n2LMsUz7Ynu7DoQrSQkBf8iNrjOGyPLrdSg802vk6XT3FtsgX6JbE8IHRvposskFm9SNxzkLYGSq9QdpLYpRNA==} cpu: [x64] os: [darwin] + '@rollup/rollup-darwin-x64@4.39.0': + resolution: {integrity: sha512-mKXpNZLvtEbgu6WCkNij7CGycdw9cJi2k9v0noMb++Vab12GZjFgUXD69ilAbBh034Zwn95c2PNSz9xM7KYEAQ==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.39.0': + resolution: {integrity: sha512-jivRRlh2Lod/KvDZx2zUR+I4iBfHcu2V/BA2vasUtdtTN2Uk3jfcZczLa81ESHZHPHy4ih3T/W5rPFZ/hX7RtQ==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.39.0': + resolution: {integrity: sha512-8RXIWvYIRK9nO+bhVz8DwLBepcptw633gv/QT4015CpJ0Ht8punmoHU/DuEd3iw9Hr8UwUV+t+VNNuZIWYeY7Q==} + cpu: [x64] + os: [freebsd] + '@rollup/rollup-linux-arm-gnueabihf@4.18.0': resolution: {integrity: sha512-C/zbRYRXFjWvz9Z4haRxcTdnkPt1BtCkz+7RtBSuNmKzMzp3ZxdM28Mpccn6pt28/UWUCTXa+b0Mx1k3g6NOMA==} cpu: [arm] os: [linux] + '@rollup/rollup-linux-arm-gnueabihf@4.39.0': + resolution: {integrity: sha512-mz5POx5Zu58f2xAG5RaRRhp3IZDK7zXGk5sdEDj4o96HeaXhlUwmLFzNlc4hCQi5sGdR12VDgEUqVSHer0lI9g==} + cpu: [arm] + os: [linux] + '@rollup/rollup-linux-arm-musleabihf@4.18.0': resolution: {integrity: sha512-l3m9ewPgjQSXrUMHg93vt0hYCGnrMOcUpTz6FLtbwljo2HluS4zTXFy2571YQbisTnfTKPZ01u/ukJdQTLGh9A==} cpu: [arm] os: [linux] + '@rollup/rollup-linux-arm-musleabihf@4.39.0': + resolution: {integrity: sha512-+YDwhM6gUAyakl0CD+bMFpdmwIoRDzZYaTWV3SDRBGkMU/VpIBYXXEvkEcTagw/7VVkL2vA29zU4UVy1mP0/Yw==} + cpu: [arm] + os: [linux] + '@rollup/rollup-linux-arm64-gnu@4.18.0': resolution: {integrity: sha512-rJ5D47d8WD7J+7STKdCUAgmQk49xuFrRi9pZkWoRD1UeSMakbcepWXPF8ycChBoAqs1pb2wzvbY6Q33WmN2ftw==} cpu: [arm64] os: [linux] + '@rollup/rollup-linux-arm64-gnu@4.39.0': + resolution: {integrity: sha512-EKf7iF7aK36eEChvlgxGnk7pdJfzfQbNvGV/+l98iiMwU23MwvmV0Ty3pJ0p5WQfm3JRHOytSIqD9LB7Bq7xdQ==} + cpu: [arm64] + os: [linux] + '@rollup/rollup-linux-arm64-musl@4.18.0': resolution: {integrity: sha512-be6Yx37b24ZwxQ+wOQXXLZqpq4jTckJhtGlWGZs68TgdKXJgw54lUUoFYrg6Zs/kjzAQwEwYbp8JxZVzZLRepQ==} cpu: [arm64] os: [linux] + '@rollup/rollup-linux-arm64-musl@4.39.0': + resolution: {integrity: sha512-vYanR6MtqC7Z2SNr8gzVnzUul09Wi1kZqJaek3KcIlI/wq5Xtq4ZPIZ0Mr/st/sv/NnaPwy/D4yXg5x0B3aUUA==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loongarch64-gnu@4.39.0': + resolution: {integrity: sha512-NMRUT40+h0FBa5fb+cpxtZoGAggRem16ocVKIv5gDB5uLDgBIwrIsXlGqYbLwW8YyO3WVTk1FkFDjMETYlDqiw==} + cpu: [loong64] + os: [linux] + '@rollup/rollup-linux-powerpc64le-gnu@4.18.0': resolution: {integrity: sha512-hNVMQK+qrA9Todu9+wqrXOHxFiD5YmdEi3paj6vP02Kx1hjd2LLYR2eaN7DsEshg09+9uzWi2W18MJDlG0cxJA==} cpu: [ppc64] os: [linux] + '@rollup/rollup-linux-powerpc64le-gnu@4.39.0': + resolution: {integrity: sha512-0pCNnmxgduJ3YRt+D+kJ6Ai/r+TaePu9ZLENl+ZDV/CdVczXl95CbIiwwswu4L+K7uOIGf6tMo2vm8uadRaICQ==} + cpu: [ppc64] + os: [linux] + '@rollup/rollup-linux-riscv64-gnu@4.18.0': resolution: {integrity: sha512-ROCM7i+m1NfdrsmvwSzoxp9HFtmKGHEqu5NNDiZWQtXLA8S5HBCkVvKAxJ8U+CVctHwV2Gb5VUaK7UAkzhDjlg==} cpu: [riscv64] os: [linux] + '@rollup/rollup-linux-riscv64-gnu@4.39.0': + resolution: {integrity: sha512-t7j5Zhr7S4bBtksT73bO6c3Qa2AV/HqiGlj9+KB3gNF5upcVkx+HLgxTm8DK4OkzsOYqbdqbLKwvGMhylJCPhQ==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-musl@4.39.0': + resolution: {integrity: sha512-m6cwI86IvQ7M93MQ2RF5SP8tUjD39Y7rjb1qjHgYh28uAPVU8+k/xYWvxRO3/tBN2pZkSMa5RjnPuUIbrwVxeA==} + cpu: [riscv64] + os: [linux] + '@rollup/rollup-linux-s390x-gnu@4.18.0': resolution: {integrity: sha512-0UyyRHyDN42QL+NbqevXIIUnKA47A+45WyasO+y2bGJ1mhQrfrtXUpTxCOrfxCR4esV3/RLYyucGVPiUsO8xjg==} cpu: [s390x] os: [linux] + '@rollup/rollup-linux-s390x-gnu@4.39.0': + resolution: {integrity: sha512-iRDJd2ebMunnk2rsSBYlsptCyuINvxUfGwOUldjv5M4tpa93K8tFMeYGpNk2+Nxl+OBJnBzy2/JCscGeO507kA==} + cpu: [s390x] + os: [linux] + '@rollup/rollup-linux-x64-gnu@4.18.0': resolution: {integrity: sha512-xuglR2rBVHA5UsI8h8UbX4VJ470PtGCf5Vpswh7p2ukaqBGFTnsfzxUBetoWBWymHMxbIG0Cmx7Y9qDZzr648w==} cpu: [x64] os: [linux] + '@rollup/rollup-linux-x64-gnu@4.39.0': + resolution: {integrity: sha512-t9jqYw27R6Lx0XKfEFe5vUeEJ5pF3SGIM6gTfONSMb7DuG6z6wfj2yjcoZxHg129veTqU7+wOhY6GX8wmf90dA==} + cpu: [x64] + os: [linux] + '@rollup/rollup-linux-x64-musl@4.18.0': resolution: {integrity: sha512-LKaqQL9osY/ir2geuLVvRRs+utWUNilzdE90TpyoX0eNqPzWjRm14oMEE+YLve4k/NAqCdPkGYDaDF5Sw+xBfg==} cpu: [x64] os: [linux] + '@rollup/rollup-linux-x64-musl@4.39.0': + resolution: {integrity: sha512-ThFdkrFDP55AIsIZDKSBWEt/JcWlCzydbZHinZ0F/r1h83qbGeenCt/G/wG2O0reuENDD2tawfAj2s8VK7Bugg==} + cpu: [x64] + os: [linux] + '@rollup/rollup-win32-arm64-msvc@4.18.0': resolution: {integrity: sha512-7J6TkZQFGo9qBKH0pk2cEVSRhJbL6MtfWxth7Y5YmZs57Pi+4x6c2dStAUvaQkHQLnEQv1jzBUW43GvZW8OFqA==} cpu: [arm64] os: [win32] + '@rollup/rollup-win32-arm64-msvc@4.39.0': + resolution: {integrity: sha512-jDrLm6yUtbOg2TYB3sBF3acUnAwsIksEYjLeHL+TJv9jg+TmTwdyjnDex27jqEMakNKf3RwwPahDIt7QXCSqRQ==} + cpu: [arm64] + os: [win32] + '@rollup/rollup-win32-ia32-msvc@4.18.0': resolution: {integrity: sha512-Txjh+IxBPbkUB9+SXZMpv+b/vnTEtFyfWZgJ6iyCmt2tdx0OF5WhFowLmnh8ENGNpfUlUZkdI//4IEmhwPieNg==} cpu: [ia32] os: [win32] + '@rollup/rollup-win32-ia32-msvc@4.39.0': + resolution: {integrity: sha512-6w9uMuza+LbLCVoNKL5FSLE7yvYkq9laSd09bwS0tMjkwXrmib/4KmoJcrKhLWHvw19mwU+33ndC69T7weNNjQ==} + cpu: [ia32] + os: [win32] + '@rollup/rollup-win32-x64-msvc@4.18.0': resolution: {integrity: sha512-UOo5FdvOL0+eIVTgS4tIdbW+TtnBLWg1YBCcU2KWM7nuNwRz9bksDX1bekJJCpu25N1DVWaCwnT39dVQxzqS8g==} cpu: [x64] os: [win32] + '@rollup/rollup-win32-x64-msvc@4.39.0': + resolution: {integrity: sha512-yAkUOkIKZlK5dl7u6dg897doBgLXmUHhIINM2c+sND3DZwnrdQkkSiDh7N75Ll4mM4dxSkYfXqU9fW3lLkMFug==} + cpu: [x64] + os: [win32] + '@sideway/address@4.1.4': resolution: {integrity: sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw==} @@ -1715,39 +1731,32 @@ packages: resolution: {integrity: sha512-rUV5WyJrJLoloD4NDN1V1+LDMDWOa4OTsT4yYJwQNpTU6FWxkxHpL7eu4w+DmiH8x/EAM1otkPE1+LaspIbplw==} engines: {node: '>=18'} - '@svelte-put/shortcut@3.1.0': - resolution: {integrity: sha512-EWMEDkZ0+O3yMhb9yrqe5UYisV9CNRKX6Pl/JW3x62t74CiN+3COu1L9NzZUG0omagc2Z3J14PZNYxs77IC9NA==} + '@svelte-put/shortcut@4.1.0': + resolution: {integrity: sha512-wImNEIkbxAIWFqlfuhcbC+jRPDeRa/uJGIXHMEVVD+jqL9xCwWNnkGQJ6Qb2XVszuRLHlb8SGZDL3Io/h3vs8w==} + peerDependencies: + svelte: ^5.1.0 - '@sveltejs/adapter-auto@3.1.1': - resolution: {integrity: sha512-6LeZft2Fo/4HfmLBi5CucMYmgRxgcETweQl/yQoZo/895K3S9YWYN4Sfm/IhwlIpbJp3QNvhKmwCHbsqQNYQpw==} + '@sveltejs/acorn-typescript@1.0.5': + resolution: {integrity: sha512-IwQk4yfwLdibDlrXVE04jTZYlLnwsTT2PIOQQGNLWfjavGifnk1JD1LcZjZaBTRcxZu2FfPfNLOE04DSu9lqtQ==} + peerDependencies: + acorn: ^8.9.0 + + '@sveltejs/adapter-auto@6.0.0': + resolution: {integrity: sha512-7mR2/G7vlXakaOj6QBSG9dwBfTgWjV+UnEMB5Z6Xu0ZbdXda6c0su1fNkg0ab0zlilSkloMA2NjCna02/DR7sA==} peerDependencies: '@sveltejs/kit': ^2.0.0 - '@sveltejs/adapter-auto@3.2.1': - resolution: {integrity: sha512-/3xx8ZFCD5UBc/7AbyXkFF3HNCzWAp2xncH8HA4doGjoGQEN7PmwiRx4Y9nOzi4mqDqYYUic0gaIAE2khWWU4Q==} - peerDependencies: - '@sveltejs/kit': ^2.0.0 - - '@sveltejs/kit@2.5.10': - resolution: {integrity: sha512-OqoyTmFG2cYmCFAdBfW+Qxbg8m23H4dv6KqwEt7ofr/ROcfcIl3Z/VT56L22H9f0uNZyr+9Bs1eh2gedOCK9kA==} + '@sveltejs/kit@2.20.7': + resolution: {integrity: sha512-dVbLMubpJJSLI4OYB+yWYNHGAhgc2bVevWuBjDj8jFUXIJOAnLwYP3vsmtcgoxNGUXoq0rHS5f7MFCsryb6nzg==} engines: {node: '>=18.13'} hasBin: true peerDependencies: - '@sveltejs/vite-plugin-svelte': ^3.0.0 + '@sveltejs/vite-plugin-svelte': ^3.0.0 || ^4.0.0-next.1 || ^5.0.0 svelte: ^4.0.0 || ^5.0.0-next.0 - vite: ^5.0.3 + vite: ^5.0.3 || ^6.0.0 - '@sveltejs/kit@2.5.4': - resolution: {integrity: sha512-eDxK2d4EGzk99QsZNoPXe7jlzA5EGqfcCpUwZ912bhnalsZ2ZsG5wGRthkydupVjYyqdmzEanVKFhLxU2vkPSQ==} - engines: {node: '>=18.13'} - hasBin: true - peerDependencies: - '@sveltejs/vite-plugin-svelte': ^3.0.0 - svelte: ^4.0.0 || ^5.0.0-next.0 - vite: ^5.0.3 - - '@sveltejs/package@2.3.0': - resolution: {integrity: sha512-wmtwEfi3gQnmtotAjygRHR6cmLfpblQl1dU764f3N2I5DPe34llFs44bHOYcuk91Bp2sSq6bWUmNwxGlYCchOA==} + '@sveltejs/package@2.3.11': + resolution: {integrity: sha512-DSMt2U0XNAdoQBYksrmgQi5dKy7jUTVDJLiagS/iXF7AShjAmTbGJQKruBuT/FfYAWvNxfQTSjkXU8eAIjVeNg==} engines: {node: ^16.14 || >=18} hasBin: true peerDependencies: @@ -1761,13 +1770,13 @@ packages: svelte: ^3.54.0 || ^4.0.0 vite: ^4.0.0 - '@sveltejs/vite-plugin-svelte-inspector@2.1.0': - resolution: {integrity: sha512-9QX28IymvBlSCqsCll5t0kQVxipsfhFFL+L2t3nTWfXnddYwxBuAEtTtlaVQpRz9c37BhJjltSeY4AJSC03SSg==} - engines: {node: ^18.0.0 || >=20} + '@sveltejs/vite-plugin-svelte-inspector@4.0.1': + resolution: {integrity: sha512-J/Nmb2Q2y7mck2hyCX4ckVHcR5tu2J+MtBEQqpDrrgELZ2uvraQcK/ioCV61AqkdXFgriksOKIceDcQmqnGhVw==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22} peerDependencies: - '@sveltejs/vite-plugin-svelte': ^3.0.0 - svelte: ^4.0.0 || ^5.0.0-next.0 - vite: ^5.0.0 + '@sveltejs/vite-plugin-svelte': ^5.0.0 + svelte: ^5.0.0 + vite: ^6.0.0 '@sveltejs/vite-plugin-svelte@2.5.3': resolution: {integrity: sha512-erhNtXxE5/6xGZz/M9eXsmI7Pxa6MS7jyTy06zN3Ck++ldrppOnOlJwHHTsMC7DHDQdgUp4NAc4cDNQ9eGdB/w==} @@ -1776,12 +1785,12 @@ packages: svelte: ^3.54.0 || ^4.0.0 || ^5.0.0-next.0 vite: ^4.0.0 - '@sveltejs/vite-plugin-svelte@3.1.1': - resolution: {integrity: sha512-rimpFEAboBBHIlzISibg94iP09k/KYdHgVhJlcsTfn7KMBhc70jFX/GRWkRdFCc2fdnk+4+Bdfej23cMDnJS6A==} - engines: {node: ^18.0.0 || >=20} + '@sveltejs/vite-plugin-svelte@5.0.3': + resolution: {integrity: sha512-MCFS6CrQDu1yGwspm4qtli0e63vaPCehf6V7pIMP15AsWgMKrqDGCPFF/0kn4SP0ii4aySu4Pa62+fIRGFMjgw==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22} peerDependencies: - svelte: ^4.0.0 || ^5.0.0-next.0 - vite: ^5.0.0 + svelte: ^5.0.0 + vite: ^6.0.0 '@swc/core-darwin-arm64@1.3.96': resolution: {integrity: sha512-8hzgXYVd85hfPh6mJ9yrG26rhgzCmcLO0h1TIl8U31hwmTbfZLzRitFQ/kqMJNbIBCwmNH1RU2QcJnL3d7f69A==} @@ -1919,6 +1928,12 @@ packages: '@types/estree@1.0.5': resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} + '@types/estree@1.0.6': + resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} + + '@types/estree@1.0.7': + resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==} + '@types/hast@2.3.7': resolution: {integrity: sha512-EVLigw5zInURhzfXUM65eixfadfsHKomGKUakToXo84t8gGIJuTcD2xooM2See7GyQ7DRtYjhCHnSUQez8JaLw==} @@ -1967,9 +1982,6 @@ packages: '@types/prop-types@15.7.9': resolution: {integrity: sha512-n1yyPsugYNSmHgxDFjicaI2+gCNjsBck8UX9kuofAKlc0h1bL+20oSF72KeNaW2DUlesbEVCFgyV2dPGTiY42g==} - '@types/pug@2.0.10': - resolution: {integrity: sha512-Sk/uYFOBAB7mb74XcpizmH0KOR2Pv3D2Hmrh1Dmy5BmK3MpdSa5kqZcg6EKBdklU0bFXX9gCfzvpnyUehrPIuA==} - '@types/react-dom@18.2.14': resolution: {integrity: sha512-V835xgdSVmyQmI1KLV2BEIUgqEuinxp9O4G6g3FqO/SqLac049E53aysv0oEFD2kHfejeKU+ZqL2bcFWj9gLAQ==} @@ -2000,9 +2012,6 @@ packages: '@types/semver@6.2.5': resolution: {integrity: sha512-NAxro9/RqWXTqdSjccDZAjA4nXK+6zRun+HvibYJfGy8TQhpOC7Vv6v2rlHYKrT0Q8jGGoNRd/xVdHRIQRNlFQ==} - '@types/semver@7.5.6': - resolution: {integrity: sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==} - '@types/sinonjs__fake-timers@8.1.1': resolution: {integrity: sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g==} @@ -2021,181 +2030,99 @@ packages: '@types/yauzl@2.10.3': resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==} - '@typescript-eslint/eslint-plugin@6.10.0': - resolution: {integrity: sha512-uoLj4g2OTL8rfUQVx2AFO1hp/zja1wABJq77P6IclQs6I/m9GLrm7jCdgzZkvWdDCQf1uEvoa8s8CupsgWQgVg==} - engines: {node: ^16.0.0 || >=18.0.0} - peerDependencies: - '@typescript-eslint/parser': ^6.0.0 || ^6.0.0-alpha - eslint: ^7.0.0 || ^8.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - - '@typescript-eslint/eslint-plugin@7.2.0': - resolution: {integrity: sha512-mdekAHOqS9UjlmyF/LSs6AIEvfceV749GFxoBAjwAv0nkevfKHWQFDMcBZWUiIC5ft6ePWivXoS36aKQ0Cy3sw==} - engines: {node: ^16.0.0 || >=18.0.0} - peerDependencies: - '@typescript-eslint/parser': ^7.0.0 - eslint: ^8.56.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - - '@typescript-eslint/eslint-plugin@8.0.0': - resolution: {integrity: sha512-STIZdwEQRXAHvNUS6ILDf5z3u95Gc8jzywunxSNqX00OooIemaaNIA0vEgynJlycL5AjabYLLrIyHd4iazyvtg==} + '@typescript-eslint/eslint-plugin@8.29.1': + resolution: {integrity: sha512-ba0rr4Wfvg23vERs3eB+P3lfj2E+2g3lhWcCVukUuhtcdUx5lSIFZlGFEBHKr+3zizDa/TvZTptdNHVZWAkSBg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0 eslint: ^8.57.0 || ^9.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/parser@6.10.0': - resolution: {integrity: sha512-+sZwIj+s+io9ozSxIWbNB5873OSdfeBEH/FR0re14WLI6BaKuSOnnwCJ2foUiu8uXf4dRp1UqHP0vrZ1zXGrog==} - engines: {node: ^16.0.0 || >=18.0.0} + '@typescript-eslint/eslint-plugin@8.30.1': + resolution: {integrity: sha512-v+VWphxMjn+1t48/jO4t950D6KR8JaJuNXzi33Ve6P8sEmPr5k6CEXjdGwT6+LodVnEa91EQCtwjWNUCPweo+Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - eslint: ^7.0.0 || ^8.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0 + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/parser@7.2.0': - resolution: {integrity: sha512-5FKsVcHTk6TafQKQbuIVkXq58Fnbkd2wDL4LB7AURN7RUOu1utVP+G8+6u3ZhEroW3DF6hyo3ZEXxgKgp4KeCg==} - engines: {node: ^16.0.0 || >=18.0.0} - peerDependencies: - eslint: ^8.56.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - - '@typescript-eslint/parser@8.0.0': - resolution: {integrity: sha512-pS1hdZ+vnrpDIxuFXYQpLTILglTjSYJ9MbetZctrUawogUsPdz31DIIRZ9+rab0LhYNTsk88w4fIzVheiTbWOQ==} + '@typescript-eslint/parser@8.29.1': + resolution: {integrity: sha512-zczrHVEqEaTwh12gWBIJWj8nx+ayDcCJs06yoNMY0kwjMWDM6+kppljY+BxWI06d2Ja+h4+WdufDcwMnnMEWmg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/scope-manager@6.10.0': - resolution: {integrity: sha512-TN/plV7dzqqC2iPNf1KrxozDgZs53Gfgg5ZHyw8erd6jd5Ta/JIEcdCheXFt9b1NYb93a1wmIIVW/2gLkombDg==} - engines: {node: ^16.0.0 || >=18.0.0} - - '@typescript-eslint/scope-manager@7.2.0': - resolution: {integrity: sha512-Qh976RbQM/fYtjx9hs4XkayYujB/aPwglw2choHmf3zBjB4qOywWSdt9+KLRdHubGcoSwBnXUH2sR3hkyaERRg==} - engines: {node: ^16.0.0 || >=18.0.0} - - '@typescript-eslint/scope-manager@8.0.0': - resolution: {integrity: sha512-V0aa9Csx/ZWWv2IPgTfY7T4agYwJyILESu/PVqFtTFz9RIS823mAze+NbnBI8xiwdX3iqeQbcTYlvB04G9wyQw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@typescript-eslint/type-utils@6.10.0': - resolution: {integrity: sha512-wYpPs3hgTFblMYwbYWPT3eZtaDOjbLyIYuqpwuLBBqhLiuvJ+9sEp2gNRJEtR5N/c9G1uTtQQL5AhV0fEPJYcg==} - engines: {node: ^16.0.0 || >=18.0.0} - peerDependencies: - eslint: ^7.0.0 || ^8.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - - '@typescript-eslint/type-utils@7.2.0': - resolution: {integrity: sha512-xHi51adBHo9O9330J8GQYQwrKBqbIPJGZZVQTHHmy200hvkLZFWJIFtAG/7IYTWUyun6DE6w5InDReePJYJlJA==} - engines: {node: ^16.0.0 || >=18.0.0} - peerDependencies: - eslint: ^8.56.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - - '@typescript-eslint/type-utils@8.0.0': - resolution: {integrity: sha512-mJAFP2mZLTBwAn5WI4PMakpywfWFH5nQZezUQdSKV23Pqo6o9iShQg1hP2+0hJJXP2LnZkWPphdIq4juYYwCeg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - - '@typescript-eslint/types@6.10.0': - resolution: {integrity: sha512-36Fq1PWh9dusgo3vH7qmQAj5/AZqARky1Wi6WpINxB6SkQdY5vQoT2/7rW7uBIsPDcvvGCLi4r10p0OJ7ITAeg==} - engines: {node: ^16.0.0 || >=18.0.0} - - '@typescript-eslint/types@7.2.0': - resolution: {integrity: sha512-XFtUHPI/abFhm4cbCDc5Ykc8npOKBSJePY3a3s+lwumt7XWJuzP5cZcfZ610MIPHjQjNsOLlYK8ASPaNG8UiyA==} - engines: {node: ^16.0.0 || >=18.0.0} - - '@typescript-eslint/types@8.0.0': - resolution: {integrity: sha512-wgdSGs9BTMWQ7ooeHtu5quddKKs5Z5dS+fHLbrQI+ID0XWJLODGMHRfhwImiHoeO2S5Wir2yXuadJN6/l4JRxw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@typescript-eslint/typescript-estree@6.10.0': - resolution: {integrity: sha512-ek0Eyuy6P15LJVeghbWhSrBCj/vJpPXXR+EpaRZqou7achUWL8IdYnMSC5WHAeTWswYQuP2hAZgij/bC9fanBg==} - engines: {node: ^16.0.0 || >=18.0.0} - peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - - '@typescript-eslint/typescript-estree@7.2.0': - resolution: {integrity: sha512-cyxS5WQQCoBwSakpMrvMXuMDEbhOo9bNHHrNcEWis6XHx6KF518tkF1wBvKIn/tpq5ZpUYK7Bdklu8qY0MsFIA==} - engines: {node: ^16.0.0 || >=18.0.0} - peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - - '@typescript-eslint/typescript-estree@8.0.0': - resolution: {integrity: sha512-5b97WpKMX+Y43YKi4zVcCVLtK5F98dFls3Oxui8LbnmRsseKenbbDinmvxrWegKDMmlkIq/XHuyy0UGLtpCDKg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - - '@typescript-eslint/utils@6.10.0': - resolution: {integrity: sha512-v+pJ1/RcVyRc0o4wAGux9x42RHmAjIGzPRo538Z8M1tVx6HOnoQBCX/NoadHQlZeC+QO2yr4nNSFWOoraZCAyg==} - engines: {node: ^16.0.0 || >=18.0.0} - peerDependencies: - eslint: ^7.0.0 || ^8.0.0 - - '@typescript-eslint/utils@7.2.0': - resolution: {integrity: sha512-YfHpnMAGb1Eekpm3XRK8hcMwGLGsnT6L+7b2XyRv6ouDuJU1tZir1GS2i0+VXRatMwSI1/UfcyPe53ADkU+IuA==} - engines: {node: ^16.0.0 || >=18.0.0} - peerDependencies: - eslint: ^8.56.0 - - '@typescript-eslint/utils@8.0.0': - resolution: {integrity: sha512-k/oS/A/3QeGLRvOWCg6/9rATJL5rec7/5s1YmdS0ZU6LHveJyGFwBvLhSRBv6i9xaj7etmosp+l+ViN1I9Aj/Q==} + '@typescript-eslint/parser@8.30.1': + resolution: {integrity: sha512-H+vqmWwT5xoNrXqWs/fesmssOW70gxFlgcMlYcBaWNPIEWDgLa4W9nkSPmhuOgLnXq9QYgkZ31fhDyLhleCsAg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/visitor-keys@6.10.0': - resolution: {integrity: sha512-xMGluxQIEtOM7bqFCo+rCMh5fqI+ZxV5RUUOa29iVPz1OgCZrtc7rFnz5cLUazlkPKYqX+75iuDq7m0HQ48nCg==} - engines: {node: ^16.0.0 || >=18.0.0} - - '@typescript-eslint/visitor-keys@7.2.0': - resolution: {integrity: sha512-c6EIQRHhcpl6+tO8EMR+kjkkV+ugUNXOmeASA1rlzkd8EPIriavpWoiEz1HR/VLhbVIdhqnV6E7JZm00cBDx2A==} - engines: {node: ^16.0.0 || >=18.0.0} - - '@typescript-eslint/visitor-keys@8.0.0': - resolution: {integrity: sha512-oN0K4nkHuOyF3PVMyETbpP5zp6wfyOvm7tWhTMfoqxSSsPmJIh6JNASuZDlODE8eE+0EB9uar+6+vxr9DBTYOA==} + '@typescript-eslint/scope-manager@8.29.1': + resolution: {integrity: sha512-2nggXGX5F3YrsGN08pw4XpMLO1Rgtnn4AzTegC2MDesv6q3QaTU5yU7IbS1tf1IwCR0Hv/1EFygLn9ms6LIpDA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@ungap/structured-clone@1.2.0': - resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} + '@typescript-eslint/scope-manager@8.30.1': + resolution: {integrity: sha512-+C0B6ChFXZkuaNDl73FJxRYT0G7ufVPOSQkqkpM/U198wUwUFOtgo1k/QzFh1KjpBitaK7R1tgjVz6o9HmsRPg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/type-utils@8.29.1': + resolution: {integrity: sha512-DkDUSDwZVCYN71xA4wzySqqcZsHKic53A4BLqmrWFFpOpNSoxX233lwGu/2135ymTCR04PoKiEEEvN1gFYg4Tw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/type-utils@8.30.1': + resolution: {integrity: sha512-64uBF76bfQiJyHgZISC7vcNz3adqQKIccVoKubyQcOnNcdJBvYOILV1v22Qhsw3tw3VQu5ll8ND6hycgAR5fEA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/types@8.29.1': + resolution: {integrity: sha512-VT7T1PuJF1hpYC3AGm2rCgJBjHL3nc+A/bhOp9sGMKfi5v0WufsX/sHCFBfNTx2F+zA6qBc/PD0/kLRLjdt8mQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/types@8.30.1': + resolution: {integrity: sha512-81KawPfkuulyWo5QdyG/LOKbspyyiW+p4vpn4bYO7DM/hZImlVnFwrpCTnmNMOt8CvLRr5ojI9nU1Ekpw4RcEw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@8.29.1': + resolution: {integrity: sha512-l1enRoSaUkQxOQnbi0KPUtqeZkSiFlqrx9/3ns2rEDhGKfTa+88RmXqedC1zmVTOWrLc2e6DEJrTA51C9iLH5g==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/typescript-estree@8.30.1': + resolution: {integrity: sha512-kQQnxymiUy9tTb1F2uep9W6aBiYODgq5EMSk6Nxh4Z+BDUoYUSa029ISs5zTzKBFnexQEh71KqwjKnRz58lusQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/utils@8.29.1': + resolution: {integrity: sha512-QAkFEbytSaB8wnmB+DflhUPz6CLbFWE2SnSCrRMEa+KnXIzDYbpsn++1HGvnfAsUY44doDXmvRkO5shlM/3UfA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/utils@8.30.1': + resolution: {integrity: sha512-T/8q4R9En2tcEsWPQgB5BQ0XJVOtfARcUvOa8yJP3fh9M/mXraLxZrkCfGb6ChrO/V3W+Xbd04RacUEqk1CFEQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/visitor-keys@8.29.1': + resolution: {integrity: sha512-RGLh5CRaUEf02viP5c1Vh1cMGffQscyHe7HPAzGpfmfflFg1wUz2rYxd+OZqwpeypYvZ8UxSxuIpF++fmOzEcg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/visitor-keys@8.30.1': + resolution: {integrity: sha512-aEhgas7aJ6vZnNFC7K4/vMGDGyOiqWcYZPpIWrTKuTAlsvDNKy2GFDqh9smL+iq069ZvR0YzEeq0B8NJlLzjFA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@vitejs/plugin-react-swc@3.4.1': resolution: {integrity: sha512-7YQOQcVV5x1luD8nkbCDdyYygFvn1hjqJk68UvNAzY2QG4o4N5EwAhLLFNOcd1HrdMwDl0VElP8VutoWf9IvJg==} @@ -2229,8 +2156,8 @@ packages: engines: {node: '>=0.4.0'} hasBin: true - acorn@8.11.3: - resolution: {integrity: sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==} + acorn@8.14.0: + resolution: {integrity: sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==} engines: {node: '>=0.4.0'} hasBin: true @@ -2294,6 +2221,10 @@ packages: aria-query@5.3.0: resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} + aria-query@5.3.2: + resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} + engines: {node: '>= 0.4'} + array-buffer-byte-length@1.0.0: resolution: {integrity: sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==} @@ -2301,8 +2232,8 @@ packages: resolution: {integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==} engines: {node: '>= 0.4'} - array-includes@3.1.7: - resolution: {integrity: sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==} + array-buffer-byte-length@1.0.2: + resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} engines: {node: '>= 0.4'} array-includes@3.1.8: @@ -2324,13 +2255,10 @@ packages: resolution: {integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==} engines: {node: '>= 0.4'} - array.prototype.flatmap@1.3.2: - resolution: {integrity: sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==} + array.prototype.flatmap@1.3.3: + resolution: {integrity: sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==} engines: {node: '>= 0.4'} - array.prototype.tosorted@1.1.2: - resolution: {integrity: sha512-HuQCHOlk1Weat5jzStICBCd83NxiIMwqDg/dHEsoefabn/hJRj5pVdWcPUSpRrwhwxZOsQassMpgN/xRYFBMIg==} - array.prototype.tosorted@1.1.4: resolution: {integrity: sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==} engines: {node: '>= 0.4'} @@ -2343,6 +2271,10 @@ packages: resolution: {integrity: sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==} engines: {node: '>= 0.4'} + arraybuffer.prototype.slice@1.0.4: + resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} + engines: {node: '>= 0.4'} + arrify@1.0.1: resolution: {integrity: sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==} engines: {node: '>=0.10.0'} @@ -2366,9 +2298,6 @@ packages: async@3.2.5: resolution: {integrity: sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==} - asynciterator.prototype@1.0.0: - resolution: {integrity: sha512-wwHYEIS0Q80f5mosx3L/dfG5t5rjEa9Ft51GTaNt862EnpyGHpgz2RkZvLPp1oF5TnAiTohkEKVEu8pQPJI7Vg==} - asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} @@ -2383,8 +2312,8 @@ packages: peerDependencies: postcss: ^8.1.0 - autoprefixer@10.4.18: - resolution: {integrity: sha512-1DKbDfsr6KUElM6wg+0zRNkB/Q7WcKYAaK+pzXn+Xqmszm/5Xa9coeNdtP88Vi+dPzZnMjhge8GIV49ZQkDa+g==} + autoprefixer@10.4.21: + resolution: {integrity: sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==} engines: {node: ^10 || ^12 || >=14} hasBin: true peerDependencies: @@ -2410,8 +2339,9 @@ packages: axobject-query@3.2.1: resolution: {integrity: sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg==} - axobject-query@4.0.0: - resolution: {integrity: sha512-+60uv1hiVFhHZeO+Lz0RYzsVHy5Wr1ayX0mwda9KPDVLNJgZ1T9Ny7VmFbLDzxsH0D87I86vgj3gFrjTJUYznw==} + axobject-query@4.1.0: + resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} + engines: {node: '>= 0.4'} b4a@1.6.4: resolution: {integrity: sha512-fpWrvyVHEKyeEvbKZTVOeZF3VSKKWtJxFIxX/jaVPf+cLbGUSitjb49pHLqPV2BUNNZ0LcoeEGfE/YCpyDYHIw==} @@ -2473,13 +2403,13 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true - browserslist@4.23.0: - resolution: {integrity: sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==} + browserslist@4.24.2: + resolution: {integrity: sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true - browserslist@4.23.1: - resolution: {integrity: sha512-TUfofFo/KsK/bWZ9TWQ5O26tsWW4Uhmt8IYklbnUa70udB6P2wA7w7o4PY4muaEPBQaAX+CEnmmIA41NVHtPVw==} + browserslist@4.24.4: + resolution: {integrity: sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true @@ -2503,6 +2433,10 @@ packages: resolution: {integrity: sha512-9EtFOZR8g22CL7BWjJ9BUx1+A/djkofnyW3aOXZORNW2kxoUpx2h+uN2cOqwPmFhnpVmxg+KW2OjOSgChTEvsQ==} engines: {node: '>=6'} + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + call-bind@1.0.5: resolution: {integrity: sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==} @@ -2510,6 +2444,14 @@ packages: resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} engines: {node: '>= 0.4'} + call-bind@1.0.8: + resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} + engines: {node: '>= 0.4'} + + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} @@ -2532,11 +2474,11 @@ packages: caniuse-lite@1.0.30001553: resolution: {integrity: sha512-N0ttd6TrFfuqKNi+pMgWJTb9qrdJu4JSpgPFLe/lrD19ugC6fZgF0pUewRowDwzdDnb9V41mFcdlYgl/PyKf4A==} - caniuse-lite@1.0.30001594: - resolution: {integrity: sha512-VblSX6nYqyJVs8DKFMldE2IVCJjZ225LW00ydtUWwh5hk9IfkTOffO6r8gJNsH0qqqeAF8KrbMYA2VEwTlGW5g==} + caniuse-lite@1.0.30001680: + resolution: {integrity: sha512-rPQy70G6AGUMnbwS1z6Xg+RkHYPAi18ihs47GH0jcxIG7wArmPgY3XbS2sRdBbxJljp3thdT8BIqv9ccCypiPA==} - caniuse-lite@1.0.30001639: - resolution: {integrity: sha512-eFHflNTBIlFwP2AIKaYuBQN/apnUoKNhBdza8ZnW/h2di4LCZ4xFqYlxUxo+LQ76KFI1PGcC1QDxMbxTZpSCAg==} + caniuse-lite@1.0.30001712: + resolution: {integrity: sha512-MBqPpGYYdQ7/hfKiet9SCI+nmN5/hp4ZzveOJubl5DTAMa5oggjAuoi0Z4onBpKPFI2ePGnQuQIzF3VxDjDJig==} caseless@0.12.0: resolution: {integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==} @@ -2576,9 +2518,9 @@ packages: resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} engines: {node: '>= 8.10.0'} - chokidar@3.6.0: - resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} - engines: {node: '>= 8.10.0'} + chokidar@4.0.3: + resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} + engines: {node: '>= 14.16.0'} chownr@1.1.4: resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} @@ -2633,6 +2575,10 @@ packages: resolution: {integrity: sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==} engines: {node: '>=6'} + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + code-red@1.0.4: resolution: {integrity: sha512-7qJWqItLA8/VPVlKJlFXU+NBlo/qyfs39aJcuMT/2ere32ZqvF5OSxgdM5xOfJJ7O429gg2HM47y8v9P+9wrNw==} @@ -2719,14 +2665,18 @@ packages: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + css-declaration-sorter@6.4.1: resolution: {integrity: sha512-rtdthzxKuyq6IzqX6jEcIzQF/YqccluefyCYheovBOLhFT/drQA9zj/UbRAa9J7C0o6EG6u3E6g+vKkay7/k3g==} engines: {node: ^10 || ^12 || >=14} peerDependencies: postcss: ^8.0.9 - css-declaration-sorter@7.1.1: - resolution: {integrity: sha512-dZ3bVTEEc1vxr3Bek9vGwfB5Z6ESPULhcRvO472mfjVnj8jRcTnKO8/JTczlvxM10Myb+wBM++1MtdO76eWcaQ==} + css-declaration-sorter@7.2.0: + resolution: {integrity: sha512-h70rUM+3PNFuaBDTLe8wF/cdWu+dOZmb7pJt8Z2sedYbAcQVQV/tEchueg3GWxwqS0cxtbxmaHEdkNACqcvsow==} engines: {node: ^14 || ^16 || >=18} peerDependencies: postcss: ^8.0.9 @@ -2757,9 +2707,9 @@ packages: peerDependencies: postcss: ^8.2.15 - cssnano-preset-default@6.1.0: - resolution: {integrity: sha512-4DUXZoDj+PI3fRl3MqMjl9DwLGjcsFP4qt+92nLUcN1RGfw2TY+GwNoG2B38Usu1BrcTs8j9pxNfSusmvtSjfg==} - engines: {node: ^14 || ^16 || >=18.0} + cssnano-preset-default@7.0.6: + resolution: {integrity: sha512-ZzrgYupYxEvdGGuqL+JKOY70s7+saoNlHSCK/OGn1vB2pQK8KSET8jvenzItcY+kA7NoWvfbb/YhlzuzNKjOhQ==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.4.31 @@ -2769,9 +2719,9 @@ packages: peerDependencies: postcss: ^8.2.15 - cssnano-utils@4.0.2: - resolution: {integrity: sha512-ZR1jHg+wZ8o4c3zqf1SIUSTIvm/9mU343FMR6Obe/unskbvpGhZOo1J6d/r8D1pzkRQYuwbcH3hToOuoA2G7oQ==} - engines: {node: ^14 || ^16 || >=18.0} + cssnano-utils@5.0.0: + resolution: {integrity: sha512-Uij0Xdxc24L6SirFr25MlwC2rCFX6scyUmuKpzI+JQ7cyqDEwD42fJ0xfB3yLfOnRDU5LKGgjQ9FA6LYh76GWQ==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.4.31 @@ -2781,9 +2731,9 @@ packages: peerDependencies: postcss: ^8.2.15 - cssnano@6.1.0: - resolution: {integrity: sha512-e2v4w/t3OFM6HTuSweI4RSdABaqgVgHlJp5FZrQsopHnKKHLFIvK2D3C4kHWeFIycN/1L1J5VIrg5KlDzn3r/g==} - engines: {node: ^14 || ^16 || >=18.0} + cssnano@7.0.6: + resolution: {integrity: sha512-54woqx8SCbp8HwvNZYn68ZFAepuouZW4lTwiMVnBErM3VkO7/Sd4oTOt3Zz3bPx3kxQ36aISppyXj2Md4lg8bw==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.4.31 @@ -2866,14 +2816,26 @@ packages: resolution: {integrity: sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==} engines: {node: '>= 0.4'} + data-view-buffer@1.0.2: + resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} + engines: {node: '>= 0.4'} + data-view-byte-length@1.0.1: resolution: {integrity: sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==} engines: {node: '>= 0.4'} + data-view-byte-length@1.0.2: + resolution: {integrity: sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==} + engines: {node: '>= 0.4'} + data-view-byte-offset@1.0.0: resolution: {integrity: sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==} engines: {node: '>= 0.4'} + data-view-byte-offset@1.0.1: + resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} + engines: {node: '>= 0.4'} + dataloader@1.4.0: resolution: {integrity: sha512-68s5jYdlvasItOJnCuI2Q9s4q98g0pCyL3HrcKJu8KNugUl8ahgmZYg38ysLTgQjjXX3H8CJLkAvWrclWfcalw==} @@ -2918,6 +2880,24 @@ packages: supports-color: optional: true + debug@4.3.7: + resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.4.0: + resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + decamelize-keys@1.1.1: resolution: {integrity: sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==} engines: {node: '>=0.10.0'} @@ -2970,6 +2950,10 @@ packages: resolution: {integrity: sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==} engines: {node: '>= 0.6.0'} + dependency-graph@1.0.0: + resolution: {integrity: sha512-cW3gggJ28HZ/LExwxP2B++aiKxhJXMSIt9K48FOXQkm+vuG5gyatXnLsONRJdzO/7VfjDIiaOOa/bs4l464Lwg==} + engines: {node: '>=4'} + dequal@2.0.3: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} @@ -2985,8 +2969,8 @@ packages: devalue@4.3.2: resolution: {integrity: sha512-KqFl6pOgOW+Y6wJgu80rHpo2/3H07vr8ntR9rkkFIRETewbf5GaYYcakYfiKz89K+sLsuPkQIZaXDMjUObZwWg==} - devalue@5.0.0: - resolution: {integrity: sha512-gO+/OMXF7488D+u3ue+G7Y4AA3ZmUnB3eHJXmBTgNHvr4ZNzl36A0ZtG+XCRNYCkYx/bFmw4qtkoFLa+wSrwAA==} + devalue@5.1.1: + resolution: {integrity: sha512-maua5KUiapvEwiEAe+XnlZ3Rh0GD+qI1J/nb9vrJc3muPXvcF/8gXYTWF76+5DAqHyDUtOIImEuo0YKE9mshVw==} diff@5.1.0: resolution: {integrity: sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==} @@ -3024,8 +3008,8 @@ packages: resolution: {integrity: sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==} engines: {node: '>=12'} - dotenv@16.4.5: - resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} + dotenv@16.5.0: + resolution: {integrity: sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==} engines: {node: '>=12'} dotenv@8.6.0: @@ -3036,6 +3020,10 @@ packages: resolution: {integrity: sha512-g/M9sqy3oHe477Ar4voQxWtaPIFw1jTdKZuomOjhCcBx9nHUNn0pu6NopuFFrTh/TRZIKEj+76vLWFu9BNKk+Q==} engines: {node: '>=4'} + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + duplexer@0.1.2: resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==} @@ -3048,11 +3036,11 @@ packages: electron-to-chromium@1.4.563: resolution: {integrity: sha512-dg5gj5qOgfZNkPNeyKBZQAQitIQ/xwfIDmEQJHCbXaD9ebTZxwJXUsDYcBlAvZGZLi+/354l35J1wkmP6CqYaw==} - electron-to-chromium@1.4.693: - resolution: {integrity: sha512-/if4Ueg0GUQlhCrW2ZlXwDAm40ipuKo+OgeHInlL8sbjt+hzISxZK949fZeJaVsheamrzANXvw1zQTvbxTvSHw==} + electron-to-chromium@1.5.134: + resolution: {integrity: sha512-zSwzrLg3jNP3bwsLqWHmS5z2nIOQ5ngMnfMZOWWtXnqqQkPVyOipxK98w+1beLw1TB+EImPNcG8wVP/cLVs2Og==} - electron-to-chromium@1.4.815: - resolution: {integrity: sha512-OvpTT2ItpOXJL7IGcYakRjHCt8L5GrrN/wHCQsRB4PQa1X9fe+X9oen245mIId7s14xvArCGSTIq644yPUKKLg==} + electron-to-chromium@1.5.56: + resolution: {integrity: sha512-7lXb9dAvimCFdvUMTyucD4mnIndt/xhRKFAlky0CyFogdnNmdPQNoHI23msF/2V4mpTxMzgMdjK4+YRlFlRQZw==} emoji-regex@10.3.0: resolution: {integrity: sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==} @@ -3085,19 +3073,24 @@ packages: resolution: {integrity: sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==} engines: {node: '>= 0.4'} + es-abstract@1.23.9: + resolution: {integrity: sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==} + engines: {node: '>= 0.4'} + es-define-property@1.0.0: resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==} engines: {node: '>= 0.4'} + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + es-errors@1.3.0: resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} engines: {node: '>= 0.4'} - es-iterator-helpers@1.0.15: - resolution: {integrity: sha512-GhoY8uYqd6iwUl2kgjTm4CZAf6oo5mHK7BPqx3rKgx893YSsy0LGHV6gfqqQvZt/8xM8xeOnfXBCfqclMKkJ5g==} - - es-iterator-helpers@1.0.19: - resolution: {integrity: sha512-zoMwbCcH5hwUkKJkT8kDIBZSz9I6mVG//+lDCinLCGov4+r7NIy0ld8o03M0cJxl2spVf6ESYVS6/gpIfq1FFw==} + es-iterator-helpers@1.2.1: + resolution: {integrity: sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==} engines: {node: '>= 0.4'} es-module-lexer@1.3.1: @@ -3107,6 +3100,10 @@ packages: resolution: {integrity: sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==} engines: {node: '>= 0.4'} + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + es-set-tostringtag@2.0.2: resolution: {integrity: sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q==} engines: {node: '>= 0.4'} @@ -3115,6 +3112,10 @@ packages: resolution: {integrity: sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==} engines: {node: '>= 0.4'} + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + es-shim-unscopables@1.0.2: resolution: {integrity: sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==} @@ -3122,8 +3123,9 @@ packages: resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} engines: {node: '>= 0.4'} - es6-promise@3.3.1: - resolution: {integrity: sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==} + es-to-primitive@1.3.0: + resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} + engines: {node: '>= 0.4'} esbuild@0.18.20: resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==} @@ -3135,22 +3137,17 @@ packages: engines: {node: '>=12'} hasBin: true - esbuild@0.20.2: - resolution: {integrity: sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==} - engines: {node: '>=12'} - hasBin: true - - esbuild@0.21.5: - resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} - engines: {node: '>=12'} + esbuild@0.25.2: + resolution: {integrity: sha512-16854zccKPnC+toMywC+uKNeYSv+/eXkevRAfwRD/G9Cleq66m8XFIrigkbvauLLlCfDL45Q2cWegSg53gGBnQ==} + engines: {node: '>=18'} hasBin: true escalade@3.1.1: resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} engines: {node: '>=6'} - escalade@3.1.2: - resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} escape-string-regexp@1.0.5: @@ -3165,34 +3162,23 @@ packages: resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} engines: {node: '>=12'} - eslint-compat-utils@0.1.2: - resolution: {integrity: sha512-Jia4JDldWnFNIru1Ehx1H5s9/yxiRHY/TimCuUc0jNexew3cF1gI6CYZil1ociakfWO3rRqFjl1mskBblB3RYg==} - engines: {node: '>=12'} - peerDependencies: - eslint: '>=6.0.0' - - eslint-config-prettier@8.8.0: - resolution: {integrity: sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA==} + eslint-config-prettier@10.1.1: + resolution: {integrity: sha512-4EQQr6wXwS+ZJSzaR5ZCrYgLxqvUjdXctaEtBqHcbkW944B1NQyO4qpdHQbXBONfwxXdkAY81HH4+LUfrg+zPw==} hasBin: true peerDependencies: eslint: '>=7.0.0' - eslint-config-prettier@9.0.0: - resolution: {integrity: sha512-IcJsTkJae2S35pRsRAwoCE+925rJJStOdkKnLVgtE+tEpqU0EVVM7OqrwxqgptKdX29NUwC82I5pXsGFIgSevw==} + eslint-config-prettier@10.1.2: + resolution: {integrity: sha512-Epgp/EofAUeEpIdZkW60MHKvPyru1ruQJxPL+WIycnaPApuseK0Zpkrh/FwL9oIpQvIhJwV7ptOy0DWUjTlCiA==} hasBin: true peerDependencies: eslint: '>=7.0.0' - eslint-config-prettier@9.1.0: - resolution: {integrity: sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==} - hasBin: true - peerDependencies: - eslint: '>=7.0.0' - - eslint-config-turbo@2.0.3: - resolution: {integrity: sha512-D1+lNOpTFEuAgPWJfRHXHjzvAfO+0TVmORfftmYQNw+uk2UIBjhelhwERBceYFy2oFJnckHsqt69dp/zIM6/0g==} + eslint-config-turbo@2.5.0: + resolution: {integrity: sha512-QJvZBEWDWQx1JyQCr0uwf4aQYhDSAGoHBdx+cPtpPzNEjZw16Ig8BglXxHZBh3I8/fI1U53cLgXwvb28BUZhPA==} peerDependencies: eslint: '>6.6.0' + turbo: '>2.0.0' eslint-plugin-prettier@4.2.1: resolution: {integrity: sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==} @@ -3205,68 +3191,65 @@ packages: eslint-config-prettier: optional: true - eslint-plugin-react@7.33.2: - resolution: {integrity: sha512-73QQMKALArI8/7xGLNI/3LylrEYrlKZSb5C9+q3OtOewTnMQi5cT+aE9E41sLCmli3I9PGGmD1yiZydyo4FEPw==} - engines: {node: '>=4'} - peerDependencies: - eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 - - eslint-plugin-react@7.35.0: - resolution: {integrity: sha512-v501SSMOWv8gerHkk+IIQBkcGRGrO2nfybfj5pLxuJNFTPxxA3PSryhXTK+9pNbtkggheDdsC0E9Q8CuPk6JKA==} + eslint-plugin-react@7.37.5: + resolution: {integrity: sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==} engines: {node: '>=4'} peerDependencies: eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7 - eslint-plugin-svelte@2.35.0: - resolution: {integrity: sha512-3WDFxNrkXaMlpqoNo3M1ZOQuoFLMO9+bdnN6oVVXaydXC7nzCJuGy9a0zqoNDHMSRPYt0Rqo6hIdHMEaI5sQnw==} - engines: {node: ^14.17.0 || >=16.0.0} + eslint-plugin-svelte@3.5.1: + resolution: {integrity: sha512-Qn1slddZHfqYiDO6IN8/iN3YL+VuHlgYjm30FT+hh0Jf/TX0jeZMTJXQMajFm5f6f6hURi+XO8P+NPYD+T4jkg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - eslint: ^7.0.0 || ^8.0.0-0 - svelte: ^3.37.0 || ^4.0.0 + eslint: ^8.57.1 || ^9.0.0 + svelte: ^3.37.0 || ^4.0.0 || ^5.0.0 peerDependenciesMeta: svelte: optional: true - eslint-plugin-svelte@2.35.1: - resolution: {integrity: sha512-IF8TpLnROSGy98Z3NrsKXWDSCbNY2ReHDcrYTuXZMbfX7VmESISR78TWgO9zdg4Dht1X8coub5jKwHzP0ExRug==} - engines: {node: ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^7.0.0 || ^8.0.0-0 - svelte: ^3.37.0 || ^4.0.0 - peerDependenciesMeta: - svelte: - optional: true - - eslint-plugin-turbo@2.0.3: - resolution: {integrity: sha512-mplP4nYaRvtTNuwF5QTLYKLu0/8LTRsHPgX4ARhaof+QZI2ttglONe1/iJpKB4pg0KqFp7WHziKoJL+s0+CJ1w==} + eslint-plugin-turbo@2.5.0: + resolution: {integrity: sha512-qQk54MrUZv0gnpxV23sccTc+FL3UJ8q7vG7HmXuS2RP8gdjWDwI1CCJTJD8EdRIDjsMxF0xi0AKcMY0CwIlXVg==} peerDependencies: eslint: '>6.6.0' + turbo: '>2.0.0' eslint-scope@7.2.2: resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + eslint-scope@8.3.0: + resolution: {integrity: sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + eslint-visitor-keys@3.4.3: resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + eslint-visitor-keys@4.2.0: + resolution: {integrity: sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + eslint@8.43.0: resolution: {integrity: sha512-aaCpf2JqqKesMFGgmRPessmVKjcGXqdlAYLLC3THM8t5nBRZRQ+st5WM/hoJXkdioEXLLbXgclUpM0TXo5HX5Q==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} hasBin: true - eslint@8.53.0: - resolution: {integrity: sha512-N4VuiPjXDUa4xVeV/GC/RV3hQW9Nw+Y463lkWaKKXKYMvmRiRDAtfpuPFLN+E1/6ZhyR8J2ig+eVREnYgUsiag==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + eslint@9.24.0: + resolution: {integrity: sha512-eh/jxIEJyZrvbWRe4XuVclLPDYSYYYgLy5zXGGxD6j8zjSAxFEzI2fL/8xNq6O2yKqVt+eF2YhV+hxjV6UKXwQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true - eslint@8.57.0: - resolution: {integrity: sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - hasBin: true + esm-env@1.2.2: + resolution: {integrity: sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==} - esm-env@1.0.0: - resolution: {integrity: sha512-Cf6VksWPsTuW01vU9Mk/3vRue91Zevka5SjyNf3nEpokFRuqt/KjUQoGAwq9qMmhpLTHmXzSIrFRw8zxWzmFBA==} + espree@10.3.0: + resolution: {integrity: sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} espree@9.6.1: resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} @@ -3281,6 +3264,9 @@ packages: resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} engines: {node: '>=0.10'} + esrap@1.4.6: + resolution: {integrity: sha512-F/D2mADJ9SHY3IwksD4DAXjTt7qt7GWUf3/8RhCNWmC/67tyb55dpimHmy7EplakFaflV0R/PC+fdSPqrRHAQw==} + esrecurse@4.3.0: resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} engines: {node: '>=4.0'} @@ -3377,6 +3363,14 @@ packages: fd-slicer@1.1.0: resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} + fdir@6.4.3: + resolution: {integrity: sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + figures@3.2.0: resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==} engines: {node: '>=8'} @@ -3385,6 +3379,10 @@ packages: resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} engines: {node: ^10.12.0 || >=12.0.0} + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + fill-range@7.0.1: resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} engines: {node: '>=8'} @@ -3404,6 +3402,10 @@ packages: resolution: {integrity: sha512-/qM2b3LUIaIgviBQovTLvijfyOQXPtSRnRK26ksj2J7rzPIecePUIpJsZ4T02Qg+xiAEKIs5K8dsHEd+VaKa/Q==} engines: {node: '>=12.0.0'} + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + flatted@3.2.9: resolution: {integrity: sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==} @@ -3419,6 +3421,10 @@ packages: for-each@0.3.3: resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} + for-each@0.3.5: + resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} + engines: {node: '>= 0.4'} + forever-agent@0.6.1: resolution: {integrity: sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==} @@ -3475,6 +3481,10 @@ packages: resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==} engines: {node: '>= 0.4'} + function.prototype.name@1.1.8: + resolution: {integrity: sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==} + engines: {node: '>= 0.4'} + functions-have-names@1.2.3: resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} @@ -3493,6 +3503,14 @@ packages: resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} engines: {node: '>= 0.4'} + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + get-stdin@9.0.0: resolution: {integrity: sha512-dVKBjfWisLAicarI2Sf+JuBE/DghV4UzNAVe9yhEJuzeREd3JhOTE9cUaJTeSa77fsbQUK3pcOpJfM59+VKZaA==} engines: {node: '>=12'} @@ -3517,6 +3535,10 @@ packages: resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==} engines: {node: '>= 0.4'} + get-symbol-description@1.1.0: + resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} + engines: {node: '>= 0.4'} + getos@3.2.1: resolution: {integrity: sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q==} @@ -3558,12 +3580,21 @@ packages: resolution: {integrity: sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==} engines: {node: '>=8'} + globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} + + globals@16.0.0: + resolution: {integrity: sha512-iInW14XItCXET01CQFqudPOWP2jYMl7T+QRQT+UNcR/iQncN/F0UNpgd76iFkBPgNQb4+X3LV9tLJYzwh+Gl3A==} + engines: {node: '>=18'} + globalthis@1.0.3: resolution: {integrity: sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==} engines: {node: '>= 0.4'} - globalyzer@0.1.0: - resolution: {integrity: sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q==} + globalthis@1.0.4: + resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} + engines: {node: '>= 0.4'} globby@11.1.0: resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} @@ -3573,12 +3604,13 @@ packages: resolution: {integrity: sha512-/1WM/LNHRAOH9lZta77uGbq0dAEQM+XjNesWwhlERDVenqothRbnzTrL3/LrIoEPPjeUHC3vrS6TwoyxeHs7MQ==} engines: {node: '>=18'} - globrex@0.1.2: - resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} - gopd@1.0.1: resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} @@ -3624,10 +3656,18 @@ packages: resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==} engines: {node: '>= 0.4'} + has-proto@1.2.0: + resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==} + engines: {node: '>= 0.4'} + has-symbols@1.0.3: resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} engines: {node: '>= 0.4'} + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + has-tostringtag@1.0.0: resolution: {integrity: sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==} engines: {node: '>= 0.4'} @@ -3728,9 +3768,6 @@ packages: import-meta-resolve@3.1.1: resolution: {integrity: sha512-qeywsE/KC3w9Fd2ORrRDUw6nS/nLwZpXgfrOc2IILvZYnCaEMd+D56Vfg9k4G29gIeVi3XKql1RQatME8iYsiw==} - import-meta-resolve@4.0.0: - resolution: {integrity: sha512-okYUR7ZQPH+efeuMJGlq4f8ubUgO50kByRPyt/Cy1Io4PSRsPjxME+YlVaCOx+NIToW7hCsZNFJyTPFFKepRSA==} - import-meta-resolve@4.1.0: resolution: {integrity: sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==} @@ -3764,6 +3801,10 @@ packages: resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==} engines: {node: '>= 0.4'} + internal-slot@1.1.0: + resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} + engines: {node: '>= 0.4'} + is-array-buffer@3.0.2: resolution: {integrity: sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==} @@ -3771,6 +3812,10 @@ packages: resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==} engines: {node: '>= 0.4'} + is-array-buffer@3.0.5: + resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} + engines: {node: '>= 0.4'} + is-arrayish@0.2.1: resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} @@ -3784,6 +3829,10 @@ packages: is-bigint@1.0.4: resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} + is-bigint@1.1.0: + resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==} + engines: {node: '>= 0.4'} + is-binary-path@2.1.0: resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} engines: {node: '>=8'} @@ -3792,6 +3841,10 @@ packages: resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} engines: {node: '>= 0.4'} + is-boolean-object@1.2.2: + resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} + engines: {node: '>= 0.4'} + is-buffer@2.0.5: resolution: {integrity: sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==} engines: {node: '>=4'} @@ -3815,10 +3868,18 @@ packages: resolution: {integrity: sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==} engines: {node: '>= 0.4'} + is-data-view@1.0.2: + resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==} + engines: {node: '>= 0.4'} + is-date-object@1.0.5: resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} engines: {node: '>= 0.4'} + is-date-object@1.1.0: + resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} + engines: {node: '>= 0.4'} + is-docker@3.0.0: resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -3832,8 +3893,9 @@ packages: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} - is-finalizationregistry@1.0.2: - resolution: {integrity: sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==} + is-finalizationregistry@1.1.1: + resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==} + engines: {node: '>= 0.4'} is-fullwidth-code-point@3.0.0: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} @@ -3860,8 +3922,9 @@ packages: resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==} engines: {node: '>=12'} - is-map@2.0.2: - resolution: {integrity: sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==} + is-map@2.0.3: + resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} + engines: {node: '>= 0.4'} is-module@1.0.0: resolution: {integrity: sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==} @@ -3878,6 +3941,10 @@ packages: resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} engines: {node: '>= 0.4'} + is-number-object@1.1.1: + resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} + engines: {node: '>= 0.4'} + is-number@7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} @@ -3900,12 +3967,20 @@ packages: is-reference@3.0.2: resolution: {integrity: sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==} + is-reference@3.0.3: + resolution: {integrity: sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==} + is-regex@1.1.4: resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} engines: {node: '>= 0.4'} - is-set@2.0.2: - resolution: {integrity: sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==} + is-regex@1.2.1: + resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} + engines: {node: '>= 0.4'} + + is-set@2.0.3: + resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} + engines: {node: '>= 0.4'} is-shared-array-buffer@1.0.2: resolution: {integrity: sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==} @@ -3914,6 +3989,10 @@ packages: resolution: {integrity: sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==} engines: {node: '>= 0.4'} + is-shared-array-buffer@1.0.4: + resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==} + engines: {node: '>= 0.4'} + is-stream@2.0.1: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} engines: {node: '>=8'} @@ -3926,6 +4005,10 @@ packages: resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} engines: {node: '>= 0.4'} + is-string@1.1.1: + resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==} + engines: {node: '>= 0.4'} + is-subdir@1.2.0: resolution: {integrity: sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==} engines: {node: '>=4'} @@ -3934,6 +4017,10 @@ packages: resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} engines: {node: '>= 0.4'} + is-symbol@1.1.1: + resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==} + engines: {node: '>= 0.4'} + is-typed-array@1.1.12: resolution: {integrity: sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==} engines: {node: '>= 0.4'} @@ -3942,6 +4029,10 @@ packages: resolution: {integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==} engines: {node: '>= 0.4'} + is-typed-array@1.1.15: + resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} + engines: {node: '>= 0.4'} + is-typedarray@1.0.0: resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==} @@ -3953,14 +4044,20 @@ packages: resolution: {integrity: sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==} engines: {node: '>=12'} - is-weakmap@2.0.1: - resolution: {integrity: sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==} + is-weakmap@2.0.2: + resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} + engines: {node: '>= 0.4'} is-weakref@1.0.2: resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} - is-weakset@2.0.2: - resolution: {integrity: sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==} + is-weakref@1.1.1: + resolution: {integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==} + engines: {node: '>= 0.4'} + + is-weakset@2.0.4: + resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} + engines: {node: '>= 0.4'} is-windows@1.0.2: resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} @@ -3979,8 +4076,9 @@ packages: isstream@0.1.2: resolution: {integrity: sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==} - iterator.prototype@1.1.2: - resolution: {integrity: sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==} + iterator.prototype@1.1.5: + resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==} + engines: {node: '>= 0.4'} joi@17.11.0: resolution: {integrity: sha512-NgB+lZLNoqISVy1rZocE9PZI36bL/77ie924Ri43yEvi9GUUMPeyVIr8KdFTMUlby1p0PBYMk9spIxEUQYqrJQ==} @@ -4059,8 +4157,8 @@ packages: resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} engines: {node: '>=6'} - known-css-properties@0.29.0: - resolution: {integrity: sha512-Ne7wqW7/9Cz54PDt4I3tcV+hAyat8ypyOGzYRJQfdxnnjeWsTxt1cy8pjvvKeI5kfXuyvULyeeAvwvvtAX3ayQ==} + known-css-properties@0.35.0: + resolution: {integrity: sha512-a/RAk2BfKk+WFGhhOCAYqSiFLc34k8Mt/6NWRI4joER0EYUzXIcFivjjnoD3+XU1DggLn/tZc3DOAgke7l8a4A==} lazy-ass@1.6.0: resolution: {integrity: sha512-cc8oEVoctTvsFZ/Oje/kGnHbpWHYBe8IAJe4C0QNc3t8uM/0Y8+erSz/7Y1ALuXTEZTMvxXwO6YbX1ey3ujiZw==} @@ -4081,8 +4179,8 @@ packages: resolution: {integrity: sha512-K2U4W2Ff5ibV7j7ydLr+zLAkIg5JJ4lPn1Ltsdt+Tz/IjQ8buJ55pZAxoP34lqIiwtF9iAvtLv3JGv7CAyAg+g==} engines: {node: '>=14'} - lilconfig@3.1.1: - resolution: {integrity: sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==} + lilconfig@3.1.2: + resolution: {integrity: sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==} engines: {node: '>=14'} lines-and-columns@1.2.4: @@ -4168,14 +4266,13 @@ packages: magic-string@0.30.10: resolution: {integrity: sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==} + magic-string@0.30.17: + resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} + magic-string@0.30.5: resolution: {integrity: sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==} engines: {node: '>=12'} - magic-string@0.30.8: - resolution: {integrity: sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==} - engines: {node: '>=12'} - map-obj@1.0.1: resolution: {integrity: sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==} engines: {node: '>=0.10.0'} @@ -4190,6 +4287,10 @@ packages: markdown-table@3.0.3: resolution: {integrity: sha512-Z1NL3Tb1M9wH4XESsCDEksWoKTdlUafKc4pt0GRwjUyXaCFZ+dc3g2erqB6zm3szA2IUSi7VnPI+o/9jnxh9hw==} + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + mdast-util-definitions@5.1.2: resolution: {integrity: sha512-8SVPMuHqlPME/z3gqVwWY4zVXn8lqKv/pAhC57FuJ40ImXyBpmO5ukh98zB2v7Blql2FiHjHv9LVztSIqjY+MA==} @@ -4373,10 +4474,6 @@ packages: resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} engines: {node: '>=10'} - minimatch@9.0.3: - resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==} - engines: {node: '>=16 || 14 >=14.17'} - minimatch@9.0.5: resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} engines: {node: '>=16 || 14 >=14.17'} @@ -4395,10 +4492,6 @@ packages: mkdirp-classic@0.5.3: resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} - mkdirp@0.5.6: - resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} - hasBin: true - mri@1.2.0: resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} engines: {node: '>=4'} @@ -4426,6 +4519,11 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + nanoid@3.3.8: + resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + napi-build-utils@1.0.2: resolution: {integrity: sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==} @@ -4462,8 +4560,11 @@ packages: node-releases@2.0.13: resolution: {integrity: sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==} - node-releases@2.0.14: - resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==} + node-releases@2.0.18: + resolution: {integrity: sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==} + + node-releases@2.0.19: + resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} normalize-package-data@2.5.0: resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} @@ -4494,6 +4595,10 @@ packages: object-inspect@1.13.1: resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==} + object-inspect@1.13.4: + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + engines: {node: '>= 0.4'} + object-keys@1.1.1: resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} engines: {node: '>= 0.4'} @@ -4502,31 +4607,20 @@ packages: resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==} engines: {node: '>= 0.4'} - object.entries@1.1.7: - resolution: {integrity: sha512-jCBs/0plmPsOnrKAfFQXRG2NFjlhZgjjcBLSmTnEhU8U6vVTsVe8ANeQJCHTl3gSsI4J+0emOoCgoKlmQPMgmA==} + object.assign@4.1.7: + resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} engines: {node: '>= 0.4'} - object.entries@1.1.8: - resolution: {integrity: sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==} - engines: {node: '>= 0.4'} - - object.fromentries@2.0.7: - resolution: {integrity: sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==} + object.entries@1.1.9: + resolution: {integrity: sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==} engines: {node: '>= 0.4'} object.fromentries@2.0.8: resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==} engines: {node: '>= 0.4'} - object.hasown@1.1.3: - resolution: {integrity: sha512-fFI4VcYpRHvSLXxP7yiZOMAd331cPfd2p7PFDVbgUsYOfCT3tICVqXWngbjr4m49OvsBwUBQ6O2uQoJvy3RexA==} - - object.values@1.1.7: - resolution: {integrity: sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==} - engines: {node: '>= 0.4'} - - object.values@1.2.0: - resolution: {integrity: sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==} + object.values@1.2.1: + resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} engines: {node: '>= 0.4'} once@1.4.0: @@ -4558,6 +4652,10 @@ packages: outdent@0.5.0: resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==} + own-keys@1.0.1: + resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} + engines: {node: '>= 0.4'} + p-filter@2.1.0: resolution: {integrity: sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==} engines: {node: '>=8'} @@ -4656,13 +4754,17 @@ packages: picocolors@1.0.0: resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} - picocolors@1.0.1: - resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==} + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} picomatch@2.3.1: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} + picomatch@4.0.2: + resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} + engines: {node: '>=12'} + pify@2.3.0: resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} engines: {node: '>=0.10.0'} @@ -4680,8 +4782,8 @@ packages: engines: {node: '>=16'} hasBin: true - playwright-core@1.45.0: - resolution: {integrity: sha512-lZmHlFQ0VYSpAs43dRq1/nJ9G/6SiTI7VPqidld9TDefL9tX87bTKExWZZUF5PeRyqtXqd8fQi2qmfIedkwsNQ==} + playwright-core@1.51.1: + resolution: {integrity: sha512-/crRMj8+j/Nq5s8QcvegseuyeZPxpQCZb6HNk3Sos3BlZyAknRjoyJPFWkpNn8v0+P3WiwqFF8P+zQo4eqiNuw==} engines: {node: '>=18'} hasBin: true @@ -4690,8 +4792,8 @@ packages: engines: {node: '>=16'} hasBin: true - playwright@1.45.0: - resolution: {integrity: sha512-4z3ac3plDfYzGB6r0Q3LF8POPR20Z8D0aXcxbJvmfMgSSq1hkcgvFRXJk9rUq5H/MJ0Ktal869hhOdI/zUTeLA==} + playwright@1.51.1: + resolution: {integrity: sha512-kkx+MB2KQRkyxjYPc3a0wLZZoDczmppyGJIvQ43l+aZihkaVvmu/21kiyaHeHjiFxjxNNFnUncKmcGIyOojsaw==} engines: {node: '>=18'} hasBin: true @@ -4699,6 +4801,12 @@ packages: resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} engines: {node: '>= 0.4'} + postcss-calc@10.0.2: + resolution: {integrity: sha512-DT/Wwm6fCKgpYVI7ZEWuPJ4az8hiEHtCUeYjZXqU7Ou4QqYh1Df2yCQ7Ca6N7xqKPFkxN3fhf+u9KSoOCJNAjg==} + engines: {node: ^18.12 || ^20.9 || >=22.0} + peerDependencies: + postcss: ^8.4.38 + postcss-calc@9.0.1: resolution: {integrity: sha512-TipgjGyzP5QzEhsOZUaIkeO5mKeMFpebWzRogWG/ysonUlnHcq5aJe0jOjpfzUU8PeSaBQnrE8ehR0QA5vs8PQ==} engines: {node: ^14 || ^16 || >=18.0} @@ -4712,15 +4820,22 @@ packages: peerDependencies: postcss: ^8.0.0 + postcss-cli@11.0.1: + resolution: {integrity: sha512-0UnkNPSayHKRe/tc2YGW6XnSqqOA9eqpiRMgRlV1S6HdGi16vwJBx7lviARzbV1HpQHqLLRH3o8vTcB0cLc+5g==} + engines: {node: '>=18'} + hasBin: true + peerDependencies: + postcss: ^8.0.0 + postcss-colormin@6.0.0: resolution: {integrity: sha512-EuO+bAUmutWoZYgHn2T1dG1pPqHU6L4TjzPlu4t1wZGXQ/fxV16xg2EJmYi0z+6r+MGV1yvpx1BHkUaRrPa2bw==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.2.15 - postcss-colormin@6.1.0: - resolution: {integrity: sha512-x9yX7DOxeMAR+BgGVnNSAxmAj98NX/YxEMNFP+SDCEeNLb2r3i6Hh1ksMsnW8Ub5SLCpbescQqn9YEbE9554Sw==} - engines: {node: ^14 || ^16 || >=18.0} + postcss-colormin@7.0.2: + resolution: {integrity: sha512-YntRXNngcvEvDbEjTdRWGU606eZvB5prmHG4BF0yLmVpamXbpsRJzevyy6MZVyuecgzI2AWAlvFi8DAeCqwpvA==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.4.31 @@ -4736,9 +4851,9 @@ packages: peerDependencies: postcss: ^8.2.15 - postcss-convert-values@6.1.0: - resolution: {integrity: sha512-zx8IwP/ts9WvUM6NkVSkiU902QZL1bwPhaVaLynPtCsOTqp+ZKbNi+s6XJg3rfqpKGA/oc7Oxk5t8pOQJcwl/w==} - engines: {node: ^14 || ^16 || >=18.0} + postcss-convert-values@7.0.4: + resolution: {integrity: sha512-e2LSXPqEHVW6aoGbjV9RsSSNDO3A0rZLCBxN24zvxF25WknMPpX8Dm9UxxThyEbaytzggRuZxaGXqaOhxQ514Q==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.4.31 @@ -4748,9 +4863,9 @@ packages: peerDependencies: postcss: ^8.2.15 - postcss-discard-comments@6.0.2: - resolution: {integrity: sha512-65w/uIqhSBBfQmYnG92FO1mWZjJ4GL5b8atm5Yw2UgrwD7HiNiSSNwJor1eCFGzUgYnN/iIknhNRVqjrrpuglw==} - engines: {node: ^14 || ^16 || >=18.0} + postcss-discard-comments@7.0.3: + resolution: {integrity: sha512-q6fjd4WU4afNhWOA2WltHgCbkRhZPgQe7cXF74fuVB/ge4QbM9HEaOIzGSiMvM+g/cOsNAUGdf2JDzqA2F8iLA==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.4.31 @@ -4760,9 +4875,9 @@ packages: peerDependencies: postcss: ^8.2.15 - postcss-discard-duplicates@6.0.3: - resolution: {integrity: sha512-+JA0DCvc5XvFAxwx6f/e68gQu/7Z9ud584VLmcgto28eB8FqSFZwtrLwB5Kcp70eIoWP/HXqz4wpo8rD8gpsTw==} - engines: {node: ^14 || ^16 || >=18.0} + postcss-discard-duplicates@7.0.1: + resolution: {integrity: sha512-oZA+v8Jkpu1ct/xbbrntHRsfLGuzoP+cpt0nJe5ED2FQF8n8bJtn7Bo28jSmBYwqgqnqkuSXJfSUEE7if4nClQ==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.4.31 @@ -4772,9 +4887,9 @@ packages: peerDependencies: postcss: ^8.2.15 - postcss-discard-empty@6.0.3: - resolution: {integrity: sha512-znyno9cHKQsK6PtxL5D19Fj9uwSzC2mB74cpT66fhgOadEUPyXFkbgwm5tvc3bt3NAy8ltE5MrghxovZRVnOjQ==} - engines: {node: ^14 || ^16 || >=18.0} + postcss-discard-empty@7.0.0: + resolution: {integrity: sha512-e+QzoReTZ8IAwhnSdp/++7gBZ/F+nBq9y6PomfwORfP7q9nBpK5AMP64kOt0bA+lShBFbBDcgpJ3X4etHg4lzA==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.4.31 @@ -4784,9 +4899,9 @@ packages: peerDependencies: postcss: ^8.2.15 - postcss-discard-overridden@6.0.2: - resolution: {integrity: sha512-j87xzI4LUggC5zND7KdjsI25APtyMuynXZSujByMaav2roV6OZX+8AaCUcZSWqckZpjAjRyFDdpqybgjFO0HJQ==} - engines: {node: ^14 || ^16 || >=18.0} + postcss-discard-overridden@7.0.0: + resolution: {integrity: sha512-GmNAzx88u3k2+sBTZrJSDauR0ccpE24omTQCVmaTTZFz1du6AasspjaUPMJ2ud4RslZpoFKyf+6MSPETLojc6w==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.4.31 @@ -4796,8 +4911,8 @@ packages: peerDependencies: postcss: ^8.0.0 - postcss-import@16.0.1: - resolution: {integrity: sha512-i2Pci0310NaLHr/5JUFSw1j/8hf1CzwMY13g6ZDxgOavmRHQi2ba3PmUHoihO+sjaum+KmCNzskNsw7JDrg03g==} + postcss-import@16.1.0: + resolution: {integrity: sha512-7hsAZ4xGXl4MW+OKEWCnF6T5jqBw80/EE9aXg1r2yyn1RsVEU8EtKXbijEODa+rg7iih4bKf7vlvTGYR4CnPNg==} engines: {node: '>=18.0.0'} peerDependencies: postcss: ^8.0.0 @@ -4832,9 +4947,9 @@ packages: peerDependencies: postcss: ^8.2.15 - postcss-merge-longhand@6.0.4: - resolution: {integrity: sha512-vAfWGcxUUGlFiPM3nDMZA+/Yo9sbpc3JNkcYZez8FfJDv41Dh7tAgA3QGVTocaHCZZL6aXPXPOaBMJsjujodsA==} - engines: {node: ^14 || ^16 || >=18.0} + postcss-merge-longhand@7.0.4: + resolution: {integrity: sha512-zer1KoZA54Q8RVHKOY5vMke0cCdNxMP3KBfDerjH/BYHh4nCIh+1Yy0t1pAEQF18ac/4z3OFclO+ZVH8azjR4A==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.4.31 @@ -4844,9 +4959,9 @@ packages: peerDependencies: postcss: ^8.2.15 - postcss-merge-rules@6.1.0: - resolution: {integrity: sha512-lER+W3Gr6XOvxOYk1Vi/6UsAgKMg6MDBthmvbNqi2XxAk/r9XfhdYZSigfWjuWWn3zYw2wLelvtM8XuAEFqRkA==} - engines: {node: ^14 || ^16 || >=18.0} + postcss-merge-rules@7.0.4: + resolution: {integrity: sha512-ZsaamiMVu7uBYsIdGtKJ64PkcQt6Pcpep/uO90EpLS3dxJi6OXamIobTYcImyXGoW0Wpugh7DSD3XzxZS9JCPg==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.4.31 @@ -4856,9 +4971,9 @@ packages: peerDependencies: postcss: ^8.2.15 - postcss-minify-font-values@6.0.3: - resolution: {integrity: sha512-SmAeTA1We5rMnN3F8X9YBNo9bj9xB4KyDHnaNJnBfQIPi+60fNiR9OTRnIaMqkYzAQX0vObIw4Pn0vuKEOettg==} - engines: {node: ^14 || ^16 || >=18.0} + postcss-minify-font-values@7.0.0: + resolution: {integrity: sha512-2ckkZtgT0zG8SMc5aoNwtm5234eUx1GGFJKf2b1bSp8UflqaeFzR50lid4PfqVI9NtGqJ2J4Y7fwvnP/u1cQog==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.4.31 @@ -4868,9 +4983,9 @@ packages: peerDependencies: postcss: ^8.2.15 - postcss-minify-gradients@6.0.3: - resolution: {integrity: sha512-4KXAHrYlzF0Rr7uc4VrfwDJ2ajrtNEpNEuLxFgwkhFZ56/7gaE4Nr49nLsQDZyUe+ds+kEhf+YAUolJiYXF8+Q==} - engines: {node: ^14 || ^16 || >=18.0} + postcss-minify-gradients@7.0.0: + resolution: {integrity: sha512-pdUIIdj/C93ryCHew0UgBnL2DtUS3hfFa5XtERrs4x+hmpMYGhbzo6l/Ir5de41O0GaKVpK1ZbDNXSY6GkXvtg==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.4.31 @@ -4880,9 +4995,9 @@ packages: peerDependencies: postcss: ^8.2.15 - postcss-minify-params@6.1.0: - resolution: {integrity: sha512-bmSKnDtyyE8ujHQK0RQJDIKhQ20Jq1LYiez54WiaOoBtcSuflfK3Nm596LvbtlFcpipMjgClQGyGr7GAs+H1uA==} - engines: {node: ^14 || ^16 || >=18.0} + postcss-minify-params@7.0.2: + resolution: {integrity: sha512-nyqVLu4MFl9df32zTsdcLqCFfE/z2+f8GE1KHPxWOAmegSo6lpV2GNy5XQvrzwbLmiU7d+fYay4cwto1oNdAaQ==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.4.31 @@ -4892,9 +5007,9 @@ packages: peerDependencies: postcss: ^8.2.15 - postcss-minify-selectors@6.0.3: - resolution: {integrity: sha512-IcV7ZQJcaXyhx4UBpWZMsinGs2NmiUC60rJSkyvjPCPqhNjVGsrJUM+QhAtCaikZ0w0/AbZuH4wVvF/YMuMhvA==} - engines: {node: ^14 || ^16 || >=18.0} + postcss-minify-selectors@7.0.4: + resolution: {integrity: sha512-JG55VADcNb4xFCf75hXkzc1rNeURhlo7ugf6JjiiKRfMsKlDzN9CXHZDyiG6x/zGchpjQS+UAgb1d4nqXqOpmA==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.4.31 @@ -4904,9 +5019,9 @@ packages: peerDependencies: postcss: ^8.2.14 - postcss-nested@6.0.1: - resolution: {integrity: sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==} - engines: {node: '>=12.0'} + postcss-nested@7.0.2: + resolution: {integrity: sha512-5osppouFc0VR9/VYzYxO03VaDa3e8F23Kfd6/9qcZTUI8P58GIYlArOET2Wq0ywSl2o2PjELhYOFI4W7l5QHKw==} + engines: {node: '>=18.0'} peerDependencies: postcss: ^8.2.14 @@ -4916,9 +5031,9 @@ packages: peerDependencies: postcss: ^8.2.15 - postcss-normalize-charset@6.0.2: - resolution: {integrity: sha512-a8N9czmdnrjPHa3DeFlwqst5eaL5W8jYu3EBbTTkI5FHkfMhFZh1EGbku6jhHhIzTA6tquI2P42NtZ59M/H/kQ==} - engines: {node: ^14 || ^16 || >=18.0} + postcss-normalize-charset@7.0.0: + resolution: {integrity: sha512-ABisNUXMeZeDNzCQxPxBCkXexvBrUHV+p7/BXOY+ulxkcjUZO0cp8ekGBwvIh2LbCwnWbyMPNJVtBSdyhM2zYQ==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.4.31 @@ -4928,9 +5043,9 @@ packages: peerDependencies: postcss: ^8.2.15 - postcss-normalize-display-values@6.0.2: - resolution: {integrity: sha512-8H04Mxsb82ON/aAkPeq8kcBbAtI5Q2a64X/mnRRfPXBq7XeogoQvReqxEfc0B4WPq1KimjezNC8flUtC3Qz6jg==} - engines: {node: ^14 || ^16 || >=18.0} + postcss-normalize-display-values@7.0.0: + resolution: {integrity: sha512-lnFZzNPeDf5uGMPYgGOw7v0BfB45+irSRz9gHQStdkkhiM0gTfvWkWB5BMxpn0OqgOQuZG/mRlZyJxp0EImr2Q==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.4.31 @@ -4940,9 +5055,9 @@ packages: peerDependencies: postcss: ^8.2.15 - postcss-normalize-positions@6.0.2: - resolution: {integrity: sha512-/JFzI441OAB9O7VnLA+RtSNZvQ0NCFZDOtp6QPFo1iIyawyXg0YI3CYM9HBy1WvwCRHnPep/BvI1+dGPKoXx/Q==} - engines: {node: ^14 || ^16 || >=18.0} + postcss-normalize-positions@7.0.0: + resolution: {integrity: sha512-I0yt8wX529UKIGs2y/9Ybs2CelSvItfmvg/DBIjTnoUSrPxSV7Z0yZ8ShSVtKNaV/wAY+m7bgtyVQLhB00A1NQ==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.4.31 @@ -4952,9 +5067,9 @@ packages: peerDependencies: postcss: ^8.2.15 - postcss-normalize-repeat-style@6.0.2: - resolution: {integrity: sha512-YdCgsfHkJ2jEXwR4RR3Tm/iOxSfdRt7jplS6XRh9Js9PyCR/aka/FCb6TuHT2U8gQubbm/mPmF6L7FY9d79VwQ==} - engines: {node: ^14 || ^16 || >=18.0} + postcss-normalize-repeat-style@7.0.0: + resolution: {integrity: sha512-o3uSGYH+2q30ieM3ppu9GTjSXIzOrRdCUn8UOMGNw7Af61bmurHTWI87hRybrP6xDHvOe5WlAj3XzN6vEO8jLw==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.4.31 @@ -4964,9 +5079,9 @@ packages: peerDependencies: postcss: ^8.2.15 - postcss-normalize-string@6.0.2: - resolution: {integrity: sha512-vQZIivlxlfqqMp4L9PZsFE4YUkWniziKjQWUtsxUiVsSSPelQydwS8Wwcuw0+83ZjPWNTl02oxlIvXsmmG+CiQ==} - engines: {node: ^14 || ^16 || >=18.0} + postcss-normalize-string@7.0.0: + resolution: {integrity: sha512-w/qzL212DFVOpMy3UGyxrND+Kb0fvCiBBujiaONIihq7VvtC7bswjWgKQU/w4VcRyDD8gpfqUiBQ4DUOwEJ6Qg==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.4.31 @@ -4976,9 +5091,9 @@ packages: peerDependencies: postcss: ^8.2.15 - postcss-normalize-timing-functions@6.0.2: - resolution: {integrity: sha512-a+YrtMox4TBtId/AEwbA03VcJgtyW4dGBizPl7e88cTFULYsprgHWTbfyjSLyHeBcK/Q9JhXkt2ZXiwaVHoMzA==} - engines: {node: ^14 || ^16 || >=18.0} + postcss-normalize-timing-functions@7.0.0: + resolution: {integrity: sha512-tNgw3YV0LYoRwg43N3lTe3AEWZ66W7Dh7lVEpJbHoKOuHc1sLrzMLMFjP8SNULHaykzsonUEDbKedv8C+7ej6g==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.4.31 @@ -4988,9 +5103,9 @@ packages: peerDependencies: postcss: ^8.2.15 - postcss-normalize-unicode@6.1.0: - resolution: {integrity: sha512-QVC5TQHsVj33otj8/JD869Ndr5Xcc/+fwRh4HAsFsAeygQQXm+0PySrKbr/8tkDKzW+EVT3QkqZMfFrGiossDg==} - engines: {node: ^14 || ^16 || >=18.0} + postcss-normalize-unicode@7.0.2: + resolution: {integrity: sha512-ztisabK5C/+ZWBdYC+Y9JCkp3M9qBv/XFvDtSw0d/XwfT3UaKeW/YTm/MD/QrPNxuecia46vkfEhewjwcYFjkg==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.4.31 @@ -5000,9 +5115,9 @@ packages: peerDependencies: postcss: ^8.2.15 - postcss-normalize-url@6.0.2: - resolution: {integrity: sha512-kVNcWhCeKAzZ8B4pv/DnrU1wNh458zBNp8dh4y5hhxih5RZQ12QWMuQrDgPRw3LRl8mN9vOVfHl7uhvHYMoXsQ==} - engines: {node: ^14 || ^16 || >=18.0} + postcss-normalize-url@7.0.0: + resolution: {integrity: sha512-+d7+PpE+jyPX1hDQZYG+NaFD+Nd2ris6r8fPTBAjE8z/U41n/bib3vze8x7rKs5H1uEw5ppe9IojewouHk0klQ==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.4.31 @@ -5012,9 +5127,9 @@ packages: peerDependencies: postcss: ^8.2.15 - postcss-normalize-whitespace@6.0.2: - resolution: {integrity: sha512-sXZ2Nj1icbJOKmdjXVT9pnyHQKiSAyuNQHSgRCUgThn2388Y9cGVDR+E9J9iAYbSbLHI+UUwLVl1Wzco/zgv0Q==} - engines: {node: ^14 || ^16 || >=18.0} + postcss-normalize-whitespace@7.0.0: + resolution: {integrity: sha512-37/toN4wwZErqohedXYqWgvcHUGlT8O/m2jVkAfAe9Bd4MzRqlBmXrJRePH0e9Wgnz2X7KymTgTOaaFizQe3AQ==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.4.31 @@ -5024,9 +5139,9 @@ packages: peerDependencies: postcss: ^8.2.15 - postcss-ordered-values@6.0.2: - resolution: {integrity: sha512-VRZSOB+JU32RsEAQrO94QPkClGPKJEL/Z9PCBImXMhIeK5KAYo6slP/hBYlLgrCjFxyqvn5VC81tycFEDBLG1Q==} - engines: {node: ^14 || ^16 || >=18.0} + postcss-ordered-values@7.0.1: + resolution: {integrity: sha512-irWScWRL6nRzYmBOXReIKch75RRhNS86UPUAxXdmW/l0FcAsg0lvAXQCby/1lymxn/o0gVa6Rv/0f03eJOwHxw==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.4.31 @@ -5036,9 +5151,9 @@ packages: peerDependencies: postcss: ^8.2.15 - postcss-reduce-initial@6.1.0: - resolution: {integrity: sha512-RarLgBK/CrL1qZags04oKbVbrrVK2wcxhvta3GCxrZO4zveibqbRPmm2VI8sSgCXwoUHEliRSbOfpR0b/VIoiw==} - engines: {node: ^14 || ^16 || >=18.0} + postcss-reduce-initial@7.0.2: + resolution: {integrity: sha512-pOnu9zqQww7dEKf62Nuju6JgsW2V0KRNBHxeKohU+JkHd/GAH5uvoObqFLqkeB2n20mr6yrlWDvo5UBU5GnkfA==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.4.31 @@ -5048,9 +5163,9 @@ packages: peerDependencies: postcss: ^8.2.15 - postcss-reduce-transforms@6.0.2: - resolution: {integrity: sha512-sB+Ya++3Xj1WaT9+5LOOdirAxP7dJZms3GRcYheSPi1PiTMigsxHAdkrbItHxwYHr4kt1zL7mmcHstgMYT+aiA==} - engines: {node: ^14 || ^16 || >=18.0} + postcss-reduce-transforms@7.0.0: + resolution: {integrity: sha512-pnt1HKKZ07/idH8cpATX/ujMbtOGhUfE+m8gbqwJE05aTaNw8gbo34a2e3if0xc0dlu75sUOiqvwCGY3fzOHew==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.4.31 @@ -5066,11 +5181,11 @@ packages: peerDependencies: postcss: ^8.1.0 - postcss-safe-parser@6.0.0: - resolution: {integrity: sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ==} - engines: {node: '>=12.0'} + postcss-safe-parser@7.0.1: + resolution: {integrity: sha512-0AioNCJZ2DPYz5ABT6bddIqlhgwhpHZ/l65YAYo0BCIn0xiDpsnTHz0gnoTGk0OXZW0JRs+cDwL8u/teRdz+8A==} + engines: {node: '>=18.0'} peerDependencies: - postcss: ^8.3.3 + postcss: ^8.4.31 postcss-scss@4.0.9: resolution: {integrity: sha512-AjKOeiwAitL/MXxQW2DliT28EKukvvbEWx3LBmJIRN8KfBGZbRTxNYW0kSqi1COiTZ57nZ9NW06S6ux//N1c9A==} @@ -5082,8 +5197,12 @@ packages: resolution: {integrity: sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==} engines: {node: '>=4'} - postcss-selector-parser@6.0.16: - resolution: {integrity: sha512-A0RVJrX+IUkVZbW3ClroRWurercFhieevHB38sr2+l9eUClMqome3LmEmnhlNy+5Mr2EYN6B2Kaw9wYdd+VHiw==} + postcss-selector-parser@6.1.2: + resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} + engines: {node: '>=4'} + + postcss-selector-parser@7.0.0: + resolution: {integrity: sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ==} engines: {node: '>=4'} postcss-svgo@6.0.0: @@ -5092,9 +5211,9 @@ packages: peerDependencies: postcss: ^8.2.15 - postcss-svgo@6.0.3: - resolution: {integrity: sha512-dlrahRmxP22bX6iKEjOM+c8/1p+81asjKT+V5lrgOH944ryx/OHpclnIbGsKVd3uWOXFLYJwCVf0eEkJGvO96g==} - engines: {node: ^14 || ^16 || >= 18} + postcss-svgo@7.0.1: + resolution: {integrity: sha512-0WBUlSL4lhD9rA5k1e5D8EN5wCEyZD6HJk0jIvRxl+FDVOMlJ7DePHYWGGVc5QRqrJ3/06FTXM0bxjmJpmTPSA==} + engines: {node: ^18.12.0 || ^20.9.0 || >= 18} peerDependencies: postcss: ^8.4.31 @@ -5104,9 +5223,9 @@ packages: peerDependencies: postcss: ^8.2.15 - postcss-unique-selectors@6.0.3: - resolution: {integrity: sha512-NFXbYr8qdmCr/AFceaEfdcsKGCvWTeGO6QVC9h2GvtWgj0/0dklKQcaMMVzs6tr8bY+ase8hOtHW8OBTTRvS8A==} - engines: {node: ^14 || ^16 || >=18.0} + postcss-unique-selectors@7.0.3: + resolution: {integrity: sha512-J+58u5Ic5T1QjP/LDV9g3Cx4CNOgB5vz+kM6+OxHHhFACdcDeKhBXjQmB7fnIZM12YSTvsL0Opwco83DmacW2g==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.4.31 @@ -5121,12 +5240,8 @@ packages: resolution: {integrity: sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==} engines: {node: ^10 || ^12 || >=14} - postcss@8.4.38: - resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==} - engines: {node: ^10 || ^12 || >=14} - - postcss@8.4.39: - resolution: {integrity: sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==} + postcss@8.5.3: + resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==} engines: {node: ^10 || ^12 || >=14} prebuild-install@7.1.1: @@ -5146,14 +5261,8 @@ packages: resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==} engines: {node: '>=6.0.0'} - prettier-plugin-svelte@3.0.3: - resolution: {integrity: sha512-dLhieh4obJEK1hnZ6koxF+tMUrZbV5YGvRpf2+OADyanjya5j0z1Llo8iGwiHmFWZVG/hLEw/AJD5chXd9r3XA==} - peerDependencies: - prettier: ^3.0.0 - svelte: ^3.2.0 || ^4.0.0-next.0 - - prettier-plugin-svelte@3.2.2: - resolution: {integrity: sha512-ZzzE/wMuf48/1+Lf2Ffko0uDa6pyCfgHV6+uAhtg2U0AAXGrhCSW88vEJNAkAxW5qyrFY1y1zZ4J8TgHrjW++Q==} + prettier-plugin-svelte@3.3.3: + resolution: {integrity: sha512-yViK9zqQ+H2qZD1w/bH7W8i+bVfKrD8GIFjkFe4Thl6kCT9SlAsXVNmt3jCvQOCsnOhcvYgsoVlRV/Eu6x5nNw==} peerDependencies: prettier: ^3.0.0 svelte: ^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0 @@ -5163,13 +5272,8 @@ packages: engines: {node: '>=10.13.0'} hasBin: true - prettier@3.0.3: - resolution: {integrity: sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==} - engines: {node: '>=14'} - hasBin: true - - prettier@3.2.5: - resolution: {integrity: sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==} + prettier@3.5.3: + resolution: {integrity: sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==} engines: {node: '>=14'} hasBin: true @@ -5331,6 +5435,10 @@ packages: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} + readdirp@4.0.2: + resolution: {integrity: sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==} + engines: {node: '>= 14.16.0'} + redent@3.0.0: resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} engines: {node: '>=8'} @@ -5343,8 +5451,8 @@ packages: redux@5.0.1: resolution: {integrity: sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==} - reflect.getprototypeof@1.0.4: - resolution: {integrity: sha512-ECkTw8TmJwW60lOTR+ZkODISW6RQ8+2CL3COqtiJKLd6MmB45hN51HprHFziKLGkAuTGQhBb91V8cy+KHlaCjw==} + reflect.getprototypeof@1.0.10: + resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} engines: {node: '>= 0.4'} regenerator-runtime@0.14.0: @@ -5358,6 +5466,10 @@ packages: resolution: {integrity: sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==} engines: {node: '>= 0.4'} + regexp.prototype.flags@1.5.4: + resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} + engines: {node: '>= 0.4'} + rehype-parse@8.0.5: resolution: {integrity: sha512-Ds3RglaY/+clEX2U2mHflt7NlMA72KspZ0JLUJgBBLpRddBcEw3H8uYZQliQriku22NZpYMfjDdSgHcjxue24A==} @@ -5442,11 +5554,6 @@ packages: rfdc@1.3.1: resolution: {integrity: sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg==} - rimraf@2.7.1: - resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==} - deprecated: Rimraf versions prior to v4 are no longer supported - hasBin: true - rimraf@3.0.2: resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} deprecated: Rimraf versions prior to v4 are no longer supported @@ -5467,6 +5574,11 @@ packages: engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true + rollup@4.39.0: + resolution: {integrity: sha512-thI8kNc02yNvnmJp8dr3fNWJ9tCONDhp6TV35X6HkKGGs9E6q7YWCHbe5vKiTa7TAiNcFEmXKj3X/pG2b3ci0g==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} @@ -5485,9 +5597,17 @@ packages: resolution: {integrity: sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==} engines: {node: '>=0.4'} + safe-array-concat@1.1.3: + resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==} + engines: {node: '>=0.4'} + safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + safe-push-apply@1.0.0: + resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==} + engines: {node: '>= 0.4'} + safe-regex-test@1.0.0: resolution: {integrity: sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==} @@ -5495,12 +5615,13 @@ packages: resolution: {integrity: sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==} engines: {node: '>= 0.4'} + safe-regex-test@1.1.0: + resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} + engines: {node: '>= 0.4'} + safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - sander@0.5.1: - resolution: {integrity: sha512-3lVqBir7WuKDHGrKRDn/1Ye3kwpXaDOMsiRP1wd6wpZW56gJhsbp5RqQpA6JG/P+pkXizygnr1dKR8vzWaVsfA==} - sax@1.3.0: resolution: {integrity: sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==} @@ -5532,6 +5653,11 @@ packages: engines: {node: '>=10'} hasBin: true + semver@7.6.3: + resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==} + engines: {node: '>=10'} + hasBin: true + serialize-javascript@6.0.2: resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} @@ -5552,6 +5678,10 @@ packages: resolution: {integrity: sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g==} engines: {node: '>= 0.4'} + set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + set-function-name@2.0.1: resolution: {integrity: sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==} engines: {node: '>= 0.4'} @@ -5560,6 +5690,10 @@ packages: resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} engines: {node: '>= 0.4'} + set-proto@1.0.0: + resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} + engines: {node: '>= 0.4'} + sharp@0.32.6: resolution: {integrity: sha512-KyLTWwgcR9Oe4d9HwCwNM2l7+J0dUQwn/yf7S0EnTtb0eVS4RxO0eUSvxPtzT4F3SY+C4K6fqdv/DO27sJ/v/w==} engines: {node: '>=14.15.0'} @@ -5586,13 +5720,26 @@ packages: shiki@0.14.5: resolution: {integrity: sha512-1gCAYOcmCFONmErGTrS1fjzJLA7MGZmKzrBNX7apqSwhyITJg2O102uFzXUeBxNnEkDA9vHIKLyeKq0V083vIw==} - side-channel@1.0.4: - resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==} + side-channel-list@1.0.0: + resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} + engines: {node: '>= 0.4'} + + side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} + + side-channel-weakmap@1.0.2: + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} side-channel@1.0.6: resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==} engines: {node: '>= 0.4'} + side-channel@1.1.0: + resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} + engines: {node: '>= 0.4'} + signal-exit@3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} @@ -5609,9 +5756,9 @@ packages: simple-swizzle@0.2.2: resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} - sirv@2.0.4: - resolution: {integrity: sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==} - engines: {node: '>= 10'} + sirv@3.0.0: + resolution: {integrity: sha512-BPwJGUeDaDCHihkORDchNyyTvWFhcusy1XMmhEVTQTwGeybFbp8YEmB+njbPnth1FibULBSBVwCQni25XlCUDg==} + engines: {node: '>=18'} sisteransi@1.0.5: resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} @@ -5640,16 +5787,12 @@ packages: smob@1.5.0: resolution: {integrity: sha512-g6T+p7QO8npa+/hNx9ohv1E5pVCmWrVCUzUXJyLdMmftX6ER0oiWY/w9knEonLpnOp6b6FenKnMfR8gqwWdwig==} - sorcery@0.11.0: - resolution: {integrity: sha512-J69LQ22xrQB1cIFJhPfgtLuI6BpWRiWu1Y3vSsIwK/eAScqJxd/+CJlUuHQRdX2C9NGFamq+KqNywGgaThwfHw==} - hasBin: true - source-map-js@1.0.2: resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} engines: {node: '>=0.10.0'} - source-map-js@1.2.0: - resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} source-map-support@0.5.21: @@ -5724,16 +5867,17 @@ packages: resolution: {integrity: sha512-k01swCJAgQmuADB0YIc+7TuatfNvTBVOoaUWJjTB9R4VJzR5vNWzf5t42ESVZFPS8xTySF7CAdV4t/aaIm3UnQ==} engines: {node: '>=16'} - string.prototype.matchall@4.0.10: - resolution: {integrity: sha512-rGXbGmOEosIQi6Qva94HUjgPs9vKW+dkG7Y8Q5O2OYkWL6wFaTRZO8zM4mhP94uX55wgyrXzfS2aGtGzUL7EJQ==} - - string.prototype.matchall@4.0.11: - resolution: {integrity: sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==} + string.prototype.matchall@4.0.12: + resolution: {integrity: sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==} engines: {node: '>= 0.4'} string.prototype.repeat@1.0.0: resolution: {integrity: sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==} + string.prototype.trim@1.2.10: + resolution: {integrity: sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==} + engines: {node: '>= 0.4'} + string.prototype.trim@1.2.8: resolution: {integrity: sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==} engines: {node: '>= 0.4'} @@ -5748,6 +5892,10 @@ packages: string.prototype.trimend@1.0.8: resolution: {integrity: sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==} + string.prototype.trimend@1.0.9: + resolution: {integrity: sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==} + engines: {node: '>= 0.4'} + string.prototype.trimstart@1.0.7: resolution: {integrity: sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==} @@ -5807,9 +5955,9 @@ packages: peerDependencies: postcss: ^8.2.15 - stylehacks@6.1.0: - resolution: {integrity: sha512-ETErsPFgwlfYZ/CSjMO2Ddf+TsnkCVPBPaoB99Ro8WMAxf7cglzmFsRBhRmKObFjibtcvlNxFFPHuyr3sNlNUQ==} - engines: {node: ^14 || ^16 || >=18.0} + stylehacks@7.0.4: + resolution: {integrity: sha512-i4zfNrGMt9SB4xRK9L83rlsFCgdGANfeDAYacO1pkqcE7cRHPdWHwnKZVz7WY17Veq/FvyYsRAU++Ga+qDFIww==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.4.31 @@ -5829,23 +5977,19 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} - svelte-check@3.6.6: - resolution: {integrity: sha512-b9q9rOHOMYF3U8XllK7LmXTq1LeWQ98waGfEJzrFutViadkNl1tgdEtxIQ8yuPx+VQ4l7YrknYol+0lfZocaZw==} + svelte-check@4.1.6: + resolution: {integrity: sha512-P7w/6tdSfk3zEVvfsgrp3h3DFC75jCdZjTQvgGJtjPORs1n7/v2VMPIoty3PWv7jnfEm3x0G/p9wH4pecTb0Wg==} + engines: {node: '>= 18.0.0'} hasBin: true peerDependencies: - svelte: ^3.55.0 || ^4.0.0-next.0 || ^4.0.0 || ^5.0.0-next.0 + svelte: ^4.0.0 || ^5.0.0-next.0 + typescript: '>=5.0.0' - svelte-check@3.6.7: - resolution: {integrity: sha512-tKEjemK9FYCySAseCaIt+ps5o0XRvLC7ECjyJXXtO7vOQhR9E6JavgoUbGP1PCulD2OTcB/fi9RjV3nyF1AROw==} - hasBin: true + svelte-eslint-parser@1.1.2: + resolution: {integrity: sha512-vqFBRamDKo1l70KMfxxXj1/0Cco5TfMDnqaAjgz6D8PyoMhfMcDOLRkAwPg8WkMyZjMtQL3wW66TZ0x59iqO2w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - svelte: ^3.55.0 || ^4.0.0-next.0 || ^4.0.0 || ^5.0.0-next.0 - - svelte-eslint-parser@0.33.1: - resolution: {integrity: sha512-vo7xPGTlKBGdLH8T5L64FipvTrqv3OQRx9d2z5X05KKZDlF4rQk8KViZO4flKERY+5BiVdOh7zZ7JGJWo5P0uA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - svelte: ^3.37.0 || ^4.0.0 + svelte: ^3.37.0 || ^4.0.0 || ^5.0.0 peerDependenciesMeta: svelte: optional: true @@ -5856,27 +6000,21 @@ packages: peerDependencies: svelte: ^3.19.0 || ^4.0.0 - svelte-hmr@0.16.0: - resolution: {integrity: sha512-Gyc7cOS3VJzLlfj7wKS0ZnzDVdv3Pn2IuVeJPk9m2skfhcu5bq3wtIZyQGggr7/Iim5rH5cncyQft/kRLupcnA==} - engines: {node: ^12.20 || ^14.13.1 || >= 16} - peerDependencies: - svelte: ^3.19.0 || ^4.0.0 - - svelte-preprocess@5.1.3: - resolution: {integrity: sha512-xxAkmxGHT+J/GourS5mVJeOXZzne1FR5ljeOUAMXUkfEhkLEllRreXpbl3dIYJlcJRfL1LO1uIAPpBpBfiqGPw==} - engines: {node: '>= 16.0.0', pnpm: ^8.0.0} + svelte-preprocess@6.0.3: + resolution: {integrity: sha512-PLG2k05qHdhmRG7zR/dyo5qKvakhm8IJ+hD2eFRQmMLHp7X3eJnjeupUtvuRpbNiF31RjVw45W+abDwHEmP5OA==} + engines: {node: '>= 18.0.0'} peerDependencies: '@babel/core': ^7.10.2 coffeescript: ^2.5.1 less: ^3.11.3 || ^4.0.0 postcss: ^7 || ^8 - postcss-load-config: ^2.1.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 + postcss-load-config: '>=3' pug: ^3.0.0 sass: ^1.26.8 - stylus: ^0.55.0 + stylus: '>=0.55' sugarss: ^2.0.0 || ^3.0.0 || ^4.0.0 - svelte: ^3.23.0 || ^4.0.0-next.0 || ^4.0.0 || ^5.0.0-next.0 - typescript: '>=3.9.5 || ^4.0.0 || ^5.0.0' + svelte: ^4.0.0 || ^5.0.0-next.100 || ^5.0.0 + typescript: ^5.0.0 peerDependenciesMeta: '@babel/core': optional: true @@ -5905,8 +6043,8 @@ packages: svelte: ^3.55 || ^4.0.0-next.0 || ^4.0 typescript: ^4.9.4 || ^5.0.0 - svelte2tsx@0.7.3: - resolution: {integrity: sha512-yrjJFvqp32Ag4Oke+T1xXZLqMNrS0gjzAM//+L6+7OhXWMDuMs0fkyb9ymixK9keVOLJ1GbeEVfg59c3E2IN+w==} + svelte2tsx@0.7.34: + resolution: {integrity: sha512-WTMhpNhFf8/h3SMtR5dkdSy2qfveomkhYei/QW9gSPccb0/b82tjHvLop6vT303ZkGswU/da1s6XvrLgthQPCw==} peerDependencies: svelte: ^3.55 || ^4.0.0-next.0 || ^4.0 || ^5.0.0-next.0 typescript: ^4.9.4 || ^5.0.0 @@ -5915,17 +6053,17 @@ packages: resolution: {integrity: sha512-LpLqY2Jr7cRxkrTc796/AaaoMLF/1ax7cto8Ot76wrvKQhrPmZ0JgajiWPmg9mTSDqO16SSLiD17r9MsvAPTmw==} engines: {node: '>=16'} - svelte@4.2.12: - resolution: {integrity: sha512-d8+wsh5TfPwqVzbm4/HCXC783/KPHV60NvwitJnyTA5lWn1elhXMNWhXGCJ7PwPa8qFUnyJNIyuIRt2mT0WMug==} - engines: {node: '>=16'} + svelte@5.27.0: + resolution: {integrity: sha512-Uai13Ydt1ZE+bUHme6b9U38PCYVNCqBRoBMkUKbFbKiD7kHWjdUUrklYAQZJxyKK81qII4mrBwe/YmvEMSlC9w==} + engines: {node: '>=18'} svgo@3.0.2: resolution: {integrity: sha512-Z706C1U2pb1+JGP48fbazf3KxHrWOsLme6Rv7imFBn5EnuanDW1GPaA/P1/dvObE670JDePC3mnj0k0B7P0jjQ==} engines: {node: '>=14.0.0'} hasBin: true - svgo@3.2.0: - resolution: {integrity: sha512-4PP6CMW/V7l/GmKRKzsLR8xxjdHTV4IMvhTnpuHwwBazSIlw5W/5SmPjN8Dwyt7lKbSJrRDgp4t9ph0HgChFBQ==} + svgo@3.3.2: + resolution: {integrity: sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==} engines: {node: '>=14.0.0'} hasBin: true @@ -5963,8 +6101,9 @@ packages: through@2.3.8: resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} - tiny-glob@0.2.9: - resolution: {integrity: sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg==} + tinyglobby@0.2.12: + resolution: {integrity: sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==} + engines: {node: '>=12.0.0'} tmp@0.0.33: resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} @@ -6007,23 +6146,17 @@ packages: trough@2.1.0: resolution: {integrity: sha512-AqTiAOLcj85xS7vQ8QkAV41hPDIJ71XJB4RCUrzo/1GM2CQwhkJGaf9Hgr7BOugMRpgGUrqRg/DrBDl4H40+8g==} - ts-api-utils@1.0.3: - resolution: {integrity: sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==} - engines: {node: '>=16.13.0'} + ts-api-utils@2.1.0: + resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==} + engines: {node: '>=18.12'} peerDependencies: - typescript: '>=4.2.0' - - ts-api-utils@1.3.0: - resolution: {integrity: sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==} - engines: {node: '>=16'} - peerDependencies: - typescript: '>=4.2.0' + typescript: '>=4.8.4' tsconfig-resolver@3.0.1: resolution: {integrity: sha512-ZHqlstlQF449v8glscGRXzL6l2dZvASPCdXJRWG4gHEZlUVx2Jtmr+a2zeVG4LCsKhDXKRj5R3h0C/98UcVAQg==} - tslib@2.6.2: - resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} tty-table@4.2.2: resolution: {integrity: sha512-2gvCArMZLxgvpZ2NvQKdnYWIFLe7I/z5JClMuhrDXunmKgSZcQKcZRjN9XjAFiToMz2pUo1dEIXyrm0AwgV5Tw==} @@ -6106,6 +6239,10 @@ packages: resolution: {integrity: sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==} engines: {node: '>= 0.4'} + typed-array-buffer@1.0.3: + resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} + engines: {node: '>= 0.4'} + typed-array-byte-length@1.0.0: resolution: {integrity: sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==} engines: {node: '>= 0.4'} @@ -6114,6 +6251,10 @@ packages: resolution: {integrity: sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==} engines: {node: '>= 0.4'} + typed-array-byte-length@1.0.3: + resolution: {integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==} + engines: {node: '>= 0.4'} + typed-array-byte-offset@1.0.0: resolution: {integrity: sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==} engines: {node: '>= 0.4'} @@ -6122,6 +6263,10 @@ packages: resolution: {integrity: sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==} engines: {node: '>= 0.4'} + typed-array-byte-offset@1.0.4: + resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==} + engines: {node: '>= 0.4'} + typed-array-length@1.0.4: resolution: {integrity: sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==} @@ -6129,32 +6274,42 @@ packages: resolution: {integrity: sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==} engines: {node: '>= 0.4'} - typescript@5.1.3: - resolution: {integrity: sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw==} - engines: {node: '>=14.17'} - hasBin: true + typed-array-length@1.0.7: + resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} + engines: {node: '>= 0.4'} - 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-eslint@8.30.1: + resolution: {integrity: sha512-D7lC0kcehVH7Mb26MRQi64LMyRJsj3dToJxM1+JVTl53DQSV5/7oUGWQLcKl1C1KnoVHxMMU2FNQMffr7F3Row==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' typescript@5.4.5: resolution: {integrity: sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==} engines: {node: '>=14.17'} hasBin: true + typescript@5.7.3: + resolution: {integrity: sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==} + engines: {node: '>=14.17'} + hasBin: true + + typescript@5.8.3: + resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} + engines: {node: '>=14.17'} + hasBin: true + ultrahtml@1.5.2: resolution: {integrity: sha512-qh4mBffhlkiXwDAOxvSGxhL0QEQsTbnP9BozOK3OYPEGvPvdWzvAUaXNtUSMdNsKDtuyjEbyVUPFZ52SSLhLqw==} unbox-primitive@1.0.2: resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} + unbox-primitive@1.1.0: + resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} + engines: {node: '>= 0.4'} + undici-types@5.26.5: resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} @@ -6227,8 +6382,8 @@ packages: peerDependencies: browserslist: '>= 4.21.0' - update-browserslist-db@1.0.16: - resolution: {integrity: sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ==} + update-browserslist-db@1.1.1: + resolution: {integrity: sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==} hasBin: true peerDependencies: browserslist: '>= 4.21.0' @@ -6328,54 +6483,34 @@ packages: terser: optional: true - vite@5.2.12: - resolution: {integrity: sha512-/gC8GxzxMK5ntBwb48pR32GGhENnjtY30G4A0jemunsBkiEZFw60s8InGpN8gkhHEkjnRK1aSAxeQgwvFhUHAA==} - engines: {node: ^18.0.0 || >=20.0.0} + vite@6.3.0: + resolution: {integrity: sha512-9aC0n4pr6hIbvi1YOpFjwQ+QOTGssvbJKoeYkuHHGWwlXfdxQlI8L2qNMo9awEEcCPSiS+5mJZk5jH1PAqoDeQ==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true peerDependencies: - '@types/node': ^18.0.0 || >=20.0.0 + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + jiti: '>=1.21.0' less: '*' lightningcss: ^1.21.0 sass: '*' + sass-embedded: '*' stylus: '*' sugarss: '*' - terser: ^5.4.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 peerDependenciesMeta: '@types/node': optional: true + jiti: + optional: true less: optional: true lightningcss: optional: true sass: optional: true - stylus: - optional: true - sugarss: - optional: true - terser: - optional: true - - vite@5.3.2: - resolution: {integrity: sha512-6lA7OBHBlXUxiJxbO5aAY2fsHHzDr1q7DvXYnyZycRs2Dz+dXBWuhpWHvmljTRTpQC2uvGmUFFkSHF2vGo90MA==} - engines: {node: ^18.0.0 || >=20.0.0} - hasBin: true - peerDependencies: - '@types/node': ^18.0.0 || >=20.0.0 - less: '*' - lightningcss: ^1.21.0 - sass: '*' - stylus: '*' - sugarss: '*' - terser: ^5.4.0 - peerDependenciesMeta: - '@types/node': - optional: true - less: - optional: true - lightningcss: - optional: true - sass: + sass-embedded: optional: true stylus: optional: true @@ -6383,6 +6518,10 @@ packages: optional: true terser: optional: true + tsx: + optional: true + yaml: + optional: true vitefu@0.2.5: resolution: {integrity: sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q==} @@ -6392,6 +6531,14 @@ packages: vite: optional: true + vitefu@1.0.4: + resolution: {integrity: sha512-y6zEE3PQf6uu/Mt6DTJ9ih+kyJLr4XcSgHR2zUkM8SWDhuixEJxfJ6CZGMHh1Ec3vPLoEA0IHU5oWzVqw8ulow==} + peerDependencies: + vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 + peerDependenciesMeta: + vite: + optional: true + vscode-oniguruma@1.7.0: resolution: {integrity: sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==} @@ -6418,12 +6565,17 @@ packages: which-boxed-primitive@1.0.2: resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} - which-builtin-type@1.1.3: - resolution: {integrity: sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==} + which-boxed-primitive@1.1.1: + resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} engines: {node: '>= 0.4'} - which-collection@1.0.1: - resolution: {integrity: sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==} + which-builtin-type@1.2.1: + resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==} + engines: {node: '>= 0.4'} + + which-collection@1.0.2: + resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} + engines: {node: '>= 0.4'} which-module@2.0.1: resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==} @@ -6448,6 +6600,10 @@ packages: resolution: {integrity: sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==} engines: {node: '>= 0.4'} + which-typed-array@1.1.19: + resolution: {integrity: sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==} + engines: {node: '>= 0.4'} + which@1.3.1: resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} hasBin: true @@ -6527,6 +6683,9 @@ packages: resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==} engines: {node: '>=12.20'} + zimmerframe@1.1.2: + resolution: {integrity: sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==} + zod@3.21.1: resolution: {integrity: sha512-+dTu2m6gmCbO9Ahm4ZBDapx2O6ZY9QSPXst2WXjcznPMwf2YNpn3RevLx4KkZp1OPW/ouFcoBtBzFz/LeY69oA==} @@ -6617,12 +6776,12 @@ snapshots: - supports-color - vite - '@astrojs/svelte@4.0.2(astro@3.2.2(@types/node@20.14.6)(terser@5.31.0))(svelte@4.2.1)(typescript@5.4.5)(vite@4.5.3(@types/node@20.14.6)(terser@5.31.0))': + '@astrojs/svelte@4.0.2(astro@3.2.2(@types/node@20.14.6)(terser@5.31.0))(svelte@4.2.1)(typescript@5.8.3)(vite@4.5.3(@types/node@20.14.6)(terser@5.31.0))': dependencies: '@sveltejs/vite-plugin-svelte': 2.5.3(svelte@4.2.1)(vite@4.5.3(@types/node@20.14.6)(terser@5.31.0)) astro: 3.2.2(@types/node@20.14.6)(terser@5.31.0) svelte: 4.2.1 - svelte2tsx: 0.6.23(svelte@4.2.1)(typescript@5.4.5) + svelte2tsx: 0.6.23(svelte@4.2.1)(typescript@5.8.3) transitivePeerDependencies: - supports-color - typescript @@ -6654,7 +6813,7 @@ snapshots: '@babel/code-frame@7.24.7': dependencies: '@babel/highlight': 7.24.7 - picocolors: 1.0.1 + picocolors: 1.1.1 '@babel/compat-data@7.23.2': {} @@ -6693,7 +6852,7 @@ snapshots: '@babel/traverse': 7.24.7 '@babel/types': 7.24.7 convert-source-map: 2.0.0 - debug: 4.3.5 + debug: 4.4.0 gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -6730,7 +6889,7 @@ snapshots: dependencies: '@babel/compat-data': 7.24.7 '@babel/helper-validator-option': 7.24.7 - browserslist: 4.23.1 + browserslist: 4.24.4 lru-cache: 5.1.1 semver: 6.3.1 @@ -6856,7 +7015,7 @@ snapshots: '@babel/helper-validator-identifier': 7.24.7 chalk: 2.4.2 js-tokens: 4.0.0 - picocolors: 1.0.1 + picocolors: 1.1.1 '@babel/parser@7.23.0': dependencies: @@ -6945,7 +7104,7 @@ snapshots: '@babel/helper-split-export-declaration': 7.24.7 '@babel/parser': 7.24.7 '@babel/types': 7.24.7 - debug: 4.3.5 + debug: 4.4.0 globals: 11.12.0 transitivePeerDependencies: - supports-color @@ -7174,16 +7333,13 @@ snapshots: transitivePeerDependencies: - supports-color - '@dagrejs/dagre@1.0.4': + '@dagrejs/dagre@1.1.4': dependencies: - '@dagrejs/graphlib': 2.1.13 + '@dagrejs/graphlib': 2.2.4 - '@dagrejs/graphlib@2.1.13': {} + '@dagrejs/graphlib@2.2.4': {} - '@esbuild/aix-ppc64@0.20.2': - optional: true - - '@esbuild/aix-ppc64@0.21.5': + '@esbuild/aix-ppc64@0.25.2': optional: true '@esbuild/android-arm64@0.18.20': @@ -7192,10 +7348,7 @@ snapshots: '@esbuild/android-arm64@0.19.5': optional: true - '@esbuild/android-arm64@0.20.2': - optional: true - - '@esbuild/android-arm64@0.21.5': + '@esbuild/android-arm64@0.25.2': optional: true '@esbuild/android-arm@0.18.20': @@ -7204,10 +7357,7 @@ snapshots: '@esbuild/android-arm@0.19.5': optional: true - '@esbuild/android-arm@0.20.2': - optional: true - - '@esbuild/android-arm@0.21.5': + '@esbuild/android-arm@0.25.2': optional: true '@esbuild/android-x64@0.18.20': @@ -7216,10 +7366,7 @@ snapshots: '@esbuild/android-x64@0.19.5': optional: true - '@esbuild/android-x64@0.20.2': - optional: true - - '@esbuild/android-x64@0.21.5': + '@esbuild/android-x64@0.25.2': optional: true '@esbuild/darwin-arm64@0.18.20': @@ -7228,10 +7375,7 @@ snapshots: '@esbuild/darwin-arm64@0.19.5': optional: true - '@esbuild/darwin-arm64@0.20.2': - optional: true - - '@esbuild/darwin-arm64@0.21.5': + '@esbuild/darwin-arm64@0.25.2': optional: true '@esbuild/darwin-x64@0.18.20': @@ -7240,10 +7384,7 @@ snapshots: '@esbuild/darwin-x64@0.19.5': optional: true - '@esbuild/darwin-x64@0.20.2': - optional: true - - '@esbuild/darwin-x64@0.21.5': + '@esbuild/darwin-x64@0.25.2': optional: true '@esbuild/freebsd-arm64@0.18.20': @@ -7252,10 +7393,7 @@ snapshots: '@esbuild/freebsd-arm64@0.19.5': optional: true - '@esbuild/freebsd-arm64@0.20.2': - optional: true - - '@esbuild/freebsd-arm64@0.21.5': + '@esbuild/freebsd-arm64@0.25.2': optional: true '@esbuild/freebsd-x64@0.18.20': @@ -7264,10 +7402,7 @@ snapshots: '@esbuild/freebsd-x64@0.19.5': optional: true - '@esbuild/freebsd-x64@0.20.2': - optional: true - - '@esbuild/freebsd-x64@0.21.5': + '@esbuild/freebsd-x64@0.25.2': optional: true '@esbuild/linux-arm64@0.18.20': @@ -7276,10 +7411,7 @@ snapshots: '@esbuild/linux-arm64@0.19.5': optional: true - '@esbuild/linux-arm64@0.20.2': - optional: true - - '@esbuild/linux-arm64@0.21.5': + '@esbuild/linux-arm64@0.25.2': optional: true '@esbuild/linux-arm@0.18.20': @@ -7288,10 +7420,7 @@ snapshots: '@esbuild/linux-arm@0.19.5': optional: true - '@esbuild/linux-arm@0.20.2': - optional: true - - '@esbuild/linux-arm@0.21.5': + '@esbuild/linux-arm@0.25.2': optional: true '@esbuild/linux-ia32@0.18.20': @@ -7300,10 +7429,7 @@ snapshots: '@esbuild/linux-ia32@0.19.5': optional: true - '@esbuild/linux-ia32@0.20.2': - optional: true - - '@esbuild/linux-ia32@0.21.5': + '@esbuild/linux-ia32@0.25.2': optional: true '@esbuild/linux-loong64@0.18.20': @@ -7312,10 +7438,7 @@ snapshots: '@esbuild/linux-loong64@0.19.5': optional: true - '@esbuild/linux-loong64@0.20.2': - optional: true - - '@esbuild/linux-loong64@0.21.5': + '@esbuild/linux-loong64@0.25.2': optional: true '@esbuild/linux-mips64el@0.18.20': @@ -7324,10 +7447,7 @@ snapshots: '@esbuild/linux-mips64el@0.19.5': optional: true - '@esbuild/linux-mips64el@0.20.2': - optional: true - - '@esbuild/linux-mips64el@0.21.5': + '@esbuild/linux-mips64el@0.25.2': optional: true '@esbuild/linux-ppc64@0.18.20': @@ -7336,10 +7456,7 @@ snapshots: '@esbuild/linux-ppc64@0.19.5': optional: true - '@esbuild/linux-ppc64@0.20.2': - optional: true - - '@esbuild/linux-ppc64@0.21.5': + '@esbuild/linux-ppc64@0.25.2': optional: true '@esbuild/linux-riscv64@0.18.20': @@ -7348,10 +7465,7 @@ snapshots: '@esbuild/linux-riscv64@0.19.5': optional: true - '@esbuild/linux-riscv64@0.20.2': - optional: true - - '@esbuild/linux-riscv64@0.21.5': + '@esbuild/linux-riscv64@0.25.2': optional: true '@esbuild/linux-s390x@0.18.20': @@ -7360,10 +7474,7 @@ snapshots: '@esbuild/linux-s390x@0.19.5': optional: true - '@esbuild/linux-s390x@0.20.2': - optional: true - - '@esbuild/linux-s390x@0.21.5': + '@esbuild/linux-s390x@0.25.2': optional: true '@esbuild/linux-x64@0.18.20': @@ -7372,10 +7483,10 @@ snapshots: '@esbuild/linux-x64@0.19.5': optional: true - '@esbuild/linux-x64@0.20.2': + '@esbuild/linux-x64@0.25.2': optional: true - '@esbuild/linux-x64@0.21.5': + '@esbuild/netbsd-arm64@0.25.2': optional: true '@esbuild/netbsd-x64@0.18.20': @@ -7384,10 +7495,10 @@ snapshots: '@esbuild/netbsd-x64@0.19.5': optional: true - '@esbuild/netbsd-x64@0.20.2': + '@esbuild/netbsd-x64@0.25.2': optional: true - '@esbuild/netbsd-x64@0.21.5': + '@esbuild/openbsd-arm64@0.25.2': optional: true '@esbuild/openbsd-x64@0.18.20': @@ -7396,10 +7507,7 @@ snapshots: '@esbuild/openbsd-x64@0.19.5': optional: true - '@esbuild/openbsd-x64@0.20.2': - optional: true - - '@esbuild/openbsd-x64@0.21.5': + '@esbuild/openbsd-x64@0.25.2': optional: true '@esbuild/sunos-x64@0.18.20': @@ -7408,10 +7516,7 @@ snapshots: '@esbuild/sunos-x64@0.19.5': optional: true - '@esbuild/sunos-x64@0.20.2': - optional: true - - '@esbuild/sunos-x64@0.21.5': + '@esbuild/sunos-x64@0.25.2': optional: true '@esbuild/win32-arm64@0.18.20': @@ -7420,10 +7525,7 @@ snapshots: '@esbuild/win32-arm64@0.19.5': optional: true - '@esbuild/win32-arm64@0.20.2': - optional: true - - '@esbuild/win32-arm64@0.21.5': + '@esbuild/win32-arm64@0.25.2': optional: true '@esbuild/win32-ia32@0.18.20': @@ -7432,10 +7534,7 @@ snapshots: '@esbuild/win32-ia32@0.19.5': optional: true - '@esbuild/win32-ia32@0.20.2': - optional: true - - '@esbuild/win32-ia32@0.21.5': + '@esbuild/win32-ia32@0.25.2': optional: true '@esbuild/win32-x64@0.18.20': @@ -7444,10 +7543,7 @@ snapshots: '@esbuild/win32-x64@0.19.5': optional: true - '@esbuild/win32-x64@0.20.2': - optional: true - - '@esbuild/win32-x64@0.21.5': + '@esbuild/win32-x64@0.25.2': optional: true '@eslint-community/eslint-utils@4.4.0(eslint@8.43.0)': @@ -7455,20 +7551,40 @@ snapshots: eslint: 8.43.0 eslint-visitor-keys: 3.4.3 - '@eslint-community/eslint-utils@4.4.0(eslint@8.53.0)': + '@eslint-community/eslint-utils@4.4.0(eslint@9.24.0)': dependencies: - eslint: 8.53.0 + eslint: 9.24.0 eslint-visitor-keys: 3.4.3 - '@eslint-community/eslint-utils@4.4.0(eslint@8.57.0)': + '@eslint-community/eslint-utils@4.5.1(eslint@9.24.0)': dependencies: - eslint: 8.57.0 + eslint: 9.24.0 eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.10.0': {} + '@eslint-community/regexpp@4.12.1': {} + '@eslint-community/regexpp@4.9.1': {} + '@eslint/config-array@0.20.0': + dependencies: + '@eslint/object-schema': 2.1.6 + debug: 4.4.0 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + + '@eslint/config-helpers@0.2.1': {} + + '@eslint/core@0.12.0': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/core@0.13.0': + dependencies: + '@types/json-schema': 7.0.15 + '@eslint/eslintrc@2.1.2': dependencies: ajv: 6.12.6 @@ -7483,27 +7599,13 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/eslintrc@2.1.3': + '@eslint/eslintrc@3.3.1': dependencies: ajv: 6.12.6 - debug: 4.3.4(supports-color@8.1.1) - espree: 9.6.1 - globals: 13.23.0 - ignore: 5.3.0 - import-fresh: 3.3.0 - js-yaml: 4.1.0 - minimatch: 3.1.2 - strip-json-comments: 3.1.1 - transitivePeerDependencies: - - supports-color - - '@eslint/eslintrc@2.1.4': - dependencies: - ajv: 6.12.6 - debug: 4.3.4(supports-color@8.1.1) - espree: 9.6.1 - globals: 13.23.0 - ignore: 5.3.0 + debug: 4.4.0 + espree: 10.3.0 + globals: 14.0.0 + ignore: 5.3.1 import-fresh: 3.3.0 js-yaml: 4.1.0 minimatch: 3.1.2 @@ -7513,9 +7615,14 @@ snapshots: '@eslint/js@8.43.0': {} - '@eslint/js@8.53.0': {} + '@eslint/js@9.24.0': {} - '@eslint/js@8.57.0': {} + '@eslint/object-schema@2.1.6': {} + + '@eslint/plugin-kit@0.2.8': + dependencies: + '@eslint/core': 0.13.0 + levn: 0.4.1 '@fastify/busboy@2.0.0': {} @@ -7525,6 +7632,13 @@ snapshots: dependencies: '@hapi/hoek': 9.3.0 + '@humanfs/core@0.19.1': {} + + '@humanfs/node@0.16.6': + dependencies: + '@humanfs/core': 0.19.1 + '@humanwhocodes/retry': 0.3.1 + '@humanwhocodes/config-array@0.11.13': dependencies: '@humanwhocodes/object-schema': 2.0.1 @@ -7533,30 +7647,24 @@ snapshots: transitivePeerDependencies: - supports-color - '@humanwhocodes/config-array@0.11.14': - dependencies: - '@humanwhocodes/object-schema': 2.0.2 - debug: 4.3.4(supports-color@8.1.1) - minimatch: 3.1.2 - transitivePeerDependencies: - - supports-color - '@humanwhocodes/module-importer@1.0.1': {} '@humanwhocodes/object-schema@2.0.1': {} - '@humanwhocodes/object-schema@2.0.2': {} + '@humanwhocodes/retry@0.3.1': {} + + '@humanwhocodes/retry@0.4.2': {} '@jridgewell/gen-mapping@0.3.3': dependencies: '@jridgewell/set-array': 1.1.2 - '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/sourcemap-codec': 1.5.0 '@jridgewell/trace-mapping': 0.3.20 '@jridgewell/gen-mapping@0.3.5': dependencies: '@jridgewell/set-array': 1.2.1 - '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/sourcemap-codec': 1.5.0 '@jridgewell/trace-mapping': 0.3.25 '@jridgewell/resolve-uri@3.1.1': {} @@ -7574,6 +7682,8 @@ snapshots: '@jridgewell/sourcemap-codec@1.4.15': {} + '@jridgewell/sourcemap-codec@1.5.0': {} + '@jridgewell/trace-mapping@0.3.20': dependencies: '@jridgewell/resolve-uri': 3.1.1 @@ -7612,42 +7722,50 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.15.0 - '@playwright/experimental-ct-core@1.45.0(@types/node@20.14.6)(terser@5.31.0)': + '@playwright/experimental-ct-core@1.51.1(@types/node@20.14.6)(terser@5.31.0)': dependencies: - playwright: 1.45.0 - playwright-core: 1.45.0 - vite: 5.3.2(@types/node@20.14.6)(terser@5.31.0) + playwright: 1.51.1 + playwright-core: 1.51.1 + vite: 6.3.0(@types/node@20.14.6)(terser@5.31.0) transitivePeerDependencies: - '@types/node' + - jiti - less - lightningcss - sass + - sass-embedded - stylus - sugarss - terser + - tsx + - yaml - '@playwright/experimental-ct-react@1.45.0(@types/node@20.14.6)(terser@5.31.0)(vite@5.3.2(@types/node@20.14.6)(terser@5.31.0))': + '@playwright/experimental-ct-react@1.51.1(@types/node@20.14.6)(terser@5.31.0)(vite@6.3.0(@types/node@20.14.6)(terser@5.31.0))': dependencies: - '@playwright/experimental-ct-core': 1.45.0(@types/node@20.14.6)(terser@5.31.0) - '@vitejs/plugin-react': 4.3.1(vite@5.3.2(@types/node@20.14.6)(terser@5.31.0)) + '@playwright/experimental-ct-core': 1.51.1(@types/node@20.14.6)(terser@5.31.0) + '@vitejs/plugin-react': 4.3.1(vite@6.3.0(@types/node@20.14.6)(terser@5.31.0)) transitivePeerDependencies: - '@types/node' + - jiti - less - lightningcss - sass + - sass-embedded - stylus - sugarss - supports-color - terser + - tsx - vite + - yaml '@playwright/test@1.44.1': dependencies: playwright: 1.44.1 - '@playwright/test@1.45.0': + '@playwright/test@1.51.1': dependencies: - playwright: 1.45.0 + playwright: 1.51.1 '@polka/url@1.0.0-next.24': {} @@ -7700,14 +7818,14 @@ snapshots: optionalDependencies: 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.8.1)(typescript@5.7.3)': dependencies: '@rollup/pluginutils': 5.1.0(rollup@4.18.0) resolve: 1.22.8 - typescript: 5.4.2 + typescript: 5.7.3 optionalDependencies: rollup: 4.18.0 - tslib: 2.6.2 + tslib: 2.8.1 '@rollup/pluginutils@5.1.0(rollup@4.18.0)': dependencies: @@ -7720,51 +7838,111 @@ snapshots: '@rollup/rollup-android-arm-eabi@4.18.0': optional: true + '@rollup/rollup-android-arm-eabi@4.39.0': + optional: true + '@rollup/rollup-android-arm64@4.18.0': optional: true + '@rollup/rollup-android-arm64@4.39.0': + optional: true + '@rollup/rollup-darwin-arm64@4.18.0': optional: true + '@rollup/rollup-darwin-arm64@4.39.0': + optional: true + '@rollup/rollup-darwin-x64@4.18.0': optional: true + '@rollup/rollup-darwin-x64@4.39.0': + optional: true + + '@rollup/rollup-freebsd-arm64@4.39.0': + optional: true + + '@rollup/rollup-freebsd-x64@4.39.0': + optional: true + '@rollup/rollup-linux-arm-gnueabihf@4.18.0': optional: true + '@rollup/rollup-linux-arm-gnueabihf@4.39.0': + optional: true + '@rollup/rollup-linux-arm-musleabihf@4.18.0': optional: true + '@rollup/rollup-linux-arm-musleabihf@4.39.0': + optional: true + '@rollup/rollup-linux-arm64-gnu@4.18.0': optional: true + '@rollup/rollup-linux-arm64-gnu@4.39.0': + optional: true + '@rollup/rollup-linux-arm64-musl@4.18.0': optional: true + '@rollup/rollup-linux-arm64-musl@4.39.0': + optional: true + + '@rollup/rollup-linux-loongarch64-gnu@4.39.0': + optional: true + '@rollup/rollup-linux-powerpc64le-gnu@4.18.0': optional: true + '@rollup/rollup-linux-powerpc64le-gnu@4.39.0': + optional: true + '@rollup/rollup-linux-riscv64-gnu@4.18.0': optional: true + '@rollup/rollup-linux-riscv64-gnu@4.39.0': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.39.0': + optional: true + '@rollup/rollup-linux-s390x-gnu@4.18.0': optional: true + '@rollup/rollup-linux-s390x-gnu@4.39.0': + optional: true + '@rollup/rollup-linux-x64-gnu@4.18.0': optional: true + '@rollup/rollup-linux-x64-gnu@4.39.0': + optional: true + '@rollup/rollup-linux-x64-musl@4.18.0': optional: true + '@rollup/rollup-linux-x64-musl@4.39.0': + optional: true + '@rollup/rollup-win32-arm64-msvc@4.18.0': optional: true + '@rollup/rollup-win32-arm64-msvc@4.39.0': + optional: true + '@rollup/rollup-win32-ia32-msvc@4.18.0': optional: true + '@rollup/rollup-win32-ia32-msvc@4.39.0': + optional: true + '@rollup/rollup-win32-x64-msvc@4.18.0': optional: true + '@rollup/rollup-win32-x64-msvc@4.39.0': + optional: true + '@sideway/address@4.1.4': dependencies: '@hapi/hoek': 9.3.0 @@ -7775,62 +7953,44 @@ snapshots: '@sindresorhus/merge-streams@1.0.0': {} - '@svelte-put/shortcut@3.1.0': {} - - '@sveltejs/adapter-auto@3.1.1(@sveltejs/kit@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)))': + '@svelte-put/shortcut@4.1.0(svelte@5.27.0)': dependencies: - '@sveltejs/kit': 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)) - import-meta-resolve: 4.0.0 + svelte: 5.27.0 - '@sveltejs/adapter-auto@3.2.1(@sveltejs/kit@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)))': + '@sveltejs/acorn-typescript@1.0.5(acorn@8.14.0)': dependencies: - '@sveltejs/kit': 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)) + acorn: 8.14.0 + + '@sveltejs/adapter-auto@6.0.0(@sveltejs/kit@2.20.7(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.27.0)(vite@6.3.0(@types/node@20.14.6)(terser@5.31.0)))(svelte@5.27.0)(vite@6.3.0(@types/node@20.14.6)(terser@5.31.0)))': + dependencies: + '@sveltejs/kit': 2.20.7(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.27.0)(vite@6.3.0(@types/node@20.14.6)(terser@5.31.0)))(svelte@5.27.0)(vite@6.3.0(@types/node@20.14.6)(terser@5.31.0)) import-meta-resolve: 4.1.0 - '@sveltejs/kit@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))': + '@sveltejs/kit@2.20.7(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.27.0)(vite@6.3.0(@types/node@20.14.6)(terser@5.31.0)))(svelte@5.27.0)(vite@6.3.0(@types/node@20.14.6)(terser@5.31.0))': dependencies: - '@sveltejs/vite-plugin-svelte': 3.1.1(svelte@4.2.12)(vite@5.2.12(@types/node@20.14.6)(terser@5.31.0)) + '@sveltejs/vite-plugin-svelte': 5.0.3(svelte@5.27.0)(vite@6.3.0(@types/node@20.14.6)(terser@5.31.0)) '@types/cookie': 0.6.0 cookie: 0.6.0 - devalue: 5.0.0 - esm-env: 1.0.0 + devalue: 5.1.1 + esm-env: 1.2.2 import-meta-resolve: 4.1.0 kleur: 4.1.5 - magic-string: 0.30.10 + magic-string: 0.30.17 mrmime: 2.0.0 sade: 1.8.1 set-cookie-parser: 2.6.0 - sirv: 2.0.4 - svelte: 4.2.12 - tiny-glob: 0.2.9 - vite: 5.2.12(@types/node@20.14.6)(terser@5.31.0) + sirv: 3.0.0 + svelte: 5.27.0 + vite: 6.3.0(@types/node@20.14.6)(terser@5.31.0) - '@sveltejs/kit@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@2.3.11(svelte@5.27.0)(typescript@5.8.3)': dependencies: - '@sveltejs/vite-plugin-svelte': 3.1.1(svelte@4.2.12)(vite@5.3.2(@types/node@20.14.6)(terser@5.31.0)) - '@types/cookie': 0.6.0 - cookie: 0.6.0 - devalue: 4.3.2 - esm-env: 1.0.0 - import-meta-resolve: 4.0.0 - kleur: 4.1.5 - magic-string: 0.30.8 - mrmime: 2.0.0 - sade: 1.8.1 - set-cookie-parser: 2.6.0 - sirv: 2.0.4 - svelte: 4.2.12 - tiny-glob: 0.2.9 - 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)': - dependencies: - chokidar: 3.6.0 + chokidar: 4.0.3 kleur: 4.1.5 sade: 1.8.1 - semver: 7.6.0 - svelte: 4.2.12 - svelte2tsx: 0.7.3(svelte@4.2.12)(typescript@5.4.2) + semver: 7.6.3 + svelte: 5.27.0 + svelte2tsx: 0.7.34(svelte@5.27.0)(typescript@5.8.3) transitivePeerDependencies: - typescript @@ -7843,21 +8003,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@sveltejs/vite-plugin-svelte-inspector@2.1.0(@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))': + '@sveltejs/vite-plugin-svelte-inspector@4.0.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.27.0)(vite@6.3.0(@types/node@20.14.6)(terser@5.31.0)))(svelte@5.27.0)(vite@6.3.0(@types/node@20.14.6)(terser@5.31.0))': dependencies: - '@sveltejs/vite-plugin-svelte': 3.1.1(svelte@4.2.12)(vite@5.2.12(@types/node@20.14.6)(terser@5.31.0)) - debug: 4.3.5 - svelte: 4.2.12 - vite: 5.2.12(@types/node@20.14.6)(terser@5.31.0) - transitivePeerDependencies: - - supports-color - - '@sveltejs/vite-plugin-svelte-inspector@2.1.0(@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))': - dependencies: - '@sveltejs/vite-plugin-svelte': 3.1.1(svelte@4.2.12)(vite@5.3.2(@types/node@20.14.6)(terser@5.31.0)) - debug: 4.3.5 - svelte: 4.2.12 - vite: 5.3.2(@types/node@20.14.6)(terser@5.31.0) + '@sveltejs/vite-plugin-svelte': 5.0.3(svelte@5.27.0)(vite@6.3.0(@types/node@20.14.6)(terser@5.31.0)) + debug: 4.4.0 + svelte: 5.27.0 + vite: 6.3.0(@types/node@20.14.6)(terser@5.31.0) transitivePeerDependencies: - supports-color @@ -7875,31 +8026,16 @@ snapshots: transitivePeerDependencies: - supports-color - '@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.12)(vite@5.2.12(@types/node@20.14.6)(terser@5.31.0))': + '@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.27.0)(vite@6.3.0(@types/node@20.14.6)(terser@5.31.0))': dependencies: - '@sveltejs/vite-plugin-svelte-inspector': 2.1.0(@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)) - debug: 4.3.5 + '@sveltejs/vite-plugin-svelte-inspector': 4.0.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.27.0)(vite@6.3.0(@types/node@20.14.6)(terser@5.31.0)))(svelte@5.27.0)(vite@6.3.0(@types/node@20.14.6)(terser@5.31.0)) + debug: 4.4.0 deepmerge: 4.3.1 kleur: 4.1.5 - magic-string: 0.30.10 - svelte: 4.2.12 - svelte-hmr: 0.16.0(svelte@4.2.12) - vite: 5.2.12(@types/node@20.14.6)(terser@5.31.0) - vitefu: 0.2.5(vite@5.2.12(@types/node@20.14.6)(terser@5.31.0)) - transitivePeerDependencies: - - supports-color - - '@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.12)(vite@5.3.2(@types/node@20.14.6)(terser@5.31.0))': - dependencies: - '@sveltejs/vite-plugin-svelte-inspector': 2.1.0(@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)) - debug: 4.3.5 - deepmerge: 4.3.1 - kleur: 4.1.5 - magic-string: 0.30.10 - svelte: 4.2.12 - svelte-hmr: 0.16.0(svelte@4.2.12) - vite: 5.3.2(@types/node@20.14.6)(terser@5.31.0) - vitefu: 0.2.5(vite@5.3.2(@types/node@20.14.6)(terser@5.31.0)) + magic-string: 0.30.17 + svelte: 5.27.0 + vite: 6.3.0(@types/node@20.14.6)(terser@5.31.0) + vitefu: 1.0.4(vite@6.3.0(@types/node@20.14.6)(terser@5.31.0)) transitivePeerDependencies: - supports-color @@ -8033,6 +8169,10 @@ snapshots: '@types/estree@1.0.5': {} + '@types/estree@1.0.6': {} + + '@types/estree@1.0.7': {} + '@types/hast@2.3.7': dependencies: '@types/unist': 2.0.9 @@ -8077,8 +8217,6 @@ snapshots: '@types/prop-types@15.7.9': {} - '@types/pug@2.0.10': {} - '@types/react-dom@18.2.14': dependencies: '@types/react': 18.2.36 @@ -8116,8 +8254,6 @@ snapshots: '@types/semver@6.2.5': {} - '@types/semver@7.5.6': {} - '@types/sinonjs__fake-timers@8.1.1': {} '@types/sizzle@2.3.8': {} @@ -8133,259 +8269,159 @@ snapshots: '@types/node': 18.7.16 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@8.29.1(@typescript-eslint/parser@8.29.1(eslint@9.24.0)(typescript@5.8.3))(eslint@9.24.0)(typescript@5.8.3)': dependencies: '@eslint-community/regexpp': 4.10.0 - '@typescript-eslint/parser': 6.10.0(eslint@8.53.0)(typescript@5.2.2) - '@typescript-eslint/scope-manager': 6.10.0 - '@typescript-eslint/type-utils': 6.10.0(eslint@8.53.0)(typescript@5.2.2) - '@typescript-eslint/utils': 6.10.0(eslint@8.53.0)(typescript@5.2.2) - '@typescript-eslint/visitor-keys': 6.10.0 - debug: 4.3.4(supports-color@8.1.1) - eslint: 8.53.0 - graphemer: 1.4.0 - ignore: 5.2.4 - natural-compare: 1.4.0 - semver: 7.5.4 - ts-api-utils: 1.0.3(typescript@5.2.2) - optionalDependencies: - typescript: 5.2.2 - transitivePeerDependencies: - - 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)': - dependencies: - '@eslint-community/regexpp': 4.10.0 - '@typescript-eslint/parser': 7.2.0(eslint@8.57.0)(typescript@5.4.2) - '@typescript-eslint/scope-manager': 7.2.0 - '@typescript-eslint/type-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.2) - '@typescript-eslint/visitor-keys': 7.2.0 - debug: 4.3.4(supports-color@8.1.1) - eslint: 8.57.0 - graphemer: 1.4.0 - ignore: 5.3.0 - natural-compare: 1.4.0 - semver: 7.6.0 - ts-api-utils: 1.0.3(typescript@5.4.2) - optionalDependencies: - typescript: 5.4.2 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/eslint-plugin@8.0.0(@typescript-eslint/parser@8.0.0(eslint@8.43.0)(typescript@5.1.3))(eslint@8.43.0)(typescript@5.1.3)': - dependencies: - '@eslint-community/regexpp': 4.10.0 - '@typescript-eslint/parser': 8.0.0(eslint@8.43.0)(typescript@5.1.3) - '@typescript-eslint/scope-manager': 8.0.0 - '@typescript-eslint/type-utils': 8.0.0(eslint@8.43.0)(typescript@5.1.3) - '@typescript-eslint/utils': 8.0.0(eslint@8.43.0)(typescript@5.1.3) - '@typescript-eslint/visitor-keys': 8.0.0 - eslint: 8.43.0 + '@typescript-eslint/parser': 8.29.1(eslint@9.24.0)(typescript@5.8.3) + '@typescript-eslint/scope-manager': 8.29.1 + '@typescript-eslint/type-utils': 8.29.1(eslint@9.24.0)(typescript@5.8.3) + '@typescript-eslint/utils': 8.29.1(eslint@9.24.0)(typescript@5.8.3) + '@typescript-eslint/visitor-keys': 8.29.1 + eslint: 9.24.0 graphemer: 1.4.0 ignore: 5.3.1 natural-compare: 1.4.0 - ts-api-utils: 1.3.0(typescript@5.1.3) - optionalDependencies: - typescript: 5.1.3 + ts-api-utils: 2.1.0(typescript@5.8.3) + typescript: 5.8.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@6.10.0(eslint@8.53.0)(typescript@5.2.2)': + '@typescript-eslint/eslint-plugin@8.30.1(@typescript-eslint/parser@8.30.1(eslint@9.24.0)(typescript@5.8.3))(eslint@9.24.0)(typescript@5.8.3)': dependencies: - '@typescript-eslint/scope-manager': 6.10.0 - '@typescript-eslint/types': 6.10.0 - '@typescript-eslint/typescript-estree': 6.10.0(typescript@5.2.2) - '@typescript-eslint/visitor-keys': 6.10.0 - debug: 4.3.4(supports-color@8.1.1) - eslint: 8.53.0 - optionalDependencies: - typescript: 5.2.2 + '@eslint-community/regexpp': 4.12.1 + '@typescript-eslint/parser': 8.30.1(eslint@9.24.0)(typescript@5.8.3) + '@typescript-eslint/scope-manager': 8.30.1 + '@typescript-eslint/type-utils': 8.30.1(eslint@9.24.0)(typescript@5.8.3) + '@typescript-eslint/utils': 8.30.1(eslint@9.24.0)(typescript@5.8.3) + '@typescript-eslint/visitor-keys': 8.30.1 + eslint: 9.24.0 + graphemer: 1.4.0 + ignore: 5.3.1 + natural-compare: 1.4.0 + ts-api-utils: 2.1.0(typescript@5.8.3) + typescript: 5.8.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.2)': + '@typescript-eslint/parser@8.29.1(eslint@9.24.0)(typescript@5.8.3)': dependencies: - '@typescript-eslint/scope-manager': 7.2.0 - '@typescript-eslint/types': 7.2.0 - '@typescript-eslint/typescript-estree': 7.2.0(typescript@5.4.2) - '@typescript-eslint/visitor-keys': 7.2.0 - debug: 4.3.4(supports-color@8.1.1) - eslint: 8.57.0 - optionalDependencies: - typescript: 5.4.2 + '@typescript-eslint/scope-manager': 8.29.1 + '@typescript-eslint/types': 8.29.1 + '@typescript-eslint/typescript-estree': 8.29.1(typescript@5.8.3) + '@typescript-eslint/visitor-keys': 8.29.1 + debug: 4.4.0 + eslint: 9.24.0 + typescript: 5.8.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.0.0(eslint@8.43.0)(typescript@5.1.3)': + '@typescript-eslint/parser@8.30.1(eslint@9.24.0)(typescript@5.8.3)': dependencies: - '@typescript-eslint/scope-manager': 8.0.0 - '@typescript-eslint/types': 8.0.0 - '@typescript-eslint/typescript-estree': 8.0.0(typescript@5.1.3) - '@typescript-eslint/visitor-keys': 8.0.0 - debug: 4.3.4(supports-color@8.1.1) - eslint: 8.43.0 - optionalDependencies: - typescript: 5.1.3 + '@typescript-eslint/scope-manager': 8.30.1 + '@typescript-eslint/types': 8.30.1 + '@typescript-eslint/typescript-estree': 8.30.1(typescript@5.8.3) + '@typescript-eslint/visitor-keys': 8.30.1 + debug: 4.4.0 + eslint: 9.24.0 + typescript: 5.8.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@6.10.0': + '@typescript-eslint/scope-manager@8.29.1': dependencies: - '@typescript-eslint/types': 6.10.0 - '@typescript-eslint/visitor-keys': 6.10.0 + '@typescript-eslint/types': 8.29.1 + '@typescript-eslint/visitor-keys': 8.29.1 - '@typescript-eslint/scope-manager@7.2.0': + '@typescript-eslint/scope-manager@8.30.1': dependencies: - '@typescript-eslint/types': 7.2.0 - '@typescript-eslint/visitor-keys': 7.2.0 + '@typescript-eslint/types': 8.30.1 + '@typescript-eslint/visitor-keys': 8.30.1 - '@typescript-eslint/scope-manager@8.0.0': + '@typescript-eslint/type-utils@8.29.1(eslint@9.24.0)(typescript@5.8.3)': dependencies: - '@typescript-eslint/types': 8.0.0 - '@typescript-eslint/visitor-keys': 8.0.0 - - '@typescript-eslint/type-utils@6.10.0(eslint@8.53.0)(typescript@5.2.2)': - dependencies: - '@typescript-eslint/typescript-estree': 6.10.0(typescript@5.2.2) - '@typescript-eslint/utils': 6.10.0(eslint@8.53.0)(typescript@5.2.2) - debug: 4.3.4(supports-color@8.1.1) - eslint: 8.53.0 - ts-api-utils: 1.0.3(typescript@5.2.2) - optionalDependencies: - typescript: 5.2.2 + '@typescript-eslint/typescript-estree': 8.29.1(typescript@5.8.3) + '@typescript-eslint/utils': 8.29.1(eslint@9.24.0)(typescript@5.8.3) + debug: 4.4.0 + eslint: 9.24.0 + ts-api-utils: 2.1.0(typescript@5.8.3) + typescript: 5.8.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/type-utils@7.2.0(eslint@8.57.0)(typescript@5.4.2)': + '@typescript-eslint/type-utils@8.30.1(eslint@9.24.0)(typescript@5.8.3)': dependencies: - '@typescript-eslint/typescript-estree': 7.2.0(typescript@5.4.2) - '@typescript-eslint/utils': 7.2.0(eslint@8.57.0)(typescript@5.4.2) - debug: 4.3.4(supports-color@8.1.1) - eslint: 8.57.0 - ts-api-utils: 1.0.3(typescript@5.4.2) - optionalDependencies: - typescript: 5.4.2 + '@typescript-eslint/typescript-estree': 8.30.1(typescript@5.8.3) + '@typescript-eslint/utils': 8.30.1(eslint@9.24.0)(typescript@5.8.3) + debug: 4.4.0 + eslint: 9.24.0 + ts-api-utils: 2.1.0(typescript@5.8.3) + typescript: 5.8.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/type-utils@8.0.0(eslint@8.43.0)(typescript@5.1.3)': + '@typescript-eslint/types@8.29.1': {} + + '@typescript-eslint/types@8.30.1': {} + + '@typescript-eslint/typescript-estree@8.29.1(typescript@5.8.3)': dependencies: - '@typescript-eslint/typescript-estree': 8.0.0(typescript@5.1.3) - '@typescript-eslint/utils': 8.0.0(eslint@8.43.0)(typescript@5.1.3) - debug: 4.3.5 - ts-api-utils: 1.3.0(typescript@5.1.3) - optionalDependencies: - typescript: 5.1.3 - transitivePeerDependencies: - - eslint - - supports-color - - '@typescript-eslint/types@6.10.0': {} - - '@typescript-eslint/types@7.2.0': {} - - '@typescript-eslint/types@8.0.0': {} - - '@typescript-eslint/typescript-estree@6.10.0(typescript@5.2.2)': - dependencies: - '@typescript-eslint/types': 6.10.0 - '@typescript-eslint/visitor-keys': 6.10.0 - debug: 4.3.4(supports-color@8.1.1) - globby: 11.1.0 - is-glob: 4.0.3 - semver: 7.5.4 - ts-api-utils: 1.0.3(typescript@5.2.2) - optionalDependencies: - typescript: 5.2.2 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/typescript-estree@7.2.0(typescript@5.4.2)': - dependencies: - '@typescript-eslint/types': 7.2.0 - '@typescript-eslint/visitor-keys': 7.2.0 - debug: 4.3.4(supports-color@8.1.1) - globby: 11.1.0 - is-glob: 4.0.3 - minimatch: 9.0.3 - semver: 7.6.0 - ts-api-utils: 1.0.3(typescript@5.4.2) - optionalDependencies: - typescript: 5.4.2 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/typescript-estree@8.0.0(typescript@5.1.3)': - dependencies: - '@typescript-eslint/types': 8.0.0 - '@typescript-eslint/visitor-keys': 8.0.0 - debug: 4.3.5 - globby: 11.1.0 + '@typescript-eslint/types': 8.29.1 + '@typescript-eslint/visitor-keys': 8.29.1 + debug: 4.4.0 + fast-glob: 3.3.2 is-glob: 4.0.3 minimatch: 9.0.5 - semver: 7.6.0 - ts-api-utils: 1.3.0(typescript@5.1.3) - optionalDependencies: - typescript: 5.1.3 + semver: 7.6.3 + ts-api-utils: 2.1.0(typescript@5.8.3) + typescript: 5.8.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@6.10.0(eslint@8.53.0)(typescript@5.2.2)': + '@typescript-eslint/typescript-estree@8.30.1(typescript@5.8.3)': dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.53.0) - '@types/json-schema': 7.0.15 - '@types/semver': 7.5.6 - '@typescript-eslint/scope-manager': 6.10.0 - '@typescript-eslint/types': 6.10.0 - '@typescript-eslint/typescript-estree': 6.10.0(typescript@5.2.2) - eslint: 8.53.0 - semver: 7.5.4 + '@typescript-eslint/types': 8.30.1 + '@typescript-eslint/visitor-keys': 8.30.1 + debug: 4.4.0 + fast-glob: 3.3.2 + is-glob: 4.0.3 + minimatch: 9.0.5 + semver: 7.6.3 + ts-api-utils: 2.1.0(typescript@5.8.3) + typescript: 5.8.3 transitivePeerDependencies: - supports-color - - typescript - '@typescript-eslint/utils@7.2.0(eslint@8.57.0)(typescript@5.4.2)': + '@typescript-eslint/utils@8.29.1(eslint@9.24.0)(typescript@5.8.3)': dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) - '@types/json-schema': 7.0.15 - '@types/semver': 7.5.6 - '@typescript-eslint/scope-manager': 7.2.0 - '@typescript-eslint/types': 7.2.0 - '@typescript-eslint/typescript-estree': 7.2.0(typescript@5.4.2) - eslint: 8.57.0 - semver: 7.6.0 + '@eslint-community/eslint-utils': 4.4.0(eslint@9.24.0) + '@typescript-eslint/scope-manager': 8.29.1 + '@typescript-eslint/types': 8.29.1 + '@typescript-eslint/typescript-estree': 8.29.1(typescript@5.8.3) + eslint: 9.24.0 + typescript: 5.8.3 transitivePeerDependencies: - supports-color - - typescript - '@typescript-eslint/utils@8.0.0(eslint@8.43.0)(typescript@5.1.3)': + '@typescript-eslint/utils@8.30.1(eslint@9.24.0)(typescript@5.8.3)': dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.43.0) - '@typescript-eslint/scope-manager': 8.0.0 - '@typescript-eslint/types': 8.0.0 - '@typescript-eslint/typescript-estree': 8.0.0(typescript@5.1.3) - eslint: 8.43.0 + '@eslint-community/eslint-utils': 4.5.1(eslint@9.24.0) + '@typescript-eslint/scope-manager': 8.30.1 + '@typescript-eslint/types': 8.30.1 + '@typescript-eslint/typescript-estree': 8.30.1(typescript@5.8.3) + eslint: 9.24.0 + typescript: 5.8.3 transitivePeerDependencies: - supports-color - - typescript - '@typescript-eslint/visitor-keys@6.10.0': + '@typescript-eslint/visitor-keys@8.29.1': dependencies: - '@typescript-eslint/types': 6.10.0 - eslint-visitor-keys: 3.4.3 + '@typescript-eslint/types': 8.29.1 + eslint-visitor-keys: 4.2.0 - '@typescript-eslint/visitor-keys@7.2.0': + '@typescript-eslint/visitor-keys@8.30.1': dependencies: - '@typescript-eslint/types': 7.2.0 - eslint-visitor-keys: 3.4.3 - - '@typescript-eslint/visitor-keys@8.0.0': - dependencies: - '@typescript-eslint/types': 8.0.0 - eslint-visitor-keys: 3.4.3 - - '@ungap/structured-clone@1.2.0': {} + '@typescript-eslint/types': 8.30.1 + eslint-visitor-keys: 4.2.0 '@vitejs/plugin-react-swc@3.4.1(vite@4.5.0(@types/node@20.14.6)(terser@5.31.0))': dependencies: @@ -8416,14 +8452,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@vitejs/plugin-react@4.3.1(vite@5.3.2(@types/node@20.14.6)(terser@5.31.0))': + '@vitejs/plugin-react@4.3.1(vite@6.3.0(@types/node@20.14.6)(terser@5.31.0))': dependencies: '@babel/core': 7.24.7 '@babel/plugin-transform-react-jsx-self': 7.24.7(@babel/core@7.24.7) '@babel/plugin-transform-react-jsx-source': 7.24.7(@babel/core@7.24.7) '@types/babel__core': 7.20.5 react-refresh: 0.14.2 - vite: 5.3.2(@types/node@20.14.6)(terser@5.31.0) + vite: 6.3.0(@types/node@20.14.6)(terser@5.31.0) transitivePeerDependencies: - supports-color @@ -8431,11 +8467,15 @@ snapshots: dependencies: acorn: 8.10.0 + acorn-jsx@5.3.2(acorn@8.14.0): + dependencies: + acorn: 8.14.0 + acorn@8.10.0: {} acorn@8.11.2: {} - acorn@8.11.3: {} + acorn@8.14.0: {} aggregate-error@3.1.0: dependencies: @@ -8494,6 +8534,8 @@ snapshots: dependencies: dequal: 2.0.3 + aria-query@5.3.2: {} + array-buffer-byte-length@1.0.0: dependencies: call-bind: 1.0.7 @@ -8504,13 +8546,10 @@ snapshots: call-bind: 1.0.7 is-array-buffer: 3.0.4 - array-includes@3.1.7: + array-buffer-byte-length@1.0.2: dependencies: - call-bind: 1.0.5 - define-properties: 1.2.1 - es-abstract: 1.22.3 - get-intrinsic: 1.2.2 - is-string: 1.0.7 + call-bound: 1.0.4 + is-array-buffer: 3.0.5 array-includes@3.1.8: dependencies: @@ -8541,21 +8580,13 @@ snapshots: es-abstract: 1.22.3 es-shim-unscopables: 1.0.2 - array.prototype.flatmap@1.3.2: + array.prototype.flatmap@1.3.3: dependencies: - call-bind: 1.0.5 + call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.22.3 + es-abstract: 1.23.9 es-shim-unscopables: 1.0.2 - array.prototype.tosorted@1.1.2: - dependencies: - call-bind: 1.0.5 - define-properties: 1.2.1 - es-abstract: 1.22.3 - es-shim-unscopables: 1.0.2 - get-intrinsic: 1.2.2 - array.prototype.tosorted@1.1.4: dependencies: call-bind: 1.0.7 @@ -8585,6 +8616,16 @@ snapshots: is-array-buffer: 3.0.4 is-shared-array-buffer: 1.0.3 + arraybuffer.prototype.slice@1.0.4: + dependencies: + array-buffer-byte-length: 1.0.2 + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.23.9 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + is-array-buffer: 3.0.5 + arrify@1.0.1: {} asn1@0.2.6: @@ -8667,10 +8708,6 @@ snapshots: async@3.2.5: {} - asynciterator.prototype@1.0.0: - dependencies: - has-symbols: 1.0.3 - asynckit@0.4.0: {} at-least-node@1.0.0: {} @@ -8685,14 +8722,14 @@ snapshots: postcss: 8.4.21 postcss-value-parser: 4.2.0 - autoprefixer@10.4.18(postcss@8.4.35): + autoprefixer@10.4.21(postcss@8.5.3): dependencies: - browserslist: 4.23.0 - caniuse-lite: 1.0.30001594 + browserslist: 4.24.4 + caniuse-lite: 1.0.30001712 fraction.js: 4.3.7 normalize-range: 0.1.2 - picocolors: 1.0.0 - postcss: 8.4.35 + picocolors: 1.1.1 + postcss: 8.5.3 postcss-value-parser: 4.2.0 available-typed-arrays@1.0.5: {} @@ -8716,9 +8753,7 @@ snapshots: dependencies: dequal: 2.0.3 - axobject-query@4.0.0: - dependencies: - dequal: 2.0.3 + axobject-query@4.1.0: {} b4a@1.6.4: optional: true @@ -8793,19 +8828,19 @@ snapshots: node-releases: 2.0.13 update-browserslist-db: 1.0.13(browserslist@4.22.1) - browserslist@4.23.0: + browserslist@4.24.2: dependencies: - caniuse-lite: 1.0.30001594 - electron-to-chromium: 1.4.693 - node-releases: 2.0.14 - update-browserslist-db: 1.0.13(browserslist@4.23.0) + caniuse-lite: 1.0.30001680 + electron-to-chromium: 1.5.56 + node-releases: 2.0.18 + update-browserslist-db: 1.1.1(browserslist@4.24.2) - browserslist@4.23.1: + browserslist@4.24.4: dependencies: - caniuse-lite: 1.0.30001639 - electron-to-chromium: 1.4.815 - node-releases: 2.0.14 - update-browserslist-db: 1.0.16(browserslist@4.23.1) + caniuse-lite: 1.0.30001712 + electron-to-chromium: 1.5.134 + node-releases: 2.0.19 + update-browserslist-db: 1.1.1(browserslist@4.24.4) buffer-crc32@0.2.13: {} @@ -8825,6 +8860,11 @@ snapshots: cachedir@2.4.0: {} + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + call-bind@1.0.5: dependencies: function-bind: 1.1.2 @@ -8839,6 +8879,18 @@ snapshots: get-intrinsic: 1.2.4 set-function-length: 1.2.1 + call-bind@1.0.8: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.0 + get-intrinsic: 1.2.4 + set-function-length: 1.2.2 + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + callsites@3.1.0: {} camelcase-keys@6.2.2: @@ -8860,9 +8912,9 @@ snapshots: caniuse-lite@1.0.30001553: {} - caniuse-lite@1.0.30001594: {} + caniuse-lite@1.0.30001680: {} - caniuse-lite@1.0.30001639: {} + caniuse-lite@1.0.30001712: {} caseless@0.12.0: {} @@ -8903,17 +8955,9 @@ snapshots: optionalDependencies: fsevents: 2.3.3 - chokidar@3.6.0: + chokidar@4.0.3: dependencies: - anymatch: 3.1.3 - braces: 3.0.2 - glob-parent: 5.1.2 - is-binary-path: 2.1.0 - is-glob: 4.0.3 - normalize-path: 3.0.0 - readdirp: 3.6.0 - optionalDependencies: - fsevents: 2.3.3 + readdirp: 4.0.2 chownr@1.1.4: optional: true @@ -8963,6 +9007,8 @@ snapshots: clsx@2.0.0: {} + clsx@2.1.1: {} + code-red@1.0.4: dependencies: '@jridgewell/sourcemap-codec': 1.4.15 @@ -9051,13 +9097,19 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + css-declaration-sorter@6.4.1(postcss@8.4.21): dependencies: postcss: 8.4.21 - css-declaration-sorter@7.1.1(postcss@8.4.35): + css-declaration-sorter@7.2.0(postcss@8.5.3): dependencies: - postcss: 8.4.35 + postcss: 8.5.3 css-select@5.1.0: dependencies: @@ -9070,7 +9122,7 @@ snapshots: css-tree@2.2.1: dependencies: mdn-data: 2.0.28 - source-map-js: 1.0.2 + source-map-js: 1.2.1 css-tree@2.3.1: dependencies: @@ -9114,47 +9166,47 @@ snapshots: postcss-svgo: 6.0.0(postcss@8.4.21) postcss-unique-selectors: 6.0.0(postcss@8.4.21) - cssnano-preset-default@6.1.0(postcss@8.4.35): + cssnano-preset-default@7.0.6(postcss@8.5.3): dependencies: - browserslist: 4.23.0 - css-declaration-sorter: 7.1.1(postcss@8.4.35) - cssnano-utils: 4.0.2(postcss@8.4.35) - postcss: 8.4.35 - postcss-calc: 9.0.1(postcss@8.4.35) - postcss-colormin: 6.1.0(postcss@8.4.35) - postcss-convert-values: 6.1.0(postcss@8.4.35) - postcss-discard-comments: 6.0.2(postcss@8.4.35) - postcss-discard-duplicates: 6.0.3(postcss@8.4.35) - postcss-discard-empty: 6.0.3(postcss@8.4.35) - postcss-discard-overridden: 6.0.2(postcss@8.4.35) - postcss-merge-longhand: 6.0.4(postcss@8.4.35) - postcss-merge-rules: 6.1.0(postcss@8.4.35) - postcss-minify-font-values: 6.0.3(postcss@8.4.35) - postcss-minify-gradients: 6.0.3(postcss@8.4.35) - postcss-minify-params: 6.1.0(postcss@8.4.35) - postcss-minify-selectors: 6.0.3(postcss@8.4.35) - postcss-normalize-charset: 6.0.2(postcss@8.4.35) - postcss-normalize-display-values: 6.0.2(postcss@8.4.35) - postcss-normalize-positions: 6.0.2(postcss@8.4.35) - postcss-normalize-repeat-style: 6.0.2(postcss@8.4.35) - postcss-normalize-string: 6.0.2(postcss@8.4.35) - postcss-normalize-timing-functions: 6.0.2(postcss@8.4.35) - postcss-normalize-unicode: 6.1.0(postcss@8.4.35) - postcss-normalize-url: 6.0.2(postcss@8.4.35) - postcss-normalize-whitespace: 6.0.2(postcss@8.4.35) - postcss-ordered-values: 6.0.2(postcss@8.4.35) - postcss-reduce-initial: 6.1.0(postcss@8.4.35) - postcss-reduce-transforms: 6.0.2(postcss@8.4.35) - postcss-svgo: 6.0.3(postcss@8.4.35) - postcss-unique-selectors: 6.0.3(postcss@8.4.35) + browserslist: 4.24.2 + css-declaration-sorter: 7.2.0(postcss@8.5.3) + cssnano-utils: 5.0.0(postcss@8.5.3) + postcss: 8.5.3 + postcss-calc: 10.0.2(postcss@8.5.3) + postcss-colormin: 7.0.2(postcss@8.5.3) + postcss-convert-values: 7.0.4(postcss@8.5.3) + postcss-discard-comments: 7.0.3(postcss@8.5.3) + postcss-discard-duplicates: 7.0.1(postcss@8.5.3) + postcss-discard-empty: 7.0.0(postcss@8.5.3) + postcss-discard-overridden: 7.0.0(postcss@8.5.3) + postcss-merge-longhand: 7.0.4(postcss@8.5.3) + postcss-merge-rules: 7.0.4(postcss@8.5.3) + postcss-minify-font-values: 7.0.0(postcss@8.5.3) + postcss-minify-gradients: 7.0.0(postcss@8.5.3) + postcss-minify-params: 7.0.2(postcss@8.5.3) + postcss-minify-selectors: 7.0.4(postcss@8.5.3) + postcss-normalize-charset: 7.0.0(postcss@8.5.3) + postcss-normalize-display-values: 7.0.0(postcss@8.5.3) + postcss-normalize-positions: 7.0.0(postcss@8.5.3) + postcss-normalize-repeat-style: 7.0.0(postcss@8.5.3) + postcss-normalize-string: 7.0.0(postcss@8.5.3) + postcss-normalize-timing-functions: 7.0.0(postcss@8.5.3) + postcss-normalize-unicode: 7.0.2(postcss@8.5.3) + postcss-normalize-url: 7.0.0(postcss@8.5.3) + postcss-normalize-whitespace: 7.0.0(postcss@8.5.3) + postcss-ordered-values: 7.0.1(postcss@8.5.3) + postcss-reduce-initial: 7.0.2(postcss@8.5.3) + postcss-reduce-transforms: 7.0.0(postcss@8.5.3) + postcss-svgo: 7.0.1(postcss@8.5.3) + postcss-unique-selectors: 7.0.3(postcss@8.5.3) cssnano-utils@4.0.0(postcss@8.4.21): dependencies: postcss: 8.4.21 - cssnano-utils@4.0.2(postcss@8.4.35): + cssnano-utils@5.0.0(postcss@8.5.3): dependencies: - postcss: 8.4.35 + postcss: 8.5.3 cssnano@6.0.1(postcss@8.4.21): dependencies: @@ -9162,11 +9214,11 @@ snapshots: lilconfig: 2.1.0 postcss: 8.4.21 - cssnano@6.1.0(postcss@8.4.35): + cssnano@7.0.6(postcss@8.5.3): dependencies: - cssnano-preset-default: 6.1.0(postcss@8.4.35) - lilconfig: 3.1.1 - postcss: 8.4.35 + cssnano-preset-default: 7.0.6(postcss@8.5.3) + lilconfig: 3.1.2 + postcss: 8.5.3 csso@5.0.5: dependencies: @@ -9287,18 +9339,36 @@ snapshots: es-errors: 1.3.0 is-data-view: 1.0.1 + data-view-buffer@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + data-view-byte-length@1.0.1: dependencies: call-bind: 1.0.7 es-errors: 1.3.0 is-data-view: 1.0.1 + data-view-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + data-view-byte-offset@1.0.0: dependencies: call-bind: 1.0.7 es-errors: 1.3.0 is-data-view: 1.0.1 + data-view-byte-offset@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + dataloader@1.4.0: {} date-fns@2.30.0: @@ -9327,6 +9397,14 @@ snapshots: dependencies: ms: 2.1.2 + debug@4.3.7: + dependencies: + ms: 2.1.3 + + debug@4.4.0: + dependencies: + ms: 2.1.3 + decamelize-keys@1.1.1: dependencies: decamelize: 1.2.0 @@ -9378,6 +9456,8 @@ snapshots: dependency-graph@0.11.0: {} + dependency-graph@1.0.0: {} + dequal@2.0.3: {} detect-indent@6.1.0: {} @@ -9387,7 +9467,7 @@ snapshots: devalue@4.3.2: {} - devalue@5.0.0: {} + devalue@5.1.1: {} diff@5.1.0: {} @@ -9425,12 +9505,18 @@ snapshots: dotenv@16.0.3: {} - dotenv@16.4.5: {} + dotenv@16.5.0: {} dotenv@8.6.0: {} dset@3.1.2: {} + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + duplexer@0.1.2: {} eastasianwidth@0.2.0: {} @@ -9442,9 +9528,9 @@ snapshots: electron-to-chromium@1.4.563: {} - electron-to-chromium@1.4.693: {} + electron-to-chromium@1.5.134: {} - electron-to-chromium@1.4.815: {} + electron-to-chromium@1.5.56: {} emoji-regex@10.3.0: {} @@ -9558,45 +9644,86 @@ snapshots: unbox-primitive: 1.0.2 which-typed-array: 1.1.15 + es-abstract@1.23.9: + dependencies: + array-buffer-byte-length: 1.0.2 + arraybuffer.prototype.slice: 1.0.4 + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.4 + data-view-buffer: 1.0.2 + data-view-byte-length: 1.0.2 + data-view-byte-offset: 1.0.1 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.0.0 + es-set-tostringtag: 2.1.0 + es-to-primitive: 1.3.0 + function.prototype.name: 1.1.8 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + get-symbol-description: 1.1.0 + globalthis: 1.0.4 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + has-proto: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + internal-slot: 1.1.0 + is-array-buffer: 3.0.5 + is-callable: 1.2.7 + is-data-view: 1.0.2 + is-regex: 1.2.1 + is-shared-array-buffer: 1.0.4 + is-string: 1.1.1 + is-typed-array: 1.1.15 + is-weakref: 1.1.1 + math-intrinsics: 1.1.0 + object-inspect: 1.13.4 + object-keys: 1.1.1 + object.assign: 4.1.7 + own-keys: 1.0.1 + regexp.prototype.flags: 1.5.4 + safe-array-concat: 1.1.3 + safe-push-apply: 1.0.0 + safe-regex-test: 1.1.0 + set-proto: 1.0.0 + string.prototype.trim: 1.2.10 + string.prototype.trimend: 1.0.9 + string.prototype.trimstart: 1.0.8 + typed-array-buffer: 1.0.3 + typed-array-byte-length: 1.0.3 + typed-array-byte-offset: 1.0.4 + typed-array-length: 1.0.7 + unbox-primitive: 1.1.0 + which-typed-array: 1.1.19 + es-define-property@1.0.0: dependencies: get-intrinsic: 1.2.4 + es-define-property@1.0.1: {} + es-errors@1.3.0: {} - es-iterator-helpers@1.0.15: + es-iterator-helpers@1.2.1: dependencies: - asynciterator.prototype: 1.0.0 - call-bind: 1.0.5 + call-bind: 1.0.8 + call-bound: 1.0.4 define-properties: 1.2.1 - es-abstract: 1.22.3 - es-set-tostringtag: 2.0.2 - function-bind: 1.1.2 - get-intrinsic: 1.2.2 - globalthis: 1.0.3 - has-property-descriptors: 1.0.1 - has-proto: 1.0.1 - has-symbols: 1.0.3 - internal-slot: 1.0.6 - iterator.prototype: 1.1.2 - safe-array-concat: 1.0.1 - - es-iterator-helpers@1.0.19: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.3 + es-abstract: 1.23.9 es-errors: 1.3.0 es-set-tostringtag: 2.0.3 function-bind: 1.1.2 - get-intrinsic: 1.2.4 - globalthis: 1.0.3 + get-intrinsic: 1.3.0 + globalthis: 1.0.4 + gopd: 1.2.0 has-property-descriptors: 1.0.2 - has-proto: 1.0.3 - has-symbols: 1.0.3 - internal-slot: 1.0.7 - iterator.prototype: 1.1.2 - safe-array-concat: 1.1.2 + has-proto: 1.2.0 + has-symbols: 1.1.0 + internal-slot: 1.1.0 + iterator.prototype: 1.1.5 + safe-array-concat: 1.1.3 es-module-lexer@1.3.1: {} @@ -9604,6 +9731,10 @@ snapshots: dependencies: es-errors: 1.3.0 + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + es-set-tostringtag@2.0.2: dependencies: get-intrinsic: 1.2.4 @@ -9612,7 +9743,14 @@ snapshots: es-set-tostringtag@2.0.3: dependencies: - get-intrinsic: 1.2.4 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 has-tostringtag: 1.0.2 hasown: 2.0.2 @@ -9626,7 +9764,11 @@ snapshots: is-date-object: 1.0.5 is-symbol: 1.0.4 - es6-promise@3.3.1: {} + es-to-primitive@1.3.0: + dependencies: + is-callable: 1.2.7 + is-date-object: 1.0.5 + is-symbol: 1.0.4 esbuild@0.18.20: optionalDependencies: @@ -9678,61 +9820,37 @@ snapshots: '@esbuild/win32-ia32': 0.19.5 '@esbuild/win32-x64': 0.19.5 - esbuild@0.20.2: + esbuild@0.25.2: optionalDependencies: - '@esbuild/aix-ppc64': 0.20.2 - '@esbuild/android-arm': 0.20.2 - '@esbuild/android-arm64': 0.20.2 - '@esbuild/android-x64': 0.20.2 - '@esbuild/darwin-arm64': 0.20.2 - '@esbuild/darwin-x64': 0.20.2 - '@esbuild/freebsd-arm64': 0.20.2 - '@esbuild/freebsd-x64': 0.20.2 - '@esbuild/linux-arm': 0.20.2 - '@esbuild/linux-arm64': 0.20.2 - '@esbuild/linux-ia32': 0.20.2 - '@esbuild/linux-loong64': 0.20.2 - '@esbuild/linux-mips64el': 0.20.2 - '@esbuild/linux-ppc64': 0.20.2 - '@esbuild/linux-riscv64': 0.20.2 - '@esbuild/linux-s390x': 0.20.2 - '@esbuild/linux-x64': 0.20.2 - '@esbuild/netbsd-x64': 0.20.2 - '@esbuild/openbsd-x64': 0.20.2 - '@esbuild/sunos-x64': 0.20.2 - '@esbuild/win32-arm64': 0.20.2 - '@esbuild/win32-ia32': 0.20.2 - '@esbuild/win32-x64': 0.20.2 - - esbuild@0.21.5: - optionalDependencies: - '@esbuild/aix-ppc64': 0.21.5 - '@esbuild/android-arm': 0.21.5 - '@esbuild/android-arm64': 0.21.5 - '@esbuild/android-x64': 0.21.5 - '@esbuild/darwin-arm64': 0.21.5 - '@esbuild/darwin-x64': 0.21.5 - '@esbuild/freebsd-arm64': 0.21.5 - '@esbuild/freebsd-x64': 0.21.5 - '@esbuild/linux-arm': 0.21.5 - '@esbuild/linux-arm64': 0.21.5 - '@esbuild/linux-ia32': 0.21.5 - '@esbuild/linux-loong64': 0.21.5 - '@esbuild/linux-mips64el': 0.21.5 - '@esbuild/linux-ppc64': 0.21.5 - '@esbuild/linux-riscv64': 0.21.5 - '@esbuild/linux-s390x': 0.21.5 - '@esbuild/linux-x64': 0.21.5 - '@esbuild/netbsd-x64': 0.21.5 - '@esbuild/openbsd-x64': 0.21.5 - '@esbuild/sunos-x64': 0.21.5 - '@esbuild/win32-arm64': 0.21.5 - '@esbuild/win32-ia32': 0.21.5 - '@esbuild/win32-x64': 0.21.5 + '@esbuild/aix-ppc64': 0.25.2 + '@esbuild/android-arm': 0.25.2 + '@esbuild/android-arm64': 0.25.2 + '@esbuild/android-x64': 0.25.2 + '@esbuild/darwin-arm64': 0.25.2 + '@esbuild/darwin-x64': 0.25.2 + '@esbuild/freebsd-arm64': 0.25.2 + '@esbuild/freebsd-x64': 0.25.2 + '@esbuild/linux-arm': 0.25.2 + '@esbuild/linux-arm64': 0.25.2 + '@esbuild/linux-ia32': 0.25.2 + '@esbuild/linux-loong64': 0.25.2 + '@esbuild/linux-mips64el': 0.25.2 + '@esbuild/linux-ppc64': 0.25.2 + '@esbuild/linux-riscv64': 0.25.2 + '@esbuild/linux-s390x': 0.25.2 + '@esbuild/linux-x64': 0.25.2 + '@esbuild/netbsd-arm64': 0.25.2 + '@esbuild/netbsd-x64': 0.25.2 + '@esbuild/openbsd-arm64': 0.25.2 + '@esbuild/openbsd-x64': 0.25.2 + '@esbuild/sunos-x64': 0.25.2 + '@esbuild/win32-arm64': 0.25.2 + '@esbuild/win32-ia32': 0.25.2 + '@esbuild/win32-x64': 0.25.2 escalade@3.1.1: {} - escalade@3.1.2: {} + escalade@3.2.0: {} escape-string-regexp@1.0.5: {} @@ -9740,135 +9858,87 @@ snapshots: escape-string-regexp@5.0.0: {} - eslint-compat-utils@0.1.2(eslint@8.53.0): + eslint-config-prettier@10.1.1(eslint@9.24.0): dependencies: - eslint: 8.53.0 + eslint: 9.24.0 - eslint-compat-utils@0.1.2(eslint@8.57.0): + eslint-config-prettier@10.1.2(eslint@9.24.0): dependencies: - eslint: 8.57.0 + eslint: 9.24.0 - eslint-config-prettier@8.8.0(eslint@8.43.0): + eslint-config-turbo@2.5.0(eslint@9.24.0)(turbo@2.0.3): dependencies: - eslint: 8.43.0 + eslint: 9.24.0 + eslint-plugin-turbo: 2.5.0(eslint@9.24.0)(turbo@2.0.3) + turbo: 2.0.3 - eslint-config-prettier@9.0.0(eslint@8.53.0): + eslint-plugin-prettier@4.2.1(eslint-config-prettier@10.1.1(eslint@9.24.0))(eslint@9.24.0)(prettier@3.5.3): dependencies: - eslint: 8.53.0 - - eslint-config-prettier@9.1.0(eslint@8.57.0): - dependencies: - eslint: 8.57.0 - - eslint-config-turbo@2.0.3(eslint@8.43.0): - dependencies: - eslint: 8.43.0 - eslint-plugin-turbo: 2.0.3(eslint@8.43.0) - - eslint-plugin-prettier@4.2.1(eslint-config-prettier@8.8.0(eslint@8.43.0))(eslint@8.43.0)(prettier@2.8.8): - dependencies: - eslint: 8.43.0 - prettier: 2.8.8 + eslint: 9.24.0 + prettier: 3.5.3 prettier-linter-helpers: 1.0.0 optionalDependencies: - eslint-config-prettier: 8.8.0(eslint@8.43.0) + eslint-config-prettier: 10.1.1(eslint@9.24.0) - eslint-plugin-react@7.33.2(eslint@8.43.0): - dependencies: - array-includes: 3.1.7 - array.prototype.flatmap: 1.3.2 - array.prototype.tosorted: 1.1.2 - doctrine: 2.1.0 - es-iterator-helpers: 1.0.15 - eslint: 8.43.0 - estraverse: 5.3.0 - jsx-ast-utils: 3.3.5 - minimatch: 3.1.2 - object.entries: 1.1.7 - object.fromentries: 2.0.7 - object.hasown: 1.1.3 - object.values: 1.1.7 - prop-types: 15.8.1 - resolve: 2.0.0-next.5 - semver: 6.3.1 - string.prototype.matchall: 4.0.10 - - eslint-plugin-react@7.35.0(eslint@8.43.0): + eslint-plugin-react@7.37.5(eslint@9.24.0): dependencies: array-includes: 3.1.8 array.prototype.findlast: 1.2.5 - array.prototype.flatmap: 1.3.2 + array.prototype.flatmap: 1.3.3 array.prototype.tosorted: 1.1.4 doctrine: 2.1.0 - es-iterator-helpers: 1.0.19 - eslint: 8.43.0 + es-iterator-helpers: 1.2.1 + eslint: 9.24.0 estraverse: 5.3.0 hasown: 2.0.2 jsx-ast-utils: 3.3.5 minimatch: 3.1.2 - object.entries: 1.1.8 + object.entries: 1.1.9 object.fromentries: 2.0.8 - object.values: 1.2.0 + object.values: 1.2.1 prop-types: 15.8.1 resolve: 2.0.0-next.5 semver: 6.3.1 - string.prototype.matchall: 4.0.11 + string.prototype.matchall: 4.0.12 string.prototype.repeat: 1.0.0 - eslint-plugin-svelte@2.35.0(eslint@8.53.0)(svelte@4.2.12): + eslint-plugin-svelte@3.5.1(eslint@9.24.0)(svelte@5.27.0): dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.53.0) - '@jridgewell/sourcemap-codec': 1.4.15 - debug: 4.3.4(supports-color@8.1.1) - eslint: 8.53.0 - eslint-compat-utils: 0.1.2(eslint@8.53.0) + '@eslint-community/eslint-utils': 4.5.1(eslint@9.24.0) + '@jridgewell/sourcemap-codec': 1.5.0 + eslint: 9.24.0 esutils: 2.0.3 - known-css-properties: 0.29.0 - postcss: 8.4.35 - postcss-load-config: 3.1.4(postcss@8.4.35) - postcss-safe-parser: 6.0.0(postcss@8.4.35) - postcss-selector-parser: 6.0.13 - semver: 7.5.4 - svelte-eslint-parser: 0.33.1(svelte@4.2.12) + known-css-properties: 0.35.0 + postcss: 8.5.3 + postcss-load-config: 3.1.4(postcss@8.5.3) + postcss-safe-parser: 7.0.1(postcss@8.5.3) + semver: 7.6.3 + svelte-eslint-parser: 1.1.2(svelte@5.27.0) optionalDependencies: - svelte: 4.2.12 + svelte: 5.27.0 transitivePeerDependencies: - - supports-color - ts-node - eslint-plugin-svelte@2.35.1(eslint@8.57.0)(svelte@4.2.12): - dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) - '@jridgewell/sourcemap-codec': 1.4.15 - debug: 4.3.4(supports-color@8.1.1) - eslint: 8.57.0 - eslint-compat-utils: 0.1.2(eslint@8.57.0) - esutils: 2.0.3 - known-css-properties: 0.29.0 - postcss: 8.4.35 - postcss-load-config: 3.1.4(postcss@8.4.35) - postcss-safe-parser: 6.0.0(postcss@8.4.35) - postcss-selector-parser: 6.0.13 - semver: 7.6.0 - svelte-eslint-parser: 0.33.1(svelte@4.2.12) - optionalDependencies: - svelte: 4.2.12 - transitivePeerDependencies: - - supports-color - - ts-node - - eslint-plugin-turbo@2.0.3(eslint@8.43.0): + eslint-plugin-turbo@2.5.0(eslint@9.24.0)(turbo@2.0.3): dependencies: dotenv: 16.0.3 - eslint: 8.43.0 + eslint: 9.24.0 + turbo: 2.0.3 eslint-scope@7.2.2: dependencies: esrecurse: 4.3.0 estraverse: 5.3.0 + eslint-scope@8.3.0: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + eslint-visitor-keys@3.4.3: {} + eslint-visitor-keys@4.2.0: {} + eslint@8.43.0: dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@8.43.0) @@ -9913,93 +9983,53 @@ snapshots: transitivePeerDependencies: - supports-color - eslint@8.53.0: + eslint@9.24.0: dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.53.0) - '@eslint-community/regexpp': 4.10.0 - '@eslint/eslintrc': 2.1.3 - '@eslint/js': 8.53.0 - '@humanwhocodes/config-array': 0.11.13 + '@eslint-community/eslint-utils': 4.4.0(eslint@9.24.0) + '@eslint-community/regexpp': 4.12.1 + '@eslint/config-array': 0.20.0 + '@eslint/config-helpers': 0.2.1 + '@eslint/core': 0.12.0 + '@eslint/eslintrc': 3.3.1 + '@eslint/js': 9.24.0 + '@eslint/plugin-kit': 0.2.8 + '@humanfs/node': 0.16.6 '@humanwhocodes/module-importer': 1.0.1 - '@nodelib/fs.walk': 1.2.8 - '@ungap/structured-clone': 1.2.0 + '@humanwhocodes/retry': 0.4.2 + '@types/estree': 1.0.6 + '@types/json-schema': 7.0.15 ajv: 6.12.6 chalk: 4.1.2 - cross-spawn: 7.0.3 - debug: 4.3.4(supports-color@8.1.1) - doctrine: 3.0.0 + cross-spawn: 7.0.6 + debug: 4.4.0 escape-string-regexp: 4.0.0 - eslint-scope: 7.2.2 - eslint-visitor-keys: 3.4.3 - espree: 9.6.1 + eslint-scope: 8.3.0 + eslint-visitor-keys: 4.2.0 + espree: 10.3.0 esquery: 1.5.0 esutils: 2.0.3 fast-deep-equal: 3.1.3 - file-entry-cache: 6.0.1 + file-entry-cache: 8.0.0 find-up: 5.0.0 glob-parent: 6.0.2 - globals: 13.23.0 - graphemer: 1.4.0 - ignore: 5.2.4 + ignore: 5.3.1 imurmurhash: 0.1.4 is-glob: 4.0.3 - is-path-inside: 3.0.3 - js-yaml: 4.1.0 json-stable-stringify-without-jsonify: 1.0.1 - levn: 0.4.1 lodash.merge: 4.6.2 minimatch: 3.1.2 natural-compare: 1.4.0 optionator: 0.9.3 - strip-ansi: 6.0.1 - text-table: 0.2.0 transitivePeerDependencies: - supports-color - eslint@8.57.0: + esm-env@1.2.2: {} + + espree@10.3.0: dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) - '@eslint-community/regexpp': 4.10.0 - '@eslint/eslintrc': 2.1.4 - '@eslint/js': 8.57.0 - '@humanwhocodes/config-array': 0.11.14 - '@humanwhocodes/module-importer': 1.0.1 - '@nodelib/fs.walk': 1.2.8 - '@ungap/structured-clone': 1.2.0 - ajv: 6.12.6 - chalk: 4.1.2 - cross-spawn: 7.0.3 - debug: 4.3.4(supports-color@8.1.1) - doctrine: 3.0.0 - escape-string-regexp: 4.0.0 - eslint-scope: 7.2.2 - eslint-visitor-keys: 3.4.3 - espree: 9.6.1 - esquery: 1.5.0 - esutils: 2.0.3 - fast-deep-equal: 3.1.3 - file-entry-cache: 6.0.1 - find-up: 5.0.0 - glob-parent: 6.0.2 - globals: 13.23.0 - graphemer: 1.4.0 - ignore: 5.3.0 - imurmurhash: 0.1.4 - is-glob: 4.0.3 - is-path-inside: 3.0.3 - js-yaml: 4.1.0 - json-stable-stringify-without-jsonify: 1.0.1 - levn: 0.4.1 - lodash.merge: 4.6.2 - minimatch: 3.1.2 - natural-compare: 1.4.0 - optionator: 0.9.3 - strip-ansi: 6.0.1 - text-table: 0.2.0 - transitivePeerDependencies: - - supports-color - - esm-env@1.0.0: {} + acorn: 8.14.0 + acorn-jsx: 5.3.2(acorn@8.14.0) + eslint-visitor-keys: 4.2.0 espree@9.6.1: dependencies: @@ -10013,6 +10043,10 @@ snapshots: dependencies: estraverse: 5.3.0 + esrap@1.4.6: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.0 + esrecurse@4.3.0: dependencies: estraverse: 5.3.0 @@ -10143,6 +10177,10 @@ snapshots: dependencies: pend: 1.2.0 + fdir@6.4.3(picomatch@4.0.2): + optionalDependencies: + picomatch: 4.0.2 + figures@3.2.0: dependencies: escape-string-regexp: 1.0.5 @@ -10151,6 +10189,10 @@ snapshots: dependencies: flat-cache: 3.1.1 + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + fill-range@7.0.1: dependencies: to-regex-range: 5.0.1 @@ -10176,6 +10218,11 @@ snapshots: keyv: 4.5.4 rimraf: 3.0.2 + flat-cache@4.0.1: + dependencies: + flatted: 3.2.9 + keyv: 4.5.4 + flatted@3.2.9: {} follow-redirects@1.15.3(debug@4.3.4): @@ -10186,6 +10233,10 @@ snapshots: dependencies: is-callable: 1.2.7 + for-each@0.3.5: + dependencies: + is-callable: 1.2.7 + forever-agent@0.6.1: {} form-data@2.3.3: @@ -10249,6 +10300,15 @@ snapshots: es-abstract: 1.22.3 functions-have-names: 1.2.3 + function.prototype.name@1.1.8: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + functions-have-names: 1.2.3 + hasown: 2.0.2 + is-callable: 1.2.7 + functions-have-names@1.2.3: {} gensync@1.0.0-beta.2: {} @@ -10270,6 +10330,24 @@ snapshots: has-symbols: 1.0.3 hasown: 2.0.2 + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + math-intrinsics: 1.1.0 + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.0.0 + get-stdin@9.0.0: {} get-stream@5.2.0: @@ -10291,6 +10369,12 @@ snapshots: es-errors: 1.3.0 get-intrinsic: 1.2.4 + get-symbol-description@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + getos@3.2.1: dependencies: async: 3.2.5 @@ -10339,11 +10423,18 @@ snapshots: dependencies: type-fest: 0.20.2 + globals@14.0.0: {} + + globals@16.0.0: {} + globalthis@1.0.3: dependencies: define-properties: 1.2.1 - globalyzer@0.1.0: {} + globalthis@1.0.4: + dependencies: + define-properties: 1.2.1 + gopd: 1.2.0 globby@11.1.0: dependencies: @@ -10363,12 +10454,12 @@ snapshots: slash: 5.1.0 unicorn-magic: 0.1.0 - globrex@0.1.2: {} - gopd@1.0.1: dependencies: get-intrinsic: 1.2.4 + gopd@1.2.0: {} + graceful-fs@4.2.11: {} grapheme-splitter@1.0.4: {} @@ -10406,15 +10497,21 @@ snapshots: has-proto@1.0.3: {} + has-proto@1.2.0: + dependencies: + dunder-proto: 1.0.1 + has-symbols@1.0.3: {} + has-symbols@1.1.0: {} + has-tostringtag@1.0.0: dependencies: has-symbols: 1.0.3 has-tostringtag@1.0.2: dependencies: - has-symbols: 1.0.3 + has-symbols: 1.1.0 hasown@2.0.1: dependencies: @@ -10530,8 +10627,6 @@ snapshots: import-meta-resolve@3.1.1: {} - import-meta-resolve@4.0.0: {} - import-meta-resolve@4.1.0: {} imurmurhash@0.1.4: {} @@ -10562,6 +10657,12 @@ snapshots: hasown: 2.0.2 side-channel: 1.0.6 + internal-slot@1.1.0: + dependencies: + es-errors: 1.3.0 + hasown: 2.0.2 + side-channel: 1.1.0 + is-array-buffer@3.0.2: dependencies: call-bind: 1.0.7 @@ -10573,6 +10674,12 @@ snapshots: call-bind: 1.0.7 get-intrinsic: 1.2.4 + is-array-buffer@3.0.5: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + is-arrayish@0.2.1: {} is-arrayish@0.3.2: @@ -10580,12 +10687,16 @@ snapshots: is-async-function@2.0.0: dependencies: - has-tostringtag: 1.0.0 + has-tostringtag: 1.0.2 is-bigint@1.0.4: dependencies: has-bigints: 1.0.2 + is-bigint@1.1.0: + dependencies: + has-bigints: 1.0.2 + is-binary-path@2.1.0: dependencies: binary-extensions: 2.2.0 @@ -10595,6 +10706,11 @@ snapshots: call-bind: 1.0.7 has-tostringtag: 1.0.0 + is-boolean-object@1.2.2: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + is-buffer@2.0.5: {} is-builtin-module@3.2.1: @@ -10615,25 +10731,36 @@ snapshots: dependencies: is-typed-array: 1.1.13 + is-data-view@1.0.2: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + is-typed-array: 1.1.15 + is-date-object@1.0.5: dependencies: has-tostringtag: 1.0.0 + is-date-object@1.1.0: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + is-docker@3.0.0: {} is-extendable@0.1.1: {} is-extglob@2.1.1: {} - is-finalizationregistry@1.0.2: + is-finalizationregistry@1.1.1: dependencies: - call-bind: 1.0.7 + call-bound: 1.0.4 is-fullwidth-code-point@3.0.0: {} is-generator-function@1.0.10: dependencies: - has-tostringtag: 1.0.0 + has-tostringtag: 1.0.2 is-glob@4.0.3: dependencies: @@ -10650,7 +10777,7 @@ snapshots: is-interactive@2.0.0: {} - is-map@2.0.2: {} + is-map@2.0.3: {} is-module@1.0.0: {} @@ -10662,6 +10789,11 @@ snapshots: dependencies: has-tostringtag: 1.0.0 + is-number-object@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + is-number@7.0.0: {} is-path-inside@3.0.3: {} @@ -10678,12 +10810,23 @@ snapshots: dependencies: '@types/estree': 1.0.3 + is-reference@3.0.3: + dependencies: + '@types/estree': 1.0.6 + is-regex@1.1.4: dependencies: call-bind: 1.0.7 has-tostringtag: 1.0.0 - is-set@2.0.2: {} + is-regex@1.2.1: + dependencies: + call-bound: 1.0.4 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + is-set@2.0.3: {} is-shared-array-buffer@1.0.2: dependencies: @@ -10693,6 +10836,10 @@ snapshots: dependencies: call-bind: 1.0.7 + is-shared-array-buffer@1.0.4: + dependencies: + call-bound: 1.0.4 + is-stream@2.0.1: {} is-stream@3.0.0: {} @@ -10701,6 +10848,11 @@ snapshots: dependencies: has-tostringtag: 1.0.0 + is-string@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + is-subdir@1.2.0: dependencies: better-path-resolve: 1.0.0 @@ -10709,6 +10861,12 @@ snapshots: dependencies: has-symbols: 1.0.3 + is-symbol@1.1.1: + dependencies: + call-bound: 1.0.4 + has-symbols: 1.1.0 + safe-regex-test: 1.1.0 + is-typed-array@1.1.12: dependencies: which-typed-array: 1.1.13 @@ -10717,22 +10875,30 @@ snapshots: dependencies: which-typed-array: 1.1.15 + is-typed-array@1.1.15: + dependencies: + which-typed-array: 1.1.19 + is-typedarray@1.0.0: {} is-unicode-supported@0.1.0: {} is-unicode-supported@1.3.0: {} - is-weakmap@2.0.1: {} + is-weakmap@2.0.2: {} is-weakref@1.0.2: dependencies: call-bind: 1.0.7 - is-weakset@2.0.2: + is-weakref@1.1.1: dependencies: - call-bind: 1.0.7 - get-intrinsic: 1.2.4 + call-bound: 1.0.4 + + is-weakset@2.0.4: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 is-windows@1.0.2: {} @@ -10746,13 +10912,14 @@ snapshots: isstream@0.1.2: {} - iterator.prototype@1.1.2: + iterator.prototype@1.1.5: dependencies: - define-properties: 1.2.1 - get-intrinsic: 1.2.4 - has-symbols: 1.0.3 - reflect.getprototypeof: 1.0.4 - set-function-name: 2.0.1 + define-data-property: 1.1.4 + es-object-atoms: 1.0.0 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + has-symbols: 1.1.0 + set-function-name: 2.0.2 joi@17.11.0: dependencies: @@ -10815,7 +10982,7 @@ snapshots: array-includes: 3.1.8 array.prototype.flat: 1.3.2 object.assign: 4.1.5 - object.values: 1.2.0 + object.values: 1.2.1 keyv@4.5.4: dependencies: @@ -10827,7 +10994,7 @@ snapshots: kleur@4.1.5: {} - known-css-properties@0.29.0: {} + known-css-properties@0.35.0: {} lazy-ass@1.6.0: {} @@ -10844,7 +11011,7 @@ snapshots: lilconfig@3.0.0: {} - lilconfig@3.1.1: {} + lilconfig@3.1.2: {} lines-and-columns@1.2.4: {} @@ -10919,7 +11086,7 @@ snapshots: lower-case@2.0.2: dependencies: - tslib: 2.6.2 + tslib: 2.8.1 lru-cache@4.1.5: dependencies: @@ -10938,11 +11105,11 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.4.15 - magic-string@0.30.5: + magic-string@0.30.17: dependencies: - '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/sourcemap-codec': 1.5.0 - magic-string@0.30.8: + magic-string@0.30.5: dependencies: '@jridgewell/sourcemap-codec': 1.4.15 @@ -10954,6 +11121,8 @@ snapshots: markdown-table@3.0.3: {} + math-intrinsics@1.1.0: {} + mdast-util-definitions@5.1.2: dependencies: '@types/mdast': 3.0.14 @@ -11259,7 +11428,7 @@ snapshots: micromark@3.2.0: dependencies: '@types/debug': 4.1.10 - debug: 4.3.5 + debug: 4.3.7 decode-named-character-reference: 1.0.2 micromark-core-commonmark: 1.1.0 micromark-factory-space: 1.1.0 @@ -11308,10 +11477,6 @@ snapshots: dependencies: brace-expansion: 2.0.1 - minimatch@9.0.3: - dependencies: - brace-expansion: 2.0.1 - minimatch@9.0.5: dependencies: brace-expansion: 2.0.1 @@ -11329,10 +11494,6 @@ snapshots: mkdirp-classic@0.5.3: optional: true - mkdirp@0.5.6: - dependencies: - minimist: 1.2.8 - mri@1.2.0: {} mrmime@2.0.0: {} @@ -11347,6 +11508,8 @@ snapshots: nanoid@3.3.7: {} + nanoid@3.3.8: {} + napi-build-utils@1.0.2: optional: true @@ -11367,7 +11530,7 @@ snapshots: no-case@3.0.4: dependencies: lower-case: 2.0.2 - tslib: 2.6.2 + tslib: 2.8.1 node-abi@3.51.0: dependencies: @@ -11383,7 +11546,9 @@ snapshots: node-releases@2.0.13: {} - node-releases@2.0.14: {} + node-releases@2.0.18: {} + + node-releases@2.0.19: {} normalize-package-data@2.5.0: dependencies: @@ -11412,6 +11577,8 @@ snapshots: object-inspect@1.13.1: {} + object-inspect@1.13.4: {} + object-keys@1.1.1: {} object.assign@4.1.5: @@ -11421,23 +11588,21 @@ snapshots: has-symbols: 1.0.3 object-keys: 1.1.1 - object.entries@1.1.7: + object.assign@4.1.7: dependencies: - call-bind: 1.0.5 - define-properties: 1.2.1 - es-abstract: 1.22.3 - - object.entries@1.1.8: - dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.4 define-properties: 1.2.1 es-object-atoms: 1.0.0 + has-symbols: 1.1.0 + object-keys: 1.1.1 - object.fromentries@2.0.7: + object.entries@1.1.9: dependencies: - call-bind: 1.0.5 + call-bind: 1.0.8 + call-bound: 1.0.4 define-properties: 1.2.1 - es-abstract: 1.22.3 + es-object-atoms: 1.1.1 object.fromentries@2.0.8: dependencies: @@ -11446,20 +11611,10 @@ snapshots: es-abstract: 1.23.3 es-object-atoms: 1.0.0 - object.hasown@1.1.3: + object.values@1.2.1: dependencies: - define-properties: 1.2.1 - es-abstract: 1.22.3 - - object.values@1.1.7: - dependencies: - call-bind: 1.0.5 - define-properties: 1.2.1 - es-abstract: 1.22.3 - - object.values@1.2.0: - dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.4 define-properties: 1.2.1 es-object-atoms: 1.0.0 @@ -11502,6 +11657,12 @@ snapshots: outdent@0.5.0: {} + own-keys@1.0.1: + dependencies: + get-intrinsic: 1.3.0 + object-keys: 1.1.1 + safe-push-apply: 1.0.0 + p-filter@2.1.0: dependencies: p-map: 2.1.0 @@ -11556,7 +11717,7 @@ snapshots: pascal-case@3.1.2: dependencies: no-case: 3.0.4 - tslib: 2.6.2 + tslib: 2.8.1 path-exists@4.0.0: {} @@ -11590,10 +11751,12 @@ snapshots: picocolors@1.0.0: {} - picocolors@1.0.1: {} + picocolors@1.1.1: {} picomatch@2.3.1: {} + picomatch@4.0.2: {} + pify@2.3.0: {} pify@4.0.1: {} @@ -11604,7 +11767,7 @@ snapshots: playwright-core@1.44.1: {} - playwright-core@1.45.0: {} + playwright-core@1.51.1: {} playwright@1.44.1: dependencies: @@ -11612,26 +11775,26 @@ snapshots: optionalDependencies: fsevents: 2.3.2 - playwright@1.45.0: + playwright@1.51.1: dependencies: - playwright-core: 1.45.0 + playwright-core: 1.51.1 optionalDependencies: fsevents: 2.3.2 possible-typed-array-names@1.0.0: {} + postcss-calc@10.0.2(postcss@8.5.3): + dependencies: + postcss: 8.5.3 + postcss-selector-parser: 6.1.2 + postcss-value-parser: 4.2.0 + postcss-calc@9.0.1(postcss@8.4.21): dependencies: postcss: 8.4.21 postcss-selector-parser: 6.0.13 postcss-value-parser: 4.2.0 - postcss-calc@9.0.1(postcss@8.4.35): - dependencies: - postcss: 8.4.35 - postcss-selector-parser: 6.0.13 - postcss-value-parser: 4.2.0 - postcss-cli@11.0.0(postcss@8.4.21): dependencies: chokidar: 3.5.3 @@ -11650,20 +11813,19 @@ snapshots: transitivePeerDependencies: - jiti - postcss-cli@11.0.0(postcss@8.4.35): + postcss-cli@11.0.1(postcss@8.5.3): dependencies: chokidar: 3.5.3 - dependency-graph: 0.11.0 + dependency-graph: 1.0.0 fs-extra: 11.2.0 - get-stdin: 9.0.0 - globby: 14.0.0 - picocolors: 1.0.0 - postcss: 8.4.35 - postcss-load-config: 5.0.2(postcss@8.4.35) - postcss-reporter: 7.0.5(postcss@8.4.35) + picocolors: 1.1.1 + postcss: 8.5.3 + postcss-load-config: 5.0.2(postcss@8.5.3) + postcss-reporter: 7.0.5(postcss@8.5.3) pretty-hrtime: 1.0.3 read-cache: 1.0.0 slash: 5.1.0 + tinyglobby: 0.2.12 yargs: 17.7.2 transitivePeerDependencies: - jiti @@ -11676,12 +11838,12 @@ snapshots: postcss: 8.4.21 postcss-value-parser: 4.2.0 - postcss-colormin@6.1.0(postcss@8.4.35): + postcss-colormin@7.0.2(postcss@8.5.3): dependencies: - browserslist: 4.23.0 + browserslist: 4.24.2 caniuse-api: 3.0.0 colord: 2.9.3 - postcss: 8.4.35 + postcss: 8.5.3 postcss-value-parser: 4.2.0 postcss-combine-duplicated-selectors@10.0.3(postcss@8.4.21): @@ -11689,9 +11851,9 @@ snapshots: postcss: 8.4.21 postcss-selector-parser: 6.0.13 - postcss-combine-duplicated-selectors@10.0.3(postcss@8.4.35): + postcss-combine-duplicated-selectors@10.0.3(postcss@8.5.3): dependencies: - postcss: 8.4.35 + postcss: 8.5.3 postcss-selector-parser: 6.0.13 postcss-convert-values@6.0.0(postcss@8.4.21): @@ -11700,43 +11862,44 @@ snapshots: postcss: 8.4.21 postcss-value-parser: 4.2.0 - postcss-convert-values@6.1.0(postcss@8.4.35): + postcss-convert-values@7.0.4(postcss@8.5.3): dependencies: - browserslist: 4.23.0 - postcss: 8.4.35 + browserslist: 4.24.2 + postcss: 8.5.3 postcss-value-parser: 4.2.0 postcss-discard-comments@6.0.0(postcss@8.4.21): dependencies: postcss: 8.4.21 - postcss-discard-comments@6.0.2(postcss@8.4.35): + postcss-discard-comments@7.0.3(postcss@8.5.3): dependencies: - postcss: 8.4.35 + postcss: 8.5.3 + postcss-selector-parser: 6.1.2 postcss-discard-duplicates@6.0.0(postcss@8.4.21): dependencies: postcss: 8.4.21 - postcss-discard-duplicates@6.0.3(postcss@8.4.35): + postcss-discard-duplicates@7.0.1(postcss@8.5.3): dependencies: - postcss: 8.4.35 + postcss: 8.5.3 postcss-discard-empty@6.0.0(postcss@8.4.21): dependencies: postcss: 8.4.21 - postcss-discard-empty@6.0.3(postcss@8.4.35): + postcss-discard-empty@7.0.0(postcss@8.5.3): dependencies: - postcss: 8.4.35 + postcss: 8.5.3 postcss-discard-overridden@6.0.0(postcss@8.4.21): dependencies: postcss: 8.4.21 - postcss-discard-overridden@6.0.2(postcss@8.4.35): + postcss-discard-overridden@7.0.0(postcss@8.5.3): dependencies: - postcss: 8.4.35 + postcss: 8.5.3 postcss-import@15.1.0(postcss@8.4.21): dependencies: @@ -11745,19 +11908,19 @@ snapshots: read-cache: 1.0.0 resolve: 1.22.8 - postcss-import@16.0.1(postcss@8.4.35): + postcss-import@16.1.0(postcss@8.5.3): dependencies: - postcss: 8.4.35 + postcss: 8.5.3 postcss-value-parser: 4.2.0 read-cache: 1.0.0 resolve: 1.22.8 - postcss-load-config@3.1.4(postcss@8.4.35): + postcss-load-config@3.1.4(postcss@8.5.3): dependencies: lilconfig: 2.1.0 yaml: 1.10.2 optionalDependencies: - postcss: 8.4.35 + postcss: 8.5.3 postcss-load-config@5.0.2(postcss@8.4.21): dependencies: @@ -11766,12 +11929,12 @@ snapshots: optionalDependencies: postcss: 8.4.21 - postcss-load-config@5.0.2(postcss@8.4.35): + postcss-load-config@5.0.2(postcss@8.5.3): dependencies: lilconfig: 3.0.0 yaml: 2.3.4 optionalDependencies: - postcss: 8.4.35 + postcss: 8.5.3 postcss-merge-longhand@6.0.0(postcss@8.4.21): dependencies: @@ -11779,11 +11942,11 @@ snapshots: postcss-value-parser: 4.2.0 stylehacks: 6.0.0(postcss@8.4.21) - postcss-merge-longhand@6.0.4(postcss@8.4.35): + postcss-merge-longhand@7.0.4(postcss@8.5.3): dependencies: - postcss: 8.4.35 + postcss: 8.5.3 postcss-value-parser: 4.2.0 - stylehacks: 6.1.0(postcss@8.4.35) + stylehacks: 7.0.4(postcss@8.5.3) postcss-merge-rules@6.0.1(postcss@8.4.21): dependencies: @@ -11793,22 +11956,22 @@ snapshots: postcss: 8.4.21 postcss-selector-parser: 6.0.13 - postcss-merge-rules@6.1.0(postcss@8.4.35): + postcss-merge-rules@7.0.4(postcss@8.5.3): dependencies: - browserslist: 4.23.0 + browserslist: 4.24.2 caniuse-api: 3.0.0 - cssnano-utils: 4.0.2(postcss@8.4.35) - postcss: 8.4.35 - postcss-selector-parser: 6.0.16 + cssnano-utils: 5.0.0(postcss@8.5.3) + postcss: 8.5.3 + postcss-selector-parser: 6.1.2 postcss-minify-font-values@6.0.0(postcss@8.4.21): dependencies: postcss: 8.4.21 postcss-value-parser: 4.2.0 - postcss-minify-font-values@6.0.3(postcss@8.4.35): + postcss-minify-font-values@7.0.0(postcss@8.5.3): dependencies: - postcss: 8.4.35 + postcss: 8.5.3 postcss-value-parser: 4.2.0 postcss-minify-gradients@6.0.0(postcss@8.4.21): @@ -11818,11 +11981,11 @@ snapshots: postcss: 8.4.21 postcss-value-parser: 4.2.0 - postcss-minify-gradients@6.0.3(postcss@8.4.35): + postcss-minify-gradients@7.0.0(postcss@8.5.3): dependencies: colord: 2.9.3 - cssnano-utils: 4.0.2(postcss@8.4.35) - postcss: 8.4.35 + cssnano-utils: 5.0.0(postcss@8.5.3) + postcss: 8.5.3 postcss-value-parser: 4.2.0 postcss-minify-params@6.0.0(postcss@8.4.21): @@ -11832,11 +11995,11 @@ snapshots: postcss: 8.4.21 postcss-value-parser: 4.2.0 - postcss-minify-params@6.1.0(postcss@8.4.35): + postcss-minify-params@7.0.2(postcss@8.5.3): dependencies: - browserslist: 4.23.0 - cssnano-utils: 4.0.2(postcss@8.4.35) - postcss: 8.4.35 + browserslist: 4.24.2 + cssnano-utils: 5.0.0(postcss@8.5.3) + postcss: 8.5.3 postcss-value-parser: 4.2.0 postcss-minify-selectors@6.0.0(postcss@8.4.21): @@ -11844,37 +12007,38 @@ snapshots: postcss: 8.4.21 postcss-selector-parser: 6.0.13 - postcss-minify-selectors@6.0.3(postcss@8.4.35): + postcss-minify-selectors@7.0.4(postcss@8.5.3): dependencies: - postcss: 8.4.35 - postcss-selector-parser: 6.0.16 + cssesc: 3.0.0 + postcss: 8.5.3 + postcss-selector-parser: 6.1.2 postcss-nested@6.0.0(postcss@8.4.21): dependencies: postcss: 8.4.21 postcss-selector-parser: 6.0.13 - postcss-nested@6.0.1(postcss@8.4.35): + postcss-nested@7.0.2(postcss@8.5.3): dependencies: - postcss: 8.4.35 - postcss-selector-parser: 6.0.13 + postcss: 8.5.3 + postcss-selector-parser: 7.0.0 postcss-normalize-charset@6.0.0(postcss@8.4.21): dependencies: postcss: 8.4.21 - postcss-normalize-charset@6.0.2(postcss@8.4.35): + postcss-normalize-charset@7.0.0(postcss@8.5.3): dependencies: - postcss: 8.4.35 + postcss: 8.5.3 postcss-normalize-display-values@6.0.0(postcss@8.4.21): dependencies: postcss: 8.4.21 postcss-value-parser: 4.2.0 - postcss-normalize-display-values@6.0.2(postcss@8.4.35): + postcss-normalize-display-values@7.0.0(postcss@8.5.3): dependencies: - postcss: 8.4.35 + postcss: 8.5.3 postcss-value-parser: 4.2.0 postcss-normalize-positions@6.0.0(postcss@8.4.21): @@ -11882,9 +12046,9 @@ snapshots: postcss: 8.4.21 postcss-value-parser: 4.2.0 - postcss-normalize-positions@6.0.2(postcss@8.4.35): + postcss-normalize-positions@7.0.0(postcss@8.5.3): dependencies: - postcss: 8.4.35 + postcss: 8.5.3 postcss-value-parser: 4.2.0 postcss-normalize-repeat-style@6.0.0(postcss@8.4.21): @@ -11892,9 +12056,9 @@ snapshots: postcss: 8.4.21 postcss-value-parser: 4.2.0 - postcss-normalize-repeat-style@6.0.2(postcss@8.4.35): + postcss-normalize-repeat-style@7.0.0(postcss@8.5.3): dependencies: - postcss: 8.4.35 + postcss: 8.5.3 postcss-value-parser: 4.2.0 postcss-normalize-string@6.0.0(postcss@8.4.21): @@ -11902,9 +12066,9 @@ snapshots: postcss: 8.4.21 postcss-value-parser: 4.2.0 - postcss-normalize-string@6.0.2(postcss@8.4.35): + postcss-normalize-string@7.0.0(postcss@8.5.3): dependencies: - postcss: 8.4.35 + postcss: 8.5.3 postcss-value-parser: 4.2.0 postcss-normalize-timing-functions@6.0.0(postcss@8.4.21): @@ -11912,9 +12076,9 @@ snapshots: postcss: 8.4.21 postcss-value-parser: 4.2.0 - postcss-normalize-timing-functions@6.0.2(postcss@8.4.35): + postcss-normalize-timing-functions@7.0.0(postcss@8.5.3): dependencies: - postcss: 8.4.35 + postcss: 8.5.3 postcss-value-parser: 4.2.0 postcss-normalize-unicode@6.0.0(postcss@8.4.21): @@ -11923,10 +12087,10 @@ snapshots: postcss: 8.4.21 postcss-value-parser: 4.2.0 - postcss-normalize-unicode@6.1.0(postcss@8.4.35): + postcss-normalize-unicode@7.0.2(postcss@8.5.3): dependencies: - browserslist: 4.23.0 - postcss: 8.4.35 + browserslist: 4.24.2 + postcss: 8.5.3 postcss-value-parser: 4.2.0 postcss-normalize-url@6.0.0(postcss@8.4.21): @@ -11934,9 +12098,9 @@ snapshots: postcss: 8.4.21 postcss-value-parser: 4.2.0 - postcss-normalize-url@6.0.2(postcss@8.4.35): + postcss-normalize-url@7.0.0(postcss@8.5.3): dependencies: - postcss: 8.4.35 + postcss: 8.5.3 postcss-value-parser: 4.2.0 postcss-normalize-whitespace@6.0.0(postcss@8.4.21): @@ -11944,9 +12108,9 @@ snapshots: postcss: 8.4.21 postcss-value-parser: 4.2.0 - postcss-normalize-whitespace@6.0.2(postcss@8.4.35): + postcss-normalize-whitespace@7.0.0(postcss@8.5.3): dependencies: - postcss: 8.4.35 + postcss: 8.5.3 postcss-value-parser: 4.2.0 postcss-ordered-values@6.0.0(postcss@8.4.21): @@ -11955,10 +12119,10 @@ snapshots: postcss: 8.4.21 postcss-value-parser: 4.2.0 - postcss-ordered-values@6.0.2(postcss@8.4.35): + postcss-ordered-values@7.0.1(postcss@8.5.3): dependencies: - cssnano-utils: 4.0.2(postcss@8.4.35) - postcss: 8.4.35 + cssnano-utils: 5.0.0(postcss@8.5.3) + postcss: 8.5.3 postcss-value-parser: 4.2.0 postcss-reduce-initial@6.0.0(postcss@8.4.21): @@ -11967,20 +12131,20 @@ snapshots: caniuse-api: 3.0.0 postcss: 8.4.21 - postcss-reduce-initial@6.1.0(postcss@8.4.35): + postcss-reduce-initial@7.0.2(postcss@8.5.3): dependencies: - browserslist: 4.23.0 + browserslist: 4.24.2 caniuse-api: 3.0.0 - postcss: 8.4.35 + postcss: 8.5.3 postcss-reduce-transforms@6.0.0(postcss@8.4.21): dependencies: postcss: 8.4.21 postcss-value-parser: 4.2.0 - postcss-reduce-transforms@6.0.2(postcss@8.4.35): + postcss-reduce-transforms@7.0.0(postcss@8.5.3): dependencies: - postcss: 8.4.35 + postcss: 8.5.3 postcss-value-parser: 4.2.0 postcss-rename@0.6.1(postcss@8.4.21): @@ -11988,9 +12152,9 @@ snapshots: postcss: 8.4.21 postcss-selector-parser: 6.0.13 - postcss-rename@0.6.1(postcss@8.4.35): + postcss-rename@0.6.1(postcss@8.5.3): dependencies: - postcss: 8.4.35 + postcss: 8.5.3 postcss-selector-parser: 6.0.13 postcss-reporter@7.0.5(postcss@8.4.21): @@ -11999,26 +12163,31 @@ snapshots: postcss: 8.4.21 thenby: 1.3.4 - postcss-reporter@7.0.5(postcss@8.4.35): + postcss-reporter@7.0.5(postcss@8.5.3): dependencies: picocolors: 1.0.0 - postcss: 8.4.35 + postcss: 8.5.3 thenby: 1.3.4 - postcss-safe-parser@6.0.0(postcss@8.4.35): + postcss-safe-parser@7.0.1(postcss@8.5.3): dependencies: - postcss: 8.4.35 + postcss: 8.5.3 - postcss-scss@4.0.9(postcss@8.4.35): + postcss-scss@4.0.9(postcss@8.5.3): dependencies: - postcss: 8.4.35 + postcss: 8.5.3 postcss-selector-parser@6.0.13: dependencies: cssesc: 3.0.0 util-deprecate: 1.0.2 - postcss-selector-parser@6.0.16: + postcss-selector-parser@6.1.2: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + postcss-selector-parser@7.0.0: dependencies: cssesc: 3.0.0 util-deprecate: 1.0.2 @@ -12029,21 +12198,21 @@ snapshots: postcss-value-parser: 4.2.0 svgo: 3.0.2 - postcss-svgo@6.0.3(postcss@8.4.35): + postcss-svgo@7.0.1(postcss@8.5.3): dependencies: - postcss: 8.4.35 + postcss: 8.5.3 postcss-value-parser: 4.2.0 - svgo: 3.2.0 + svgo: 3.3.2 postcss-unique-selectors@6.0.0(postcss@8.4.21): dependencies: postcss: 8.4.21 postcss-selector-parser: 6.0.13 - postcss-unique-selectors@6.0.3(postcss@8.4.35): + postcss-unique-selectors@7.0.3(postcss@8.5.3): dependencies: - postcss: 8.4.35 - postcss-selector-parser: 6.0.16 + postcss: 8.5.3 + postcss-selector-parser: 6.1.2 postcss-value-parser@4.2.0: {} @@ -12059,17 +12228,11 @@ snapshots: picocolors: 1.0.0 source-map-js: 1.0.2 - postcss@8.4.38: + postcss@8.5.3: dependencies: - nanoid: 3.3.7 - picocolors: 1.0.1 - source-map-js: 1.2.0 - - postcss@8.4.39: - dependencies: - nanoid: 3.3.7 - picocolors: 1.0.1 - source-map-js: 1.2.0 + nanoid: 3.3.8 + picocolors: 1.1.1 + source-map-js: 1.2.1 prebuild-install@7.1.1: dependencies: @@ -12100,21 +12263,14 @@ snapshots: dependencies: fast-diff: 1.3.0 - prettier-plugin-svelte@3.0.3(prettier@3.0.3)(svelte@4.2.12): + prettier-plugin-svelte@3.3.3(prettier@3.5.3)(svelte@5.27.0): dependencies: - prettier: 3.0.3 - svelte: 4.2.12 - - prettier-plugin-svelte@3.2.2(prettier@3.2.5)(svelte@4.2.12): - dependencies: - prettier: 3.2.5 - svelte: 4.2.12 + prettier: 3.5.3 + svelte: 5.27.0 prettier@2.8.8: {} - prettier@3.0.3: {} - - prettier@3.2.5: {} + prettier@3.5.3: {} pretty-bytes@5.6.0: {} @@ -12270,6 +12426,8 @@ snapshots: dependencies: picomatch: 2.3.1 + readdirp@4.0.2: {} + redent@3.0.0: dependencies: indent-string: 4.0.0 @@ -12281,14 +12439,16 @@ snapshots: redux@5.0.1: {} - reflect.getprototypeof@1.0.4: + reflect.getprototypeof@1.0.10: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.23.3 - get-intrinsic: 1.2.4 - globalthis: 1.0.3 - which-builtin-type: 1.1.3 + es-abstract: 1.23.9 + es-errors: 1.3.0 + es-object-atoms: 1.0.0 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + which-builtin-type: 1.2.1 regenerator-runtime@0.14.0: {} @@ -12305,6 +12465,15 @@ snapshots: es-errors: 1.3.0 set-function-name: 2.0.2 + regexp.prototype.flags@1.5.4: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-errors: 1.3.0 + get-proto: 1.0.1 + gopd: 1.2.0 + set-function-name: 2.0.2 + rehype-parse@8.0.5: dependencies: '@types/hast': 2.3.7 @@ -12430,10 +12599,6 @@ snapshots: rfdc@1.3.1: {} - rimraf@2.7.1: - dependencies: - glob: 7.2.3 - rimraf@3.0.2: dependencies: glob: 7.2.3 @@ -12468,13 +12633,39 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.18.0 fsevents: 2.3.3 + rollup@4.39.0: + dependencies: + '@types/estree': 1.0.7 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.39.0 + '@rollup/rollup-android-arm64': 4.39.0 + '@rollup/rollup-darwin-arm64': 4.39.0 + '@rollup/rollup-darwin-x64': 4.39.0 + '@rollup/rollup-freebsd-arm64': 4.39.0 + '@rollup/rollup-freebsd-x64': 4.39.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.39.0 + '@rollup/rollup-linux-arm-musleabihf': 4.39.0 + '@rollup/rollup-linux-arm64-gnu': 4.39.0 + '@rollup/rollup-linux-arm64-musl': 4.39.0 + '@rollup/rollup-linux-loongarch64-gnu': 4.39.0 + '@rollup/rollup-linux-powerpc64le-gnu': 4.39.0 + '@rollup/rollup-linux-riscv64-gnu': 4.39.0 + '@rollup/rollup-linux-riscv64-musl': 4.39.0 + '@rollup/rollup-linux-s390x-gnu': 4.39.0 + '@rollup/rollup-linux-x64-gnu': 4.39.0 + '@rollup/rollup-linux-x64-musl': 4.39.0 + '@rollup/rollup-win32-arm64-msvc': 4.39.0 + '@rollup/rollup-win32-ia32-msvc': 4.39.0 + '@rollup/rollup-win32-x64-msvc': 4.39.0 + fsevents: 2.3.3 + run-parallel@1.2.0: dependencies: queue-microtask: 1.2.3 rxjs@7.8.1: dependencies: - tslib: 2.6.2 + tslib: 2.8.1 sade@1.8.1: dependencies: @@ -12494,8 +12685,21 @@ snapshots: has-symbols: 1.0.3 isarray: 2.0.5 + safe-array-concat@1.1.3: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + has-symbols: 1.1.0 + isarray: 2.0.5 + safe-buffer@5.2.1: {} + safe-push-apply@1.0.0: + dependencies: + es-errors: 1.3.0 + isarray: 2.0.5 + safe-regex-test@1.0.0: dependencies: call-bind: 1.0.7 @@ -12508,14 +12712,13 @@ snapshots: es-errors: 1.3.0 is-regex: 1.1.4 - safer-buffer@2.1.2: {} - - sander@0.5.1: + safe-regex-test@1.1.0: dependencies: - es6-promise: 3.3.1 - graceful-fs: 4.2.11 - mkdirp: 0.5.6 - rimraf: 2.7.1 + call-bound: 1.0.4 + es-errors: 1.3.0 + is-regex: 1.2.1 + + safer-buffer@2.1.2: {} sax@1.3.0: {} @@ -12544,6 +12747,8 @@ snapshots: dependencies: lru-cache: 6.0.0 + semver@7.6.3: {} + serialize-javascript@6.0.2: dependencies: randombytes: 2.1.0 @@ -12570,6 +12775,15 @@ snapshots: gopd: 1.0.1 has-property-descriptors: 1.0.2 + set-function-length@1.2.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.2.4 + gopd: 1.0.1 + has-property-descriptors: 1.0.2 + set-function-name@2.0.1: dependencies: define-data-property: 1.1.1 @@ -12583,6 +12797,12 @@ snapshots: functions-have-names: 1.2.3 has-property-descriptors: 1.0.2 + set-proto@1.0.0: + dependencies: + dunder-proto: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.0.0 + sharp@0.32.6: dependencies: color: 4.2.3 @@ -12616,11 +12836,25 @@ snapshots: vscode-oniguruma: 1.7.0 vscode-textmate: 8.0.0 - side-channel@1.0.4: + side-channel-list@1.0.0: dependencies: - call-bind: 1.0.7 - get-intrinsic: 1.2.4 - object-inspect: 1.13.1 + es-errors: 1.3.0 + object-inspect: 1.13.4 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + side-channel-map: 1.0.1 side-channel@1.0.6: dependencies: @@ -12629,6 +12863,14 @@ snapshots: get-intrinsic: 1.2.4 object-inspect: 1.13.1 + side-channel@1.1.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list: 1.0.0 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + signal-exit@3.0.7: {} signal-exit@4.1.0: {} @@ -12648,7 +12890,7 @@ snapshots: is-arrayish: 0.3.2 optional: true - sirv@2.0.4: + sirv@3.0.0: dependencies: '@polka/url': 1.0.0-next.24 mrmime: 2.0.0 @@ -12683,16 +12925,9 @@ snapshots: smob@1.5.0: {} - sorcery@0.11.0: - dependencies: - '@jridgewell/sourcemap-codec': 1.4.15 - buffer-crc32: 0.2.13 - minimist: 1.2.8 - sander: 0.5.1 - source-map-js@1.0.2: {} - source-map-js@1.2.0: {} + source-map-js@1.2.1: {} source-map-support@0.5.21: dependencies: @@ -12797,37 +13032,36 @@ snapshots: emoji-regex: 10.3.0 strip-ansi: 7.1.0 - string.prototype.matchall@4.0.10: + string.prototype.matchall@4.0.12: dependencies: - call-bind: 1.0.5 + call-bind: 1.0.8 + call-bound: 1.0.4 define-properties: 1.2.1 - es-abstract: 1.22.3 - get-intrinsic: 1.2.2 - has-symbols: 1.0.3 - internal-slot: 1.0.6 - regexp.prototype.flags: 1.5.1 - set-function-name: 2.0.1 - side-channel: 1.0.4 - - string.prototype.matchall@4.0.11: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.3 + es-abstract: 1.23.9 es-errors: 1.3.0 es-object-atoms: 1.0.0 - get-intrinsic: 1.2.4 - gopd: 1.0.1 - has-symbols: 1.0.3 - internal-slot: 1.0.7 - regexp.prototype.flags: 1.5.2 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-symbols: 1.1.0 + internal-slot: 1.1.0 + regexp.prototype.flags: 1.5.4 set-function-name: 2.0.2 - side-channel: 1.0.6 + side-channel: 1.1.0 string.prototype.repeat@1.0.0: dependencies: define-properties: 1.2.1 - es-abstract: 1.22.3 + es-abstract: 1.23.3 + + string.prototype.trim@1.2.10: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-data-property: 1.1.4 + define-properties: 1.2.1 + es-abstract: 1.23.9 + es-object-atoms: 1.0.0 + has-property-descriptors: 1.0.2 string.prototype.trim@1.2.8: dependencies: @@ -12854,6 +13088,13 @@ snapshots: define-properties: 1.2.1 es-object-atoms: 1.0.0 + string.prototype.trimend@1.0.9: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.0.0 + string.prototype.trimstart@1.0.7: dependencies: call-bind: 1.0.7 @@ -12908,11 +13149,11 @@ snapshots: postcss: 8.4.21 postcss-selector-parser: 6.0.13 - stylehacks@6.1.0(postcss@8.4.35): + stylehacks@7.0.4(postcss@8.5.3): dependencies: - browserslist: 4.23.0 - postcss: 8.4.35 - postcss-selector-parser: 6.0.16 + browserslist: 4.24.2 + postcss: 8.5.3 + postcss-selector-parser: 6.1.2 supports-color@5.5.0: dependencies: @@ -12928,109 +13169,55 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} - svelte-check@3.6.6(@babel/core@7.24.7)(postcss-load-config@5.0.2(postcss@8.4.35))(postcss@8.4.35)(svelte@4.2.12): + svelte-check@4.1.6(picomatch@4.0.2)(svelte@5.27.0)(typescript@5.8.3): dependencies: '@jridgewell/trace-mapping': 0.3.25 - chokidar: 3.6.0 - fast-glob: 3.3.2 - import-fresh: 3.3.0 - picocolors: 1.0.0 + chokidar: 4.0.3 + fdir: 6.4.3(picomatch@4.0.2) + picocolors: 1.1.1 sade: 1.8.1 - 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.5) - typescript: 5.4.5 + svelte: 5.27.0 + typescript: 5.8.3 transitivePeerDependencies: - - '@babel/core' - - coffeescript - - less - - postcss - - postcss-load-config - - pug - - sass - - stylus - - sugarss + - picomatch - svelte-check@3.6.7(@babel/core@7.24.7)(postcss-load-config@5.0.2(postcss@8.4.35))(postcss@8.4.35)(svelte@4.2.12): + svelte-eslint-parser@1.1.2(svelte@5.27.0): dependencies: - '@jridgewell/trace-mapping': 0.3.25 - chokidar: 3.6.0 - fast-glob: 3.3.2 - import-fresh: 3.3.0 - picocolors: 1.0.0 - sade: 1.8.1 - 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.5) - typescript: 5.4.5 - transitivePeerDependencies: - - '@babel/core' - - coffeescript - - less - - postcss - - postcss-load-config - - pug - - sass - - stylus - - sugarss - - svelte-eslint-parser@0.33.1(svelte@4.2.12): - dependencies: - eslint-scope: 7.2.2 - eslint-visitor-keys: 3.4.3 - espree: 9.6.1 - postcss: 8.4.35 - postcss-scss: 4.0.9(postcss@8.4.35) + eslint-scope: 8.3.0 + eslint-visitor-keys: 4.2.0 + espree: 10.3.0 + postcss: 8.5.3 + postcss-scss: 4.0.9(postcss@8.5.3) + postcss-selector-parser: 7.0.0 optionalDependencies: - svelte: 4.2.12 + svelte: 5.27.0 svelte-hmr@0.15.3(svelte@4.2.1): dependencies: svelte: 4.2.1 - svelte-hmr@0.16.0(svelte@4.2.12): + svelte-preprocess@6.0.3(@babel/core@7.24.7)(postcss-load-config@5.0.2(postcss@8.5.3))(postcss@8.5.3)(svelte@5.27.0)(typescript@5.8.3): dependencies: - 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 + svelte: 5.27.0 optionalDependencies: '@babel/core': 7.24.7 - postcss: 8.4.35 - postcss-load-config: 5.0.2(postcss@8.4.35) - typescript: 5.4.2 + postcss: 8.5.3 + postcss-load-config: 5.0.2(postcss@8.5.3) + typescript: 5.8.3 - 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: - '@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.5 - - svelte2tsx@0.6.23(svelte@4.2.1)(typescript@5.4.5): + svelte2tsx@0.6.23(svelte@4.2.1)(typescript@5.8.3): dependencies: dedent-js: 1.0.1 pascal-case: 3.1.2 svelte: 4.2.1 - typescript: 5.4.5 + typescript: 5.8.3 - svelte2tsx@0.7.3(svelte@4.2.12)(typescript@5.4.2): + svelte2tsx@0.7.34(svelte@5.27.0)(typescript@5.8.3): dependencies: dedent-js: 1.0.1 pascal-case: 3.1.2 - svelte: 4.2.12 - typescript: 5.4.2 + svelte: 5.27.0 + typescript: 5.8.3 svelte@4.2.1: dependencies: @@ -13048,22 +13235,22 @@ snapshots: magic-string: 0.30.5 periscopic: 3.1.0 - svelte@4.2.12: + svelte@5.27.0: dependencies: '@ampproject/remapping': 2.3.0 - '@jridgewell/sourcemap-codec': 1.4.15 - '@jridgewell/trace-mapping': 0.3.25 - '@types/estree': 1.0.5 - acorn: 8.11.3 - aria-query: 5.3.0 - axobject-query: 4.0.0 - code-red: 1.0.4 - css-tree: 2.3.1 - estree-walker: 3.0.3 - is-reference: 3.0.2 + '@jridgewell/sourcemap-codec': 1.5.0 + '@sveltejs/acorn-typescript': 1.0.5(acorn@8.14.0) + '@types/estree': 1.0.7 + acorn: 8.14.0 + aria-query: 5.3.2 + axobject-query: 4.1.0 + clsx: 2.1.1 + esm-env: 1.2.2 + esrap: 1.4.6 + is-reference: 3.0.3 locate-character: 3.0.0 - magic-string: 0.30.8 - periscopic: 3.1.0 + magic-string: 0.30.17 + zimmerframe: 1.1.2 svgo@3.0.2: dependencies: @@ -13072,9 +13259,9 @@ snapshots: css-select: 5.1.0 css-tree: 2.3.1 csso: 5.0.5 - picocolors: 1.0.0 + picocolors: 1.1.1 - svgo@3.2.0: + svgo@3.3.2: dependencies: '@trysound/sax': 0.2.0 commander: 7.2.0 @@ -13082,7 +13269,7 @@ snapshots: css-tree: 2.3.1 css-what: 6.1.0 csso: 5.0.5 - picocolors: 1.0.0 + picocolors: 1.1.1 tar-fs@2.1.1: dependencies: @@ -13120,7 +13307,7 @@ snapshots: terser@5.31.0: dependencies: '@jridgewell/source-map': 0.3.6 - acorn: 8.11.3 + acorn: 8.14.0 commander: 2.20.3 source-map-support: 0.5.21 @@ -13132,10 +13319,10 @@ snapshots: through@2.3.8: {} - tiny-glob@0.2.9: + tinyglobby@0.2.12: dependencies: - globalyzer: 0.1.0 - globrex: 0.1.2 + fdir: 6.4.3(picomatch@4.0.2) + picomatch: 4.0.2 tmp@0.0.33: dependencies: @@ -13168,17 +13355,9 @@ snapshots: trough@2.1.0: {} - ts-api-utils@1.0.3(typescript@5.2.2): + ts-api-utils@2.1.0(typescript@5.8.3): dependencies: - typescript: 5.2.2 - - ts-api-utils@1.0.3(typescript@5.4.2): - dependencies: - typescript: 5.4.2 - - ts-api-utils@1.3.0(typescript@5.1.3): - dependencies: - typescript: 5.1.3 + typescript: 5.8.3 tsconfig-resolver@3.0.1: dependencies: @@ -13189,7 +13368,7 @@ snapshots: strip-bom: 4.0.0 type-fest: 0.13.1 - tslib@2.6.2: {} + tslib@2.8.1: {} tty-table@4.2.2: dependencies: @@ -13262,6 +13441,12 @@ snapshots: es-errors: 1.3.0 is-typed-array: 1.1.13 + typed-array-buffer@1.0.3: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-typed-array: 1.1.15 + typed-array-byte-length@1.0.0: dependencies: call-bind: 1.0.7 @@ -13277,6 +13462,14 @@ snapshots: has-proto: 1.0.3 is-typed-array: 1.1.13 + typed-array-byte-length@1.0.3: + dependencies: + call-bind: 1.0.8 + for-each: 0.3.3 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + typed-array-byte-offset@1.0.0: dependencies: available-typed-arrays: 1.0.5 @@ -13294,6 +13487,16 @@ snapshots: has-proto: 1.0.3 is-typed-array: 1.1.13 + typed-array-byte-offset@1.0.4: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + for-each: 0.3.3 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + reflect.getprototypeof: 1.0.10 + typed-array-length@1.0.4: dependencies: call-bind: 1.0.7 @@ -13309,14 +13512,31 @@ snapshots: is-typed-array: 1.1.13 possible-typed-array-names: 1.0.0 - typescript@5.1.3: {} + typed-array-length@1.0.7: + dependencies: + call-bind: 1.0.8 + for-each: 0.3.3 + gopd: 1.2.0 + is-typed-array: 1.1.15 + possible-typed-array-names: 1.0.0 + reflect.getprototypeof: 1.0.10 - typescript@5.2.2: {} - - typescript@5.4.2: {} + typescript-eslint@8.30.1(eslint@9.24.0)(typescript@5.8.3): + dependencies: + '@typescript-eslint/eslint-plugin': 8.30.1(@typescript-eslint/parser@8.30.1(eslint@9.24.0)(typescript@5.8.3))(eslint@9.24.0)(typescript@5.8.3) + '@typescript-eslint/parser': 8.30.1(eslint@9.24.0)(typescript@5.8.3) + '@typescript-eslint/utils': 8.30.1(eslint@9.24.0)(typescript@5.8.3) + eslint: 9.24.0 + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color typescript@5.4.5: {} + typescript@5.7.3: {} + + typescript@5.8.3: {} + ultrahtml@1.5.2: {} unbox-primitive@1.0.2: @@ -13326,6 +13546,13 @@ snapshots: has-symbols: 1.0.3 which-boxed-primitive: 1.0.2 + unbox-primitive@1.1.0: + dependencies: + call-bound: 1.0.4 + has-bigints: 1.0.2 + has-symbols: 1.1.0 + which-boxed-primitive: 1.1.1 + undici-types@5.26.5: {} undici@5.26.5: @@ -13407,19 +13634,19 @@ snapshots: dependencies: browserslist: 4.22.1 escalade: 3.1.1 - picocolors: 1.0.0 + picocolors: 1.1.1 - update-browserslist-db@1.0.13(browserslist@4.23.0): + update-browserslist-db@1.1.1(browserslist@4.24.2): dependencies: - browserslist: 4.23.0 - escalade: 3.1.1 - picocolors: 1.0.0 + browserslist: 4.24.2 + escalade: 3.2.0 + picocolors: 1.1.1 - update-browserslist-db@1.0.16(browserslist@4.23.1): + update-browserslist-db@1.1.1(browserslist@4.24.4): dependencies: - browserslist: 4.23.1 - escalade: 3.1.2 - picocolors: 1.0.1 + browserslist: 4.24.4 + escalade: 3.2.0 + picocolors: 1.1.1 uri-js@4.4.1: dependencies: @@ -13486,28 +13713,21 @@ snapshots: vite@4.5.3(@types/node@20.14.6)(terser@5.31.0): dependencies: esbuild: 0.18.20 - postcss: 8.4.38 + postcss: 8.5.3 rollup: 3.29.4 optionalDependencies: '@types/node': 20.14.6 fsevents: 2.3.3 terser: 5.31.0 - vite@5.2.12(@types/node@20.14.6)(terser@5.31.0): + vite@6.3.0(@types/node@20.14.6)(terser@5.31.0): dependencies: - esbuild: 0.20.2 - postcss: 8.4.38 - rollup: 4.18.0 - optionalDependencies: - '@types/node': 20.14.6 - fsevents: 2.3.3 - terser: 5.31.0 - - vite@5.3.2(@types/node@20.14.6)(terser@5.31.0): - dependencies: - esbuild: 0.21.5 - postcss: 8.4.39 - rollup: 4.18.0 + esbuild: 0.25.2 + fdir: 6.4.3(picomatch@4.0.2) + picomatch: 4.0.2 + postcss: 8.5.3 + rollup: 4.39.0 + tinyglobby: 0.2.12 optionalDependencies: '@types/node': 20.14.6 fsevents: 2.3.3 @@ -13517,13 +13737,9 @@ snapshots: optionalDependencies: vite: 4.5.3(@types/node@20.14.6)(terser@5.31.0) - vitefu@0.2.5(vite@5.2.12(@types/node@20.14.6)(terser@5.31.0)): + vitefu@1.0.4(vite@6.3.0(@types/node@20.14.6)(terser@5.31.0)): optionalDependencies: - vite: 5.2.12(@types/node@20.14.6)(terser@5.31.0) - - vitefu@0.2.5(vite@5.3.2(@types/node@20.14.6)(terser@5.31.0)): - optionalDependencies: - vite: 5.3.2(@types/node@20.14.6)(terser@5.31.0) + vite: 6.3.0(@types/node@20.14.6)(terser@5.31.0) vscode-oniguruma@1.7.0: {} @@ -13560,27 +13776,36 @@ snapshots: is-string: 1.0.7 is-symbol: 1.0.4 - which-builtin-type@1.1.3: + which-boxed-primitive@1.1.1: dependencies: - function.prototype.name: 1.1.6 - has-tostringtag: 1.0.0 - is-async-function: 2.0.0 - is-date-object: 1.0.5 - is-finalizationregistry: 1.0.2 - is-generator-function: 1.0.10 - is-regex: 1.1.4 - is-weakref: 1.0.2 - isarray: 2.0.5 - which-boxed-primitive: 1.0.2 - which-collection: 1.0.1 - which-typed-array: 1.1.13 + is-bigint: 1.1.0 + is-boolean-object: 1.2.2 + is-number-object: 1.1.1 + is-string: 1.1.1 + is-symbol: 1.1.1 - which-collection@1.0.1: + which-builtin-type@1.2.1: dependencies: - is-map: 2.0.2 - is-set: 2.0.2 - is-weakmap: 2.0.1 - is-weakset: 2.0.2 + call-bound: 1.0.4 + function.prototype.name: 1.1.8 + has-tostringtag: 1.0.2 + is-async-function: 2.0.0 + is-date-object: 1.1.0 + is-finalizationregistry: 1.1.1 + is-generator-function: 1.0.10 + is-regex: 1.2.1 + is-weakref: 1.1.1 + isarray: 2.0.5 + which-boxed-primitive: 1.1.1 + which-collection: 1.0.2 + which-typed-array: 1.1.19 + + which-collection@1.0.2: + dependencies: + is-map: 2.0.3 + is-set: 2.0.3 + is-weakmap: 2.0.2 + is-weakset: 2.0.4 which-module@2.0.1: {} @@ -13612,6 +13837,16 @@ snapshots: gopd: 1.0.1 has-tostringtag: 1.0.2 + which-typed-array@1.1.19: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.4 + for-each: 0.3.5 + get-proto: 1.0.1 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + which@1.3.1: dependencies: isexe: 2.0.0 @@ -13698,6 +13933,8 @@ snapshots: yocto-queue@1.0.0: {} + zimmerframe@1.1.2: {} + zod@3.21.1: {} zustand@4.4.1(@types/react@18.2.24)(immer@10.0.4)(react@18.2.0): diff --git a/tests/playwright/e2e/edges.spec.ts b/tests/playwright/e2e/edges.spec.ts index 5d5e4258..768f056c 100644 --- a/tests/playwright/e2e/edges.spec.ts +++ b/tests/playwright/e2e/edges.spec.ts @@ -92,7 +92,7 @@ test.describe('Edges', () => { await edge.click(); await expect(edge).toHaveClass(/selected/); - await page.keyboard.press('Backspace'); + await page.keyboard.press('d'); await expect(edge).not.toBeAttached(); }); @@ -112,10 +112,9 @@ test.describe('Edges', () => { await page.mouse.down(); await page.mouse.up(); - // TODO: times out on webkit await expect(edge).toHaveClass(/selected/); - await page.keyboard.press('Backspace'); + await page.keyboard.press('d'); await expect(edge).toBeAttached(); }); @@ -139,12 +138,10 @@ test.describe('Edges', () => { const edgeBox = await edge.boundingBox(); - //FIXME: Negative values are not working. Investigate further await page.mouse.move(edgeBox!.x + edgeBox!.width * 0.5 + 21, edgeBox!.y + edgeBox!.height * 0.5); await page.mouse.down(); await page.mouse.up(); - // TODO: times out on webkit await expect(edge).toHaveClass(/selected/); }); diff --git a/tests/playwright/e2e/nodes.spec.ts b/tests/playwright/e2e/nodes.spec.ts index decd2c4f..b405d0e0 100644 --- a/tests/playwright/e2e/nodes.spec.ts +++ b/tests/playwright/e2e/nodes.spec.ts @@ -17,40 +17,37 @@ test.describe('Nodes', () => { }); test('selecting multiple nodes with shift drag', async ({ page }) => { + let nodeSelection = page.locator( + FRAMEWORK === 'react' ? '.react-flow__nodesselection' : '.svelte-flow__selection' + ); + let selection = page.locator(FRAMEWORK === 'react' ? '.react-flow__selection' : '.svelte-flow__selection'); + const nodes = page.locator(`.${FRAMEWORK}-flow__node`); - const firstNode = nodes.first(); - const secondNode = nodes.nth(1); - const thirdNode = nodes.nth(2); - await expect(firstNode).toBeInViewport(); - await expect(secondNode).toBeInViewport(); - await expect(thirdNode).toBeInViewport(); + await expect(nodes.first()).toHaveCSS('visibility', 'visible'); + await expect(nodes.nth(1)).toHaveCSS('visibility', 'visible'); + await expect(nodes.nth(2)).toHaveCSS('visibility', 'visible'); + const box = await nodes.first().boundingBox(); - const box = await firstNode.boundingBox(); - - await page.mouse.move(box!.x - 150, box!.y - 25); await page.keyboard.down('Shift'); + await page.mouse.move(box!.x - 150, box!.y - 25); await page.mouse.down(); await page.mouse.move(box!.x + 275, box!.y + 200); + + await expect(selection).toBeInViewport(); await page.mouse.up(); await page.keyboard.up('Shift'); - await expect(firstNode).toHaveClass(/selected/); - await expect(secondNode).toHaveClass(/selected/); - await expect(thirdNode).toHaveClass(/selected/); + await expect(nodes.first()).toHaveClass(/selected/); + await expect(nodes.nth(1)).toHaveClass(/selected/); + await expect(nodes.nth(2)).toHaveClass(/selected/); - let selection: Locator | undefined; - if (FRAMEWORK === 'react') { - selection = page.locator('.react-flow__nodesselection'); - } else if (FRAMEWORK === 'svelte') { - selection = page.locator('.svelte-flow__selection'); - } - - if (selection) await expect(selection).toBeInViewport(); + await expect(nodeSelection).toBeInViewport(); }); test('selectable=false prevents selection', async ({ page }) => { const locator = page.locator(`.${FRAMEWORK}-flow__node`).and(page.locator('[data-id="notSelectable"]')); + await expect(page.locator(`.${FRAMEWORK}-flow__node`).first()).toHaveCSS('visibility', 'visible'); await locator.click(); await expect(locator).not.toHaveClass(/selected/); @@ -61,6 +58,8 @@ test.describe('Nodes', () => { test('dragging a node', async ({ page }) => { const node = page.locator(`.${FRAMEWORK}-flow__node`).first(); + await expect(node).toHaveCSS('visibility', 'visible'); + const transformBeforeMove = await node.evaluate((element) => { return element.style.transform; }); @@ -79,6 +78,7 @@ test.describe('Nodes', () => { test('draggable=false prevents dragging', async ({ page }) => { const node = page.locator(`.${FRAMEWORK}-flow__node`).and(page.locator('[data-id="notDraggable"]')); + await expect(node).toHaveCSS('visibility', 'visible'); const transformBeforeMove = await node.evaluate((element) => { return element.style.transform; @@ -98,6 +98,7 @@ test.describe('Nodes', () => { test('custom drag handle works', async ({ page }) => { const node = page.locator(`.${FRAMEWORK}-flow__node`).and(page.locator('[data-id="drag-handle"]')); + await expect(node).toHaveCSS('visibility', 'visible'); const dragHandle = page.locator('.custom-drag-handle'); const transformBeforeMove = await node.evaluate((element) => { @@ -133,9 +134,10 @@ test.describe('Nodes', () => { test.describe('deleting', () => { test('deleting a node and its edges', async ({ page }) => { const node = page.locator(`.${FRAMEWORK}-flow__node`).and(page.locator('[data-id="Node-1"]')); + await expect(node).toHaveCSS('visibility', 'visible'); await node.click(); - await page.keyboard.press('Backspace'); + await page.keyboard.press('d'); await expect(node).not.toBeAttached(); @@ -143,14 +145,15 @@ test.describe('Nodes', () => { expect(edges).toHaveLength(0); }); - // TODO: pressing backspace creates problems on webkit test('deletable=false prevents deletion', async ({ page }) => { const node = page.locator(`.${FRAMEWORK}-flow__node`).and(page.locator('[data-id="notDeletable"]')); + await expect(node).toHaveCSS('visibility', 'visible'); await expect(node).toBeAttached(); await node.click(); - await page.keyboard.press('Backspace'); + // pressing backspace breaks webkit + await page.keyboard.press('d'); await expect(node).toBeAttached(); }); @@ -158,60 +161,86 @@ test.describe('Nodes', () => { test.describe('connecting', () => { test('connecting two nodes', async ({ page }) => { + let connectionLine = page.locator(`.${FRAMEWORK}-flow__connectionline`); const outputSourceHandle = page.locator(`.${FRAMEWORK}-flow__handle`).and(page.locator('[data-nodeid="Node-1"]')); const inputSourceHandle = page.locator(`.${FRAMEWORK}-flow__handle`).and(page.locator('[data-nodeid="Node-4"]')); + await expect(page.locator(`.${FRAMEWORK}-flow__node`).first()).toHaveCSS('visibility', 'visible'); await expect(outputSourceHandle).toBeInViewport(); await expect(inputSourceHandle).toBeInViewport(); - const edgesBefore = await page.locator(`.${FRAMEWORK}-flow__edge`).all(); + await expect(page.locator(`.${FRAMEWORK}-flow__edge`)).toHaveCount(2); await outputSourceHandle.hover(); await page.mouse.down(); await inputSourceHandle.hover(); + await expect(connectionLine).toBeInViewport(); await page.mouse.up(); - const edgesAfter = await page.locator(`.${FRAMEWORK}-flow__edge`).all(); - expect(edgesAfter).toHaveLength(edgesBefore.length + 1); + await expect(connectionLine).not.toBeInViewport(); + + await expect(page.locator('[data-id="xy-edge__Node-1-Node-4"]')).toBeInViewport(); + + await expect(page.locator(`.${FRAMEWORK}-flow__edge`)).toHaveCount(3); }); test('connecting two output handles does not work', async ({ page }) => { + let connectionLine = page.locator(`.${FRAMEWORK}-flow__connectionline`); const firstOutputHandle = page.locator(`.${FRAMEWORK}-flow__handle`).and(page.locator('[data-nodeid="Node-2"]')); const secondOutputHandle = page.locator(`.${FRAMEWORK}-flow__handle`).and(page.locator('[data-nodeid="Node-4"]')); + await expect(page.locator(`.${FRAMEWORK}-flow__node`).first()).toHaveCSS('visibility', 'visible'); await expect(firstOutputHandle).toBeInViewport(); await expect(secondOutputHandle).toBeInViewport(); - const edgesBefore = await page.locator(`.${FRAMEWORK}-flow__edge`).all(); + await expect(page.locator(`.${FRAMEWORK}-flow__edge`)).toHaveCount(2); await firstOutputHandle.hover(); await page.mouse.down(); - await secondOutputHandle.hover(); + + // await secondOutputHandle.hover(); + // Does not work in SvelteFlow for whatever reason!? + // but the following works... + const box = await secondOutputHandle.boundingBox(); + await page.mouse.move(box!.x + 2, box!.y + 2); + + await expect(connectionLine).toBeInViewport(); await page.mouse.up(); - const edgesAfter = await page.locator(`.${FRAMEWORK}-flow__edge`).all(); - expect(edgesAfter).toHaveLength(edgesBefore.length); + await expect(connectionLine).not.toBeInViewport(); + + await expect(page.locator(`.${FRAMEWORK}-flow__edge`)).toHaveCount(2); }); test('connecting two input handles does not work', async ({ page }) => { + let connectionLine = page.locator(`.${FRAMEWORK}-flow__connectionline`); const firstInputHandle = page.locator(`.${FRAMEWORK}-flow__handle`).and(page.locator('[data-nodeid="Node-1"]')); const secondInputHandle = page .locator(`.${FRAMEWORK}-flow__handle`) .and(page.locator('[data-nodeid="Node-3"]')) .and(page.locator('.source')); + await expect(page.locator(`.${FRAMEWORK}-flow__node`).first()).toHaveCSS('visibility', 'visible'); await expect(firstInputHandle).toBeInViewport(); await expect(secondInputHandle).toBeInViewport(); - const edgesBefore = await page.locator(`.${FRAMEWORK}-flow__edge`).all(); + await expect(page.locator(`.${FRAMEWORK}-flow__edge`)).toHaveCount(2); await firstInputHandle.hover(); await page.mouse.down(); - await secondInputHandle.hover(); + + // await secondInputHandle.hover(); + // Does not work in SvelteFlow for whatever reason!? + // but the following works... + const box = await secondInputHandle.boundingBox(); + await page.mouse.move(box!.x + 2, box!.y + 2); + + await expect(connectionLine).toBeInViewport(); await page.mouse.up(); - const edgesAfter = await page.locator(`.${FRAMEWORK}-flow__edge`).all(); - expect(edgesAfter).toHaveLength(edgesBefore.length); + await expect(connectionLine).not.toBeInViewport(); + + await expect(page.locator(`.${FRAMEWORK}-flow__edge`)).toHaveCount(2); }); test('connectable=false prevents connections', async ({ page }) => { @@ -222,18 +251,18 @@ test.describe('Nodes', () => { const notConnectableBox = await notConnectableHandle.boundingBox(); + await expect(page.locator(`.${FRAMEWORK}-flow__node`).first()).toHaveCSS('visibility', 'visible'); await expect(outputHandle).toBeInViewport(); await expect(notConnectableHandle).toBeInViewport(); - const edgesBefore = await page.locator(`.${FRAMEWORK}-flow__edge`).all(); + await expect(page.locator(`.${FRAMEWORK}-flow__edge`)).toHaveCount(2); await outputHandle.hover(); await page.mouse.down(); await page.mouse.move(notConnectableBox!.x + 2, notConnectableBox!.y + 2); await page.mouse.up(); - const edgesAfter = await page.locator(`.${FRAMEWORK}-flow__edge`).all(); - expect(edgesAfter).toHaveLength(edgesBefore.length); + await expect(page.locator(`.${FRAMEWORK}-flow__edge`)).toHaveCount(2); }); }); @@ -245,12 +274,14 @@ test.describe('Nodes', () => { test('classes get applied', async ({ page }) => { const node = page.locator(`.${FRAMEWORK}-flow__node`).and(page.locator('[data-id="Node-1"]')); + await expect(node).toHaveCSS('visibility', 'visible'); await expect(node).toHaveClass(/playwright-test-class-123/); }); test('styles get applied', async ({ page }) => { const node = page.locator(`.${FRAMEWORK}-flow__node`).and(page.locator('[data-id="Node-1"]')); + await expect(node).toHaveCSS('visibility', 'visible'); await expect(node).toHaveCSS('background-color', 'rgb(255, 0, 0)'); }); diff --git a/tests/playwright/e2e/pane.spec.ts b/tests/playwright/e2e/pane.spec.ts index fade5e0c..9f51ae5c 100644 --- a/tests/playwright/e2e/pane.spec.ts +++ b/tests/playwright/e2e/pane.spec.ts @@ -20,18 +20,21 @@ test.describe('Pane default', () => { await expect(pane).toBeAttached(); const paneBox = await pane.boundingBox(); - const transformsBefore = await getTransform(viewport); + const movementPx = 100; await pane.hover(); await page.mouse.down(); // Move pane by 100, 100 - await page.mouse.move(paneBox!.x + paneBox!.width * 0.5 + 100, paneBox!.y + paneBox!.height * 0.5 + 100); + await page.mouse.move( + paneBox!.x + paneBox!.width * 0.5 + movementPx, + paneBox!.y + paneBox!.height * 0.5 + movementPx + ); const transformsAfter = await getTransform(viewport); - expect(transformsAfter.translateX - transformsBefore.translateX).toBe(100); - expect(transformsAfter.translateY - transformsBefore.translateY).toBe(100); + expect(movementPx - Math.floor(transformsAfter.translateX - transformsBefore.translateX)).toBeLessThan(1); + expect(movementPx - Math.floor(transformsAfter.translateY - transformsBefore.translateY)).toBeLessThan(1); }); test('scrolling the default pane zooms it', async ({ page }) => { @@ -169,13 +172,12 @@ test.describe('Pane non-default', () => { }); }); -test.describe('Pane activation keys', () => { - test.beforeEach(async ({ page }) => { - // Go to the starting url before each test. - await page.goto('/tests/generic/pane/activation-keys'); +// test.describe('Pane activation keys', () => { +// test.beforeEach(async ({ page }) => { +// // Go to the starting url before each test. +// await page.goto('/tests/generic/pane/activation-keys'); - // Wait till the edges are rendered - await page.waitForSelector('[data-id="first-edge"]', { timeout: 5000 }); - }); - // TODO -}); +// // Wait till the edges are rendered +// await page.waitForSelector('[data-id="first-edge"]', { timeout: 5000 }); +// }); +// }); diff --git a/tests/playwright/e2e/props.spec.ts b/tests/playwright/e2e/props.spec.ts index b8104633..d9d3e7eb 100644 --- a/tests/playwright/e2e/props.spec.ts +++ b/tests/playwright/e2e/props.spec.ts @@ -6,17 +6,19 @@ test.describe('Props', () => { test.describe('colorMode', async () => { test('render default light color mode', async ({ page }) => { await page.goto('/examples/color-mode'); - const locator = page.locator(`.${FRAMEWORK}-flow`); - await expect(locator).not.toHaveClass(/dark/); + await expect(page.locator(`.${FRAMEWORK}-flow__node`).first()).toHaveCSS('visibility', 'visible'); + + await expect(page.locator(`.${FRAMEWORK}-flow`)).not.toHaveClass(/dark/); }); test('render dark color mode', async ({ page }) => { await page.goto('/examples/color-mode'); - const locator = page.locator(`.${FRAMEWORK}-flow`); + await expect(page.locator(`.${FRAMEWORK}-flow__node`).first()).toHaveCSS('visibility', 'visible'); + await page.getByTestId('colormode-select').selectOption({ label: 'dark' }); - await expect(locator).toHaveClass(/dark/); + await expect(page.locator(`.${FRAMEWORK}-flow`)).toHaveClass(/dark/); }); }); }); diff --git a/tests/playwright/package-lock.json b/tests/playwright/package-lock.json index e6abed25..e7164ee6 100644 --- a/tests/playwright/package-lock.json +++ b/tests/playwright/package-lock.json @@ -9,15 +9,15 @@ "version": "1.0.0", "license": "ISC", "dependencies": { - "@playwright/experimental-ct-react": "^1.44.1", + "@playwright/experimental-ct-react": "^1.51.1", "@types/react": "^18.3.3", "@types/react-dom": "^18.3.0", "react": "^18.3.1", "react-dom": "^18.3.1", - "typescript": "^5.4.5" + "typescript": "^5.7.3" }, "devDependencies": { - "@playwright/test": "^1.44.1", + "@playwright/test": "^1.51.1", "@types/node": "^20.14.6" } }, @@ -352,6 +352,7 @@ "os": [ "aix" ], + "peer": true, "engines": { "node": ">=12" } @@ -367,6 +368,7 @@ "os": [ "android" ], + "peer": true, "engines": { "node": ">=12" } @@ -382,6 +384,7 @@ "os": [ "android" ], + "peer": true, "engines": { "node": ">=12" } @@ -397,6 +400,7 @@ "os": [ "android" ], + "peer": true, "engines": { "node": ">=12" } @@ -412,6 +416,7 @@ "os": [ "darwin" ], + "peer": true, "engines": { "node": ">=12" } @@ -427,6 +432,7 @@ "os": [ "darwin" ], + "peer": true, "engines": { "node": ">=12" } @@ -442,6 +448,7 @@ "os": [ "freebsd" ], + "peer": true, "engines": { "node": ">=12" } @@ -457,6 +464,7 @@ "os": [ "freebsd" ], + "peer": true, "engines": { "node": ">=12" } @@ -472,6 +480,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=12" } @@ -487,6 +496,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=12" } @@ -502,6 +512,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=12" } @@ -517,6 +528,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=12" } @@ -532,6 +544,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=12" } @@ -547,6 +560,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=12" } @@ -562,6 +576,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=12" } @@ -577,6 +592,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=12" } @@ -592,10 +608,26 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=12" } }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.2.tgz", + "integrity": "sha512-talAIBoY5M8vHc6EeI2WW9d/CkiO9MQJ0IOWX8hrLhxGbro/vBXJvaQXefW2cP0z0nQVTdQ/eNyGFV1GSKrxfw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@esbuild/netbsd-x64": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", @@ -607,10 +639,26 @@ "os": [ "netbsd" ], + "peer": true, "engines": { "node": ">=12" } }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.2.tgz", + "integrity": "sha512-dcXYOC6NXOqcykeDlwId9kB6OkPUxOEqU+rkrYVqJbK2hagWOMrsTGsMr8+rW02M+d5Op5NNlgMmjzecaRf7Tg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@esbuild/openbsd-x64": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", @@ -622,6 +670,7 @@ "os": [ "openbsd" ], + "peer": true, "engines": { "node": ">=12" } @@ -637,6 +686,7 @@ "os": [ "sunos" ], + "peer": true, "engines": { "node": ">=12" } @@ -652,6 +702,7 @@ "os": [ "win32" ], + "peer": true, "engines": { "node": ">=12" } @@ -667,6 +718,7 @@ "os": [ "win32" ], + "peer": true, "engines": { "node": ">=12" } @@ -682,6 +734,7 @@ "os": [ "win32" ], + "peer": true, "engines": { "node": ">=12" } @@ -730,52 +783,522 @@ } }, "node_modules/@playwright/experimental-ct-core": { - "version": "1.44.1", - "resolved": "https://registry.npmjs.org/@playwright/experimental-ct-core/-/experimental-ct-core-1.44.1.tgz", - "integrity": "sha512-IqeXzfmpHH8yHA0fGQ//l/tDJHzUmg2dQj3t28E1tCshvnYc9fVr53Na9+/B8ME//vw0UFpv+CSKcOTHwWrhQg==", + "version": "1.51.1", + "resolved": "https://registry.npmjs.org/@playwright/experimental-ct-core/-/experimental-ct-core-1.51.1.tgz", + "integrity": "sha512-kpRZWBT3SMukL1fx8BwEj385Pkgtp86bBKzmrmJU30lWlQiIDFNaIHosgxQC68c8y2mg3Una/lBSHNc2Fotgkw==", "dependencies": { - "playwright": "1.44.1", - "playwright-core": "1.44.1", - "vite": "^5.2.8" + "playwright": "1.51.1", + "playwright-core": "1.51.1", + "vite": "^5.4.14 || ^6.0.0" }, "engines": { - "node": ">=16" + "node": ">=18" + } + }, + "node_modules/@playwright/experimental-ct-core/node_modules/@esbuild/aix-ppc64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.2.tgz", + "integrity": "sha512-wCIboOL2yXZym2cgm6mlA742s9QeJ8DjGVaL39dLN4rRwrOgOyYSnOaFPhKZGLb2ngj4EyfAFjsNJwPXZvseag==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@playwright/experimental-ct-core/node_modules/@esbuild/android-arm": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.2.tgz", + "integrity": "sha512-NQhH7jFstVY5x8CKbcfa166GoV0EFkaPkCKBQkdPJFvo5u+nGXLEH/ooniLb3QI8Fk58YAx7nsPLozUWfCBOJA==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@playwright/experimental-ct-core/node_modules/@esbuild/android-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.2.tgz", + "integrity": "sha512-5ZAX5xOmTligeBaeNEPnPaeEuah53Id2tX4c2CVP3JaROTH+j4fnfHCkr1PjXMd78hMst+TlkfKcW/DlTq0i4w==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@playwright/experimental-ct-core/node_modules/@esbuild/android-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.2.tgz", + "integrity": "sha512-Ffcx+nnma8Sge4jzddPHCZVRvIfQ0kMsUsCMcJRHkGJ1cDmhe4SsrYIjLUKn1xpHZybmOqCWwB0zQvsjdEHtkg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@playwright/experimental-ct-core/node_modules/@esbuild/darwin-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.2.tgz", + "integrity": "sha512-MpM6LUVTXAzOvN4KbjzU/q5smzryuoNjlriAIx+06RpecwCkL9JpenNzpKd2YMzLJFOdPqBpuub6eVRP5IgiSA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@playwright/experimental-ct-core/node_modules/@esbuild/darwin-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.2.tgz", + "integrity": "sha512-5eRPrTX7wFyuWe8FqEFPG2cU0+butQQVNcT4sVipqjLYQjjh8a8+vUTfgBKM88ObB85ahsnTwF7PSIt6PG+QkA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@playwright/experimental-ct-core/node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.2.tgz", + "integrity": "sha512-mLwm4vXKiQ2UTSX4+ImyiPdiHjiZhIaE9QvC7sw0tZ6HoNMjYAqQpGyui5VRIi5sGd+uWq940gdCbY3VLvsO1w==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@playwright/experimental-ct-core/node_modules/@esbuild/freebsd-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.2.tgz", + "integrity": "sha512-6qyyn6TjayJSwGpm8J9QYYGQcRgc90nmfdUb0O7pp1s4lTY+9D0H9O02v5JqGApUyiHOtkz6+1hZNvNtEhbwRQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@playwright/experimental-ct-core/node_modules/@esbuild/linux-arm": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.2.tgz", + "integrity": "sha512-UHBRgJcmjJv5oeQF8EpTRZs/1knq6loLxTsjc3nxO9eXAPDLcWW55flrMVc97qFPbmZP31ta1AZVUKQzKTzb0g==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@playwright/experimental-ct-core/node_modules/@esbuild/linux-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.2.tgz", + "integrity": "sha512-gq/sjLsOyMT19I8obBISvhoYiZIAaGF8JpeXu1u8yPv8BE5HlWYobmlsfijFIZ9hIVGYkbdFhEqC0NvM4kNO0g==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@playwright/experimental-ct-core/node_modules/@esbuild/linux-ia32": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.2.tgz", + "integrity": "sha512-bBYCv9obgW2cBP+2ZWfjYTU+f5cxRoGGQ5SeDbYdFCAZpYWrfjjfYwvUpP8MlKbP0nwZ5gyOU/0aUzZ5HWPuvQ==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@playwright/experimental-ct-core/node_modules/@esbuild/linux-loong64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.2.tgz", + "integrity": "sha512-SHNGiKtvnU2dBlM5D8CXRFdd+6etgZ9dXfaPCeJtz+37PIUlixvlIhI23L5khKXs3DIzAn9V8v+qb1TRKrgT5w==", + "cpu": [ + "loong64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@playwright/experimental-ct-core/node_modules/@esbuild/linux-mips64el": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.2.tgz", + "integrity": "sha512-hDDRlzE6rPeoj+5fsADqdUZl1OzqDYow4TB4Y/3PlKBD0ph1e6uPHzIQcv2Z65u2K0kpeByIyAjCmjn1hJgG0Q==", + "cpu": [ + "mips64el" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@playwright/experimental-ct-core/node_modules/@esbuild/linux-ppc64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.2.tgz", + "integrity": "sha512-tsHu2RRSWzipmUi9UBDEzc0nLc4HtpZEI5Ba+Omms5456x5WaNuiG3u7xh5AO6sipnJ9r4cRWQB2tUjPyIkc6g==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@playwright/experimental-ct-core/node_modules/@esbuild/linux-riscv64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.2.tgz", + "integrity": "sha512-k4LtpgV7NJQOml/10uPU0s4SAXGnowi5qBSjaLWMojNCUICNu7TshqHLAEbkBdAszL5TabfvQ48kK84hyFzjnw==", + "cpu": [ + "riscv64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@playwright/experimental-ct-core/node_modules/@esbuild/linux-s390x": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.2.tgz", + "integrity": "sha512-GRa4IshOdvKY7M/rDpRR3gkiTNp34M0eLTaC1a08gNrh4u488aPhuZOCpkF6+2wl3zAN7L7XIpOFBhnaE3/Q8Q==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@playwright/experimental-ct-core/node_modules/@esbuild/linux-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.2.tgz", + "integrity": "sha512-QInHERlqpTTZ4FRB0fROQWXcYRD64lAoiegezDunLpalZMjcUcld3YzZmVJ2H/Cp0wJRZ8Xtjtj0cEHhYc/uUg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@playwright/experimental-ct-core/node_modules/@esbuild/netbsd-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.2.tgz", + "integrity": "sha512-voZT9Z+tpOxrvfKFyfDYPc4DO4rk06qamv1a/fkuzHpiVBMOhpjK+vBmWM8J1eiB3OLSMFYNaOaBNLXGChf5tg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@playwright/experimental-ct-core/node_modules/@esbuild/openbsd-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.2.tgz", + "integrity": "sha512-t/TkWwahkH0Tsgoq1Ju7QfgGhArkGLkF1uYz8nQS/PPFlXbP5YgRpqQR3ARRiC2iXoLTWFxc6DJMSK10dVXluw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@playwright/experimental-ct-core/node_modules/@esbuild/sunos-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.2.tgz", + "integrity": "sha512-cfZH1co2+imVdWCjd+D1gf9NjkchVhhdpgb1q5y6Hcv9TP6Zi9ZG/beI3ig8TvwT9lH9dlxLq5MQBBgwuj4xvA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@playwright/experimental-ct-core/node_modules/@esbuild/win32-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.2.tgz", + "integrity": "sha512-7Loyjh+D/Nx/sOTzV8vfbB3GJuHdOQyrOryFdZvPHLf42Tk9ivBU5Aedi7iyX+x6rbn2Mh68T4qq1SDqJBQO5Q==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@playwright/experimental-ct-core/node_modules/@esbuild/win32-ia32": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.2.tgz", + "integrity": "sha512-WRJgsz9un0nqZJ4MfhabxaD9Ft8KioqU3JMinOTvobbX6MOSUigSBlogP8QB3uxpJDsFS6yN+3FDBdqE5lg9kg==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@playwright/experimental-ct-core/node_modules/@esbuild/win32-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.2.tgz", + "integrity": "sha512-kM3HKb16VIXZyIeVrM1ygYmZBKybX8N4p754bw390wGO3Tf2j4L2/WYL+4suWujpgf6GBYs3jv7TyUivdd05JA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@playwright/experimental-ct-core/node_modules/esbuild": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.2.tgz", + "integrity": "sha512-16854zccKPnC+toMywC+uKNeYSv+/eXkevRAfwRD/G9Cleq66m8XFIrigkbvauLLlCfDL45Q2cWegSg53gGBnQ==", + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.2", + "@esbuild/android-arm": "0.25.2", + "@esbuild/android-arm64": "0.25.2", + "@esbuild/android-x64": "0.25.2", + "@esbuild/darwin-arm64": "0.25.2", + "@esbuild/darwin-x64": "0.25.2", + "@esbuild/freebsd-arm64": "0.25.2", + "@esbuild/freebsd-x64": "0.25.2", + "@esbuild/linux-arm": "0.25.2", + "@esbuild/linux-arm64": "0.25.2", + "@esbuild/linux-ia32": "0.25.2", + "@esbuild/linux-loong64": "0.25.2", + "@esbuild/linux-mips64el": "0.25.2", + "@esbuild/linux-ppc64": "0.25.2", + "@esbuild/linux-riscv64": "0.25.2", + "@esbuild/linux-s390x": "0.25.2", + "@esbuild/linux-x64": "0.25.2", + "@esbuild/netbsd-arm64": "0.25.2", + "@esbuild/netbsd-x64": "0.25.2", + "@esbuild/openbsd-arm64": "0.25.2", + "@esbuild/openbsd-x64": "0.25.2", + "@esbuild/sunos-x64": "0.25.2", + "@esbuild/win32-arm64": "0.25.2", + "@esbuild/win32-ia32": "0.25.2", + "@esbuild/win32-x64": "0.25.2" + } + }, + "node_modules/@playwright/experimental-ct-core/node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/@playwright/experimental-ct-core/node_modules/vite": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.0.tgz", + "integrity": "sha512-9aC0n4pr6hIbvi1YOpFjwQ+QOTGssvbJKoeYkuHHGWwlXfdxQlI8L2qNMo9awEEcCPSiS+5mJZk5jH1PAqoDeQ==", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.4.3", + "picomatch": "^4.0.2", + "postcss": "^8.5.3", + "rollup": "^4.34.9", + "tinyglobby": "^0.2.12" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } } }, "node_modules/@playwright/experimental-ct-react": { - "version": "1.44.1", - "resolved": "https://registry.npmjs.org/@playwright/experimental-ct-react/-/experimental-ct-react-1.44.1.tgz", - "integrity": "sha512-qRhv2zmZVwtzAYWwQO4j+It0S5zLUuZg/7Ke61ymCC5jGqlwf2kYqogFxBiDdhAO1sz/dN0UtdU+6df0HK5yzw==", + "version": "1.51.1", + "resolved": "https://registry.npmjs.org/@playwright/experimental-ct-react/-/experimental-ct-react-1.51.1.tgz", + "integrity": "sha512-Lu14QiemO2TWfiL6TQPWBH2n+4IvPYH/W6cfblzYpLu/TZdJ7e608l3GwqAGRN5FJym3YOonC7x1Q5EAwcBarg==", "dependencies": { - "@playwright/experimental-ct-core": "1.44.1", + "@playwright/experimental-ct-core": "1.51.1", "@vitejs/plugin-react": "^4.2.1" }, "bin": { "playwright": "cli.js" }, "engines": { - "node": ">=16" + "node": ">=18" } }, "node_modules/@playwright/test": { - "version": "1.44.1", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.44.1.tgz", - "integrity": "sha512-1hZ4TNvD5z9VuhNJ/walIjvMVvYkZKf71axoF/uiAqpntQJXpG64dlXhoDXE3OczPuTuvjf/M5KWFg5VAVUS3Q==", + "version": "1.51.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.51.1.tgz", + "integrity": "sha512-nM+kEaTSAoVlXmMPH10017vn3FSiFqr/bh4fKg9vmAdMfd9SDqRZNvPSiAHADc/itWak+qPvMPZQOPwCBW7k7Q==", "dev": true, "dependencies": { - "playwright": "1.44.1" + "playwright": "1.51.1" }, "bin": { "playwright": "cli.js" }, "engines": { - "node": ">=16" + "node": ">=18" } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.18.0.tgz", - "integrity": "sha512-Tya6xypR10giZV1XzxmH5wr25VcZSncG0pZIjfePT0OVBvqNEurzValetGNarVrGiq66EBVAFn15iYX4w6FKgQ==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.40.0.tgz", + "integrity": "sha512-+Fbls/diZ0RDerhE8kyC6hjADCXA1K4yVNlH0EYfd2XjyH0UGgzaQ8MlT0pCXAThfxv3QUAczHaL+qSv1E4/Cg==", "cpu": [ "arm" ], @@ -785,9 +1308,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.18.0.tgz", - "integrity": "sha512-avCea0RAP03lTsDhEyfy+hpfr85KfyTctMADqHVhLAF3MlIkq83CP8UfAHUssgXTYd+6er6PaAhx/QGv4L1EiA==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.40.0.tgz", + "integrity": "sha512-PPA6aEEsTPRz+/4xxAmaoWDqh67N7wFbgFUJGMnanCFs0TV99M0M8QhhaSCks+n6EbQoFvLQgYOGXxlMGQe/6w==", "cpu": [ "arm64" ], @@ -797,9 +1320,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.18.0.tgz", - "integrity": "sha512-IWfdwU7KDSm07Ty0PuA/W2JYoZ4iTj3TUQjkVsO/6U+4I1jN5lcR71ZEvRh52sDOERdnNhhHU57UITXz5jC1/w==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.40.0.tgz", + "integrity": "sha512-GwYOcOakYHdfnjjKwqpTGgn5a6cUX7+Ra2HeNj/GdXvO2VJOOXCiYYlRFU4CubFM67EhbmzLOmACKEfvp3J1kQ==", "cpu": [ "arm64" ], @@ -809,9 +1332,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.18.0.tgz", - "integrity": "sha512-n2LMsUz7Ynu7DoQrSQkBf8iNrjOGyPLrdSg802vk6XT3FtsgX6JbE8IHRvposskFm9SNxzkLYGSq9QdpLYpRNA==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.40.0.tgz", + "integrity": "sha512-CoLEGJ+2eheqD9KBSxmma6ld01czS52Iw0e2qMZNpPDlf7Z9mj8xmMemxEucinev4LgHalDPczMyxzbq+Q+EtA==", "cpu": [ "x64" ], @@ -820,10 +1343,34 @@ "darwin" ] }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.40.0.tgz", + "integrity": "sha512-r7yGiS4HN/kibvESzmrOB/PxKMhPTlz+FcGvoUIKYoTyGd5toHp48g1uZy1o1xQvybwwpqpe010JrcGG2s5nkg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.40.0.tgz", + "integrity": "sha512-mVDxzlf0oLzV3oZOr0SMJ0lSDd3xC4CmnWJ8Val8isp9jRGl5Dq//LLDSPFrasS7pSm6m5xAcKaw3sHXhBjoRw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ] + }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.18.0.tgz", - "integrity": "sha512-C/zbRYRXFjWvz9Z4haRxcTdnkPt1BtCkz+7RtBSuNmKzMzp3ZxdM28Mpccn6pt28/UWUCTXa+b0Mx1k3g6NOMA==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.40.0.tgz", + "integrity": "sha512-y/qUMOpJxBMy8xCXD++jeu8t7kzjlOCkoxxajL58G62PJGBZVl/Gwpm7JK9+YvlB701rcQTzjUZ1JgUoPTnoQA==", "cpu": [ "arm" ], @@ -833,9 +1380,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.18.0.tgz", - "integrity": "sha512-l3m9ewPgjQSXrUMHg93vt0hYCGnrMOcUpTz6FLtbwljo2HluS4zTXFy2571YQbisTnfTKPZ01u/ukJdQTLGh9A==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.40.0.tgz", + "integrity": "sha512-GoCsPibtVdJFPv/BOIvBKO/XmwZLwaNWdyD8TKlXuqp0veo2sHE+A/vpMQ5iSArRUz/uaoj4h5S6Pn0+PdhRjg==", "cpu": [ "arm" ], @@ -845,9 +1392,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.18.0.tgz", - "integrity": "sha512-rJ5D47d8WD7J+7STKdCUAgmQk49xuFrRi9pZkWoRD1UeSMakbcepWXPF8ycChBoAqs1pb2wzvbY6Q33WmN2ftw==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.40.0.tgz", + "integrity": "sha512-L5ZLphTjjAD9leJzSLI7rr8fNqJMlGDKlazW2tX4IUF9P7R5TMQPElpH82Q7eNIDQnQlAyiNVfRPfP2vM5Avvg==", "cpu": [ "arm64" ], @@ -857,9 +1404,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.18.0.tgz", - "integrity": "sha512-be6Yx37b24ZwxQ+wOQXXLZqpq4jTckJhtGlWGZs68TgdKXJgw54lUUoFYrg6Zs/kjzAQwEwYbp8JxZVzZLRepQ==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.40.0.tgz", + "integrity": "sha512-ATZvCRGCDtv1Y4gpDIXsS+wfFeFuLwVxyUBSLawjgXK2tRE6fnsQEkE4csQQYWlBlsFztRzCnBvWVfcae/1qxQ==", "cpu": [ "arm64" ], @@ -868,10 +1415,22 @@ "linux" ] }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.40.0.tgz", + "integrity": "sha512-wG9e2XtIhd++QugU5MD9i7OnpaVb08ji3P1y/hNbxrQ3sYEelKJOq1UJ5dXczeo6Hj2rfDEL5GdtkMSVLa/AOg==", + "cpu": [ + "loong64" + ], + "optional": true, + "os": [ + "linux" + ] + }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.18.0.tgz", - "integrity": "sha512-hNVMQK+qrA9Todu9+wqrXOHxFiD5YmdEi3paj6vP02Kx1hjd2LLYR2eaN7DsEshg09+9uzWi2W18MJDlG0cxJA==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.40.0.tgz", + "integrity": "sha512-vgXfWmj0f3jAUvC7TZSU/m/cOE558ILWDzS7jBhiCAFpY2WEBn5jqgbqvmzlMjtp8KlLcBlXVD2mkTSEQE6Ixw==", "cpu": [ "ppc64" ], @@ -881,9 +1440,21 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.18.0.tgz", - "integrity": "sha512-ROCM7i+m1NfdrsmvwSzoxp9HFtmKGHEqu5NNDiZWQtXLA8S5HBCkVvKAxJ8U+CVctHwV2Gb5VUaK7UAkzhDjlg==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.40.0.tgz", + "integrity": "sha512-uJkYTugqtPZBS3Z136arevt/FsKTF/J9dEMTX/cwR7lsAW4bShzI2R0pJVw+hcBTWF4dxVckYh72Hk3/hWNKvA==", + "cpu": [ + "riscv64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.40.0.tgz", + "integrity": "sha512-rKmSj6EXQRnhSkE22+WvrqOqRtk733x3p5sWpZilhmjnkHkpeCgWsFFo0dGnUGeA+OZjRl3+VYq+HyCOEuwcxQ==", "cpu": [ "riscv64" ], @@ -893,9 +1464,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.18.0.tgz", - "integrity": "sha512-0UyyRHyDN42QL+NbqevXIIUnKA47A+45WyasO+y2bGJ1mhQrfrtXUpTxCOrfxCR4esV3/RLYyucGVPiUsO8xjg==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.40.0.tgz", + "integrity": "sha512-SpnYlAfKPOoVsQqmTFJ0usx0z84bzGOS9anAC0AZ3rdSo3snecihbhFTlJZ8XMwzqAcodjFU4+/SM311dqE5Sw==", "cpu": [ "s390x" ], @@ -905,9 +1476,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.18.0.tgz", - "integrity": "sha512-xuglR2rBVHA5UsI8h8UbX4VJ470PtGCf5Vpswh7p2ukaqBGFTnsfzxUBetoWBWymHMxbIG0Cmx7Y9qDZzr648w==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.40.0.tgz", + "integrity": "sha512-RcDGMtqF9EFN8i2RYN2W+64CdHruJ5rPqrlYw+cgM3uOVPSsnAQps7cpjXe9be/yDp8UC7VLoCoKC8J3Kn2FkQ==", "cpu": [ "x64" ], @@ -917,9 +1488,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.18.0.tgz", - "integrity": "sha512-LKaqQL9osY/ir2geuLVvRRs+utWUNilzdE90TpyoX0eNqPzWjRm14oMEE+YLve4k/NAqCdPkGYDaDF5Sw+xBfg==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.40.0.tgz", + "integrity": "sha512-HZvjpiUmSNx5zFgwtQAV1GaGazT2RWvqeDi0hV+AtC8unqqDSsaFjPxfsO6qPtKRRg25SisACWnJ37Yio8ttaw==", "cpu": [ "x64" ], @@ -929,9 +1500,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.18.0.tgz", - "integrity": "sha512-7J6TkZQFGo9qBKH0pk2cEVSRhJbL6MtfWxth7Y5YmZs57Pi+4x6c2dStAUvaQkHQLnEQv1jzBUW43GvZW8OFqA==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.40.0.tgz", + "integrity": "sha512-UtZQQI5k/b8d7d3i9AZmA/t+Q4tk3hOC0tMOMSq2GlMYOfxbesxG4mJSeDp0EHs30N9bsfwUvs3zF4v/RzOeTQ==", "cpu": [ "arm64" ], @@ -941,9 +1512,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.18.0.tgz", - "integrity": "sha512-Txjh+IxBPbkUB9+SXZMpv+b/vnTEtFyfWZgJ6iyCmt2tdx0OF5WhFowLmnh8ENGNpfUlUZkdI//4IEmhwPieNg==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.40.0.tgz", + "integrity": "sha512-+m03kvI2f5syIqHXCZLPVYplP8pQch9JHyXKZ3AGMKlg8dCyr2PKHjwRLiW53LTrN/Nc3EqHOKxUxzoSPdKddA==", "cpu": [ "ia32" ], @@ -953,9 +1524,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.18.0.tgz", - "integrity": "sha512-UOo5FdvOL0+eIVTgS4tIdbW+TtnBLWg1YBCcU2KWM7nuNwRz9bksDX1bekJJCpu25N1DVWaCwnT39dVQxzqS8g==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.40.0.tgz", + "integrity": "sha512-lpPE1cLfP5oPzVjKMx10pgBmKELQnFJXHgvtHCtuJWOv8MxqdEIMNtgHgBFf7Ea2/7EuVwa9fodWUfXAlXZLZQ==", "cpu": [ "x64" ], @@ -1002,9 +1573,9 @@ } }, "node_modules/@types/estree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==" + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", + "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==" }, "node_modules/@types/node": { "version": "20.14.6", @@ -1178,6 +1749,7 @@ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", "hasInstallScript": true, + "peer": true, "bin": { "esbuild": "bin/esbuild" }, @@ -1226,6 +1798,19 @@ "node": ">=0.8.0" } }, + "node_modules/fdir": { + "version": "6.4.3", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.3.tgz", + "integrity": "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, "node_modules/fsevents": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", @@ -1315,9 +1900,9 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", "funding": [ { "type": "github", @@ -1337,42 +1922,53 @@ "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==" }, "node_modules/picocolors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", - "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==" + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" + }, + "node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } }, "node_modules/playwright": { - "version": "1.44.1", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.44.1.tgz", - "integrity": "sha512-qr/0UJ5CFAtloI3avF95Y0L1xQo6r3LQArLIg/z/PoGJ6xa+EwzrwO5lpNr/09STxdHuUoP2mvuELJS+hLdtgg==", + "version": "1.51.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.51.1.tgz", + "integrity": "sha512-kkx+MB2KQRkyxjYPc3a0wLZZoDczmppyGJIvQ43l+aZihkaVvmu/21kiyaHeHjiFxjxNNFnUncKmcGIyOojsaw==", "dependencies": { - "playwright-core": "1.44.1" + "playwright-core": "1.51.1" }, "bin": { "playwright": "cli.js" }, "engines": { - "node": ">=16" + "node": ">=18" }, "optionalDependencies": { "fsevents": "2.3.2" } }, "node_modules/playwright-core": { - "version": "1.44.1", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.44.1.tgz", - "integrity": "sha512-wh0JWtYTrhv1+OSsLPgFzGzt67Y7BE/ZS3jEqgGBlp2ppp1ZDj8c+9IARNW4dwf1poq5MgHreEM2KV/GuR4cFA==", + "version": "1.51.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.51.1.tgz", + "integrity": "sha512-/crRMj8+j/Nq5s8QcvegseuyeZPxpQCZb6HNk3Sos3BlZyAknRjoyJPFWkpNn8v0+P3WiwqFF8P+zQo4eqiNuw==", "bin": { "playwright-core": "cli.js" }, "engines": { - "node": ">=16" + "node": ">=18" } }, "node_modules/postcss": { - "version": "8.4.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", - "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", + "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", "funding": [ { "type": "opencollective", @@ -1388,9 +1984,9 @@ } ], "dependencies": { - "nanoid": "^3.3.7", - "picocolors": "^1.0.0", - "source-map-js": "^1.2.0" + "nanoid": "^3.3.8", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" }, "engines": { "node": "^10 || ^12 || >=14" @@ -1428,11 +2024,11 @@ } }, "node_modules/rollup": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.18.0.tgz", - "integrity": "sha512-QmJz14PX3rzbJCN1SG4Xe/bAAX2a6NpCP8ab2vfu2GiUr8AQcr2nCV/oEO3yneFarB67zk8ShlIyWb2LGTb3Sg==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.40.0.tgz", + "integrity": "sha512-Noe455xmA96nnqH5piFtLobsGbCij7Tu+tb3c1vYjNbTkfzGqXqQXG3wJaYXkRZuQ0vEYN4bhwg7QnIrqB5B+w==", "dependencies": { - "@types/estree": "1.0.5" + "@types/estree": "1.0.7" }, "bin": { "rollup": "dist/bin/rollup" @@ -1442,22 +2038,26 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.18.0", - "@rollup/rollup-android-arm64": "4.18.0", - "@rollup/rollup-darwin-arm64": "4.18.0", - "@rollup/rollup-darwin-x64": "4.18.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.18.0", - "@rollup/rollup-linux-arm-musleabihf": "4.18.0", - "@rollup/rollup-linux-arm64-gnu": "4.18.0", - "@rollup/rollup-linux-arm64-musl": "4.18.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.18.0", - "@rollup/rollup-linux-riscv64-gnu": "4.18.0", - "@rollup/rollup-linux-s390x-gnu": "4.18.0", - "@rollup/rollup-linux-x64-gnu": "4.18.0", - "@rollup/rollup-linux-x64-musl": "4.18.0", - "@rollup/rollup-win32-arm64-msvc": "4.18.0", - "@rollup/rollup-win32-ia32-msvc": "4.18.0", - "@rollup/rollup-win32-x64-msvc": "4.18.0", + "@rollup/rollup-android-arm-eabi": "4.40.0", + "@rollup/rollup-android-arm64": "4.40.0", + "@rollup/rollup-darwin-arm64": "4.40.0", + "@rollup/rollup-darwin-x64": "4.40.0", + "@rollup/rollup-freebsd-arm64": "4.40.0", + "@rollup/rollup-freebsd-x64": "4.40.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.40.0", + "@rollup/rollup-linux-arm-musleabihf": "4.40.0", + "@rollup/rollup-linux-arm64-gnu": "4.40.0", + "@rollup/rollup-linux-arm64-musl": "4.40.0", + "@rollup/rollup-linux-loongarch64-gnu": "4.40.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.40.0", + "@rollup/rollup-linux-riscv64-gnu": "4.40.0", + "@rollup/rollup-linux-riscv64-musl": "4.40.0", + "@rollup/rollup-linux-s390x-gnu": "4.40.0", + "@rollup/rollup-linux-x64-gnu": "4.40.0", + "@rollup/rollup-linux-x64-musl": "4.40.0", + "@rollup/rollup-win32-arm64-msvc": "4.40.0", + "@rollup/rollup-win32-ia32-msvc": "4.40.0", + "@rollup/rollup-win32-x64-msvc": "4.40.0", "fsevents": "~2.3.2" } }, @@ -1478,9 +2078,9 @@ } }, "node_modules/source-map-js": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", - "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "engines": { "node": ">=0.10.0" } @@ -1496,6 +2096,21 @@ "node": ">=4" } }, + "node_modules/tinyglobby": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.12.tgz", + "integrity": "sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==", + "dependencies": { + "fdir": "^6.4.3", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, "node_modules/to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", @@ -1505,9 +2120,9 @@ } }, "node_modules/typescript": { - "version": "5.4.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", - "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", + "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -1555,6 +2170,7 @@ "version": "5.3.1", "resolved": "https://registry.npmjs.org/vite/-/vite-5.3.1.tgz", "integrity": "sha512-XBmSKRLXLxiaPYamLv3/hnP/KXDai1NDexN0FpkTaZXTfycHvkRHoenpgl/fvuK/kPbB6xAgoyiryAhQNxYmAQ==", + "peer": true, "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.38", @@ -1614,6 +2230,7 @@ "os": [ "darwin" ], + "peer": true, "engines": { "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } diff --git a/tests/playwright/package.json b/tests/playwright/package.json index a6dea9d4..6f3c27c2 100644 --- a/tests/playwright/package.json +++ b/tests/playwright/package.json @@ -14,15 +14,15 @@ "author": "", "license": "ISC", "devDependencies": { - "@playwright/test": "^1.45.0", + "@playwright/test": "^1.51.1", "@types/node": "^20.14.6" }, "dependencies": { - "@playwright/experimental-ct-react": "^1.45.0", + "@playwright/experimental-ct-react": "^1.51.1", "@types/react": "^18.3.3", "@types/react-dom": "^18.3.0", "react": "^18.3.1", "react-dom": "^18.3.1", - "typescript": "^5.4.5" + "typescript": "^5.7.3" } } diff --git a/tooling/eslint-config/CHANGELOG.md b/tooling/eslint-config/CHANGELOG.md new file mode 100644 index 00000000..f10cdc0f --- /dev/null +++ b/tooling/eslint-config/CHANGELOG.md @@ -0,0 +1,7 @@ +# @xyflow/eslint-config + +## 0.0.1 + +### Patch Changes + +- [#5003](https://github.com/xyflow/xyflow/pull/5003) [`e8e0d684`](https://github.com/xyflow/xyflow/commit/e8e0d684957b95d53a6cc11598c8755ff02117c7) Thanks [@dimaMachina](https://github.com/dimaMachina)! - repair lint command diff --git a/tooling/eslint-config/package.json b/tooling/eslint-config/package.json index b0ab4c4e..48b1dcbb 100644 --- a/tooling/eslint-config/package.json +++ b/tooling/eslint-config/package.json @@ -1,13 +1,15 @@ { "name": "@xyflow/eslint-config", - "version": "0.0.0", + "version": "0.0.1", "private": true, "license": "MIT", "main": "src/index.js", "devDependencies": { - "eslint": "^8.22.0", - "eslint-config-prettier": "^8.5.0", - "eslint-config-turbo": "^2.0.3", - "eslint-plugin-react": "^7.33.2" + "@typescript-eslint/eslint-plugin": "^8.23.0", + "@typescript-eslint/parser": "^8.23.0", + "eslint-config-prettier": "^10.0.1", + "eslint-config-turbo": "^2.4.0", + "eslint-plugin-prettier": "^4.2.1", + "eslint-plugin-react": "^7.37.4" } } diff --git a/tooling/eslint-config/src/index.js b/tooling/eslint-config/src/index.js index f2ce34a0..60f82b0e 100644 --- a/tooling/eslint-config/src/index.js +++ b/tooling/eslint-config/src/index.js @@ -29,4 +29,16 @@ module.exports = { rules: { '@typescript-eslint/no-non-null-assertion': 'off', }, + overrides: [ + { + files: ['**/*.{ts,tsx,cts,mts}'], + parserOptions: { + projectService: true, + }, + rules: { + '@typescript-eslint/no-deprecated': 'error', + '@typescript-eslint/no-unnecessary-type-assertion': 'error', + }, + }, + ], }; diff --git a/tooling/rollup-config/package.json b/tooling/rollup-config/package.json index c8871f61..d04d3d93 100644 --- a/tooling/rollup-config/package.json +++ b/tooling/rollup-config/package.json @@ -14,6 +14,6 @@ "@rollup/plugin-typescript": "11.1.6", "rollup": "^4.18.0", "rollup-plugin-peer-deps-external": "^2.2.4", - "typescript": "^5.1.3" + "typescript": "^5.4.5" } } diff --git a/tooling/rollup-config/src/index.js b/tooling/rollup-config/src/index.js index da5450c3..d96b64d3 100644 --- a/tooling/rollup-config/src/index.js +++ b/tooling/rollup-config/src/index.js @@ -79,4 +79,4 @@ const umdConfig = defineConfig({ ], }); -export default isProd ? [esmMjsConfig, esmJsConfig, umdConfig] : esmMjsConfig; +export default isProd ? [esmMjsConfig, esmJsConfig, umdConfig] : [esmJsConfig, esmMjsConfig];