Compare commits

..

1 Commits

Author SHA1 Message Date
8e2616dfe1 feat: init vite 2022-12-19 22:21:22 +08:00
50 changed files with 18533 additions and 7993 deletions

View File

@ -13,7 +13,6 @@ Indomi <indomi126@gmail.com>
James <san>
Jógvan <lse>
Ken <ei>
Kent <oo>
Limbo <49612796+JUST-Limbo@users.noreply.github.com>
Lixu <37231473+wflixu@users.noreply.github.com>
Lloyd <ho>
@ -80,7 +79,6 @@ zdc1111 <39116292+zdc1111@users.noreply.github.com>
€alix <qq287649920@gmail.com>
九思⚡⚡⚡ <2228429150@qq.com>
何腾飞 <avrin.live.cn@outlook.com>
依枫 <deng25st@163.com>
偏右 <afc163@gmail.com>
小耀 <jinyue.gjy@antfin.com>
崖 <bubkoo.wy@gmail.com>

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 12 MiB

After

Width:  |  Height:  |  Size: 12 MiB

View File

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2021-2023 Alipay.inc
Copyright (c) 2021-2022 Alipay.inc
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

24
examples/vite-example/.gitignore vendored Normal file
View File

@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

View File

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/antv.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>s</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/index.tsx"></script>
</body>
</html>

View File

@ -0,0 +1,30 @@
{
"name": "@antv/vite-project",
"private": true,
"version": "2.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"prebuild": "rimraf dist",
"preview": "vite preview"
},
"dependencies": {
"lodash-es": "^4.17.15",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-helmet": "^6.0.0",
"react-router": "^6.5.0",
"react-router-dom": "^6.5.0"
},
"devDependencies": {
"@types/lodash-es": "^4.17.4",
"@types/react": "^18.0.26",
"@types/react-dom": "^18.0.9",
"@types/react-helmet": "^6.0.0",
"@vitejs/plugin-react-swc": "^3.0.0",
"less": "^4.1.1",
"typescript": "^4.9.3",
"vite": "^4.0.0"
}
}

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="28"><defs><linearGradient id="a" x1=".004%" x2="100.131%" y1="49.993%" y2="49.993%"><stop offset="0%" stop-color="#6500FF"/><stop offset="16%" stop-color="#6A09FF"/><stop offset="43%" stop-color="#7623FF"/><stop offset="77%" stop-color="#8A4CFF"/><stop offset="99%" stop-color="#996BFF"/></linearGradient><linearGradient id="b" x1="50.004%" x2="50.004%" y1="100.012%" y2="0%"><stop offset="0%" stop-color="#6500FF"/><stop offset="16%" stop-color="#6909FF"/><stop offset="43%" stop-color="#7523FF"/><stop offset="77%" stop-color="#894CFF"/><stop offset="99%" stop-color="#976BFF"/></linearGradient><linearGradient id="c" x1="49.854%" x2="49.854%" y1="100.255%" y2="0%"><stop offset="0%" stop-color="#FF6E06"/><stop offset="28%" stop-color="#FF770C"/><stop offset="75%" stop-color="#FF911C"/><stop offset="100%" stop-color="#FFA126"/></linearGradient><linearGradient id="d" x1="57351%" x2="57351%" y1="59860%" y2="35023%"><stop offset="0%" stop-color="#FF6E06"/><stop offset="28%" stop-color="#FF770C"/><stop offset="75%" stop-color="#FF911C"/><stop offset="100%" stop-color="#FFA126"/></linearGradient></defs><g fill="none" fill-rule="nonzero"><path fill="url(#a)" d="M1.955.004c-.029 0-.057 0-.088.002a1.41 1.41 0 0 0-.197.017A2.312 2.312 0 0 0 1.4.08a3.479 3.479 0 0 0-.179.061 3.768 3.768 0 0 0-.075.032l-.029.013-.057.028-.031.016L.98.257.95.275.91.298.883.317.825.355.79.381l-.03.022L.728.43.7.453C.68.468.662.483.645.5L.621.521.588.553l-.02.02A1.898 1.898 0 0 0 .46.691L.446.709.414.75A2.707 2.707 0 0 0 .348.84L.337.857.308.9l-.01.017L.29.931l3.784 2.214.801.855a.406.406 0 0 1-.019-.42.446.446 0 0 1 .37-.227H18.12c.045.003.09.005.137.005.961-.014 1.734-.758 1.737-1.674.002-.916-.765-1.664-1.726-1.683L1.955.004Z" transform="translate(.006)"/><path fill="url(#b)" d="m22.737 11.315-6.392 11.01a.438.438 0 0 1-.74.025l-4.063-6.995a1.724 1.724 0 0 0-.117-.23L4.667 3.459a2.32 2.32 0 0 1-.005-2.248A2.345 2.345 0 0 1 6.57.005L1.981.001A1.899 1.899 0 0 0 .908.294l-.1.068a2.405 2.405 0 0 0-.15.12A1.7 1.7 0 0 0 .462.69a1.797 1.797 0 0 0-.17.243 1.767 1.767 0 0 0-.028 1.9l.166.289 13.874 23.895a1.882 1.882 0 0 0 .793.776l.108.05a2.537 2.537 0 0 0 .18.069 2.14 2.14 0 0 0 .28.066 2.148 2.148 0 0 0 .19.02l.106.002c.04 0 .063 0 .086-.002a1.206 1.206 0 0 0 .19-.016 2.065 2.065 0 0 0 .27-.06 1.927 1.927 0 0 0 .168-.06l.089-.038.07-.036c.058-.03.073-.04.088-.048a2.596 2.596 0 0 0 .207-.142 1.851 1.851 0 0 0 .464-.537l.376-.64.081-.14 7.68-13.225a1.716 1.716 0 0 0 .047-1.75 1.74 1.74 0 0 0-3.039.01Z" transform="translate(.006)"/><path fill="url(#c)" d="M30.137 0h-6.459c-.935.018-1.684.847-1.684 1.863s.75 1.845 1.684 1.864c.046 0 .09-.003.134-.006h2.925c.214.032.374.23.374.465a.501.501 0 0 1-.025.157l-1.542 2.9c-.454.904-.147 2.036.685 2.53.832.492 1.874.159 2.328-.745l3.19-6c.029-.055.056-.111.08-.17l.003-.007c.015-.036.03-.072.043-.11l.002-.005.017-.052.008-.027c.008-.024.015-.048.021-.073l.012-.042.01-.04a2.826 2.826 0 0 0 .033-.191 2.31 2.31 0 0 0 .01-.077v-.011l.005-.09v-.022l.003-.091c0-1.115-.83-2.019-1.857-2.02Z" transform="translate(.006)"/><path fill="url(#d)" d="M19.881 7.41a.84.84 0 0 0-.725-.41h-6.325a.84.84 0 0 0-.725.41.804.804 0 0 0 0 .82l3.163 5.36a.84.84 0 0 0 .725.41.84.84 0 0 0 .725-.41l3.162-5.36a.804.804 0 0 0 0-.82Z" transform="translate(.006)"/></g></svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@ -0,0 +1,72 @@
:root {
color: rgb(255 255 255 / 87%);
font-weight: 400;
font-size: 16px;
font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
line-height: 24px;
background-color: #242424;
color-scheme: light dark;
font-synthesis: none;
text-rendering: optimizelegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-size-adjust: 100%;
}
a {
color: #646cff;
font-weight: 500;
text-decoration: inherit;
}
a:hover {
color: #535bf2;
}
body {
margin: 0;
}
html,
body,
#root {
height: 100%;
}
button {
padding: 0.6em 1.2em;
font-weight: 500;
font-size: 1em;
font-family: inherit;
background-color: #1a1a1a;
border: 1px solid transparent;
border-radius: 8px;
cursor: pointer;
transition: border-color 0.25s;
}
button:hover {
border-color: #646cff;
}
button:focus,
button:focus-visible {
outline: 4px auto -webkit-focus-ring-color;
}
@media (prefers-color-scheme: light) {
:root {
color: #213547;
background-color: #fff;
}
a:hover {
color: #747bff;
}
button {
background-color: #f9f9f9;
}
}

View File

@ -0,0 +1,14 @@
import React from 'react'
import ReactDOM from 'react-dom/client'
import { createBrowserRouter, RouterProvider } from 'react-router-dom'
import { getRoutes } from './routes'
import './index.less'
const routes = getRoutes()
const router = createBrowserRouter(routes)
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
<React.StrictMode>
<RouterProvider router={router} />
</React.StrictMode>,
)

View File

@ -0,0 +1,43 @@
.wrap {
position: relative;
display: flex;
flex-direction: row;
height: 100%;
}
.nav {
display: flex;
flex-direction: column;
width: 220px;
padding: 0 16px;
border-right: 1px solid #e9e9e9;
section {
flex: 1;
overflow-x: hidden;
overflow-y: auto;
> ul {
margin: 0;
padding: 0;
}
ul {
padding-left: 16px;
li {
line-height: 1.6;
list-style: none;
}
}
}
}
.content {
flex: 1;
box-sizing: border-box;
padding: 24px;
overflow-x: hidden;
overflow-y: auto;
background: #fff;
}

View File

@ -0,0 +1,57 @@
import React from 'react'
import { RouteObject } from 'react-router'
import { Outlet, NavLink } from 'react-router-dom'
import style from './_layout.module.less'
function renderLink(route: RouteObject, parentPath: string) {
let children = route.children
let indexRoute: RouteObject | null = null
if (children) {
children = children.slice()
const idx = children.findIndex((r) => r.index)
if (idx >= 0) {
indexRoute = children[idx]
children.splice(idx, 1)
}
}
const routeName = route.path
const routePath = `${parentPath}/${routeName}`
const element = indexRoute ? indexRoute.element : route.element
return (
<React.Fragment key={routePath}>
<li>
{element ? (
<NavLink
to={routePath}
className={({ isActive }) => (isActive ? 'active' : undefined)}
>
{routeName}
</NavLink>
) : (
routeName
)}
</li>
{children && <li>{renderLinks(children, routePath)}</li>}
</React.Fragment>
)
}
function renderLinks(routes: RouteObject[], parentPath = '') {
return <ul>{routes.map((route) => renderLink(route, parentPath))}</ul>
}
export default function Layout(props: { routes: RouteObject[] }) {
return (
<div className={style.wrap}>
<div className={style.nav}>
<h2>Examples</h2>
<section>{renderLinks(props.routes)}</section>
</div>
<div className={style.content}>
<Outlet />
</div>
</div>
)
}

