Merge pull request #5596 from xyflow/chore/svelte-typed-context
Chore/svelte typed context
This commit is contained in:
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
'@xyflow/svelte': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Refactor internal use of contexts
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
.DS_Store
|
|
||||||
node_modules
|
|
||||||
/build
|
|
||||||
/.svelte-kit
|
|
||||||
/package
|
|
||||||
.env
|
|
||||||
.env.*
|
|
||||||
!.env.example
|
|
||||||
|
|
||||||
# Ignore files for PNPM, NPM and YARN
|
|
||||||
pnpm-lock.yaml
|
|
||||||
package-lock.json
|
|
||||||
yarn.lock
|
|
||||||
@@ -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'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
@@ -1,10 +1,24 @@
|
|||||||
.DS_Store
|
|
||||||
node_modules
|
node_modules
|
||||||
/build
|
|
||||||
|
# Output
|
||||||
|
.output
|
||||||
|
.vercel
|
||||||
|
.netlify
|
||||||
|
.wrangler
|
||||||
/.svelte-kit
|
/.svelte-kit
|
||||||
/package
|
/build
|
||||||
|
/dist
|
||||||
|
|
||||||
|
# OS
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Env
|
||||||
.env
|
.env
|
||||||
.env.*
|
.env.*
|
||||||
!.env.example
|
!.env.example
|
||||||
|
!.env.test
|
||||||
|
|
||||||
|
# Vite
|
||||||
vite.config.js.timestamp-*
|
vite.config.js.timestamp-*
|
||||||
vite.config.ts.timestamp-*
|
vite.config.ts.timestamp-*
|
||||||
|
|||||||
@@ -1,2 +1 @@
|
|||||||
engine-strict=true
|
engine-strict=true
|
||||||
resolution-mode=highest
|
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
import prettier from 'eslint-config-prettier';
|
||||||
|
import { fileURLToPath } from 'node:url';
|
||||||
|
import { includeIgnoreFile } from '@eslint/compat';
|
||||||
|
import js from '@eslint/js';
|
||||||
|
import svelte from 'eslint-plugin-svelte';
|
||||||
|
import { defineConfig } from 'eslint/config';
|
||||||
|
import globals from 'globals';
|
||||||
|
import ts from 'typescript-eslint';
|
||||||
|
import svelteConfig from './svelte.config.js';
|
||||||
|
|
||||||
|
const gitignorePath = fileURLToPath(new URL('./.gitignore', import.meta.url));
|
||||||
|
|
||||||
|
export default defineConfig(
|
||||||
|
includeIgnoreFile(gitignorePath),
|
||||||
|
js.configs.recommended,
|
||||||
|
...ts.configs.recommended,
|
||||||
|
...svelte.configs.recommended,
|
||||||
|
prettier,
|
||||||
|
...svelte.configs.prettier,
|
||||||
|
{
|
||||||
|
languageOptions: {
|
||||||
|
globals: { ...globals.browser, ...globals.node }
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
// typescript-eslint strongly recommend that you do not use the no-undef lint rule on TypeScript projects.
|
||||||
|
// see: https://typescript-eslint.io/troubleshooting/faqs/eslint/#i-get-errors-from-the-no-undef-rule-about-global-variables-not-being-defined-even-though-there-are-no-typescript-errors
|
||||||
|
'no-undef': 'off'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ['**/*.svelte', '**/*.svelte.ts', '**/*.svelte.js'],
|
||||||
|
languageOptions: {
|
||||||
|
parserOptions: {
|
||||||
|
projectService: true,
|
||||||
|
extraFileExtensions: ['.svelte'],
|
||||||
|
parser: ts.parser,
|
||||||
|
svelteConfig
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
@@ -12,25 +12,25 @@
|
|||||||
"format": "prettier --plugin-search-dir . --write ."
|
"format": "prettier --plugin-search-dir . --write ."
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@sveltejs/adapter-auto": "^6.0.0",
|
"@sveltejs/adapter-auto": "^7.0.0",
|
||||||
"@sveltejs/kit": "^2.20.7",
|
"@sveltejs/kit": "^2.49.1",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.34.0",
|
"@typescript-eslint/eslint-plugin": "^8.48.1",
|
||||||
"@typescript-eslint/parser": "^8.34.0",
|
"@typescript-eslint/parser": "^8.48.1",
|
||||||
"eslint": "^9.24.0",
|
"eslint": "^9.39.1",
|
||||||
"eslint-config-prettier": "^10.1.2",
|
"eslint-config-prettier": "^10.1.8",
|
||||||
"eslint-plugin-svelte": "^3.5.1",
|
"eslint-plugin-svelte": "^3.13.0",
|
||||||
"prettier": "^3.5.3",
|
"prettier": "^3.7.4",
|
||||||
"prettier-plugin-svelte": "^3.3.3",
|
"prettier-plugin-svelte": "^3.4.0",
|
||||||
"svelte": "^5.27.0",
|
"svelte": "^5.45.4",
|
||||||
"svelte-check": "^4.1.6",
|
"svelte-check": "^4.3.4",
|
||||||
"tslib": "^2.8.1",
|
"tslib": "^2.8.1",
|
||||||
"typescript": "^5.8.3",
|
"typescript": "^5.9.3",
|
||||||
"vite": "^6.3.0"
|
"vite": "^7.2.6"
|
||||||
},
|
},
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@dagrejs/dagre": "^1.1.4",
|
"@dagrejs/dagre": "^1.1.8",
|
||||||
"@sveltejs/vite-plugin-svelte": "^5.0.3",
|
"@sveltejs/vite-plugin-svelte": "^6.2.1",
|
||||||
"@xyflow/svelte": "workspace:^"
|
"@xyflow/svelte": "workspace:^"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,6 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<BaseEdge {id} path={edgePath} />
|
<BaseEdge {id} path={edgePath} />
|
||||||
<EdgeToolbar edgeId={id} x={labelX} y={labelY} isVisible>
|
<EdgeToolbar x={labelX} y={labelY} isVisible>
|
||||||
<button onclick={deleteEdge}>Delete</button>
|
<button onclick={deleteEdge}>Delete</button>
|
||||||
</EdgeToolbar>
|
</EdgeToolbar>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
|
|||||||
const config = {
|
const config = {
|
||||||
// Consult https://kit.svelte.dev/docs/integrations#preprocessors
|
// Consult https://kit.svelte.dev/docs/integrations#preprocessors
|
||||||
// for more information about preprocessors
|
// for more information about preprocessors
|
||||||
preprocess: vitePreprocess({ script: true }),
|
preprocess: vitePreprocess(),
|
||||||
compilerOptions: {
|
compilerOptions: {
|
||||||
runes: true
|
runes: true
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -9,7 +9,9 @@
|
|||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"sourceMap": true,
|
"sourceMap": true,
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"target": "ES2022",
|
"target": "esnext",
|
||||||
|
"module": "esnext",
|
||||||
|
"moduleResolution": "bundler",
|
||||||
"verbatimModuleSyntax": true,
|
"verbatimModuleSyntax": true,
|
||||||
"isolatedModules": true
|
"isolatedModules": true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,29 +1,34 @@
|
|||||||
import globals from 'globals';
|
import prettier from 'eslint-config-prettier';
|
||||||
|
import { fileURLToPath } from 'node:url';
|
||||||
|
import { includeIgnoreFile } from '@eslint/compat';
|
||||||
import js from '@eslint/js';
|
import js from '@eslint/js';
|
||||||
import ts from 'typescript-eslint';
|
|
||||||
import svelte from 'eslint-plugin-svelte';
|
import svelte from 'eslint-plugin-svelte';
|
||||||
import eslintConfigPrettier from 'eslint-config-prettier';
|
import { defineConfig } from 'eslint/config';
|
||||||
|
import globals from 'globals';
|
||||||
|
import ts from 'typescript-eslint';
|
||||||
import svelteConfig from './svelte.config.js';
|
import svelteConfig from './svelte.config.js';
|
||||||
|
|
||||||
export default ts.config(
|
const gitignorePath = fileURLToPath(new URL('./.gitignore', import.meta.url));
|
||||||
|
|
||||||
|
export default defineConfig(
|
||||||
|
includeIgnoreFile(gitignorePath),
|
||||||
js.configs.recommended,
|
js.configs.recommended,
|
||||||
...ts.configs.recommended,
|
...ts.configs.recommended,
|
||||||
...svelte.configs.recommended,
|
...svelte.configs.recommended,
|
||||||
eslintConfigPrettier,
|
prettier,
|
||||||
|
...svelte.configs.prettier,
|
||||||
{
|
{
|
||||||
languageOptions: {
|
languageOptions: {
|
||||||
ecmaVersion: 2020,
|
globals: { ...globals.browser, ...globals.node }
|
||||||
sourceType: 'module',
|
},
|
||||||
globals: {
|
rules: {
|
||||||
...globals.browser,
|
// typescript-eslint strongly recommend that you do not use the no-undef lint rule on TypeScript projects.
|
||||||
...globals.es2017,
|
// see: https://typescript-eslint.io/troubleshooting/faqs/eslint/#i-get-errors-from-the-no-undef-rule-about-global-variables-not-being-defined-even-though-there-are-no-typescript-errors
|
||||||
...globals.node
|
'no-undef': 'off'
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
files: ['**/*.svelte', '**/*.svelte.ts', '**/*.svelte.js'],
|
files: ['**/*.svelte', '**/*.svelte.ts', '**/*.svelte.js'],
|
||||||
ignores: ['eslint.config.js', 'svelte.config.js'],
|
|
||||||
languageOptions: {
|
languageOptions: {
|
||||||
parserOptions: {
|
parserOptions: {
|
||||||
projectService: true,
|
projectService: true,
|
||||||
@@ -32,8 +37,5 @@ export default ts.config(
|
|||||||
svelteConfig
|
svelteConfig
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
{
|
|
||||||
ignores: ['.svelte-kit/*', 'build/*', 'dist/*'] // Add common ignore patterns
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -55,35 +55,36 @@
|
|||||||
"@xyflow/system": "workspace:*"
|
"@xyflow/system": "workspace:*"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^9.28.0",
|
"@eslint/compat": "^2.0.0",
|
||||||
"@sveltejs/adapter-auto": "^6.0.1",
|
"@eslint/js": "^9.39.1",
|
||||||
"@sveltejs/kit": "^2.21.4",
|
"@sveltejs/adapter-auto": "^7.0.0",
|
||||||
"@sveltejs/package": "^2.3.11",
|
"@sveltejs/kit": "^2.49.1",
|
||||||
"@sveltejs/vite-plugin-svelte": "^5.1.0",
|
"@sveltejs/package": "^2.5.7",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.34.0",
|
"@sveltejs/vite-plugin-svelte": "^6.2.1",
|
||||||
"@typescript-eslint/parser": "^8.34.0",
|
"@typescript-eslint/eslint-plugin": "^8.48.1",
|
||||||
"autoprefixer": "^10.4.21",
|
"@typescript-eslint/parser": "^8.48.1",
|
||||||
"cssnano": "^7.0.7",
|
"autoprefixer": "^10.4.22",
|
||||||
"dotenv": "^16.5.0",
|
"cssnano": "^7.1.2",
|
||||||
"eslint": "^9.28.0",
|
"dotenv": "^17.2.3",
|
||||||
"eslint-config-prettier": "^10.1.5",
|
"eslint": "^9.39.1",
|
||||||
"eslint-plugin-svelte": "^3.9.2",
|
"eslint-config-prettier": "^10.1.8",
|
||||||
"globals": "^16.2.0",
|
"eslint-plugin-svelte": "^3.13.0",
|
||||||
"postcss": "^8.5.4",
|
"globals": "^16.5.0",
|
||||||
|
"postcss": "^8.5.6",
|
||||||
"postcss-cli": "^11.0.1",
|
"postcss-cli": "^11.0.1",
|
||||||
"postcss-combine-duplicated-selectors": "^10.0.3",
|
"postcss-combine-duplicated-selectors": "^10.0.3",
|
||||||
"postcss-import": "^16.1.0",
|
"postcss-import": "^16.1.1",
|
||||||
"postcss-nested": "^7.0.2",
|
"postcss-nested": "^7.0.2",
|
||||||
"postcss-rename": "^0.8.0",
|
"postcss-rename": "^0.8.0",
|
||||||
"prettier": "^3.5.3",
|
"prettier": "^3.7.4",
|
||||||
"prettier-plugin-svelte": "^3.4.0",
|
"prettier-plugin-svelte": "^3.4.0",
|
||||||
"svelte": "^5.33.19",
|
"svelte": "^5.45.4",
|
||||||
"svelte-check": "^4.2.1",
|
"svelte-check": "^4.3.4",
|
||||||
"svelte-eslint-parser": "^1.2.0",
|
"svelte-eslint-parser": "^1.4.0",
|
||||||
"svelte-preprocess": "^6.0.3",
|
"svelte-preprocess": "^6.0.3",
|
||||||
"tslib": "^2.8.1",
|
"tslib": "^2.8.1",
|
||||||
"typescript": "^5.8.3",
|
"typescript": "^5.9.3",
|
||||||
"typescript-eslint": "^8.34.0"
|
"typescript-eslint": "^8.48.1"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"svelte": "^5.25.0"
|
"svelte": "^5.25.0"
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { getContext } from 'svelte';
|
import { getEdgeIdContext } from '$lib/store/context';
|
||||||
|
|
||||||
import { hideOnSSR, portal } from '$lib/actions/portal';
|
import { hideOnSSR, portal } from '$lib/actions/portal';
|
||||||
import { useStore } from '$lib/store';
|
import { useStore } from '$lib/store';
|
||||||
import type { EdgeLabelProps } from './types';
|
|
||||||
import { toPxString } from '$lib/utils';
|
import { toPxString } from '$lib/utils';
|
||||||
|
|
||||||
|
import type { EdgeLabelProps } from './types';
|
||||||
|
|
||||||
let {
|
let {
|
||||||
x = 0,
|
x = 0,
|
||||||
y = 0,
|
y = 0,
|
||||||
@@ -19,13 +19,12 @@
|
|||||||
}: EdgeLabelProps = $props();
|
}: EdgeLabelProps = $props();
|
||||||
|
|
||||||
const store = useStore();
|
const store = useStore();
|
||||||
const edgeId = getContext<string>('svelteflow__edge_id');
|
|
||||||
|
|
||||||
if (!edgeId) {
|
const edgeId = getEdgeIdContext('EdgeLabel must be used within a Custom Edge component');
|
||||||
throw new Error('EdgeLabel must be used within an edge');
|
|
||||||
}
|
|
||||||
|
|
||||||
let z = $derived(store.visible.edges.get(edgeId)?.zIndex);
|
let z = $derived.by(() => {
|
||||||
|
return store.visible.edges.get(edgeId)?.zIndex;
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
@@ -40,7 +39,7 @@
|
|||||||
style:z-index={z}
|
style:z-index={z}
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
onclick={() => {
|
onclick={() => {
|
||||||
if (selectEdgeOnClick) store.handleEdgeSelection(edgeId);
|
if (selectEdgeOnClick && edgeId) store.handleEdgeSelection(edgeId);
|
||||||
}}
|
}}
|
||||||
{...rest}
|
{...rest}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { useStore } from '$lib/store';
|
import { useStore } from '$lib/store';
|
||||||
|
import { getEdgeIdContext } from '$lib/store/context';
|
||||||
import type { Edge } from '$lib/types';
|
import type { Edge } from '$lib/types';
|
||||||
import { XYHandle, type HandleType, type OnConnectStart } from '@xyflow/system';
|
import { XYHandle, type HandleType, type OnConnectStart } from '@xyflow/system';
|
||||||
import { getContext } from 'svelte';
|
|
||||||
import { EdgeLabel } from '../EdgeLabel';
|
import { EdgeLabel } from '../EdgeLabel';
|
||||||
import type { EdgeReconnectAnchorProps } from './types';
|
import type { EdgeReconnectAnchorProps } from './types';
|
||||||
|
|
||||||
@@ -19,11 +19,9 @@
|
|||||||
|
|
||||||
const store = useStore();
|
const store = useStore();
|
||||||
|
|
||||||
let edgeId: string | undefined = getContext('svelteflow__edge_id');
|
const edgeId = getEdgeIdContext(
|
||||||
|
'EdgeReconnectAnchor must be used within a Custom Edge component'
|
||||||
if (!edgeId) {
|
);
|
||||||
throw new Error('EdgeReconnectAnchor must be used within an Edge component');
|
|
||||||
}
|
|
||||||
|
|
||||||
const onPointerDown = (event: PointerEvent) => {
|
const onPointerDown = (event: PointerEvent) => {
|
||||||
if (event.button !== 0) {
|
if (event.button !== 0) {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<script lang="ts" generics="NodeType extends Node = Node, EdgeType extends Edge = Edge">
|
<script lang="ts" generics="NodeType extends Node = Node, EdgeType extends Edge = Edge">
|
||||||
import { setContext } from 'svelte';
|
|
||||||
|
|
||||||
import { elementSelectionKeys, getMarkerId } from '@xyflow/system';
|
import { elementSelectionKeys, getMarkerId } from '@xyflow/system';
|
||||||
|
|
||||||
|
import { setEdgeIdContext } from '$lib/store/context';
|
||||||
|
|
||||||
import { BezierEdgeInternal } from '$lib/components/edges';
|
import { BezierEdgeInternal } from '$lib/components/edges';
|
||||||
|
|
||||||
import type { Node, EdgeLayouted, Edge, EdgeEvents } from '$lib/types';
|
import type { Node, EdgeLayouted, Edge, EdgeEvents } from '$lib/types';
|
||||||
@@ -22,6 +22,7 @@
|
|||||||
} & EdgeEvents<EdgeType> = $props();
|
} & EdgeEvents<EdgeType> = $props();
|
||||||
|
|
||||||
let {
|
let {
|
||||||
|
id,
|
||||||
source,
|
source,
|
||||||
target,
|
target,
|
||||||
sourceX,
|
sourceX,
|
||||||
@@ -51,12 +52,12 @@
|
|||||||
ariaLabel
|
ariaLabel
|
||||||
} = $derived(edge);
|
} = $derived(edge);
|
||||||
|
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
|
setEdgeIdContext(id);
|
||||||
|
|
||||||
// svelte-ignore non_reactive_update
|
// svelte-ignore non_reactive_update
|
||||||
let edgeRef: SVGGElement | null = null;
|
let edgeRef: SVGGElement | null = null;
|
||||||
|
|
||||||
const { id } = edge;
|
|
||||||
setContext('svelteflow__edge_id', id);
|
|
||||||
|
|
||||||
let selectable = $derived(_selectable ?? store.elementsSelectable);
|
let selectable = $derived(_selectable ?? store.elementsSelectable);
|
||||||
let focusable = $derived(_focusable ?? store.edgesFocusable);
|
let focusable = $derived(_focusable ?? store.edgesFocusable);
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { getContext } from 'svelte';
|
|
||||||
import {
|
import {
|
||||||
Position,
|
Position,
|
||||||
XYHandle,
|
XYHandle,
|
||||||
@@ -16,8 +15,8 @@
|
|||||||
|
|
||||||
import { useStore } from '$lib/store';
|
import { useStore } from '$lib/store';
|
||||||
|
|
||||||
import type { ConnectableContext } from '../NodeWrapper/types';
|
|
||||||
import type { HandleProps } from './types';
|
import type { HandleProps } from './types';
|
||||||
|
import { getNodeConnectableContext, getNodeIdContext } from '$lib/store/context';
|
||||||
|
|
||||||
let {
|
let {
|
||||||
id: handleId = null,
|
id: handleId = null,
|
||||||
@@ -35,8 +34,10 @@
|
|||||||
...rest
|
...rest
|
||||||
}: HandleProps = $props();
|
}: HandleProps = $props();
|
||||||
|
|
||||||
const nodeId = getContext<string>('svelteflow__node_id');
|
const nodeId = getNodeIdContext('Handle must be used within a Custom Node component');
|
||||||
const isConnectableContext = getContext<ConnectableContext>('svelteflow__node_connectable');
|
const isConnectableContext = getNodeConnectableContext(
|
||||||
|
'Handle must be used within a Custom Node component'
|
||||||
|
);
|
||||||
|
|
||||||
let isTarget = $derived(type === 'target');
|
let isTarget = $derived(type === 'target');
|
||||||
let isConnectable = $derived(
|
let isConnectable = $derived(
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script lang="ts" generics="NodeType extends Node = Node, EdgeType extends Edge = Edge">
|
<script lang="ts" generics="NodeType extends Node = Node, EdgeType extends Edge = Edge">
|
||||||
import { setContext, onDestroy } from 'svelte';
|
import { onDestroy } from 'svelte';
|
||||||
import {
|
import {
|
||||||
elementSelectionKeys,
|
elementSelectionKeys,
|
||||||
errorMessages,
|
errorMessages,
|
||||||
@@ -10,13 +10,15 @@
|
|||||||
} from '@xyflow/system';
|
} from '@xyflow/system';
|
||||||
|
|
||||||
import drag from '$lib/actions/drag';
|
import drag from '$lib/actions/drag';
|
||||||
import DefaultNode from '$lib/components/nodes/DefaultNode.svelte';
|
import { setNodeConnectableContext, setNodeIdContext } from '$lib/store/context';
|
||||||
|
|
||||||
import type { ConnectableContext, NodeWrapperProps } from './types';
|
|
||||||
import type { Node, Edge, NodeEvents } from '$lib/types';
|
|
||||||
import { arrowKeyDiffs, toPxString } from '$lib/utils';
|
import { arrowKeyDiffs, toPxString } from '$lib/utils';
|
||||||
import { ARIA_NODE_DESC_KEY } from '../A11yDescriptions';
|
import { ARIA_NODE_DESC_KEY } from '../A11yDescriptions';
|
||||||
|
|
||||||
|
import type { Node, Edge, NodeEvents } from '$lib/types';
|
||||||
|
import type { ConnectableContext, NodeWrapperProps } from './types';
|
||||||
|
|
||||||
|
import DefaultNode from '$lib/components/nodes/DefaultNode.svelte';
|
||||||
|
|
||||||
let {
|
let {
|
||||||
store = $bindable(),
|
store = $bindable(),
|
||||||
node,
|
node,
|
||||||
@@ -95,8 +97,9 @@
|
|||||||
return connectable;
|
return connectable;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
setContext('svelteflow__node_connectable', connectableContext);
|
|
||||||
setContext('svelteflow__node_id', id);
|
setNodeIdContext(id);
|
||||||
|
setNodeConnectableContext(connectableContext);
|
||||||
|
|
||||||
if (process.env.NODE_ENV === 'development') {
|
if (process.env.NODE_ENV === 'development') {
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
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 <SvelteFlowFlow />,
|
|
||||||
* 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 <SvelteFlowFlow />
|
|
||||||
*/
|
|
||||||
export function derivedWarning(functionName: string) {
|
|
||||||
const storeContext = getContext<StoreContext>(key);
|
|
||||||
|
|
||||||
if (!storeContext) {
|
|
||||||
throw new Error(
|
|
||||||
`In order to use ${functionName}() you need to wrap your component in a <SvelteFlowProvider />`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (storeContext.provider && typeof window === 'object' && !$effect.tracking()) {
|
|
||||||
throw new Error(`Use $derived(${functionName}()) to receive updates when values change.`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -8,7 +8,7 @@ import {
|
|||||||
} from '@xyflow/system';
|
} from '@xyflow/system';
|
||||||
|
|
||||||
import { useStore } from '$lib/store';
|
import { useStore } from '$lib/store';
|
||||||
import { getContext } from 'svelte';
|
import { getNodeIdContext } from '$lib/store/context';
|
||||||
|
|
||||||
type UseNodeConnectionsParams = {
|
type UseNodeConnectionsParams = {
|
||||||
id?: string;
|
id?: string;
|
||||||
@@ -42,7 +42,7 @@ export function useNodeConnections({
|
|||||||
}: UseNodeConnectionsParams = {}) {
|
}: UseNodeConnectionsParams = {}) {
|
||||||
const { edges, connectionLookup } = $derived(useStore());
|
const { edges, connectionLookup } = $derived(useStore());
|
||||||
|
|
||||||
const contextNodeId = getContext<string>('svelteflow__node_id');
|
const contextNodeId = getNodeIdContext();
|
||||||
const nodeId = id ?? contextNodeId;
|
const nodeId = id ?? contextNodeId;
|
||||||
|
|
||||||
let connectionMaps: { previous: ConnectionMap; next: ConnectionMap } = {
|
let connectionMaps: { previous: ConnectionMap; next: ConnectionMap } = {
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import { getContext } from 'svelte';
|
|||||||
import type { StoreContext, SvelteFlowStore } from '../store/types';
|
import type { StoreContext, SvelteFlowStore } from '../store/types';
|
||||||
|
|
||||||
import { key } from '../store';
|
import { key } from '../store';
|
||||||
import { derivedWarning } from './derivedWarning.svelte';
|
|
||||||
import type { Node, Edge } from '$lib/types';
|
import type { Node, Edge } from '$lib/types';
|
||||||
|
|
||||||
export function useStore<
|
export function useStore<
|
||||||
@@ -17,9 +16,5 @@ export function useStore<
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (process.env.NODE_ENV === 'development') {
|
|
||||||
derivedWarning('useStore');
|
|
||||||
}
|
|
||||||
|
|
||||||
return storeContext.getStore();
|
return storeContext.getStore();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/* eslint-disable svelte/prefer-svelte-reactivity */
|
/* eslint-disable svelte/prefer-svelte-reactivity */
|
||||||
import { useStore } from '$lib/store';
|
import { useStore } from '$lib/store';
|
||||||
import { getContext } from 'svelte';
|
import { getNodeIdContext } from '$lib/store/context';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When you programmatically add or remove handles to a node or update a node's
|
* When you programmatically add or remove handles to a node or update a node's
|
||||||
@@ -14,7 +14,7 @@ import { getContext } from 'svelte';
|
|||||||
*/
|
*/
|
||||||
export function useUpdateNodeInternals(): (nodeId?: string | string[]) => void {
|
export function useUpdateNodeInternals(): (nodeId?: string | string[]) => void {
|
||||||
const { domNode, updateNodeInternals } = $derived(useStore());
|
const { domNode, updateNodeInternals } = $derived(useStore());
|
||||||
const nodeId = getContext('svelteflow__node_id') as string | undefined;
|
const nodeId = getNodeIdContext();
|
||||||
|
|
||||||
// @todo: do we want to add this to system?
|
// @todo: do we want to add this to system?
|
||||||
const updateInternals = (id?: string | string[]) => {
|
const updateInternals = (id?: string | string[]) => {
|
||||||
|
|||||||
@@ -2,9 +2,10 @@
|
|||||||
import { getEdgeToolbarTransform } from '@xyflow/system';
|
import { getEdgeToolbarTransform } from '@xyflow/system';
|
||||||
|
|
||||||
import { useStore } from '$lib/store';
|
import { useStore } from '$lib/store';
|
||||||
|
import { getEdgeIdContext } from '$lib/store/context';
|
||||||
|
|
||||||
import { EdgeLabel } from '$lib/components/EdgeLabel';
|
import { EdgeLabel } from '$lib/components/EdgeLabel';
|
||||||
import type { EdgeToolbarProps } from './types';
|
import type { EdgeToolbarProps } from './types';
|
||||||
import { getContext } from 'svelte';
|
|
||||||
|
|
||||||
let {
|
let {
|
||||||
x,
|
x,
|
||||||
@@ -19,11 +20,8 @@
|
|||||||
}: EdgeToolbarProps = $props();
|
}: EdgeToolbarProps = $props();
|
||||||
|
|
||||||
const store = useStore();
|
const store = useStore();
|
||||||
const edgeId = getContext<string>('svelteflow__edge_id');
|
|
||||||
|
|
||||||
if (!edgeId) {
|
const edgeId = getEdgeIdContext('EdgeToolbar must be used within an edge');
|
||||||
throw new Error('EdgeToolbar must be used within an edge');
|
|
||||||
}
|
|
||||||
|
|
||||||
const isActive = $derived(
|
const isActive = $derived(
|
||||||
typeof isVisible === 'boolean' ? isVisible : store.edgeLookup.get(edgeId)?.selected
|
typeof isVisible === 'boolean' ? isVisible : store.edgeLookup.get(edgeId)?.selected
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { getContext, onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
|
|
||||||
import { useStore } from '$lib/store';
|
import { useStore } from '$lib/store';
|
||||||
|
import { getNodeIdContext } from '$lib/store/context';
|
||||||
import {
|
import {
|
||||||
XYResizer,
|
XYResizer,
|
||||||
ResizeControlVariant,
|
ResizeControlVariant,
|
||||||
@@ -9,8 +11,8 @@
|
|||||||
type XYResizerChange,
|
type XYResizerChange,
|
||||||
type XYResizerChildChange
|
type XYResizerChildChange
|
||||||
} from '@xyflow/system';
|
} from '@xyflow/system';
|
||||||
|
|
||||||
import type { ResizeControlProps } from './types';
|
import type { ResizeControlProps } from './types';
|
||||||
import type { Node } from '$lib/types';
|
|
||||||
|
|
||||||
let {
|
let {
|
||||||
nodeId,
|
nodeId,
|
||||||
@@ -34,10 +36,14 @@
|
|||||||
}: ResizeControlProps = $props();
|
}: ResizeControlProps = $props();
|
||||||
|
|
||||||
const store = useStore();
|
const store = useStore();
|
||||||
|
const contextNodeId = getNodeIdContext();
|
||||||
|
|
||||||
let id = $derived(
|
let id = $derived(typeof nodeId === 'string' ? nodeId : contextNodeId);
|
||||||
typeof nodeId === 'string' ? nodeId : getContext<string>('svelteflow__node_id')
|
|
||||||
);
|
// svelte-ignore state_referenced_locally
|
||||||
|
if (!id) {
|
||||||
|
throw new Error('Either pass a nodeId or use within a Custom Node component');
|
||||||
|
}
|
||||||
|
|
||||||
let resizeControlRef: HTMLDivElement;
|
let resizeControlRef: HTMLDivElement;
|
||||||
let resizer: XYResizerInstance | null = $state(null);
|
let resizer: XYResizerInstance | null = $state(null);
|
||||||
@@ -72,7 +78,7 @@
|
|||||||
changes.set(id, change);
|
changes.set(id, change);
|
||||||
|
|
||||||
for (const childChange of childChanges) {
|
for (const childChange of childChanges) {
|
||||||
changes.set(childChange.id, {x: childChange.position.x, y: childChange.position.y });
|
changes.set(childChange.id, { x: childChange.position.x, y: childChange.position.y });
|
||||||
}
|
}
|
||||||
|
|
||||||
store.nodes = store.nodes.map((node) => {
|
store.nodes = store.nodes.map((node) => {
|
||||||
@@ -81,7 +87,7 @@
|
|||||||
const vertical = !resizeDirection || resizeDirection === 'vertical';
|
const vertical = !resizeDirection || resizeDirection === 'vertical';
|
||||||
|
|
||||||
if (change) {
|
if (change) {
|
||||||
return {
|
return {
|
||||||
...node,
|
...node,
|
||||||
position: {
|
position: {
|
||||||
x: horizontal ? (change.x ?? node.position.x) : node.position.x,
|
x: horizontal ? (change.x ?? node.position.x) : node.position.x,
|
||||||
|
|||||||
@@ -53,7 +53,6 @@ export type NodeResizerProps = {
|
|||||||
|
|
||||||
export type ResizeControlProps = Pick<
|
export type ResizeControlProps = Pick<
|
||||||
NodeResizerProps,
|
NodeResizerProps,
|
||||||
| 'nodeId'
|
|
||||||
| 'color'
|
| 'color'
|
||||||
| 'minWidth'
|
| 'minWidth'
|
||||||
| 'minHeight'
|
| 'minHeight'
|
||||||
@@ -76,5 +75,7 @@ export type ResizeControlProps = Pick<
|
|||||||
* @example ResizeControlVariant.Handle, ResizeControlVariant.Line
|
* @example ResizeControlVariant.Handle, ResizeControlVariant.Line
|
||||||
*/
|
*/
|
||||||
variant?: ResizeControlVariant;
|
variant?: ResizeControlVariant;
|
||||||
|
/** nodeId must be provided when used outside a custom node */
|
||||||
|
nodeId?: string;
|
||||||
children?: Snippet;
|
children?: Snippet;
|
||||||
} & HTMLAttributes<HTMLDivElement>;
|
} & HTMLAttributes<HTMLDivElement>;
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { getContext } from 'svelte';
|
|
||||||
import { Position, getNodeToolbarTransform } from '@xyflow/system';
|
import { Position, getNodeToolbarTransform } from '@xyflow/system';
|
||||||
|
|
||||||
import { hideOnSSR, portal } from '$lib/actions/portal';
|
|
||||||
import { useStore } from '$lib/store';
|
import { useStore } from '$lib/store';
|
||||||
|
import { getNodeIdContext } from '$lib/store/context';
|
||||||
|
import { hideOnSSR, portal } from '$lib/actions/portal';
|
||||||
import { useSvelteFlow } from '$lib/hooks/useSvelteFlow.svelte';
|
import { useSvelteFlow } from '$lib/hooks/useSvelteFlow.svelte';
|
||||||
|
|
||||||
import type { InternalNode } from '$lib/types';
|
import type { InternalNode } from '$lib/types';
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
const store = useStore();
|
const store = useStore();
|
||||||
|
|
||||||
const { getNodesBounds } = useSvelteFlow();
|
const { getNodesBounds } = useSvelteFlow();
|
||||||
const contextNodeId = getContext<string>('svelteflow__node_id');
|
const contextNodeId = getNodeIdContext();
|
||||||
|
|
||||||
let toolbarNodes: InternalNode[] = $derived.by(() => {
|
let toolbarNodes: InternalNode[] = $derived.by(() => {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
||||||
@@ -30,6 +30,9 @@
|
|||||||
const nodeIds = Array.isArray(nodeId) ? nodeId : [nodeId ?? contextNodeId];
|
const nodeIds = Array.isArray(nodeId) ? nodeId : [nodeId ?? contextNodeId];
|
||||||
|
|
||||||
return nodeIds.reduce<InternalNode[]>((res, nodeId) => {
|
return nodeIds.reduce<InternalNode[]>((res, nodeId) => {
|
||||||
|
if (!nodeId) {
|
||||||
|
throw new Error('Either pass a nodeId or use within a Custom Node component');
|
||||||
|
}
|
||||||
const node = store.nodeLookup.get(nodeId);
|
const node = store.nodeLookup.get(nodeId);
|
||||||
|
|
||||||
if (node) {
|
if (node) {
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import type { ConnectableContext } from '$lib/components/NodeWrapper/types';
|
||||||
|
import { setContext, getContext, hasContext } from 'svelte';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a type-safe context getter and setter pair.
|
||||||
|
* Extended from Svelte's official createContext pattern.
|
||||||
|
* - When called with an error message string, it throws if the context is not set
|
||||||
|
* - When called without arguments, it returns the context value or undefined
|
||||||
|
*/
|
||||||
|
function createContext<T>(): [
|
||||||
|
{
|
||||||
|
(errorMessage: string): T;
|
||||||
|
(): T | undefined;
|
||||||
|
},
|
||||||
|
(context: T) => T
|
||||||
|
] {
|
||||||
|
const key = {};
|
||||||
|
|
||||||
|
return [
|
||||||
|
(errorMessage?: string) => {
|
||||||
|
if (errorMessage && !hasContext(key)) {
|
||||||
|
throw new Error(errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
return getContext(key);
|
||||||
|
},
|
||||||
|
(context) => setContext(key, context)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const [getNodeIdContext, setNodeIdContext] = createContext<string>();
|
||||||
|
export const [getNodeConnectableContext, setNodeConnectableContext] =
|
||||||
|
createContext<ConnectableContext>();
|
||||||
|
|
||||||
|
export const [getEdgeIdContext, setEdgeIdContext] = createContext<string>();
|
||||||
@@ -36,8 +36,10 @@ export interface EdgeLayoutBaseOptions<NodeType extends Node = Node, EdgeType ex
|
|||||||
onerror: OnError;
|
onerror: OnError;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface EdgeLayoutAllOptions<NodeType extends Node = Node, EdgeType extends Edge = Edge>
|
export interface EdgeLayoutAllOptions<
|
||||||
extends EdgeLayoutBaseOptions<NodeType, EdgeType> {
|
NodeType extends Node = Node,
|
||||||
|
EdgeType extends Edge = Edge
|
||||||
|
> extends EdgeLayoutBaseOptions<NodeType, EdgeType> {
|
||||||
onlyRenderVisible: never;
|
onlyRenderVisible: never;
|
||||||
visibleNodes: never;
|
visibleNodes: never;
|
||||||
transform: never;
|
transform: never;
|
||||||
|
|||||||
Generated
+1075
-978
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user