View File

@ -0,0 +1,5 @@
import React from 'react'
export default function transition() {
return <div>transition</div>
}

View File

@ -0,0 +1,5 @@
import React from 'react'
export default function transition() {
return <div>auto-size</div>
}

View File

@ -0,0 +1,87 @@
import React from 'react'
import { set } from 'lodash-es'
import { RouteObject } from 'react-router'
import { Helmet } from 'react-helmet'
function parseRouteConfig() {
const modules = import.meta.glob(`/src/pages/**/*.tsx`)
const config: Record<string, any> = {}
Object.keys(modules).forEach((filePath) => {
const routeParts = filePath
.replace(/^\/src\/pages\//, '') // 去除 /src/pages
.replace(/.tsx$/, '') // 去除文件名后缀
.replace(/\[([\w-]+)]/, ':$1') // 转换动态路由 [foo].tsx => :foo
.split('/')
set(config, routeParts, modules[filePath])
})
return config
}
function wrapSuspense(
mod: () => Promise<{
default: React.ComponentType<{ routes?: RouteObject[] }>
}>,
routes?: RouteObject[],
title?: string,
) {
if (!mod) {
return undefined
}
const Component = React.lazy(mod)
// 结合 Suspense ,这里可以自定义 loading 组件
return (
<React.Suspense fallback={null}>
{title && (
<Helmet>
<title>{title}</title>
</Helmet>
)}
{title ? <Component /> : <Component routes={routes} />}
</React.Suspense>
)
}
function wrapLayout(
routePath: string,
routeConfig: Record<string, any>,
): RouteObject {
const { _layout, ...rest } = routeConfig
const routes = routeConfigToRoute(rest, routePath)
return {
path: routePath,
element: wrapSuspense(_layout, routes),
children: routes,
}
}
function routeConfigToRoute(
config: Record<string, any>,
parentPath: string,
): RouteObject[] {
return Object.entries(config).map(([routePath, child]) => {
// () => import() 语法判断
if (typeof child === 'function') {
// 等于 index 则映射为当前根路由
const isIndex = routePath === 'index'
return {
index: isIndex,
path: isIndex ? undefined : routePath,
// 转换为组件
element: wrapSuspense(
child,
undefined,
isIndex ? parentPath : `${parentPath}/${routePath}`,
),
}
}
// 否则为目录,则查找下一层级
return wrapLayout(routePath, child)
})
}
export function getRoutes(): RouteObject[] {
const routeConfig = parseRouteConfig()
return [wrapLayout('/', routeConfig)]
}

View File

@ -0,0 +1 @@
/// <reference types="vite/client" />

View File

@ -0,0 +1,21 @@
{
"compilerOptions": {
"target": "ESNext",
"useDefineForClassFields": true,
"lib": ["DOM", "DOM.Iterable", "ESNext"],
"allowJs": false,
"skipLibCheck": true,
"esModuleInterop": false,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "ESNext",
"moduleResolution": "Node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx"
},
"include": ["src"],
"references": [{ "path": "./tsconfig.node.json" }]
}

View File

@ -0,0 +1,9 @@
{
"compilerOptions": {
"composite": true,
"module": "ESNext",
"moduleResolution": "Node",
"allowSyntheticDefaultImports": true
},
"include": ["vite.config.ts"]
}

View File

@ -0,0 +1,7 @@
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react-swc'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
})

View File

@ -2,7 +2,7 @@
"name": "x6",
"private": true,
"scripts": {
"preinstall": "npx only-allow pnpm",
"preinstall": "node ./scripts/preinstall",
"lint:ts": "eslint '**/src/**/*.{js,ts}?(x)' --cache --fix",
"lint:style": "stylelint '**/src/**/*.less' --customSyntax postcss-less --cache --fix",
"lint": "run-s lint:ts lint:style",
@ -51,7 +51,7 @@
"prettier --write --ignore-unknown"
],
"*.less": [
"stylelint --syntax less --fix"
"stylelint --custom-syntax postcss-less --fix"
],
"*.js": [
"prettier --write"
@ -137,7 +137,6 @@
"lodash": "^4.17.21",
"npm-run-all": "^4.1.5",
"postcss": "^8.4.20",
"postcss-less": "^6.0.0",
"prettier": "^2.8.0",
"pretty-quick": "^3.1.1",
"rimraf": "^3.0.2",
@ -162,6 +161,7 @@
"typescript": "^4.9.3"
},
"devDependencies": {
"@rollup/plugin-terser": "^0.2.0"
"@rollup/plugin-terser": "^0.2.0",
"postcss-less": "^6.0.0"
}
}

View File

@ -1,10 +1,3 @@
## @antv/x6-common [2.0.4](https://github.com/antvis/x6/compare/@antv/x6-common@2.0.3...@antv/x6-common@2.0.4) (2022-12-20)
### Bug Fixes
* fix window incompatibility problem ([#3070](https://github.com/antvis/x6/issues/3070)) ([d8e1e63](https://github.com/antvis/x6/commit/d8e1e637d8027b9494cd26efc87815d74bd51366))
## @antv/x6-common [2.0.1](https://github.com/antvis/x6/compare/@antv/x6-common@2.0.0...@antv/x6-common@2.0.1) (2022-11-25)

View File

@ -1,6 +1,6 @@
{
"name": "@antv/x6-common",
"version": "2.0.4",
"version": "2.0.3",
"description": "Basic toolkit for X6",
"main": "lib/index.js",
"module": "es/index.js",

View File

@ -1,10 +1,6 @@
// compatible with NodeList.prototype.forEach() before chrome 51
// https://developer.mozilla.org/en-US/docs/Web/API/NodeList/forEach
if (
typeof window === 'object' &&
window.NodeList &&
!NodeList.prototype.forEach
) {
if (window.NodeList && !NodeList.prototype.forEach) {
NodeList.prototype.forEach = Array.prototype.forEach as any
}

View File

@ -1,10 +1,3 @@
## @antv/x6-plugin-transform [2.1.5](https://github.com/antvis/x6/compare/@antv/x6-plugin-transform@2.1.4...@antv/x6-plugin-transform@2.1.5) (2022-12-24)
### Bug Fixes
* add defense for view in transform plugin ([#3092](https://github.com/antvis/x6/issues/3092)) ([fb8098c](https://github.com/antvis/x6/commit/fb8098c1c06440dd69f4e93881fd36f7e6de2a56))
## @antv/x6-plugin-transform [2.1.4](https://github.com/antvis/x6/compare/@antv/x6-plugin-transform@2.1.3...@antv/x6-plugin-transform@2.1.4) (2022-12-07)

View File

@ -1,6 +1,6 @@
{
"name": "@antv/x6-plugin-transform",
"version": "2.1.5",
"version": "2.1.4",
"description": "transform plugin for X6",
"main": "lib/index.js",
"module": "es/index.js",

View File

@ -107,11 +107,7 @@ export class TransformImpl extends View<TransformImpl.EventArgs> {
render() {
this.renderHandles()
if (this.view) {
this.view.addClass(Private.NODE_CLS)
}
this.view.addClass(Private.NODE_CLS)
Dom.addClass(this.container, this.containerClassName)
Dom.toggleClass(
this.container,
@ -157,9 +153,7 @@ export class TransformImpl extends View<TransformImpl.EventArgs> {
}
remove() {
if (this.view) {
this.view.removeClass(Private.NODE_CLS)
}
this.view.removeClass(Private.NODE_CLS)
return super.remove()
}

View File

@ -26,7 +26,10 @@ export class ReactShapeView extends NodeView<ReactShape> {
const node = this.cell
if (container) {
const elem = React.createElement(Wrap, { node, graph: this.graph })
const graph = node.model ? node.model.graph : null
// Actually in the dnd plugin, this graph is empty,
// in order to make the external perception type of graph is a graph, rather than graph | null, so hack this.
const elem = React.createElement(Wrap, { node, graph: graph! })
if (Portal.isActive()) {
const portal = createPortal(elem, container) as ReactPortal
Portal.connect(this.cell.id, portal)

View File

@ -1,10 +1,3 @@
## @antv/x6-vue-shape [2.0.9](https://github.com/antvis/x6/compare/@antv/x6-vue-shape@2.0.8...@antv/x6-vue-shape@2.0.9) (2022-12-21)
### Bug Fixes
* get graph from right place ([#3078](https://github.com/antvis/x6/issues/3078)) ([844ee5f](https://github.com/antvis/x6/commit/844ee5fa043cbcd788ec1693f88576e797426228))
## @antv/x6-vue-shape [2.0.7](https://github.com/antvis/x6/compare/@antv/x6-vue-shape@2.0.6...@antv/x6-vue-shape@2.0.7) (2022-12-09)

View File

@ -1,6 +1,6 @@
{
"name": "@antv/x6-vue-shape",
"version": "2.0.9",
"version": "2.0.7",
"description": "X6 shape for rendering vue components.",
"main": "lib/index.js",
"module": "es/index.js",

View File

@ -1,5 +1,4 @@
import { defineComponent, h, reactive, isVue3, Vue } from 'vue-demi'
import { Graph } from '@antv/x6'
import { VueShape } from './node'
let active = false
@ -10,7 +9,6 @@ export function connect(
component: any,
container: HTMLDivElement,
node: VueShape,
graph: Graph,
) {
if (active) {
const { Teleport, markRaw } = Vue as any
@ -19,7 +17,6 @@ export function connect(
render: () => h(Teleport, { to: container } as any, [h(component)]),
provide: () => ({
getNode: () => node,
getGraph: () => graph,
}),
}),
)

View File

@ -26,7 +26,6 @@ export class VueShapeView extends NodeView<VueShape> {
this.unmountVueComponent()
const root = this.getComponentContainer()
const node = this.cell
const graph = this.graph
if (root) {
const { component } = shapeMaps[node.shape]
@ -41,13 +40,12 @@ export class VueShapeView extends NodeView<VueShape> {
provide() {
return {
getNode: () => node,
getGraph: () => graph,
}
},
})
} else if (isVue3) {
if (isActive()) {
connect(this.targetId(), component, root, node, graph)
connect(this.targetId(), component, root, node)
} else {
this.vm = createApp({
render() {
@ -56,7 +54,6 @@ export class VueShapeView extends NodeView<VueShape> {
provide() {
return {
getNode: () => node,
getGraph: () => graph,
}
},
})

View File

@ -1,18 +1,3 @@
## @antv/x6 [2.1.3](https://github.com/antvis/x6/compare/@antv/x6@2.1.2...@antv/x6@2.1.3) (2022-12-24)
### Bug Fixes
* schedule edge when source and target is not ready ([#3090](https://github.com/antvis/x6/issues/3090)) ([019333d](https://github.com/antvis/x6/commit/019333d79d7f22c44c400f29d501497f4323af1a))
## @antv/x6 [2.1.1](https://github.com/antvis/x6/compare/@antv/x6@2.1.0...@antv/x6@2.1.1) (2022-12-19)
### Bug Fixes
* arrowhead not get options bug ([#3065](https://github.com/antvis/x6/issues/3065)) ([3b668fe](https://github.com/antvis/x6/commit/3b668feb4eac47994f52d0cc977d22a8a2c06acd))
* set snapToGrid to false by default ([#3066](https://github.com/antvis/x6/issues/3066)) ([e2bb71d](https://github.com/antvis/x6/commit/e2bb71d95484b29187fafca97f1a386e9b984095))
# @antv/x6 [2.1.0](https://github.com/antvis/x6/compare/@antv/x6@2.0.9...@antv/x6@2.1.0) (2022-12-19)

View File

@ -1,6 +1,6 @@
{
"name": "@antv/x6",
"version": "2.1.3",
"version": "2.1.0",
"description": "JavaScript diagramming library that uses SVG and HTML for rendering",
"main": "lib/index.js",
"module": "es/index.js",

View File

@ -55,8 +55,6 @@ export namespace Options {
onPortRendered?: (args: OnPortRenderedArgs) => void
onEdgeLabelRendered?: (args: OnEdgeLabelRenderedArgs) => void
createCellView?: (this: Graph, cell: Cell) => CellView | null | undefined
}
export interface ManualBooleans {

View File

@ -265,32 +265,6 @@ function findRoute(
return null
}
function snap(vertices: Point[], gridSize = 10) {
if (vertices.length <= 1) {
return vertices
}
for (let i = 0, len = vertices.length; i < len - 1; i += 1) {
const first = vertices[i]
const second = vertices[i + 1]
if (first.x === second.x) {
const x = gridSize * Math.round(first.x / gridSize)
if (first.x !== x) {
first.x = x
second.x = x
}
} else if (first.y === second.y) {
const y = gridSize * Math.round(first.y / gridSize)
if (first.y !== y) {
first.y = y
second.y = y
}
}
}
return vertices
}
export const router: Router.Definition<ManhattanRouterOptions> = function (
vertices,
optionsRaw,
@ -308,7 +282,7 @@ export const router: Router.Definition<ManhattanRouterOptions> = function (
)
const oldVertices = vertices.map((p) => Point.create(p))
const newVertices: Point[] = []
let newVertices: Point[] = []
// The origin of first route's grid, does not need snapping
let tailPoint = sourceEndpoint
@ -377,7 +351,10 @@ export const router: Router.Definition<ManhattanRouterOptions> = function (
}
if (options.snapToGrid) {
return snap(newVertices, edgeView.graph.grid.getGridSize())
newVertices = newVertices.map((vertice) => {
const gridSize = edgeView.graph.grid.getGridSize()
return vertice.snapToGrid(gridSize)
})
}
return newVertices

View File

@ -74,7 +74,6 @@ class Arrowhead extends ToolsView.ToolItem<EdgeView, Arrowhead.Options> {
x: coords.x,
y: coords.y,
options: {
...this.options,
toolId: this.cid,
},
})

View File

@ -1,4 +1,4 @@
import { KeyValue, Dom, Disposable, FunctionExt } from '@antv/x6-common'
import { KeyValue, Dom, Disposable } from '@antv/x6-common'
import { Rectangle } from '@antv/x6-geometry'
import { Model, Cell } from '../model'
import { View, CellView, NodeView, EdgeView } from '../view'
@ -181,15 +181,13 @@ export class Scheduler extends Disposable {
}
}
if (viewItem) {
this.requestViewUpdate(
viewItem.view,
flag,
options,
cell.isNode() ? JOB_PRIORITY.RenderNode : JOB_PRIORITY.RenderEdge,
false,
)
}
this.requestViewUpdate(
viewItem.view,
flag,
options,
cell.isNode() ? JOB_PRIORITY.RenderNode : JOB_PRIORITY.RenderEdge,
false,
)
})
this.flush()
@ -219,18 +217,6 @@ export class Scheduler extends Disposable {
if (result) {
console.log('left flag', result) // eslint-disable-line
if (
cell.isEdge() &&
(result & view.getFlag(['source', 'target'])) === 0
) {
this.queue.queueJob({
id,
priority: JOB_PRIORITY.RenderEdge,
cb: () => {
this.updateView(view, flag, options)
},
})
}
}
}
@ -389,22 +375,15 @@ export class Scheduler extends Disposable {
}
protected createCellView(cell: Cell) {
const createViewHook = this.graph.options.createCellView
if (createViewHook) {
const ret = FunctionExt.call(createViewHook, this.graph, cell)
if (ret || ret === null) {
return ret
}
}
const view = cell.view
const options = { graph: this.graph }
const view = cell.view
if (view != null && typeof view === 'string') {
const def = CellView.registry.get(view)
if (def) {
return new def(cell, options) // eslint-disable-line new-cap
}
return CellView.registry.onNotFound(view)
}

View File

@ -847,6 +847,7 @@ export class NodeView<
) {
this.graph.model.startBatch('add-edge')
const edgeView = this.createEdgeFromMagnet(magnet, x, y)
edgeView.notifyMouseDown(e, x, y) // backwards compatibility events
edgeView.setEventData(
e,
edgeView.prepareArrowheadDragging('target', {
@ -857,7 +858,6 @@ export class NodeView<
}),
)
this.setEventData<Partial<EventData.Magnet>>(e, { edgeView })
edgeView.notifyMouseDown(e, x, y)
}
protected getDefaultEdge(sourceView: CellView, sourceMagnet: Element) {

24572
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

8
scripts/preinstall Executable file
View File

@ -0,0 +1,8 @@
#!/usr/bin/env node
if (!/pnpm/.test(process.env.npm_execpath || '')) {
console.warn(
`This repository requires using pnpm as the package manager for scripts to work properly.`,
)
process.exit(1)
}

View File

@ -1,19 +1,18 @@
if (window) {
;(window as any).react = require('react')
;(window as any).reactDom = require('react-dom')
;(window as any).antd = require('antd')
;(window as any).dagre = require('dagre')
;(window as any).x6 = require('@antv/x6')
;(window as any).x6PluginSnapline = require('@antv/x6-plugin-snapline')
;(window as any).x6PluginClipboard = require('@antv/x6-plugin-clipboard')
;(window as any).x6PluginKeyboard = require('@antv/x6-plugin-keyboard')
;(window as any).x6PluginSelection = require('@antv/x6-plugin-selection')
;(window as any).x6PluginTransform = require('@antv/x6-plugin-transform')
;(window as any).x6PluginStencil = require('@antv/x6-plugin-stencil')
;(window as any).x6PluginHistory = require('@antv/x6-plugin-history')
;(window as any).x6ReactShape = require('@antv/x6-react-shape')
;(window as any).layout = require('@antv/layout')
;(window as any).classnames = require('classnames')
;(window as any).hierarchy = require('@antv/hierarchy')
;(window as any).elkjs = require('elkjs/lib/elk.bundled.js')
}
(window as any).react = require('react');
(window as any).reactDom = require('react-dom');
(window as any).antd = require('antd');
(window as any).dagre = require('dagre');
(window as any).x6 = require('@antv/x6');
(window as any).x6PluginSnapline = require('@antv/x6-plugin-snapline');
(window as any).x6PluginClipboard = require('@antv/x6-plugin-clipboard');
(window as any).x6PluginKeyboard = require('@antv/x6-plugin-keyboard');
(window as any).x6PluginSelection = require('@antv/x6-plugin-selection');
(window as any).x6PluginTransform = require('@antv/x6-plugin-transform');
(window as any).x6PluginStencil = require('@antv/x6-plugin-stencil');
(window as any).x6PluginHistory = require('@antv/x6-plugin-history');
(window as any).x6ReactShape = require('@antv/x6-react-shape');
(window as any).layout = require('@antv/layout');
(window as any).hierarchy = require('@antv/hierarchy');
(window as any).elkjs = require('elkjs/lib/elk.bundled.js');
}

View File

@ -1,17 +1,3 @@
# @antv/x6-sites [1.3.0](https://github.com/antvis/X6/compare/@antv/x6-sites@1.2.2...@antv/x6-sites@1.3.0) (2023-01-01)
### Features
* add data processing dag example ([#3091](https://github.com/antvis/X6/issues/3091)) ([d3301d3](https://github.com/antvis/X6/commit/d3301d33d575269d9219ab1337a2ec1785d61494))
## @antv/x6-sites [1.2.2](https://github.com/antvis/X6/compare/@antv/x6-sites@1.2.1...@antv/x6-sites@1.2.2) (2022-12-24)
### Bug Fixes
* schedule edge when source and target is not ready ([#3090](https://github.com/antvis/X6/issues/3090)) ([019333d](https://github.com/antvis/X6/commit/019333d79d7f22c44c400f29d501497f4323af1a))
# @antv/x6-sites [1.2.0](https://github.com/antvis/X6/compare/@antv/x6-sites@1.1.2...@antv/x6-sites@1.2.0) (2022-12-16)

View File

@ -13,29 +13,26 @@ redirect_from:
new Graph(options: Options)
```
| 选项 | 类型 | 必选 | 描述 | 默认值 |
| ------------------------------------------------------------------------------ | ------------------------------ | :--: | ------------------------------------------------------------------------------------------------- | ------------------------------------------------- |
| containers | HTMLElement | ✓ | 画布的容器。 | |
| width | number | | 画布宽度,默认使用容器宽度。 | - |
| height | number | | 画布高度,默认使用容器高度。 | - |
| scaling | { min?: number, max?: number } | | 画布的最小最大缩放级别。 | { min: 0.01, max: 16 } |
| [autoResize](/zh/docs/tutorial/basic/graph#画布大小) | boolean \| Element \| Document | | 是否监听容器大小改变,并自动更新画布大小。 | `false` |
| [panning](/zh/docs/api/graph/panning) | boolean \| `PanningManager.Options` | | 画布是否可以拖拽平移,默认禁用。 | `false` |
| [mousewheel](/zh/docs/api/graph/mousewheel) | boolean \| `MouseWheel.Options` | | 鼠标滚轮缩放,默认禁用| `false` |
| [grid](/zh/docs/api/graph/grid) | boolean \| number \| `GridManager.Options` | | 网格,默认使用 `10px` 的网格,但不绘制网格背景。 | `false` |
| [background](/zh/docs/api/graph/background) | false \| `BackgroundManager.Options` | | 背景,默认不绘制背景| `false` |
| [translating](/zh/docs/api/interacting/interaction#trasnlating) | `Translating.Options` | | 限制节点移动。 | { restrict: false } |
| [embedding](/zh/docs/api/interacting/interaction#embedding) | boolean \| `Embedding.Options` | | 嵌套节点,默认禁用| `false` |
| [connecting](/zh/docs/api/interacting/interaction#connecting) | `Connecting.Options` | | 连线选项。 | { snap: false, ... } |
| [highlighting](/zh/docs/api/interacting/interaction#highlighting) | `Highlighting.Options` | | 高亮选项。 | {...} |
| [interacting](/zh/docs/api/interacting/interaction#interacting) | `Interacting.Options` | | 定制节点和边的交互行为。 | { edgeLabelMovable: false } |
| [magnetThreshold](/zh/docs/api/graph/view#magnetthreshold) | number \| 'onleave' | | 鼠标移动多少次后才触发连线,或者设置为 `'onleave'` 时表示鼠标移出元素时才触发连线。 | `0` |
| [moveThreshold](/zh/docs/api/graph/view#movethreshold) | number | | 触发 `'mousemove'` 事件之前,允许鼠标移动的次数。 | `0` |
| [clickThreshold](/zh/docs/api/graph/view#clickthreshold) | number | | 当鼠标移动次数超过指定的数字时,将不触发鼠标点击事件| `0` |
| [preventDefaultContextMenu](/zh/docs/api/graph/view#preventdefaultcontextmenu) | boolean | | 是否禁用浏览器默认右键菜单。 | `true` |
| [preventDefaultBlankAction](/zh/docs/api/graph/view#preventdefaultblankaction) | boolean | | 在画布空白位置响应鼠标事件时,是否禁用鼠标默认行为。 | `true` |
| [async](/zh/docs/api/graph/view#async) | boolean | | 是否异步渲染 | `true` |
| [virtual](/zh/docs/api/graph/view#virtual) | boolean | | 是否只渲染可视区域内容 | `false` |
| [onPortRendered](/zh/docs/api/graph/view#onportrendered) | (args: OnPortRenderedArgs) => void | | 当某个连接桩渲染完成时触发的回调。 | - |
| [onEdgeLabelRendered](/zh/docs/api/graph/view#onedgelabelrendered) | (args: OnEdgeLabelRenderedArgs) => void | | 当边的文本标签渲染完成时触发的回调。 | - |
| [createCellView](/zh/docs/api/graph/view#createcellview) | (this: Graph, cell: Cell) => CellView \| null \| undefined | | 是自定义元素的视图。 | - |
| 选项 | 类型 | 必选 | 默认值 | 描述 |
| ------------------------------------------------------------------------------ | ------------------------------ | :--: | ------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
| [container](#container) | HTMLElement | ✓ | | 画布的容器。 |
| [width](#width) | number | | `undefined` | 画布宽度,默认使用容器宽度。 |
| [height](#height) | number | | `undefined` | 画布高度,默认使用容器高度。 |
| [autoResize](#autoresize) | boolean \| Element \| Document | | `false` | 是否监听容器大小改变,并自动更新画布大小。默认监听画布容器,也可以指定监听的元素,如 `Document` |
| [panning](/zh/docs/api/graph/panning) | object | | { enabled: false, eventTypes: ['leftMouseDown'],} | 画布是否可以拖动 |
| [grid](/zh/docs/api/graph/grid) | boolean \| number \| object | | `false` | 网格,默认使用 `10px` 的网格,但不绘制网格背景。 |
| [background](/zh/docs/api/graph/background) | false \| object | | `false` | 背景,默认不绘制背景 |
| [mousewheel](/zh/docs/api/graph/mousewheel) | boolean \| object | | `false` | 鼠标滚轮缩放,默认禁用。 |
| [translating](/zh/docs/api/graph/transform#translating) | object | | object | 平移节点 |
| [embedding](/zh/docs/api/graph/interaction#embedding) | boolean \| object | | `false` | 嵌套节点,默认禁用。 |
| [connecting](/zh/docs/api/graph/interaction#connecting) | object | | object | 连线选项 |
| [highlighting](/zh/docs/api/graph/interaction#highlighting) | object | | object | 高亮选项。 |
| [interacting](/zh/docs/api/graph/interaction#interacting) | object \| function | | `{ edgeLabelMovable: false }` | 定制节点和边的交互行为。 |
| [magnetThreshold](/zh/docs/api/graph/view#magnetThreshold) | number \| 'onleave' | | `0` | 鼠标移动多少次后才触发连线,或者设置为 `'onleave'` 时表示鼠标移出元素时才触发连线。 |
| [moveThreshold](/zh/docs/api/graph/view#moveThreshold) | number | | `0` | 触发 `'mousemove'` 事件之前,允许鼠标移动的次数。 |
| [clickThreshold](/zh/docs/api/graph/view#clickThreshold) | number | | `0` | 当鼠标移动次数超过指定的数字时,将不触发鼠标点击事件。 |
| [preventDefaultContextMenu](/zh/docs/api/graph/view#preventDefaultContextMenu) | boolean | | `true` | 是否禁用浏览器默认右键菜单 |
| [preventDefaultBlankAction](/zh/docs/api/graph/view#preventDefaultBlankAction) | boolean | | `true` | 在画布空白位置响应鼠标事件时,是否禁用鼠标默认行为。 |
| [guard](/zh/docs/api/graph/view#guard) | function | | `() => false` | 返回是否应该忽略某个鼠标事件,返回 `true` 时忽略指定的鼠标事件。 |
| [async](/zh/docs/api/graph/view#async) | boolean | | `true` | 是否异步渲染 |
| [virtual](/zh/docs/api/graph/view#virtual) | boolean | | `false` | 是否只渲染可视区域内容 |

View File

@ -10,14 +10,10 @@ redirect_from:
### async
是否是异步渲染的画布。异步渲染不会阻塞 UI对需要添加大量节点和边时的性能提升非常明显。但需要注意的是一些同步操作可能会出现意外结果比如获取某个节点的视图、获取节点/边的包围盒等,因为这些同步操作触发时异步渲染可能并没有完成。
是否是异步渲染的画布。异步渲染不会阻塞 UI对需要添加大量节点和边时的性能提升非常明显。但需要注意的是一些同步操作可能会出现意外结果比如获取某个节点的视图、获取节点/边的包围盒等,因为这些同步操作触发时异步渲染可能并没有完成,此时只能通过监听 `render:done` 事件来确保所有变更都已经生效,然后在事件回调中进行这些操作
<!-- <iframe src="/demos/api/graph/async"></iframe> -->
### virtual
是否只渲染可视区域内的元素,默认为 `false`,如果设置为 `true`,首屏加载只会渲染当前可视区域内的元素,当拖动画布或者缩放画布时,会根据画布窗口大小自动加载剩余元素。在元素数量很大的场景,性能提升非常明显。
### magnetThreshold
鼠标移动多少次后才触发连线,或者设置为 'onleave' 时表示鼠标移出元素时才触发连线,默认为 `0`
@ -38,6 +34,14 @@ redirect_from:
在画布空白位置响应鼠标事件时,是否禁用鼠标默认行为,默认为 `true`
### guard
```sign
(e: Dom.EventObject, view?: CellView | null) => boolean
```
是否应该忽略某个鼠标事件,返回 `true` 时忽略指定的鼠标事件,否则不忽略。
### onPortRendered
```sign
@ -56,7 +60,9 @@ redirect_from:
) => void
```
当某个连接桩渲染完成时触发的回调,参数如下:
当某个连接桩渲染完成时触发的回调
<span class="tag-param">args 参数<span>
| 名称 | 类型 | 非空 | 描述 |
| ---------------- | ---------------- | :--: | ---------------------------------------- |
@ -69,6 +75,7 @@ redirect_from:
| contentContainer | Element | ✓ | 连接桩内容的容器元素。 |
| contentSelectors | Markup.Selectors | | 连接桩内容 Markup 渲染后的选择器键值对。 |
<span class="tag-example">使用<span>
例如,我们可以渲染一个 React 类型的连接桩。
@ -90,7 +97,7 @@ const graph = new Graph({
});
```
<!-- <iframe src="/demos/tutorial/advanced/react/react-port"></iframe> -->
<iframe src="/demos/tutorial/advanced/react/react-port"></iframe>
### onEdgeLabelRendered
@ -106,8 +113,9 @@ const graph = new Graph({
) => void
```
当边的文本标签渲染完成时触发的回调,参数如下:
当边的文本标签渲染完成时触发的回调
<span class="tag-param">args 参数<span>
| 名称 | 类型 | 非空 | 描述 |
| --------- | ---------------- | :--: | -------------------------------------- |
@ -116,6 +124,7 @@ const graph = new Graph({
| container | Element | ✓ | 文本标签容器。 |
| selectors | Markup.Selectors | ✓ | 文本标签 Markup 渲染后的选择器键值对。 |
<span class="tag-example">使用<span>
例如,我们可以在标签上渲染任何想要的元素。
@ -155,7 +164,7 @@ const graph = new Graph({
});
```
<!-- <iframe src="/demos/tutorial/advanced/react/react-label-base"></iframe> -->
<iframe src="/demos/tutorial/advanced/react/react-label-base"></iframe>
我们也可以在定义 Label 的 Markup 时添加 `<foreignObject>` 元素来支持 HTML 和 React 的渲染能力。
@ -176,21 +185,72 @@ const graph = new Graph({
});
```
<!-- <iframe src="/demos/tutorial/advanced/react/react-label-markup"></iframe> -->
<iframe src="/demos/tutorial/advanced/react/react-label-markup"></iframe>
### createCellView
### onToolItemCreated
```sign
(
this: Graph,
cell: Cell,
) => CellView | null | undefined
args: {
name: string
cell: Cell
view: CellView
tool: View
},
) => void
```
自定义元素的视图,可以返回一个 `CellView`,会替换默认的视图,如果返回 `null`,则不会渲染,如果返回 `undefined`,会按照默认方式渲染。
当工具项渲染完成时触发的回调。
<span class="tag-param">args 参数<span>
| 名称 | 类型 | 非空 | 描述 |
| ---- | -------- | :--: | ------------- |
| cell | Cell | ✓ | 节点/边实例。 |
| view | CellView | ✓ | 节点/边视图。 |
| name | string | ✓ | 工具项名称。 |
| tool | View | ✓ | 工具视图。 |
<span class="tag-example">使用<span>
例如,我们为 `vertices` 工具设置间隔填充效果。
```ts
const graph = new Graph({
container: this.container,
grid: true,
onToolItemCreated({ name, cell, tool }) {
if (name === "vertices" && cell === edge2) {
const options = (tool as any).options;
if (options && options.index % 2 === 1) {
tool.setAttrs({ fill: "red" });
}
}
},
});
```
<iframe src="/demos/api/registry/edge-tool/vertices"></iframe>
## 方法
### isAsync()
```sign
isAsync(): boolean
```
返回画布是否是异步渲染模式。异步渲染不会阻塞 UI对需要添加大量节点和边时的性能提升非常明显异步画布的使用细节请参考 [`async`](#async) 选项。
### isFrozen()
```sign
isFrozen(): boolean
```
返回[异步画布](#async)是否处于冻结状态。处于冻结状态的画布不会立即响应画布中节点和边的变更,直到调用 [`unfreeze(...)`](#unfreeze) 方法来解除冻结并重新渲染画布。
### findView(...)
```sign

View File

@ -72,7 +72,7 @@ export interface Connecting {
### snap
`snap` 设置为 `true` 时连线的过程中距离节点或者连接桩 `50px` 时会触发自动吸附,可以通过配置 `radius` 属性自定义触发吸附的距离。当 `snap` 设置为 `false` 时不会触发自动吸附。默认值为 `false`
`snap` 设置为 `true` 时连线的过程中距离节点或者连接桩 `50px` 时会触发自动吸附,可以通过配置 `radius` 属性自定义触发吸附的距离。当 `snap` 设置为 `false` 时不会触发自动吸附。默认值为 `false`
```ts
const graph = new Graph({
@ -92,35 +92,35 @@ const graph = new Graph({
### allowBlank
是否允许连接到画布空白位置的点,默认为 `true`
是否允许连接到画布空白位置的点,默认为 `true`
### allowMulti
是否允许在相同的起始节点和终止之间创建多条边,默认为 `true` 。当设置为 `false` 时,在起始和终止节点之间只允许创建一条边,当设置为 `'withPort'` 时,在起始和终止节点的相同连接桩之间只允许创建一条边(即,起始和终止节点之间可以创建多条边,但必须要要链接在不同的连接桩上)。
是否允许在相同的起始节点和终止之间创建多条边,默认为 `true`。当设置为 `false` 时,在起始和终止节点之间只允许创建一条边,当设置为 `'withPort'` 时,在起始和终止节点的相同连接桩之间只允许创建一条边(即,起始和终止节点之间可以创建多条边,但必须要要链接在不同的连接桩上)。
### allowLoop
是否允许创建循环连线,即边的起始节点和终止节点为同一节点,默认为 `true`
是否允许创建循环连线,即边的起始节点和终止节点为同一节点,默认为 `true`
### allowNode
是否允许边链接到节点(非节点上的连接桩),默认为 `true`
是否允许边链接到节点(非节点上的连接桩),默认为 `true`
### allowEdge
是否允许边链接到另一个边,默认为 `true`
是否允许边链接到另一个边,默认为 `true`
### allowPort
是否允许边链接到连接桩,默认为 `true`
是否允许边链接到连接桩,默认为 `true`
### highlight
拖动边时,是否高亮显示所有可用的连接桩或节点,默认值为 `false`
拖动边时,是否高亮显示所有可用的连接桩或节点,默认值为 `false`
### anchor
当连接到节点时,通过 [ `anchor` ](/zh/docs/api/registry/node-anchor) 来指定被连接的节点的锚点,默认值为 `center`
当连接到节点时,通过 [`anchor`](/zh/docs/api/registry/node-anchor) 来指定被连接的节点的锚点,默认值为 `center`
#### sourceAnchor
@ -132,7 +132,7 @@ const graph = new Graph({
### edgeAnchor
当连接到边时,通过 [ `edgeAnchor` ](/zh/docs/api/registry/edge-anchor) 来指定被连接的边的锚点,默认值为 `ratio`
当连接到边时,通过 [`edgeAnchor`](/zh/docs/api/registry/edge-anchor) 来指定被连接的边的锚点,默认值为 `ratio`
### sourceEdgeAnchor
@ -144,7 +144,7 @@ const graph = new Graph({
### connectionPoint
指定[连接点](/zh/docs/api/registry/connector),默认值为 `boundary`
指定[连接点](/zh/docs/api/registry/connector),默认值为 `boundary`
### sourceConnectionPoint
@ -156,11 +156,11 @@ const graph = new Graph({
#### router
[路由](/zh/docs/api/registry/router)将边的路径点 `vertices` 做进一步转换处理,并在必要时添加额外的点,然后返回处理后的点,默认值为 `normal`
[路由](/zh/docs/api/registry/router)将边的路径点 `vertices` 做进一步转换处理,并在必要时添加额外的点,然后返回处理后的点,默认值为 `normal`
### connector
[连接器](/zh/docs/api/registry/connector)将起点、路由返回的点、终点加工为 <path> 元素的 d 属性,决定了边渲染到画布后的样式,默认值为 `normal`
[连接器](/zh/docs/api/registry/connector)将起点、路由返回的点、终点加工为 <path> 元素的 d 属性,决定了边渲染到画布后的样式,默认值为 `normal`
### createEdge
@ -168,7 +168,7 @@ const graph = new Graph({
### validateMagnet
点击 `magnet` 时 根据 `validateMagnet` 返回值来判断是否新增边,触发时机是 `magnet` 被按下,如果返回 `false` ,则没有任何反应,如果返回 `true` ,会在当前 `magnet` 创建一条新的边。
点击 `magnet` 时 根据 `validateMagnet` 返回值来判断是否新增边,触发时机是 `magnet` 被按下,如果返回 `false`,则没有任何反应,如果返回 `true`,会在当前 `magnet` 创建一条新的边。
```ts
validateMagnet({ e, magnet, view, cell }) {
@ -178,7 +178,7 @@ validateMagnet({ e, magnet, view, cell }) {
### validateConnection
在移动边的时候判断连接是否有效,如果返回 `false` ,当鼠标放开的时候,不会连接到当前元素,否则会连接到当前元素。
在移动边的时候判断连接是否有效,如果返回 `false`,当鼠标放开的时候,不会连接到当前元素,否则会连接到当前元素。
```ts
validateConnection({
@ -200,7 +200,7 @@ validateConnection({
### validateEdge
当停止拖动边的时候根据 `validateEdge` 返回值来判断边是否生效,如果返回 `false` , 该边会被清除。
当停止拖动边的时候根据 `validateEdge` 返回值来判断边是否生效,如果返回 `false`, 该边会被清除。
```ts
validateEdge({ edge, type, previous }) {
@ -240,19 +240,19 @@ export interface Embedding {
### enabled
是否允许节点之间嵌套,默认值为 `false`
是否允许节点之间嵌套,默认值为 `false`
### findParent
在节点被移动时通过 `findParent` 指定的方法返回父节点。默认值为 `bbox`
在节点被移动时通过 `findParent` 指定的方法返回父节点。默认值为 `bbox`
### frontOnly
如果 `frontOnly``true` ,则只能嵌入显示在最前面的节点,默认值为 `true`
如果 `frontOnly``true`,则只能嵌入显示在最前面的节点,默认值为 `true`
### validate
`validate` 为判断节点能否被嵌入父节点的函数,默认返回 `true`
`validate` 为判断节点能否被嵌入父节点的函数,默认返回 `true`
## interacting
@ -283,17 +283,17 @@ export type Interacting =
| ((this: Graph, cellView: CellView) => InteractionMap | boolean);
```
* `boolean` 节点或边是否可交互
* `InteractionMap` 节点或边的交互细节,支持以下属性:
+ `'nodeMovable'` 节点是否可以被移动。
+ `'magnetConnectable'` 当在具有 `'magnet'` 属性的元素上按下鼠标开始拖动时,是否触发连线交互。
+ `'edgeMovable'` 边是否可以被移动。
+ `'edgeLabelMovable'` 边的标签是否可以被移动。
+ `'arrowheadMovable'` 边的起始/终止箭头是否可以被移动。
+ `'vertexMovable'` 边的路径点是否可以被移动。
+ `'vertexAddable'` 是否可以添加边的路径点。
+ `'vertexDeletable'` 边的路径点是否可以被删除。
* `(this: Graph, cellView: CellView) => InteractionMap | boolean`
- `boolean` 节点或边是否可交互
- `InteractionMap` 节点或边的交互细节,支持以下属性:
- `'nodeMovable'` 节点是否可以被移动。
- `'magnetConnectable'` 当在具有 `'magnet'` 属性的元素上按下鼠标开始拖动时,是否触发连线交互。
- `'edgeMovable'` 边是否可以被移动。
- `'edgeLabelMovable'` 边的标签是否可以被移动。
- `'arrowheadMovable'` 边的起始/终止箭头是否可以被移动。
- `'vertexMovable'` 边的路径点是否可以被移动。
- `'vertexAddable'` 是否可以添加边的路径点。
- `'vertexDeletable'` 边的路径点是否可以被删除。
- `(this: Graph, cellView: CellView) => InteractionMap | boolean`
```ts
const graph = new Graph({
@ -334,41 +334,10 @@ new Graph({
支持的 `highlighting` 配置项有:
* `'default'` 默认高亮选项,当以下几种高亮配置缺省时被使用。
* `'embedding'` 拖动节点进行嵌入操作过程中,节点可以被嵌入时被使用。
* `'nodeAvailable'` 连线过程中,节点可以被链接时被使用。
* `'magnetAvailable'` 连线过程中,连接桩可以被链接时被使用。
* `'magnetAdsorbed'` 连线过程中,自动吸附到连接桩时被使用。
- `'default'` 默认高亮选项,当以下几种高亮配置缺省时被使用。
- `'embedding'` 拖动节点进行嵌入操作过程中,节点可以被嵌入时被使用。
- `'nodeAvailable'` 连线过程中,节点可以被链接时被使用。
- `'magnetAvailable'` 连线过程中,连接桩可以被链接时被使用。
- `'magnetAdsorbed'` 连线过程中,自动吸附到连接桩时被使用。
上面 `magnetAvailable.name` 其实是高亮器的名称X6 内置了 `stroke``className` 两种高亮器,详细信息参考 [Highlighter](/zh/docs/api/registry/highlighter)
## trasnlating
可以在全局配置 `translating` 来限制节点的移动范围。
```ts
const graph = new Graph({
translating: {
restrict: true,
},
})
```
### restrict
节点的可移动范围。支持以下两种方式:
* `boolean` 如果设置为 `true`, 节点不能移动超出画布区域
* `Rectangle.RectangleLike | (arg: CellView) => Rectangle.RectangleLike` 指定一个节点的移动范围
```ts
const graph = new Graph({
translating: {
restrict: {
x: 0,
y: 0,
width: 100,
height: 100,
}
}
})

View File

@ -11,9 +11,9 @@ X6 是基于 HTML 和 SVG 的图编辑引擎,提供低成本的定制能力和
如果你还没有使用过 X6 建议通过 [快速上手](/zh/docs/tutorial/getting-started) 抢先体验 X6 的魅力。
<p align="left">
<a href="https://github.com/antvis/X6/actions/workflows/ci.yml"><img alt="build" src="https://img.shields.io/github/actions/workflow/status/antvis/x6/ci.yml?branch=master&logo=github&style=flat-square"></a>
<!-- <a href="https://app.codecov.io/gh/antvis/X6"><img alt="coverage" src="https://img.shields.io/codecov/c/gh/antvis/x6?logo=codecov&style=flat-square&token=15CO54WYUV"></a>
<a href="https://lgtm.com/projects/g/antvis/x6/context:javascript"><img alt="Language grade: JavaScript" src="https://img.shields.io/lgtm/grade/javascript/g/antvis/x6.svg?logo=lgtm&style=flat-square"></a> -->
<a href="https://github.com/antvis/X6/actions/workflows/ci.yml"><img alt="build" src="https://img.shields.io/github/workflow/status/antvis/x6/%F0%9F%91%B7%E3%80%80CI/master?logo=github&style=flat-square"></a>
<a href="https://app.codecov.io/gh/antvis/X6"><img alt="coverage" src="https://img.shields.io/codecov/c/gh/antvis/x6?logo=codecov&style=flat-square&token=15CO54WYUV"></a>
<a href="https://lgtm.com/projects/g/antvis/x6/context:javascript"><img alt="Language grade: JavaScript" src="https://img.shields.io/lgtm/grade/javascript/g/antvis/x6.svg?logo=lgtm&style=flat-square"></a>
<a href="https://www.npmjs.com/package/@antv/x6"><img alt="NPM Package" src="https://img.shields.io/npm/v/@antv/x6.svg?style=flat-square"></a>
<a href="https://www.npmjs.com/package/@antv/x6"><img alt="NPM Downloads" src="https://img.shields.io/npm/dm/@antv/x6?logo=npm&style=flat-square"></a>
</p>

View File

@ -1,869 +0,0 @@
import React from 'react'
import { Graph, Node, Path, Edge, Platform, StringExt } from '@antv/x6'
import { Selection } from '@antv/x6-plugin-selection'
import classnames from 'classnames'
import insertCss from 'insert-css'
import { register } from '@antv/x6-react-shape'
import { Tooltip, Dropdown } from 'antd'
// 节点类型
enum NodeType {
INPUT = 'INPUT', // 数据输入
FILTER = 'FILTER', // 数据过滤
JOIN = 'JOIN', // 数据连接
UNION = 'UNION', // 数据合并
AGG = 'AGG', // 数据聚合
OUTPUT = 'OUTPUT', // 数据输出
}
// 元素校验状态
enum CellStatus {
DEFAULT = 'default',
SUCCESS = 'success',
ERROR = 'error',
}
// 节点位置信息
interface Position {
x: number
y: number
}
// 加工类型列表
const PROCESSING_TYPE_LIST = [
{
type: 'FILTER',
name: '数据筛选',
},
{
type: 'JOIN',
name: '数据连接',
},
{
type: 'UNION',
name: '数据合并',
},
{
type: 'AGG',
name: '数据聚合',
},
{
type: 'OUTPUT',
name: '数据输出',
},
]
// 不同节点类型的icon
const NODE_TYPE_LOGO = {
INPUT:
'https://mdn.alipayobjects.com/huamei_f4t1bn/afts/img/A*RXnuTpQ22xkAAAAAAAAAAAAADtOHAQ/original', // 数据输入
FILTER:
'https://mdn.alipayobjects.com/huamei_f4t1bn/afts/img/A*ZJ6qToit8P4AAAAAAAAAAAAADtOHAQ/original', // 数据筛选
JOIN: 'https://mdn.alipayobjects.com/huamei_f4t1bn/afts/img/A*EHqyQoDeBvIAAAAAAAAAAAAADtOHAQ/original', // 数据连接
UNION:
'https://mdn.alipayobjects.com/huamei_f4t1bn/afts/img/A*k4eyRaXv8gsAAAAAAAAAAAAADtOHAQ/original', // 数据合并
AGG: 'https://mdn.alipayobjects.com/huamei_f4t1bn/afts/img/A*TKG8R6nfYiAAAAAAAAAAAAAADtOHAQ/original', // 数据聚合
OUTPUT:
'https://mdn.alipayobjects.com/huamei_f4t1bn/afts/img/A*zUgORbGg1HIAAAAAAAAAAAAADtOHAQ/original', // 数据输出
}
/**
* 根据起点初始下游节点的位置信息
* @param node 起始节点
* @param graph
* @returns
*/
const getDownstreamNodePosition = (
node: Node,
graph: Graph,
dx = 250,
dy = 100,
) => {
// 找出画布中以该起始节点为起点的相关边的终点id集合
const downstreamNodeIdList: string[] = []
graph.getEdges().forEach((edge) => {
const originEdge = edge.toJSON()?.data
if (originEdge.source === node.id) {
downstreamNodeIdList.push(originEdge.target)
}
})
// 获取起点的位置信息
const position = node.getPosition()
let minX = Infinity
let maxY = -Infinity
graph.getNodes().forEach((graphNode) => {
if (downstreamNodeIdList.indexOf(graphNode.id) > -1) {
const nodePosition = graphNode.getPosition()
// 找到所有节点中最左侧的节点的x坐标
if (nodePosition.x < minX) {
minX = nodePosition.x
}
// 找到所有节点中最x下方的节点的y坐标
if (nodePosition.y > maxY) {
maxY = nodePosition.y
}
}
})
return {
x: minX !== Infinity ? minX : position.x + dx,
y: maxY !== -Infinity ? maxY + dy : position.y,
}
}
// 根据节点的类型获取ports
const getPortsByType = (type: NodeType, nodeId: string) => {
let ports = []
switch (type) {
case NodeType.INPUT:
ports = [
{
id: `${nodeId}-out`,
group: 'out',
},
]
break
case NodeType.OUTPUT:
ports = [
{
id: `${nodeId}-in`,
group: 'in',
},
]
break
default:
ports = [
{
id: `${nodeId}-in`,
group: 'in',
},
{
id: `${nodeId}-out`,
group: 'out',
},
]
break
}
return ports
}
/**
* 创建节点并添加到画布
* @param type 节点类型
* @param graph
* @param position 节点位置
* @returns
*/
export const createNode = (
type: NodeType,
graph: Graph,
position?: Position,
) => {
if (!graph) {
return {}
}
let newNode = {}
const sameTypeNodes = graph
.getNodes()
.filter((item) => item.getData()?.type === type)
const typeName = PROCESSING_TYPE_LIST?.find(
(item) => item.type === type,
)?.name
const id = StringExt.uuid()
const node = {
id,
shape: 'data-processing-dag-node',
x: position?.x,
y: position?.y,
ports: getPortsByType(type, id),
data: {
name: `${typeName}_${sameTypeNodes.length + 1}`,
type,
},
}
newNode = graph.addNode(node)
return newNode
}
/**
* 创建边并添加到画布
* @param source
* @param target
* @param graph
*/
const createEdge = (source: string, target: string, graph: Graph) => {
const edge = {
id: StringExt.uuid(),
shape: 'data-processing-curve',
source: {
cell: source,
port: `${source}-out`,
},
target: {
cell: target,
port: `${target}-in`,
},
zIndex: -1,
data: {
source,
target,
},
}
if (graph) {
graph.addEdge(edge)
}
}
class DataProcessingDagNode extends React.Component<{
node: Node
}> {
state = {
plusActionSelected: false,
}
// 创建下游的节点和边
createDownstream = (type: NodeType) => {
const { node } = this.props
const { graph } = node.model || {}
if (graph) {
// 获取下游节点的初始位置信息
const position = getDownstreamNodePosition(node, graph)
// 创建下游节点
const newNode = createNode(type, graph, position)
const source = node.id
const target = newNode.id
// 创建该节点出发到下游节点的边
createEdge(source, target, graph)
}
}
// 点击添加下游+号
clickPlusDragMenu = (type: NodeType) => {
this.createDownstream(type)
this.setState({
plusActionSelected: false,
})
}
// 获取+号下拉菜单
getPlusDagMenu = () => {
return (
<ul>
{PROCESSING_TYPE_LIST.map((item) => {
const content = (
// eslint-disable-next-line
<a onClick={() => this.clickPlusDragMenu(item.type)}>
<i
className="node-mini-logo"
style={{ backgroundImage: `url(${NODE_TYPE_LOGO[item.type]})` }}
/>
<span>{item.name}</span>
</a>
)
return (
<li className="each-sub-menu" key={item.type}>
{content}
</li>
)
})}
</ul>
)
}
// 添加下游菜单的打开状态变化
onPlusDropdownOpenChange = (value: boolean) => {
this.setState({
plusActionSelected: value,
})
}
// 鼠标进入矩形主区域的时候显示连接桩
onMainMouseEnter = () => {
const { node } = this.props
// 获取该节点下的所有连接桩
const ports = node.getPorts() || []
ports.forEach((port) => {
node.setPortProp(port.id, 'attrs/circle', {
fill: '#fff',
stroke: '#85A5FF',
})
})
}
// 鼠标离开矩形主区域的时候隐藏连接桩
onMainMouseLeave = () => {
const { node } = this.props
// 获取该节点下的所有连接桩
const ports = node.getPorts() || []
ports.forEach((port) => {
node.setPortProp(port.id, 'attrs/circle', {
fill: 'transparent',
stroke: 'transparent',
})
})
}
render() {
const { plusActionSelected } = this.state
const { node } = this.props
const data = node?.getData()
const { name, type, status, statusMsg } = data
return (
<div className="data-processing-dag-node">
<div
className="main-area"
onMouseEnter={this.onMainMouseEnter}
onMouseLeave={this.onMainMouseLeave}
>
<div className="main-info">
{/* 节点类型icon */}
<i
className="node-logo"
style={{ backgroundImage: `url(${NODE_TYPE_LOGO[type]})` }}
/>
<Tooltip title={name} mouseEnterDelay={0.8}>
<div className="ellipsis-row node-name">{name}</div>
</Tooltip>
</div>
{/* 节点状态信息 */}
<div className="status-action">
{status === CellStatus.ERROR && (
<Tooltip title={statusMsg}>
<i className="status-icon status-icon-error" />
</Tooltip>
)}
{status === CellStatus.SUCCESS && (
<i className="status-icon status-icon-success" />
)}
{/* 节点操作菜单 */}
<div className="more-action-container">
<i className="more-action" />
</div>
</div>
</div>
{/* 添加下游节点 */}
{type !== NodeType.OUTPUT && (
<div className="plus-dag">
<Dropdown
dropdownRender={this.getPlusDagMenu}
overlayClassName="processing-node-menu"
trigger={['click']}
placement="bottom"
open={plusActionSelected}
onOpenChange={this.onPlusDropdownOpenChange}
>
<i
className={classnames('plus-action', {
'plus-action-selected': plusActionSelected,
})}
/>
</Dropdown>
</div>
)}
</div>
)
}
}
register({
shape: 'data-processing-dag-node',
width: 212,
height: 48,
component: DataProcessingDagNode,
// port默认不可见
ports: {
groups: {
in: {
position: 'left',
attrs: {
circle: {
r: 4,
magnet: true,
stroke: 'transparent',
strokeWidth: 1,
fill: 'transparent',
},
},
},
out: {
position: {
name: 'right',
args: {
dx: -32,
},
},
attrs: {
circle: {
r: 4,
magnet: true,
stroke: 'transparent',
strokeWidth: 1,
fill: 'transparent',
},
},
},
},
},
})
// 注册连线
Graph.registerConnector(
'curveConnector',
(sourcePoint, targetPoint) => {
const hgap = Math.abs(targetPoint.x - sourcePoint.x)
const path = new Path()
path.appendSegment(
Path.createSegment('M', sourcePoint.x - 4, sourcePoint.y),
)
path.appendSegment(
Path.createSegment('L', sourcePoint.x + 12, sourcePoint.y),
)
// 水平三阶贝塞尔曲线
path.appendSegment(
Path.createSegment(
'C',
sourcePoint.x < targetPoint.x
? sourcePoint.x + hgap / 2
: sourcePoint.x - hgap / 2,
sourcePoint.y,
sourcePoint.x < targetPoint.x
? targetPoint.x - hgap / 2
: targetPoint.x + hgap / 2,
targetPoint.y,
targetPoint.x - 6,
targetPoint.y,
),
)
path.appendSegment(
Path.createSegment('L', targetPoint.x + 2, targetPoint.y),
)
return path.serialize()
},
true,
)
Edge.config({
markup: [
{
tagName: 'path',
selector: 'wrap',
attrs: {
fill: 'none',
cursor: 'pointer',
stroke: 'transparent',
strokeLinecap: 'round',
},
},
{
tagName: 'path',
selector: 'line',
attrs: {
fill: 'none',
pointerEvents: 'none',
},
},
],
connector: { name: 'curveConnector' },
attrs: {
wrap: {
connection: true,
strokeWidth: 10,
strokeLinejoin: 'round',
},
line: {
connection: true,
stroke: '#A2B1C3',
strokeWidth: 1,
targetMarker: {
name: 'classic',
size: 6,
},
},
},
})
Graph.registerEdge('data-processing-curve', Edge, true)
const graph: Graph = new Graph({
container: document.getElementById('container')!,
panning: {
enabled: true,
eventTypes: ['leftMouseDown', 'mouseWheel'],
},
mousewheel: {
enabled: true,
modifiers: 'ctrl',
factor: 1.1,
maxScale: 1.5,
minScale: 0.5,
},
highlighting: {
magnetAdsorbed: {
name: 'stroke',
args: {
attrs: {
fill: '#fff',
stroke: '#31d0c6',
strokeWidth: 4,
},
},
},
},
connecting: {
snap: true,
allowBlank: false,
allowLoop: false,
highlight: true,
sourceAnchor: {
name: 'left',
args: {
dx: Platform.IS_SAFARI ? 4 : 8,
},
},
targetAnchor: {
name: 'right',
args: {
dx: Platform.IS_SAFARI ? 4 : -8,
},
},
createEdge() {
return graph.createEdge({
shape: 'data-processing-curve',
attrs: {
line: {
strokeDasharray: '5 5',
},
},
zIndex: -1,
})
},
// 连接桩校验
validateConnection({ sourceMagnet, targetMagnet }) {
// 只能从输出链接桩创建连接
if (!sourceMagnet || sourceMagnet.getAttribute('port-group') === 'in') {
return false
}
// 只能连接到输入链接桩
if (!targetMagnet || targetMagnet.getAttribute('port-group') !== 'in') {
return false
}
return true
},
},
})
graph.use(
new Selection({
enabled: true,
multiple: true,
rubberEdge: true,
rubberNode: true,
modifiers: 'shift',
rubberband: true,
}),
)
// 节点状态列表
const nodeStatusList = [
{
id: 'node-0',
status: 'success',
},
{
id: 'node-1',
status: 'success',
},
{
id: 'node-2',
status: 'success',
},
{
id: 'node-3',
status: 'success',
},
{
id: 'node-4',
status: 'error',
statusMsg: '错误信息示例',
},
]
// 边状态列表
const edgeStatusList = [
{
id: 'edge-0',
status: 'success',
},
{
id: 'edge-1',
status: 'success',
},
{
id: 'edge-2',
status: 'success',
},
{
id: 'edge-3',
status: 'success',
},
]
// 显示节点状态
const showNodeStatus = () => {
nodeStatusList.forEach((item) => {
const { id, status, statusMsg } = item
const node = graph.getCellById(id)
const data = node.getData() as CellStatus
node.setData({
...data,
status,
statusMsg,
})
})
}
// 开启边的运行动画
const excuteAnimate = () => {
graph.getEdges().forEach((edge) => {
edge.attr({
line: {
stroke: '#3471F9',
},
})
edge.attr('line/strokeDasharray', 5)
edge.attr('line/style/animation', 'running-line 30s infinite linear')
})
}
// 关闭边的动画
const stopAnimate = () => {
graph.getEdges().forEach((edge) => {
edge.attr('line/strokeDasharray', 0)
edge.attr('line/style/animation', '')
})
edgeStatusList.forEach((item) => {
const { id, status } = item
const edge = graph.getCellById(id)
if (status === 'success') {
edge.attr('line/stroke', '#52c41a')
}
if (status === 'error') {
edge.attr('line/stroke', '#ff4d4f')
}
})
// 默认选中一个节点
graph.select('node-2')
}
fetch('/data/data-processing-dag.json')
.then((response) => response.json())
.then((data) => {
graph.fromJSON(data)
const zoomOptions = {
padding: {
left: 10,
right: 10,
},
}
graph.zoomToFit(zoomOptions)
setTimeout(() => {
excuteAnimate()
}, 2000)
setTimeout(() => {
showNodeStatus()
stopAnimate()
}, 3000)
})
insertCss(`
.data-processing-dag-node {
display: flex;
flex-direction: row;
align-items: center;
}
.main-area {
display: flex;
flex-direction: row;
justify-content: space-between;
padding: 12px;
width: 180px;
height: 48px;
color: rgba(0, 0, 0, 65%);
font-size: 12px;
font-family: PingFangSC;
line-height: 24px;
background-color: #fff;
box-shadow: 0 -1px 4px 0 rgba(209, 209, 209, 50%), 1px 1px 4px 0 rgba(217, 217, 217, 50%);
border-radius: 2px;
border: 1px solid transparent;
}
.main-area:hover {
border: 1px solid rgba(0, 0, 0, 10%);
box-shadow: 0 -2px 4px 0 rgba(209, 209, 209, 50%), 2px 2px 4px 0 rgba(217, 217, 217, 50%);
}
.node-logo {
display: inline-block;
width: 24px;
height: 24px;
background-repeat: no-repeat;
background-position: center;
background-size: 100%;
}
.node-name {
overflow: hidden;
display: inline-block;
width: 70px;
margin-left: 6px;
color: rgba(0, 0, 0, 65%);
font-size: 12px;
font-family: PingFangSC;
white-space: nowrap;
text-overflow: ellipsis;
vertical-align: top;
}
.status-action {
display: flex;
flex-direction: row;
align-items: center;
}
.status-icon {
display: inline-block;
width: 24px;
height: 24px;
}
.status-icon-error {
background: url('https://gw.alipayobjects.com/mdn/rms_43231b/afts/img/A*SEISQ6My-HoAAAAAAAAAAAAAARQnAQ')
no-repeat center center / 100% 100%;
}
.status-icon-success {
background: url('https://gw.alipayobjects.com/mdn/rms_43231b/afts/img/A*6l60T6h8TTQAAAAAAAAAAAAAARQnAQ')
no-repeat center center / 100% 100%;
}
.more-action-container {
margin-left: 12px;
width: 15px;
height: 15px;
text-align: center;
cursor: pointer;
}
.more-action {
display: inline-block;
width: 3px;
height: 15px;
background: url('https://mdn.alipayobjects.com/huamei_f4t1bn/afts/img/A*tFw7SIy-ttQAAAAAAAAAAAAADtOHAQ/original')
no-repeat center center / 100% 100%;
}
.plus-dag {
visibility: hidden;
position: relative;
margin-left: 12px;
height: 48px;
}
.plus-action {
position: absolute;
top: calc(50% - 8px);
left: 0;
width: 16px;
height: 16px;
background: url('https://mdn.alipayobjects.com/huamei_f4t1bn/afts/img/A*ScX2R4ODfokAAAAAAAAAAAAADtOHAQ/original')
no-repeat center center / 100% 100%;
cursor: pointer;
}
.plus-action:hover {
background-image: url('https://mdn.alipayobjects.com/huamei_f4t1bn/afts/img/A*tRaoS5XhsuQAAAAAAAAAAAAADtOHAQ/original');
}
.plus-action:active,
.plus-action-selected {
background-image: url('https://mdn.alipayobjects.com/huamei_f4t1bn/afts/img/A*k9cnSaSmlw4AAAAAAAAAAAAADtOHAQ/original');
}
.x6-node-selected .main-area {
border-color: #3471f9;
}
.x6-node-selected .plus-dag {
visibility: visible;
}
.processing-node-menu {
padding: 2px 0;
width: 105px;
background-color: #fff;
box-shadow: 0 9px 28px 8px rgba(0, 0, 0, 5%), 0 6px 16px 0 rgba(0, 0, 0, 8%),
0 3px 6px -4px rgba(0, 0, 0, 12%);
border-radius: 2px;
}
.processing-node-menu ul {
margin: 0;
padding: 0;
}
.processing-node-menu li {
list-style:none;
}
.each-sub-menu {
padding: 6px 12px;
width: 100%;
}
.each-sub-menu:hover {
background-color: rgba(0, 0, 0, 4%);
}
.each-sub-menu a {
display: inline-block;
width: 100%;
height: 16px;
font-family: PingFangSC;
font-weight: 400;
font-size: 12px;
color: rgba(0, 0, 0, 65%);
}
.each-sub-menu span {
margin-left: 8px;
vertical-align: top;
}
.each-disabled-sub-menu a {
cursor: not-allowed;
color: rgba(0, 0, 0, 35%);
}
.node-mini-logo {
display: inline-block;
width: 16px;
height: 16px;
background-repeat: no-repeat;
background-position: center;
background-size: 100%;
vertical-align: top;
}
@keyframes running-line {
to {
stroke-dashoffset: -1000;
}
}
`)

View File

@ -16,14 +16,6 @@
},
"screenshot": "https://gw.alipayobjects.com/mdn/rms_43231b/afts/img/A*RPiGRaSus3UAAAAAAAAAAAAAARQnAQ"
},
{
"filename": "dataProcessingDag.tsx",
"title": {
"zh": "数据加工 DAG 图",
"en": "DAG for Data Processing"
},
"screenshot": "https://mdn.alipayobjects.com/huamei_f4t1bn/afts/img/A*yRLDQ5KgSO4AAAAAAAAAAAAADtOHAQ/original"
},
{
"filename": "er.ts",
"title": {

View File

@ -1,7 +1,7 @@
{
"private": true,
"name": "@antv/x6-sites",
"version": "1.3.0",
"version": "1.2.0",
"description": "X6 sites deployed on gh-pages",
"scripts": {
"dev": "dumi dev",
@ -38,8 +38,6 @@
"highlight.js": "^10.1.2",
"react": "^18.0.0",
"react-dom": "^18.0.0",
"react-i18next": "^11.5.0",
"classnames": "^2.2.6"
},
"repository": "https://github.com/antvis/x6"
"react-i18next": "^11.5.0"
}
}

View File

@ -1,163 +0,0 @@
{
"nodes": [
{
"id": "node-0",
"shape": "data-processing-dag-node",
"x": 0,
"y": 100,
"ports": [
{
"id": "node-0-out",
"group": "out"
}
],
"data": {
"name": "数据输入_1",
"type": "INPUT",
"checkStatus": "sucess"
}
},
{
"id": "node-1",
"shape": "data-processing-dag-node",
"x": 250,
"y": 100,
"ports": [
{
"id": "node-1-in",
"group": "in"
},
{
"id": "node-1-out",
"group": "out"
}
],
"data": {
"name": "数据筛选_1",
"type": "FILTER"
}
},
{
"id": "node-2",
"shape": "data-processing-dag-node",
"x": 250,
"y": 200,
"ports": [
{
"id": "node-2-out",
"group": "out"
}
],
"data": {
"name": "数据输入_2",
"type": "INPUT"
}
},
{
"id": "node-3",
"shape": "data-processing-dag-node",
"x": 500,
"y": 100,
"ports": [
{
"id": "node-3-in",
"group": "in"
},
{
"id": "node-3-out",
"group": "out"
}
],
"data": {
"name": "数据连接_1",
"type": "JOIN"
}
},
{
"id": "node-4",
"shape": "data-processing-dag-node",
"x": 750,
"y": 100,
"ports": [
{
"id": "node-4-in",
"group": "in"
}
],
"data": {
"name": "数据输出_1",
"type": "OUTPUT"
}
}
],
"edges": [
{
"id": "edge-0",
"source": {
"cell": "node-0",
"port": "node-0-out"
},
"target": {
"cell": "node-1",
"port": "node-1-in"
},
"shape": "data-processing-curve",
"zIndex": -1,
"data": {
"source": "node-0",
"target": "node-1"
}
},
{
"id": "edge-1",
"source": {
"cell": "node-2",
"port": "node-2-out"
},
"target": {
"cell": "node-3",
"port": "node-3-in"
},
"shape": "data-processing-curve",
"zIndex": -1,
"data": {
"source": "node-2",
"target": "node-3"
}
},
{
"id": "edge-2",
"source": {
"cell": "node-1",
"port": "node-1-out"
},
"target": {
"cell": "node-3",
"port": "node-3-in"
},
"shape": "data-processing-curve",
"zIndex": -1,
"data": {
"source": "node-1",
"target": "node-3"
}
},
{
"id": "edge-3",
"source": {
"cell": "node-3",
"port": "node-3-out"
},
"target": {
"cell": "node-4",
"port": "node-4-in"
},
"shape": "data-processing-curve",
"zIndex": -1,
"data": {
"source": "node-3",
"target": "node-4"
}
}
]
}