Compare commits

..

1 Commits

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

View File

@ -0,0 +1,19 @@
name: 🧑 Auto Assign
on:
issues:
types: [opened]
pull_request_target:
types: [opened]
jobs:
run:
runs-on: ubuntu-latest
steps:
- uses: wow-actions/use-app-token@v2
with:
app_id: ${{ secrets.APP_ID }}
private_key: ${{ secrets.PRIVATE_KEY }}
- uses: wow-actions/auto-assign@v1
with:
GITHUB_TOKEN: ${{ env.BOT_TOKEN }}
CONFIG_FILE: .github/workflows/config/auto-assign.yml

View File

@ -0,0 +1,48 @@
name: 🚫 Delete Stale Releases
# on:
# repository_dispatch:
# types: [released]
# jobs:
# clean:
# runs-on: ubuntu-latest
# steps:
# - uses: wow-actions/use-app-token@v2
# with:
# app_id: ${{ secrets.APP_ID }}
# private_key: ${{ secrets.PRIVATE_KEY }}
#
# - uses: wow-actions/delete-stale-releases@v1
# with:
# GITHUB_TOKEN: ${{ env.BOT_TOKEN }}
# delete_tags: true
# keep_latest_count: 3
# group: '(?!^)@.*$'
# exclude: |
# @antv/x6@**
# @antv/x6-common@**
# @antv/x6-geometry@**
# @antv/x6-plugin-**@**
# @antv/x6-vue-shape@**
# @antv/x6-react-shape@**
# @antv/x6-angular-shape@**
# @antv/x6-react-components@**
# delete all releases and tag
on:
push:
branches:
- master
jobs:
clean:
runs-on: ubuntu-latest
steps:
- uses: wow-actions/use-app-token@v2
with:
app_id: ${{ secrets.APP_ID }}
private_key: ${{ secrets.PRIVATE_KEY }}
- uses: wow-actions/delete-stale-releases@v1
with:
GITHUB_TOKEN: ${{ env.BOT_TOKEN }}
delete_tags: true
keep_latest_count: -1

21
.github/workflows/backup/gitleaks.yml vendored Normal file
View File

@ -0,0 +1,21 @@
name: 🥤 GitLeaks
on:
pull_request:
push:
branches:
- master
- alpha
- beta
jobs:
gitleaks:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: '1'
- name: wget
uses: wei/wget@v1
with:
args: -O .gitleaks.toml https://raw.githubusercontent.com/ycjcl868/gitleaks/master/.gitleaks.toml
- name: gitleaks-action
uses: zricethezav/gitleaks-action@master

View File

@ -10,12 +10,16 @@ on:
- beta
jobs:
ci:
strategy:
matrix:
codecov: [x6, x6-common, x6-geometry]
runs-on: ubuntu-latest
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
steps:
- name:  Checkout
uses: actions/checkout@v3
with:
persist-credentials: false
- name: 🎉 Setup nodejs
uses: actions/setup-node@v3
@ -59,11 +63,11 @@ jobs:
- name: 💡 Codecov
uses: codecov/codecov-action@v3
uses: codecov/codecov-action@v1
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./packages/x6/test/coverage/lcov.info
flags: x6
files: ./packages/${{ matrix.codecov }}/test/coverage/lcov.info
flags: ${{ matrix.codecov }}
- name: 🔀 Dispatch(ci_passed)
uses: peter-evans/repository-dispatch@v2

42
.github/workflows/codeql.yml vendored Normal file
View File

@ -0,0 +1,42 @@
name:  CodeQL
on:
push:
branches: [ "master", "bot", "gh-pages", "v1" ]
pull_request:
branches: [ "master" ]
schedule:
- cron: "41 2 * * 1"
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ javascript ]
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
config-file: ./.github/workflows/config/codeql.yml
queries: +security-and-quality
- name: Autobuild
uses: github/codeql-action/autobuild@v2
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
with:
category: "/language:${{ matrix.language }}"

View File

@ -0,0 +1,7 @@
assignees:
- NewByVector
reviewers:
- bubkoo
- NewByVector
skipKeywords:
- wip

7
.github/workflows/config/codeql.yml vendored Normal file
View File

@ -0,0 +1,7 @@
paths-ignore:
- sites/public
query-filters:
- exclude:
id: js/use-before-declaration
- exclude:
id: js/polynomial-redos

View File

@ -0,0 +1,16 @@
name: 🚧 Create Issue Branch
on:
issue_comment:
types: [created]
jobs:
cib:
runs-on: ubuntu-latest
steps:
- uses: wow-actions/use-app-token@v2
with:
app_id: ${{ secrets.APP_ID }}
private_key: ${{ secrets.PRIVATE_KEY }}
- uses: robvanderleek/create-issue-branch@main
env:
GITHUB_TOKEN: ${{ env.BOT_TOKEN }}

78
.github/workflows/deploy-sites.yml vendored Normal file
View File

@ -0,0 +1,78 @@
name: 🚀 Deploy Sites
on:
repository_dispatch:
types: [released]
jobs:
deploy:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16.x]
steps:
- name:  Checkout
uses: actions/checkout@v3
- name: 🎉 Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- name: 🌱 Get Yarn Cache Directory
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"
- name: 🚸 Setup Cache
uses: actions/cache@v2
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
## cache webpack(babel-loader, eslint-loader)
- name: 💩 Setup Webpack Cache
uses: actions/cache@v2
with:
path: |
node_modules
sites/x6-sites-demos/packages/**/node_modules
key: ${{ runner.os }}-webpack-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-webpack-
## cache sites
- name: 💩 Setup Sites Cache
uses: actions/cache@v2
with:
path: sites/x6-sites/static/demos
key: ${{ runner.os }}-sites-${{ hashFiles('./packages/x6/package.json', './sites/x6-sites-demos/**/src') }}
restore-keys: |
${{ runner.os }}-sites-
- name: 🚧 Prepare
run: yarn global add lerna
- name: 🚀 Bootstrap
run: yarn bootstrap
- name: 🧲 Build Apps
run: yarn build:apps
- name: 📦 Build Demos
run: yarn build:demos
- name:  Build Sites
run: yarn build:sites
- name: 🔑 Generate Token
uses: wow-actions/use-app-token@v2
with:
app_id: ${{ secrets.APP_ID }}
private_key: ${{ secrets.PRIVATE_KEY }}
- name:  Deploy sites
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ env.BOT_TOKEN }}
publish_dir: ./sites/x6-sites/public
publish_branch: gh-pages

View File

@ -17,4 +17,4 @@ jobs:
- uses: wow-actions/pr-triage@v1
with:
GITHUB_TOKEN: ${{ env.BOT_TOKEN }}
WORKFLOW_ID: ${{ github.event.workflow_run.id }}
WORKFLOW-ID: ${{ github.event.workflow_run.id }}

85
.github/workflows/preview.yml vendored Normal file
View File

@ -0,0 +1,85 @@
name: 🔂 Surge PR Preview
on:
pull_request_target:
paths:
- sites/x6-sites/**
- sites/x6-sites-demos/**
- sites/x6-sites-demos-helper/**
- examples/x6-app-**
jobs:
surge:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16.x]
steps:
- uses: actions/checkout@v3
- name: 🌱 Get yarn cache directory path
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"
- name: 🚸 Setup yarn cacha
uses: actions/cache@v2
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
## cache webpack(babel-loader, eslint-loader)
- name: 💩 Setup webpack cache
uses: actions/cache@v2
with:
path: |
node_modules
sites/x6-sites-demos/packages/**/node_modules
key: ${{ runner.os }}-webpack-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-webpack-
## cache sites
- name: 💩 Setup sites cache
uses: actions/cache@v2
with:
path: sites/x6-sites/static/demos
key: ${{ runner.os }}-sites-${{ hashFiles('./packages/x6/package.json', './sites/x6-sites-demos/**/src') }}
restore-keys: |
${{ runner.os }}-sites-
- name: 🎉 Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- name: 🚧 Prepare Environment
run: |
yarn global add lerna
- name: 🚀 Bootstrap
run: yarn bootstrap
- name: 📦 Build Demos
run: yarn build:demos
- name: 🧲 Build Apps
run: yarn build:apps
- name:  Build Sites
run: yarn build:sites
- name: 🔑 Generate Token
uses: wow-actions/use-app-token@v2
with:
app_id: ${{ secrets.APP_ID }}
private_key: ${{ secrets.PRIVATE_KEY }}
- name:  Deploy Sites
uses: afc163/surge-preview@v1
with:
surge_token: ${{ secrets.SURGE_TOKEN }}
github_token: ${{ env.BOT_TOKEN }}
build: |
echo Create sites preview
dist: sites/x6-sites/public

View File

@ -15,8 +15,7 @@ jobs:
steps:
- name:  Checkout
uses: actions/checkout@v3
with:
persist-credentials: false
- name: 🎉 Setup nodejs
uses: actions/setup-node@v3
with:

16
.github/workflows/report-monthly.yml vendored Normal file
View File

@ -0,0 +1,16 @@
name: 📆 Monthly Report
on:
schedule:
- cron: '0 3 1 * *'
jobs:
run:
runs-on: ubuntu-latest
steps:
- uses: wow-actions/use-app-token@v2
with:
app_id: ${{ secrets.APP_ID }}
private_key: ${{ secrets.PRIVATE_KEY }}
- uses: wow-actions/activity-report@v1
with:
GITHUB_TOKEN: ${{ env.BOT_TOKEN }}

16
.github/workflows/report-weekly.yml vendored Normal file
View File

@ -0,0 +1,16 @@
name: 📆 Weekly Report
on:
schedule:
- cron: '30 17 * * 5'
jobs:
run:
runs-on: ubuntu-latest
steps:
- uses: wow-actions/use-app-token@v2
with:
app_id: ${{ secrets.APP_ID }}
private_key: ${{ secrets.PRIVATE_KEY }}
- uses: wow-actions/activity-report@v1
with:
GITHUB_TOKEN: ${{ env.BOT_TOKEN }}

58
.github/workflows/stale.yml vendored Normal file
View File

@ -0,0 +1,58 @@
name: 👻 Stale
on:
schedule:
- cron: "0 0 * * *"
jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: wow-actions/use-app-token@v2
with:
app_id: ${{ secrets.APP_ID }}
private_key: ${{ secrets.PRIVATE_KEY }}
- uses: actions/stale@v3
with:
repo-token: ${{ env.BOT_TOKEN }}
stale-issue-message: |
Hiya!
This issue has gone quiet. Spooky quiet. 👻
We get a lot of issues, so we currently close issues after 60 days of inactivity. Its been at least 20 days since the last update here. If we missed this issue or if you want to keep it open, please reply here. You can also add the label "not-stale" to keep this issue open!
As a friendly reminder: the best way to see this issue, or any other, fixed is to open a Pull Request. Check out [contribute](https://github.com/antvis/X6/blob/master/CONTRIBUTING.md) for more information about opening PRs, triaging issues, and contributing!
Thanks for being a part of the AntV community! 💪💯
close-issue-message: |
Hey again!
Its been 60 days since anything happened on this issue, so our friendly neighborhood robot (thats me!) is going to close it. Please keep in mind that Im only a robot 🤖, so if Ive closed this issue in error, Im `HUMAN_EMOTION_SORRY`. Please feel free to comment on this issue or create a new one if you need anything else.
As a friendly reminder: the best way to see this issue, or any other, fixed is to open a Pull Request. Check out [contribute](https://github.com/antvis/X6/blob/master/CONTRIBUTING.md) for more information about opening PRs, triaging issues, and contributing!
Thanks again for being part of the AntV community! 💪💯
stale-pr-message: |
Hiya!
This PR has gone quiet. Spooky quiet. 👻
We get a lot of PRs, so we currently close PRs after 60 days of inactivity. Its been at least 20 days since the last update here. If we missed this PR or if you want to keep it open, please reply here. You can also add the label "not-stale" to keep this PR open!
Thanks for being a part of the AntV community! 💪💯
close-pr-message: |
Hey again!
Its been 60 days since anything happened on this PR, so our friendly neighborhood robot (thats me!) is going to close it. Please keep in mind that Im only a robot 🤖, so if Ive closed this PR in error, Im `HUMAN_EMOTION_SORRY`. Please feel free to comment on this PR or create a new one if you need anything else.
Thanks again for being part of the AntV community! 💪💯
days-before-stale: 20
days-before-close: 40
stale-issue-label: 'stale'
exempt-issue-label: 'not-stale,awaiting-approval,work-in-progress'
stale-pr-label: 'stale'
exempt-pr-label: 'not-stale,awaiting-approval,work-in-progress'

21
.github/workflows/sync-labels.yml vendored Normal file
View File

@ -0,0 +1,21 @@
name: 🔄 Sync Labels
on:
push:
branches:
- master
paths:
- .github/workflows/config/labels.yml
jobs:
sync:
runs-on: ubuntu-latest
steps:
- uses: wow-actions/use-app-token@v2
with:
app_id: ${{ secrets.APP_ID }}
private_key: ${{ secrets.PRIVATE_KEY }}
- uses: micnncim/action-label-syncer@v1
env:
GITHUB_TOKEN: ${{ env.BOT_TOKEN }}
with:
manifest: .github/workflows/config/labels.yml

34
.github/workflows/sync-to-gitee.yml vendored Normal file
View File

@ -0,0 +1,34 @@
# https://github.com/marketplace/actions/gitee-pages-action
# 配置步骤如下
# 1. 在命令行终端或 Git Bash 使用命令 ssh-keygen -t rsa -C "youremail@example.com" 生成 SSH Key注意替换为自己的邮箱。生成的 id_rsa 是私钥id_rsa.pub 是公钥。(⚠️注意此处不要设置密码)
# 2. 在 GitHub 项目的「Settings -> Secrets」路径下配置好命名为 GITEE_RSA_PRIVATE_KEY 和 GITEE_PASSWORD 的两个密钥。其中GITEE_RSA_PRIVATE_KEY 存放 id_rsa 私钥GITEE_PASSWORD 存放 Gitee 帐号的密码。
# 3. 在 GitHub 的个人设置页面「Settings -> SSH and GPG keys」 配置 SSH 公钥id_rsa.pub命名随意。或者在仓库设置页面添加一个部署公钥。
# 4. 在 Gitee 的个人设置页面「安全设置 -> SSH 公钥」​ 配置 SSH 公钥id_rsa.pub命名随意。
name: 🔁 Sync to Gitee
on: [push]
jobs:
sync:
runs-on: ubuntu-latest
steps:
- name: 🔁 Sync to Gitee
uses: wearerequired/git-mirror-action@master
env:
# 注意在 Settings->Secrets 配置 GITEE_RSA_PRIVATE_KEY
SSH_PRIVATE_KEY: ${{ secrets.GITEE_RSA_PRIVATE_KEY }}
with:
# 注意替换为你的 GitHub 源仓库地址
source-repo: 'git@github.com:antvis/X6.git'
# 注意替换为你的 Gitee 目标仓库地址
destination-repo: 'git@gitee.com:antv-x6/antv-x6.git'
- name: 📦 Build Gitee Pages
uses: yanglbme/gitee-pages-action@master
with:
# 注意替换为你的 Gitee 用户名
gitee-username: afc163
# 注意在 Settings->Secrets 配置 GITEE_PASSWORD
gitee-password: ${{ secrets.GITEE_PASSWORD }}
# 注意替换为你的 Gitee 仓库
gitee-repo: antv-x6/antv-x6
# 要部署的分支
branch: gh-pages

View File

@ -20,7 +20,9 @@ jobs:
👋 @{{ author }}
Thanks for opening your first issue here! If you're reporting a 🐞 bug, please make sure you include steps to reproduce it.
To help make it easier for us to investigate your issue, please follow the [contributing guidelines](https://github.com/antvis/X6/blob/master/CONTRIBUTING.md).
We get a lot of issues on this repo, so please be patient and we will get back to you as soon as we can.
FIRST_PR: |

2
.npmrc
View File

@ -1,2 +0,0 @@
registry=https://registry.npm.taobao.org
strict-peer-dependencies=false

View File

@ -14,4 +14,3 @@ dist/
coverage/
sites/public
csstype.ts
ui.js

View File

@ -1,26 +1,21 @@
BARM <284942955@qq.com>
Candy <563378816@qq.com>
Chaoqi ZHANG <prncoprs@163.com>
Clifford Fajardo <cliffordfajardo@users.noreply.github.com>
Chaoqi <HAN>
Clifford <ajard>
DaiGang <42136433+daigang666@users.noreply.github.com>
Dong <48054715+halodong@users.noreply.github.com>
Draco <Draco.coder@gmail.com>
Eve-Sama <17764594863@163.com>
Eve-Sama <948832626@qq.com>
Gossypol <31892817+gossypol@users.noreply.github.com>
HQidea <HQidea@users.noreply.github.com>
ImgBotApp <ImgBotHelp@gmail.com>
Indomi <indomi126@gmail.com>
James Tsang <wtzeng1@gmail.com>
JasonSun <42314340+LolipopJ@users.noreply.github.com>
Jinxing Lin <172601673@qq.com>
Jógvan Olsen <jogvanolsen@hotmail.com>
Ken Geis <geis.ken@gmail.com>
Kent Wood <minzojian@hotmail.com>
Ko.Rei <32183014+Ko-Rei@users.noreply.github.com>
James <san>
Jógvan <lse>
Ken <ei>
Limbo <49612796+JUST-Limbo@users.noreply.github.com>
Lixu <37231473+wflixu@users.noreply.github.com>
Lloyd Zhou <lloydzhou@users.noreply.github.com>
Lloyd <ho>
Lyn <47809781+lyn-boyu@users.noreply.github.com>
MOMO <329053928@qq.com>
Mingfei <az8641683@163.com>
@ -30,22 +25,17 @@ NewByVector <NewByVector@users.noreply.github.com>
Olive.Wang <olivewind.wang@gmail.com>
Opportunity <opportunity@live.in>
Questions <chip@twostewards.com>
RuiLin Dong <48054715+halodong@users.noreply.github.com>
RuiLin <on>
SSC <273702440@qq.com>
Samuel Bodin <1637651+bodinsamuel@users.noreply.github.com>
Simon He <57086651+Simon-He95@users.noreply.github.com>
Simon <>
Sindori <441933726@qq.com>
Struggle <1178825961@qq.com>
Struggle Roue <47975400+struggleRoue@users.noreply.github.com>
Susan <527971893@qq.com>
Thomas Zeugner <tomsoftware@gmx.de>
Tony Wu <93302820+tonywu6@users.noreply.github.com>
Thomas <eugne>
Tony <>
Utopia <greatauk11@gmail.com>
XLZY <1017866168@qq.com>
Xia Wenqi <xiawenqi90@gmail.com>
Xingjian Zhang <44231913+THUzxj@users.noreply.github.com>
Zhenyu Hou <skyking_H@hotmail.com>
_XiaoTian <istianlei@qq.com>
Xingjian <han>
Zhenyu <o>
arthur657834 <kingkom7834@126.com>
boyu.zlj <boyu.zlj@antgroup.com>
breezefaith <nyzhangzc@qq.com>
@ -61,8 +51,6 @@ kelin.zrh <34393362+AricZhu@users.noreply.github.com>
kingshuaishuai <ken.wang@mrs.ai>
kio <1421104933@qq.com>
lijing666 <lijing241@yeah.net>
linkun <33945539+linkun-wang@users.noreply.github.com>
linkun <linkun0922@163.com>
lopn <lopnxrp@126.com>
luchunwei <luchunwei@gmail.com>
luzhuang <364439895@qq.com>
@ -72,20 +60,17 @@ newbyvector <vectorse@126.com>
niexq <1879633916@qq.com>
niexq <niexq@firstgrid.cn>
njshuisheng <34205271+njshuisheng@users.noreply.github.com>
nobugforever <84232410+mengYu-Jin@users.noreply.github.com>
pengxingjian.pxj <pengxingjian.pxj@alibaba-inc.com>
pfdgithub <pfdgithub@users.noreply.github.com>
qingchi <qinky94@163.com>
qu <33251372+Qujh97@users.noreply.github.com>
sallen450 <qinghua10199@gmail.com>
semantic-release-bot <semantic-release-bot@martynus.net>
siaikin <abc1310054026@outlook.com>
vector <vectorse@126.com>
wenbei <38773084+wb-wenbei@users.noreply.github.com>
wgf <34190465+evelope@users.noreply.github.com>
wind X <35559153+XueMeijing@users.noreply.github.com>
wind <>
wjqsummer <52412389+wjqsummer@users.noreply.github.com>
wseven7677 <caoyu_92@126.com>
wtzeng1 <wtzeng1@gmail.com>
x6-bot <x6-bot@users.noreply.github.com>
xrkffgg <xrkffgg@gmail.com>
@ -94,12 +79,10 @@ 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>
崖崖崖 <bubkoo.wy@gmail.com>
张子睿 <411489774@qq.com>
文瑀 <wenyu.jqq@antfin.com>
映月 <38279397+orientMoon@users.noreply.github.com>
杨凌 <89915256@qq.com>

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 14 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

View File

@ -90,7 +90,6 @@ graph.addEdge({
- [Versioning Release Note](https://www.yuque.com/antv/x6/bbfu6r)
- [FAQ](https://www.yuque.com/antv/x6/be9pfx)
- [CodeSanbox Template](https://codesandbox.io/s/qosj0?file=/src/app.tsx)
- [awesome-x6](https://github.com/lloydzhou/awesome-x6)
## Development

View File

@ -6,17 +6,18 @@
<p align="center"><strong>提供简单易用的节点定制能力和开箱即用的交互组件方便我们快速搭建流程图、DAG 图、ER 图等图应用</strong></p>
<p align="center">
<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&style=for-the-badge&logo=github"></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=for-the-badge&token=15CO54WYUV"></a> -->
<a href="https://www.npmjs.com/package/@antv/x6"><img alt="NPM Package" src="https://img.shields.io/npm/v/@antv/x6.svg?logo=npm&style=for-the-badge"></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=for-the-badge"></a>
<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://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>
<p align="center">
<a href="/LICENSE"><img src="https://img.shields.io/github/license/antvis/x6?style=for-the-badge" alt="MIT License"></a>
<a href="https://www.typescriptlang.org"><img alt="Language" src="https://img.shields.io/badge/language-TypeScript-blue.svg?style=for-the-badge"></a>
<a href="https://github.com/antvis/x6/pulls"><img alt="PRs Welcome" src="https://img.shields.io/badge/PRs-Welcome-brightgreen.svg?style=for-the-badge"></a>
<a href="https://x6.antv.antgroup.com"><img alt="website" src="https://img.shields.io/static/v1?label=&labelColor=505050&message=website&color=0076D6&style=for-the-badge&logo=google-chrome&logoColor=f5f5f5"></a>
<a href="/LICENSE"><img src="https://img.shields.io/github/license/antvis/x6?style=flat-square" alt="MIT License"></a>
<a href="https://www.typescriptlang.org"><img alt="Language" src="https://img.shields.io/badge/language-TypeScript-blue.svg?style=flat-square"></a>
<a href="https://github.com/antvis/x6/pulls"><img alt="PRs Welcome" src="https://img.shields.io/badge/PRs-Welcome-brightgreen.svg?style=flat-square"></a>
<a href="https://x6.antv.antgroup.com"><img alt="website" src="https://img.shields.io/static/v1?label=&labelColor=505050&message=website&color=0076D6&style=flat-square&logo=google-chrome&logoColor=0076D6"></a>
</p>
## 特性
@ -89,7 +90,6 @@ graph.addEdge({
- [更新日志](https://www.yuque.com/antv/x6/bbfu6r)
- [常见问题](https://www.yuque.com/antv/x6/tox1ukbz5cw57qfy)
- [CodeSanbox 模板](https://codesandbox.io/s/mo-ban-wchooy?file=/src/App.tsx)
- [awesome-x6](https://github.com/lloydzhou/awesome-x6)
## 本地开发

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

@ -1,24 +1,3 @@
## @antv/x6-example-features [2.1.1](https://github.com/antvis/X6/compare/@antv/x6-example-features@2.1.0...@antv/x6-example-features@2.1.1) (2023-02-18)
### Bug Fixes
* mindmap demo duplicate node id, close [#3256](https://github.com/antvis/X6/issues/3256) ([#3257](https://github.com/antvis/X6/issues/3257)) ([c510756](https://github.com/antvis/X6/commit/c510756fe4e96c8e7471c2fb558e6019ec69b057))
# @antv/x6-example-features [2.1.0](https://github.com/antvis/X6/compare/@antv/x6-example-features@2.0.3...@antv/x6-example-features@2.1.0) (2023-02-17)
### Features
* history add max stack size ([#3253](https://github.com/antvis/X6/issues/3253)) ([fba5310](https://github.com/antvis/X6/commit/fba531064ad8027c451a81b60d5efd7f7314a0fa))
## @antv/x6-example-features [2.0.2](https://github.com/antvis/X6/compare/@antv/x6-example-features@2.0.1...@antv/x6-example-features@2.0.2) (2023-01-17)
### Bug Fixes
* **mindmap demo:** can not find target id when create edge ([#3144](https://github.com/antvis/X6/issues/3144)) ([bfc8d7f](https://github.com/antvis/X6/commit/bfc8d7f17ac900f70b696c1fa7a3f3f3a389103f))
## @antv/x6-example-features [2.0.1](https://github.com/antvis/X6/compare/@antv/x6-example-features@2.0.0...@antv/x6-example-features@2.0.1) (2022-12-16)

View File

@ -1,7 +1,7 @@
{
"private": true,
"name": "@antv/x6-example-features",
"version": "2.1.1",
"version": "2.0.1",
"scripts": {
"start": "umi dev",
"build": "umi build",

View File

@ -1,5 +1,5 @@
import React from 'react'
import { Graph, Cell, Point, Timing, Interp } from '@antv/x6'
import { Graph, Cell, Point, Timing, Interp } from '@antv/x6'
import '../index.less'
export default class Example extends React.Component {

View File

@ -1,5 +1,5 @@
import React from 'react'
import { Graph, Node } from '@antv/x6'
import { Graph, Cell, Node } from '@antv/x6'
import { connectors } from '../connector/xmind-definitions'
import Hierarchy from '@antv/hierarchy'
import { Selection } from '@antv/x6-plugin-selection'
@ -212,15 +212,12 @@ export default class Example extends React.Component {
return 'right'
},
})
const cells: Cell[] = []
const traverse = (hierarchyItem: HierarchyResult) => {
if (hierarchyItem) {
const { data, children } = hierarchyItem
// 检查当前遍历的节点已经存在还是需要新添加?
if (graph.hasCell(data.id)) {
const node = graph.getCellById(data.id)
node.prop('position', { x: hierarchyItem.x, y: hierarchyItem.y })
} else {
graph.addNode({
cells.push(
graph.createNode({
id: data.id,
shape: data.type === 'topic-child' ? 'topic-child' : 'topic',
x: hierarchyItem.x,
@ -229,18 +226,13 @@ export default class Example extends React.Component {
height: data.height,
label: data.label,
type: data.type,
})
}
}),
)
if (children) {
children.forEach((item: HierarchyResult) => {
const { id, data } = item
// 先遍历子节点里面包含创建逻辑如果画布没有开启async的时候创建边会提示找不到target节点
traverse(item)
const eid = `${hierarchyItem.id}-->${id}`
// 检查当前边是否已经存在
if (!graph.hasCell(eid)) {
graph.addEdge({
id: eid,
cells.push(
graph.createEdge({
shape: 'mindmap-edge',
source: {
cell: hierarchyItem.id,
@ -265,13 +257,15 @@ export default class Example extends React.Component {
name: 'left',
},
},
})
}
}),
)
traverse(item)
})
}
}
}
traverse(result)
graph.resetCells(cells)
graph.centerContent()
}
@ -309,14 +303,9 @@ export default class Example extends React.Component {
if (dataItem) {
let item: MindMapData | null = null
const length = dataItem.children ? dataItem.children.length : 0
let nid = `${id}-${length + 1}`
if (graph.hasCell(nid)) {
// 如果通过length + 1拼接出来的节点id在画布中存在了就在id后面加上随机数
nid = nid + Math.random()
}
if (type === 'topic') {
item = {
id: nid,
id: `${id}-${length + 1}`,
type: 'topic-branch',
label: `分支主题${length + 1}`,
width: 100,
@ -324,7 +313,7 @@ export default class Example extends React.Component {
}
} else if (type === 'topic-branch') {
item = {
id: nid,
id: `${id}-${length + 1}`,
type: 'topic-child',
label: `子主题${length + 1}`,
width: 60,
@ -345,16 +334,10 @@ export default class Example extends React.Component {
const removeNode = (id: string) => {
const res = findItem(data, id)
const parentItem = res?.parent
const nodeItem = res?.node
if (parentItem && parentItem.children) {
const { children } = parentItem
const dataItem = res?.parent
if (dataItem && dataItem.children) {
const { children } = dataItem
const index = children.findIndex((item) => item.id === id)
// 删除的时候先删节点以及可能存在的子节点再调用render对data数据进行遍历
if (nodeItem && nodeItem.children) {
nodeItem.children.forEach((item) => graph.removeCell(item.id))
}
graph.removeCell(id)
return children.splice(index, 1)
}
return null

View File

@ -1,104 +0,0 @@
import React from 'react'
import { Graph } from '@antv/x6'
import { Keyboard } from '@antv/x6-plugin-keyboard'
import { Selection } from '@antv/x6-plugin-selection'
import { History } from '@antv/x6-plugin-history'
import '../index.less'
export default class Example extends React.Component<
{},
{ graph: Graph | undefined }
> {
private container: HTMLDivElement
componentDidMount() {
const graph = new Graph({
container: this.container,
width: 800,
height: 600,
grid: true,
})
this.setState({ graph })
const selection = new Selection({ enabled: true })
const keyboard = new Keyboard({ enabled: true })
const history = new History({ enabled: true, stackSize: 5 })
graph.use(selection)
graph.use(keyboard)
graph.use(history)
graph.addNode({
x: 50,
y: 50,
width: 100,
height: 40,
attrs: { label: { text: 'A' } },
})
graph.addNode({
x: 250,
y: 50,
width: 100,
height: 40,
attrs: { label: { text: 'B' } },
})
graph.addNode({
x: 350,
y: 150,
width: 100,
height: 40,
attrs: { label: { text: 'C' } },
})
keyboard.bindKey('backspace', () => {
graph.removeCells(selection.getSelectedCells())
})
keyboard.bindKey('command+z', () => {
this.undo()
})
keyboard.bindKey('command+shift+z', () => {
this.redo()
})
}
refContainer = (container: HTMLDivElement) => {
this.container = container
}
enablePlugins = () => {
const { graph } = this.state
graph?.enablePlugins('keyboard')
}
disablePlugins = () => {
const { graph } = this.state
graph?.disablePlugins('keyboard')
}
undo = () => {
const { graph } = this.state
const history = graph?.getPlugin('history') as History
history?.undo()
}
redo = () => {
const { graph } = this.state
const history = graph?.getPlugin('history') as History
history?.redo()
}
render() {
return (
<div className="x6-graph-wrap">
<div ref={this.refContainer} className="x6-graph" />
<button onClick={this.enablePlugins}>enable</button>
<button onClick={this.disablePlugins}>disable</button>
<button onClick={this.undo}>undo</button>
<button onClick={this.redo}>redo</button>
</div>
)
}
}

View File

@ -179,10 +179,6 @@ const dataSource = [
example: 'animation/transition',
description: '动画',
},
{
example: 'history',
description: '时光回溯',
},
].map((item, index) => ({ key: index, ...item }))
const columns = [

View File

@ -4,10 +4,7 @@ import { Keyboard } from '@antv/x6-plugin-keyboard'
import { Selection } from '@antv/x6-plugin-selection'
import '../index.less'
export default class Example extends React.Component<
{},
{ graph: Graph | undefined }
> {
export default class Example extends React.Component {
private container: HTMLDivElement
componentDidMount() {
@ -18,8 +15,6 @@ export default class Example extends React.Component<
grid: true,
})
this.setState({ graph })
const selection = new Selection({ enabled: true })
const keyboard = new Keyboard({ enabled: true })
graph.use(selection)
@ -58,22 +53,10 @@ export default class Example extends React.Component<
this.container = container
}
enablePlugins = () => {
const { graph } = this.state
graph.enablePlugins('keyboard')
}
disablePlugins = () => {
const { graph } = this.state
graph.disablePlugins('keyboard')
}
render() {
return (
<div className="x6-graph-wrap">
<div ref={this.refContainer} className="x6-graph" />
<button onClick={this.enablePlugins}>enable</button>
<button onClick={this.disablePlugins}>disable</button>
</div>
)
}

View File

@ -1,4 +1,4 @@
import { Shape, Path, Point, NumberExt, JSONObject } from '@antv/x6'
import { Shape, Path, Point, NumberExt, JSONObject } from '@antv/x6'
interface KnobsAttrValue extends JSONObject {
round: boolean | string | number

View File

@ -1,5 +1,3 @@
/* eslint-disable jsx-a11y/anchor-is-valid */
/* eslint-disable jsx-a11y/anchor-has-content */
import React from 'react'
import ReactDom from 'react-dom'
import { Menu, Dropdown } from 'antd'

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",
@ -14,15 +14,14 @@
"build:esm": "turbo run build:esm --filter=./packages/*",
"build:dev": "turbo run build:dev --filter=./packages/*",
"build:umd": "turbo run build:umd --filter=./packages/*",
"build:demos": "sh ./scripts/build-demos",
"clean:turbo": "pnpm -r --if-present --parallel --filter=./packages/* run clean:turbo",
"clean:build": "pnpm -r --if-present --parallel --filter=./packages/* run clean",
"clean:modules": "pnpm -r --parallel exec rimraf node_modules && rimraf node_modules",
"clean": "run-s clean:build clean:turbo clean:modules",
"update:deps": "pnpm update --interactive --latest --recursive",
"setup:husky": "husky install .husky",
"prepare": "is-ci || run-p setup:husky build:dev",
"start:demo": "cd ./examples/x6-example-features && pnpm start",
"start:site": "cd ./sites/x6-sites && pnpm start"
"prepare": "is-ci || run-p setup:husky build:dev"
},
"rss": {
"clean:turbo": "rimraf .turbo",
@ -52,7 +51,7 @@
"prettier --write --ignore-unknown"
],
"*.less": [
"stylelint --customSyntax postcss-less --fix"
"stylelint --custom-syntax postcss-less --fix"
],
"*.js": [
"prettier --write"
@ -138,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",
@ -160,13 +158,10 @@
"ts-node": "^10.2.1",
"tslib": "^2.4.1",
"turbo": "^1.6.3",
"typescript": "^4.9.3",
"cross-spawn": "^7.0.3"
"typescript": "^4.9.3"
},
"devDependencies": {
"@rollup/plugin-terser": "^0.2.0"
},
"engines": {
"pnpm": ">=7"
"@rollup/plugin-terser": "^0.2.0",
"postcss-less": "^6.0.0"
}
}

View File

@ -1,21 +0,0 @@
MIT License
Copyright (c) 2021-2023 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
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -1,136 +0,0 @@
# @antv/x6-angular-shape
## 渲染节点
我们提供了一个独立的包 `@antv/x6-angular-shape` 以支持将 Angular 的组件/模板作为节点进行渲染。
### Component 渲染
```ts
@Component({
selector: 'app-node',
templateUrl: './node.component.html',
styleUrls: ['./node.component.scss'],
})
export class NodeComponent implements AfterViewInit, OnChanges {
@Input() value: string;
}
```
```ts
import { register } from "@antv/x6-angular-shape";
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'],
})
export class AppComponent implements AfterViewInit {
ngAfterViewInit(): void {
register({
shape: 'custom-angular-component-node',
width: 120,
height: 20,
content: NodeComponent,
injector: this.injector,
});
this.graph.addNode({
shape: 'custom-angular-component-node',
x: 100,
y: 100,
data: {
// Input 的参数必须放在这里
ngArguments: {
value: '糟糕糟糕 Oh my god',
},
},
});
}
}
```
### TemplateRef 渲染
```html
<ng-template #template let-data="ngArguments">
<section class="template-container">
<span class="value">{{ data.value }}</span>
</section>
</ng-template>
```
```ts
import { register } from "@antv/x6-angular-shape";
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'],
})
export class AppComponent implements AfterViewInit {
@ViewChild('template') template: TemplateRef<{}>;
ngAfterViewInit(): void {
register({
shape: 'custom-angular-template-node',
width: 120,
height: 20,
content: this.template,
injector: this.injector,
});
this.graph.addNode({
shape: 'custom-angular-template-node',
x: 100,
y: 100,
data: {
ngArguments: {
value: '魔法怎么失灵啦',
},
},
});
}
}
```
## 更新节点
无论是使用 Component 还是 TemplateRef, 都是一样的更新方式.
```ts
node.setData({
ngArguments: {
value: '晚风中闪过 几帧从前啊',
},
});
```
## 有 demo 吗?
有的, 因为X6渲染节点部分与框架是解耦的. 因此 `x6-angular-shape` 包并非是直接在源代码里改的, 而是在一个单独的Angular环境中开发的. 该 demo 还提供了多种节点类型的性能测试, 详情请参考[Eve-Sama/x6-angular-shape](https://github.com/Eve-Sama/x6-angular-shape)、[X6与G6的性能对比, 以及X6多节点类型下的FPS临界点讨论](https://github.com/antvis/X6/issues/3266)
## FAQ
### 为什么输入属性不能直接放在 data 中而需要放在 ngArguments 中? 且为什么不叫 ngInput?
因为并非所有 `node.data` 中的属性都是输入属性, 所以遍历 `data` 中的所有属性进行赋值是不合适的. 至于为什么叫 `ngArguments` 主要是有两点考虑.
- 1.x版本中已经这么用了, 沿用该API可以降低用户升级成本
- `Input` 的概念其实是来自 `Component`, 而 `TemplateRef` 中是 `context`, 在二者的基础上抽象一个 `Arguments` 的概念更通用些
### 2.x版本的 x6-angular-shape 相比较1.x版本有什么新特性吗?
实现思路其实和之前是差不多的. 但是确实有几个点值得一提.
#### demo更聚焦
1.x版本的 demo 除了渲染组件外, 还写了连线、清除等一系列案例. 看似扩展, 实则眼花缭乱. 作为 `x6-angular-shape`的 demo, 2.x版本更加聚焦, 更加聚焦于shape的使用与性能测试等, 与插件无关的内容请查看X6官网.
#### 功能更稳定
在1.x版本中, 虽然实现了功能, 但是使用场景的思考不够全面. 比如`ngArguments`的变化, 对 `TemplateRef`的场景并不生效. 虽然对`Component`生效但是无法触发`ngOnChanges`. 而在新版本中, 这些问题都将不复存在.
### 版本要求
你的Angular版本至少在14及以上才可以. 14以下需要用 hack 的方式实现一些特性, 比较麻烦. 暂时不提供, 如有需要可提issue, 我再专门介绍下如何实现.

View File

@ -1,67 +0,0 @@
{
"name": "@antv/x6-angular-shape",
"version": "2.0.0",
"description": "X6 shape for rendering angular components.",
"main": "lib/index.js",
"module": "es/index.js",
"unpkg": "dist/index.js",
"jsdelivr": "dist/index.js",
"types": "lib/index.d.ts",
"files": [
"dist",
"es",
"lib",
"src"
],
"keywords": [
"shape",
"angular",
"render",
"x6",
"antv"
],
"scripts": {
"clean:turbo": "rss",
"clean:build": "rss",
"clean:coverage": "rss",
"clean": "rss",
"build:esm": "rss",
"build:cjs": "rss",
"build:umd": "rss",
"build:dev": "rss",
"build:watch": "rss",
"build:watch:esm": "rss",
"build:watch:cjs": "rss",
"build": "rss",
"prebuild": "rss",
"test": "rss",
"coveralls": "rss",
"pretest": "rss"
},
"peerDependencies": {
"@antv/x6": "^2.x",
"@angular/core": ">= 14"
},
"devDependencies": {
"@antv/x6": "^2.x",
"@angular/core": ">= 14"
},
"author": {
"name": "Eve-Sama",
"email": "948832626@qq.com"
},
"license": "MIT",
"homepage": "https://x6.antv.antgroup.com/tutorial/intermediate/angular",
"bugs": {
"url": "https://github.com/antvis/x6/issues"
},
"repository": {
"type": "git",
"url": "https://github.com/antvis/x6.git",
"directory": "packages/x6-angular-shape"
},
"publishConfig": {
"access": "public",
"registry": "https://registry.npmjs.org"
}
}

View File

@ -1,3 +0,0 @@
export * from './node'
export * from './view'
export * from './registry'

View File

@ -1,102 +0,0 @@
import { Node, Markup, ObjectExt } from '@antv/x6'
export class AngularShape<
Properties extends AngularShape.Properties = AngularShape.Properties,
> extends Node<Properties> {}
export namespace AngularShape {
export type Primer =
| 'rect'
| 'circle'
| 'path'
| 'ellipse'
| 'polygon'
| 'polyline'
export interface Properties extends Node.Properties {
primer?: Primer
}
}
export namespace AngularShape {
function getMarkup(primer?: Primer) {
const markup: Markup.JSONMarkup[] = []
const content = Markup.getForeignObjectMarkup()
if (primer) {
markup.push(
...[
{
tagName: primer,
selector: 'body',
},
content,
],
)
} else {
markup.push(content)
}
return markup
}
AngularShape.config<Properties>({
view: 'angular-shape-view',
markup: getMarkup(),
attrs: {
body: {
fill: 'none',
stroke: 'none',
refWidth: '100%',
refHeight: '100%',
},
fo: {
refWidth: '100%',
refHeight: '100%',
},
},
propHooks(metadata: Properties) {
if (metadata.markup == null) {
const primer = metadata.primer
if (primer) {
metadata.markup = getMarkup(primer)
let attrs = {}
switch (primer) {
case 'circle':
attrs = {
refCx: '50%',
refCy: '50%',
refR: '50%',
}
break
case 'ellipse':
attrs = {
refCx: '50%',
refCy: '50%',
refRx: '50%',
refRy: '50%',
}
break
default:
break
}
metadata.attrs = ObjectExt.merge(
{},
{
body: {
refWidth: null,
refHeight: null,
...attrs,
},
},
metadata.attrs || {},
)
}
}
return metadata
},
})
Node.registry.register('angular-shape', AngularShape, true)
}

View File

@ -1,32 +0,0 @@
import { Injector, TemplateRef, Type } from '@angular/core'
import { Graph, Node } from '@antv/x6'
export type Content = TemplateRef<any> | Type<any>
export type AngularShapeConfig = Node.Properties & {
shape: string
injector: Injector
content: Content
}
export const registerInfo: Map<
string,
{
injector: Injector
content: Content
}
> = new Map()
export function register(config: AngularShapeConfig) {
const { shape, injector, content, ...others } = config
registerInfo.set(shape, { injector, content })
Graph.registerNode(
shape,
{
inherit: 'angular-shape',
...others,
},
true,
)
}

View File

@ -1,124 +0,0 @@
import {
ComponentRef,
EmbeddedViewRef,
TemplateRef,
ViewContainerRef,
} from '@angular/core'
import { Dom, NodeView } from '@antv/x6'
import { AngularShape } from './node'
import { Content, registerInfo } from './registry'
export class AngularShapeView extends NodeView<AngularShape> {
getNodeContainer(): HTMLDivElement {
return this.selectors && (this.selectors.foContent as HTMLDivElement)
}
override confirmUpdate(flag: number): number {
const ret = super.confirmUpdate(flag)
return this.handleAction(ret, AngularShapeView.action, () =>
this.renderAngularContent(),
)
}
private getNgArguments(): Record<string, any> {
const input = (this.cell.data?.ngArguments as Record<string, any>) || {}
return input
}
/** 当执行 node.setData() 时需要对实例设置新的输入值 */
private setInstanceInput(
content: Content,
ref: EmbeddedViewRef<any> | ComponentRef<any>,
): void {
const ngArguments = this.getNgArguments()
if (content instanceof TemplateRef) {
const embeddedViewRef = ref as EmbeddedViewRef<any>
embeddedViewRef.context = { ngArguments }
} else {
const componentRef = ref as ComponentRef<any>
Object.keys(ngArguments).forEach((v) =>
componentRef.setInput(v, ngArguments[v]),
)
componentRef.changeDetectorRef.detectChanges()
}
}
protected renderAngularContent(): void {
this.unmountAngularContent()
const container = this.getNodeContainer()
if (container) {
const node = this.cell
const { injector, content } = registerInfo.get(node.shape)!
const viewContainerRef = injector.get(ViewContainerRef)
if (content instanceof TemplateRef) {
const ngArguments = this.getNgArguments()
const embeddedViewRef = viewContainerRef.createEmbeddedView(content, {
ngArguments,
})
embeddedViewRef.rootNodes.forEach((node) => container.appendChild(node))
embeddedViewRef.detectChanges()
node.on('change:data', () =>
this.setInstanceInput(content, embeddedViewRef),
)
} else {
const componentRef = viewContainerRef.createComponent(content)
const insertNode = (componentRef.hostView as EmbeddedViewRef<any>)
.rootNodes[0] as HTMLElement
container.appendChild(insertNode)
this.setInstanceInput(content, componentRef)
node.on('change:data', () =>
this.setInstanceInput(content, componentRef),
)
}
}
}
protected unmountAngularContent(): HTMLDivElement {
const container = this.getNodeContainer()
container.innerHTML = ''
return container
}
override onMouseDown(e: Dom.MouseDownEvent, x: number, y: number) {
const target = e.target as Element
const tagName = target.tagName.toLowerCase()
if (tagName === 'input') {
const type = target.getAttribute('type')
if (
type == null ||
[
'text',
'password',
'number',
'email',
'search',
'tel',
'url',
].includes(type)
) {
return
}
}
super.onMouseDown(e, x, y)
}
override unmount(): this {
this.unmountAngularContent()
super.unmount()
return this
}
}
export namespace AngularShapeView {
export const action = 'angular' as any
AngularShapeView.config({
bootstrap: [action],
actions: {
component: action,
},
})
NodeView.registry.register('angular-shape-view', AngularShapeView, true)
}

View File

@ -1,3 +0,0 @@
{
"extends": "../../tsconfig.json"
}

View File

@ -1,38 +1,3 @@
## @antv/x6-common [2.0.10](https://github.com/antvis/x6/compare/@antv/x6-common@2.0.9...@antv/x6-common@2.0.10) (2023-02-24)
### Bug Fixes
* add textLength & lengthAdjust to CASE_SENSITIVE_ATTR ([#3281](https://github.com/antvis/x6/issues/3281)) ([76fb1ac](https://github.com/antvis/x6/commit/76fb1acf74b0f1c308f7c824d02c12244b7ac8f3))
## @antv/x6-common [2.0.8](https://github.com/antvis/x6/compare/@antv/x6-common@2.0.7...@antv/x6-common@2.0.8) (2023-02-18)
### Bug Fixes
* fix typo for dom event handlers ([#3255](https://github.com/antvis/x6/issues/3255)) ([9b4fa86](https://github.com/antvis/x6/commit/9b4fa86daa587fe8818f3615bc1e40738a0f2319))
## @antv/x6-common [2.0.6](https://github.com/antvis/x6/compare/@antv/x6-common@2.0.5...@antv/x6-common@2.0.6) (2023-01-31)
### Bug Fixes
* fix index error for priorityQueue ([#3179](https://github.com/antvis/x6/issues/3179)) ([d64150b](https://github.com/antvis/x6/commit/d64150bfadf10fe21f44734a0267261260b8c53b))
## @antv/x6-common [2.0.5](https://github.com/antvis/x6/compare/@antv/x6-common@2.0.4...@antv/x6-common@2.0.5) (2023-01-18)
### Bug Fixes
* optimize css loader ([#3156](https://github.com/antvis/x6/issues/3156)) ([9c48ad8](https://github.com/antvis/x6/commit/9c48ad8dfc99e623a57855295d07c35be5483073))
## @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 @@
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

View File

@ -1,6 +1,6 @@
{
"name": "@antv/x6-common",
"version": "2.0.11",
"version": "2.0.3",
"description": "Basic toolkit for X6",
"main": "lib/index.js",
"module": "es/index.js",
@ -10,8 +10,7 @@
"files": [
"dist",
"es",
"lib",
"src"
"lib"
],
"keywords": [
"util",

View File

@ -45,7 +45,7 @@ export class PriorityQueue<T> {
*/
insert(priority: number, value: T, id?: string) {
const item: PriorityQueue.DataItem<T> = { priority, value }
const index = this.data.length
const index = this.data.length - 1
if (id) {
item.id = id
this.index[id] = index
@ -96,9 +96,7 @@ export class PriorityQueue<T> {
const data = this.data
const peek = data[0]
const last = data.pop()!
if (peek.id) {
delete this.index[peek.id]
}
delete this.index[data.length]
if (data.length > 0) {
data[0] = last

View File

@ -2,7 +2,6 @@ import { Platform } from '../platform'
interface CssModule {
name: string
loadTimes: number
styleElement: HTMLStyleElement | null
}
@ -11,10 +10,7 @@ const cssModules: CssModule[] = []
export function ensure(name: string, content: string) {
const cssModule = cssModules.find((m) => m.name === name)
if (cssModule) {
cssModule.loadTimes += 1
if (cssModule.loadTimes > 1) {
return
}
return
}
if (!Platform.isApplyingHMR()) {
@ -29,7 +25,6 @@ export function ensure(name: string, content: string) {
cssModules.push({
name,
loadTimes: 1,
styleElement,
})
}
@ -39,13 +34,7 @@ export function clean(name: string) {
const index = cssModules.findIndex((m) => m.name === name)
if (index > -1) {
const cssModule = cssModules[index]
cssModule.loadTimes -= 1
if (cssModule.loadTimes > 0) {
return
}
let styleElement = cssModule.styleElement
let styleElement = cssModules[index].styleElement
if (styleElement && styleElement.parentNode) {
styleElement.parentNode.removeChild(styleElement)
}

View File

@ -6,8 +6,6 @@ export const CASE_SENSITIVE_ATTR = [
'attributeName',
'attributeType',
'repeatCount',
'textLength',
'lengthAdjust',
]
export type Attributes = { [key: string]: string | number | null | undefined }

View File

@ -729,7 +729,7 @@ type TypeEventHandlersBase<TDelegateTarget, TData, TCurrentTarget, TTarget> = {
TCurrentTarget,
TTarget
>]?:
| TypeEventHandler<TDelegateTarget, TData, TCurrentTarget, TTarget, string>
| TypeEventHandler<TDelegateTarget, TData, TCurrentTarget, TTarget, TType>
| false
| Record<string, unknown>
}

View File

@ -198,14 +198,7 @@ export function text(
const autoLineHeight = defaultLineHeight === 'auto'
const lineHeight = autoLineHeight ? '1.5em' : defaultLineHeight || '1em'
let needEmptyElem = true
const childs = elem.children
if (childs.length === 1 && childs[0].tagName.toUpperCase() === 'TITLE') {
needEmptyElem = false
}
if (needEmptyElem) {
empty(elem)
}
empty(elem)
attr(elem, {
// Preserve spaces, do not consecutive spaces to get collapsed to one.

View File

@ -1,4 +1,4 @@
export { debounce, throttle } from 'lodash-es'
export { debounce } from 'lodash-es'
type Fn = (...args: any[]) => any

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,7 +0,0 @@
node_modules
yarn.lock
.vscode
devtools.crx
devtools.pem
.DS_Store
.vscode

View File

@ -1,49 +0,0 @@
# X6 Devtool
> A devtool for @antv/x6 in chrome, it's still WIP, you can load it in unpack way;
## Quick Start
### Import unpacked plugin
![image](https://user-images.githubusercontent.com/15213473/150081309-61f9c451-c35e-4dab-a23c-ed5e425e7ec5.png)
1. Open the Extension Management page by navigating to chrome://extensions.
2. Enable Developer Mode by clicking the toggle switch next to Developer mode.
3. Click the Load unpacked button and select the 'devtool' directory.
### Connect with X6 Graph;
#### In X6
```javascript
// init window hook
window.__x6_instances__ = [];
var graph = new Graph({...blablabla});
window.__x6_instances__.push(graph);
```
### Using devtool
After these steps, the tab 'AntV X6' should show in devtools' tab, select it and choose a canvas
![image](https://user-images.githubusercontent.com/1826685/223881189-bf99f2ad-5158-4c39-8d16-018a287ac2fe.png)
## Features
### Inspect Element in Graph
![image](https://user-images.githubusercontent.com/1826685/223881189-bf99f2ad-5158-4c39-8d16-018a287ac2fe.png)
### View and Modify Attributes of Element
![image](https://user-images.githubusercontent.com/1826685/223881189-bf99f2ad-5158-4c39-8d16-018a287ac2fe.png)
### Using select element directly in graph
![image](https://user-images.githubusercontent.com/1826685/223881189-bf99f2ad-5158-4c39-8d16-018a287ac2fe.png)

View File

@ -1,19 +0,0 @@
{
"name": "AntV X6 Dev Tool",
"description": "devtool for x6 graph",
"version": "0.0.1",
"devtools_page": "x6_devtools.html",
"minimum_chrome_version": "49",
"manifest_version": 2,
"permissions": ["file:///*", "http://*/*", "https://*/*"],
"icons": {
"16": "icons/16.png",
"32": "icons/32.png",
"48": "icons/48.png",
"128": "icons/128.png"
},
"content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'",
"web_accessible_resources": ["main.html", "panel.html", "script/backend.js"],
"browser_action": {}
}

View File

@ -1,59 +0,0 @@
<!DOCTYPE html>
<html style="display: flex">
<head>
<meta charset="utf8" />
<style>
html {
display: flex;
}
body {
margin: 0;
padding: 0;
flex: 1;
display: flex;
}
#container {
display: block;
flex: 1;
width: 100%;
position: fixed;
overflow: auto;
height: 100%;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
#desc {
width: 240px;
padding-left: 12px;
height: 100%;
position: fixed;
right: 0;
top: 0;
overflow: auto;
background: white;
border-left: 1px solid #eee;
}
.tag.selected {
background: rgba(135, 59, 244, 0.5);
}
.tag {
background: rgba(128, 128, 128, 0.5);
padding-left: 8px;
}
.tag.selected::after {
display: inline-block;
opacity: 0.4;
font-size: 12px;
content: '$gElement';
}
</style>
</head>
<body>
<div id="container"></div>
<script src="./ui/ui.js"></script>
<script src="./script/panel.js"></script>
</body>
</html>

View File

@ -1,20 +0,0 @@
chrome.runtime.onMessage.addListener((req, sender) => {
if (req.isAntVX6 && sender && sender.tab) {
if (req.disabled) {
chrome.browserAction.setIcon({
tabId: sender.tab.id,
path: 'icons/48-disabled.png',
})
} else {
chrome.browserAction.setIcon({
tabId: sender.tab.id,
path: {
16: 'icons/16.png',
32: 'icons/32.png',
48: 'icons/48.png',
128: 'icons/128.png',
},
})
}
}
})

View File

@ -1,52 +0,0 @@
/**
* global instance and flag
*/
var panelInstance
var itv
function createPanelInstance() {
if (panelInstance) {
return
}
chrome.devtools.inspectedWindow.eval(
`!!(window.__x6_instances__ && window.__x6_instances__.length)`,
function (connected, err) {
if (!connected) {
return
}
clearInterval(itv)
panelInstance = chrome.devtools.panels.create(
'AntV X6',
'icons/32.png',
'panel.html',
function (panel) {
panel.onHidden.addListener(function () {
chrome.devtools.inspectedWindow.eval(`(function() {
var elements = document.getElementsByClassName('g_devtool_rect');
[].forEach.apply(elements, [function (e) {
e.remove();
}])
})()`)
})
},
)
chrome.runtime.sendMessage({
isAntVX6: true,
disabled: false,
})
},
)
}
chrome.devtools.network.onNavigated.addListener(function () {
// createPanelIfReactLoaded();
})
createPanelInstance()
itv = setInterval(createPanelInstance, 1000)

View File

@ -1,281 +0,0 @@
//
// script execute function
//
// execute raw script in inspect window
var executeScriptInInspectWindow = function (script) {
return new Promise(function (resolve, reject) {
chrome.devtools.inspectedWindow.eval(script, function (result, exception) {
if (exception) {
console.error('eval error', script)
reject(exception)
} else {
resolve(result)
}
})
})
}
// execute function in anynomous code block
var executeFuntionInInspectWindow = function (func, args) {
return executeScriptInInspectWindow(
`(${func.toString()}).apply(window, ${JSON.stringify(args)})`,
)
}
//
// these are the function that execute in inspect window
//
function doFPSThings() {
if (window.__x6_fps) {
cancelAnimationFrame(window.__x6_fps)
}
let lastCalledTime
let fps
let delta
function requestAnimFrame() {
if (!lastCalledTime) {
lastCalledTime = performance.now()
fps = 0
} else {
delta = (performance.now() - lastCalledTime) / 1000
lastCalledTime = performance.now()
fps = 1 / delta
}
window.__x6_fps_value = Math.round(fps)
window.__x6_fps = requestAnimationFrame(requestAnimFrame)
}
requestAnimFrame()
}
// get global X6 Graph structure
function getGlobalInstances() {
var instances = window.__x6_instances__
var gmap = {}
var getGraphCells = function (graph) {
// 使用model中的引用直接获取元素
return graph.model.collection.cells
}
window.__x6_instances__.globalMap = gmap
var gInfo = []
function getX6Instance(instance) {
const ga = {}
// antv/g 实现了getChildren可以递归获取x6不行
// if (instance.getChildren && instance.getChildren()) {
// ga.children = instance.getChildren().map(function (p) {
// return getX6Instance(p);
// });
// }
if (!instance.__dev_hash) {
ga.hash = Math.random().toString(16).slice(-8)
instance.__dev_hash = ga.hash
} else {
ga.hash = instance.__dev_hash
}
gmap[ga.hash] = instance
ga.id = instance.id
ga.name = instance.name || instance.prop('shape') || instance.prop('label')
ga.type = instance.isEdge() ? 'edge' : 'node'
return ga
}
if (instances && instances.length) {
gInfo = instances.map(function (instance) {
const hash = instance.hash || Math.random().toString(16).slice(-8)
const cells = getGraphCells(instance).map((e) => getX6Instance(e))
const plugins = Array.from(instance.installedPlugins).map((plugin) => {
const hash = plugin.hash || Math.random().toString(16).slice(-8)
const ga = {
name: plugin.name,
hash,
type: 'plugin',
}
plugin.hash = ga.hash
gmap[ga.hash] = plugin
return ga
})
var ga = {
type: 'svg',
name: 'Graph',
nodeName: 'graph',
hash,
children: cells.concat(plugins),
memory: window.performance.memory.usedJSHeapSize,
fps: window.__x6_fps_value,
}
instance.hash = ga.hash
gmap[ga.hash] = instance
return ga
})
} else {
gInfo.length = 0
}
return gInfo
}
function checkGraphByHash(hash) {
return !!window.__x6_instances__.map((e) => e.hash).includes(hash)
}
function createBoxUsingId(bbox, id, color) {
var el = document.createElement('div')
window[id] = el
el.classList.add('x6_devtool_rect')
document.body.appendChild(el)
el.style.position = 'absolute'
el.style.width = `${bbox.width}px`
el.style.height = `${bbox.height}px`
el.style.top = `${bbox.top}px`
el.style.left = `${bbox.left}px`
el.style.background = color || 'rgba(135, 59, 244, 0.5)'
el.style.border = '2px dashed rgb(135, 59, 244)'
el.style.boxSizing = 'border-box'
}
function removeBoxUsingId(id) {
if (window[id]) {
window[id].remove()
}
}
function removeAllBox() {
var elements = document.getElementsByClassName('x6_devtool_rect')
;[].forEach.apply(elements, [
function (e) {
e.remove()
},
])
}
function getElemetBBoxByHash(hash) {
try {
var targetEl = window.__x6_instances__.globalMap[hash]
if (targetEl) {
const bbox = targetEl.getGraphArea
? targetEl.getGraphArea()
: targetEl.getBBox()
const pageBBox = targetEl.model.graph.localToPage(bbox)
const { width, height, x, y, left, top } = pageBBox
return { width, height, x, y, left, top }
}
} catch (e) {}
return {}
}
function getElementAttrByHash(hash) {
const instance = window.__x6_instances__.globalMap[hash]
if (instance) {
return instance.prop
? instance.prop()
: instance.options
? {
...instance.options,
container: undefined,
}
: {}
}
return {}
}
function setElementAttrByHash(hash, name, value) {
const instance = window.__x6_instances__.globalMap[hash]
// graph can not set prop
if (instance && instance.prop) {
instance.prop(name, value)
}
}
function setGElementByHash(hash) {
window.$gElemet = hash ? window.__x6_instances__.globalMap[hash] : undefined
}
function consoleElementByHash(hash, desc) {
window.console.log(
desc || '<Click To Expand>',
window.__x6_instances__.globalMap[hash],
)
}
//
// these are the functions that run in devtools panel
//
function setRect(bbox, id, color) {
executeFuntionInInspectWindow(removeBoxUsingId, [id]).finally(() => {
executeFuntionInInspectWindow(createBoxUsingId, [bbox, id, color])
})
}
function cleanRect(id) {
executeFuntionInInspectWindow(removeBoxUsingId, [id])
}
function showRect(hash, id, color) {
executeFuntionInInspectWindow(getElemetBBoxByHash, [hash]).then((bbox) => {
setRect(bbox, id, color)
})
}
function cleanAllRect() {
executeFuntionInInspectWindow(removeAllBox)
}
function getAttrs(hash) {
if (hash) {
executeFuntionInInspectWindow(setGElementByHash, [hash])
return executeFuntionInInspectWindow(getElementAttrByHash, [hash])
}
return executeFuntionInInspectWindow(setGElementByHash, [])
}
function updateAttrs(hash, name, attrs) {
return executeFuntionInInspectWindow(setElementAttrByHash, [
hash,
name,
attrs,
])
}
function consoleEl(hash, desc) {
return executeFuntionInInspectWindow(consoleElementByHash, [hash, desc])
}
function checkGraphAlive(hash) {
return executeFuntionInInspectWindow(checkGraphByHash, [hash]).then((res) => {
if (res) {
return true
} else {
return false
}
})
}
function getNowGraphData() {
return executeFuntionInInspectWindow(getGlobalInstances)
}
function startFPSMonitor() {
return executeFuntionInInspectWindow(doFPSThings)
}
getNowGraphData().then(function (data) {
const container = document.getElementById('container')
mount(data, container, {
showRect,
getAttrs,
cleanRect,
updateAttrs,
consoleEl,
checkGraphAlive,
getNowGraphData,
cleanAllRect,
startFPSMonitor,
})
})
startFPSMonitor()

File diff suppressed because one or more lines are too long

View File

@ -1,49 +0,0 @@
/*
object-assign
(c) Sindre Sorhus
@license MIT
*/
/*!
Copyright (c) 2018 Jed Watson.
Licensed under the MIT License (MIT), see
http://jedwatson.github.io/classnames
*/
/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */
/** @license React v0.20.2
* scheduler.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/** @license React v16.13.1
* react-is.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/** @license React v17.0.2
* react-dom.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/** @license React v17.0.2
* react.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

View File

@ -1,8 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<script src="./script/main.js"></script>
</head>
<body></body>
</html>

View File

@ -1,29 +0,0 @@
{
"name": "@antv/x6-devtool",
"version": "0.0.1",
"private": true,
"description": "devtool for x6 in browser",
"license": "MIT",
"main": "index.js",
"scripts": {
"start": "webpack -w",
"build": "webpack"
},
"dependencies": {
"antd": "4.18.3",
"react": "17.0.2",
"react-dom": "17.0.2",
"react-json-view": "1.21.3",
"@ant-design/icons": "^5.0.1"
},
"devDependencies": {
"@babel/core": "^7.16.7",
"@babel/preset-env": "^7.16.8",
"@babel/preset-react": "^7.16.7",
"babel-loader": "^8.2.3",
"css-loader": "^6.5.1",
"style-loader": "^3.3.1",
"webpack": "^5.66.0",
"webpack-cli": "4.9.1"
}
}

View File

@ -1,189 +0,0 @@
import React, { useState, useEffect } from 'react'
import { Row, Select, Col, Button, Tooltip, Tag, Empty } from 'antd'
import { CodeOutlined } from '@ant-design/icons'
import GTree from './GTree'
const countTree = (tree) => {
if (!tree) {
return 0
}
const count = tree?.children?.reduce((a, b) => a + countTree(b), 1) || 1
tree.count = count
return count
}
export function convertMemoryUnit(number, digits = 2) {
let value
let unit
if (number > 1000000000) {
unit = 'GB'
value = number / 1000000000
} else if (number > 1000000) {
unit = 'MB'
value = number / 1000000
} else if (number > 1000) {
unit = 'KB'
value = number / 1000
} else {
unit = 'B'
value = number
}
value = value && value % 1 !== 0 ? value.toFixed(digits) : value
return value + unit
}
const HeadBar = (props) => {
const {
data,
setSelectedHash,
actions,
selectedData,
setData,
selectedHash,
} = props
const [graphAlive, setGraphAlive] = useState(true)
useEffect(() => {
const itv = setInterval(() => {
actions.checkGraphAlive(selectedHash).then((res) => {
setGraphAlive(res)
if (res) {
actions.getNowGraphData().then((d) => {
if (d) {
setData(d)
}
})
}
})
}, 1000)
return () => {
clearInterval(itv)
}
}, [actions, setData, selectedData, selectedHash])
useEffect(() => {
if (!graphAlive) {
actions.getNowGraphData().then((d) => {
if (d) {
setData(d)
setSelectedHash(d[0].hash)
}
})
}
}, [actions, graphAlive, setData, setSelectedHash])
return (
<Row
align="middle"
style={{
padding: 2,
marginBottom: 6,
borderBottom: '1px solid #ddd',
background: 'rgba(0, 0, 0, 0.05)',
position: 'fixed',
width: '100%',
top: 0,
left: 0,
zIndex: 999,
}}
gutter={[12, 12]}
>
<Col>
<Select
bordered={false}
size="small"
defaultValue={0}
options={data.map((e, i) => ({
label: `Graph ${i}`,
value: e.hash,
info: e,
}))}
value={selectedHash}
onChange={(val) => {
setSelectedHash(val)
}}
placeholder="Choose a graph to inspect"
style={{ width: '100%' }}
/>
</Col>
{graphAlive ? (
<Col>
<Tag color="green">ALIVE</Tag>
</Col>
) : (
<Col>
<Tag color="red">DEAD</Tag>
<span>Trying to reconnect</span>
</Col>
)}
{selectedData && <Col>{selectedData?.count} Shapes</Col>}
{selectedData?.memory > 0 && (
<Col>HeapMemory:{convertMemoryUnit(selectedData.memory)}</Col>
)}
{selectedData?.fps > 0 && <Col>FPS: {selectedData?.fps}</Col>}
<Col flex={1} />
<Col>
<Button
size="small"
type="text"
onClick={() => {
actions.consoleEl(selectedData.hash)
}}
>
<Tooltip arrowPointAtCenter title="Console X6 Graph">
<CodeOutlined />
</Tooltip>
</Button>
</Col>
</Row>
)
}
const Devtool = (props) => {
const { data: initData = [], actions = {} } = props
const [selectedData, setSelectedData] = useState(initData[0])
const [selectedHash, setSelectedHash] = useState(initData[0].hash)
const [data, setData] = useState(initData)
useEffect(() => {
return () => {
actions.cleanAllRect()
actions.startFPSMonitor()
}
}, [actions, selectedHash])
useEffect(() => {
const target = data.find((e) => e.hash === selectedHash)
countTree(target)
setSelectedData(target)
}, [selectedHash, data])
return (
<div>
<HeadBar
data={data}
setSelectedHash={setSelectedHash}
selectedData={selectedData}
selectedHash={selectedHash}
actions={actions}
setData={setData}
/>
<div
style={{
marginTop: 48,
position: 'relative',
zIndex: 1,
}}
>
{selectedData ? (
<GTree actions={actions} data={selectedData} />
) : (
<Empty />
)}
</div>
</div>
)
}
export default Devtool

View File

@ -1,179 +0,0 @@
import { Drawer, Empty, Space, Tree, Typography } from 'antd'
import React, { useState, useEffect } from 'react'
import ReactJson from 'react-json-view'
import {
BlockOutlined,
PartitionOutlined,
AppstoreOutlined,
NodeIndexOutlined,
ToolOutlined,
} from '@ant-design/icons'
const iconMap = {
group: <BlockOutlined />,
svg: <PartitionOutlined />,
shape: <AppstoreOutlined />,
node: <AppstoreOutlined />,
edge: <NodeIndexOutlined />,
plugin: <ToolOutlined />,
}
const AttrsDrawer = ({ hash, getAttrs, onCancel, updateAttrs }) => {
const [val, setVal] = useState(hash)
const change = (all) => {
const { updated_src, namespace, name, existing_value, new_value } = all
const key = namespace[0] || name
const value = updated_src[key]
// 这个函数递归对新的value进行预处理。
// input框接收的new_value是字符串需要按照旧值做转换
const setNewValue = (value, paths, existing_value, new_value) => {
const key = paths.shift()
if (paths.length > 0) {
setNewValue(value[key], paths, existing_value, new_value)
} else {
if (new_value === undefined) {
// 删除
delete value[key]
} else if (typeof existing_value === 'undefined') {
// 新增
value[key] = new_value
} else if (typeof existing_value === 'boolean') {
value[key] = Boolean(new_value)
} else if (typeof existing_value === 'number') {
const val = Number(new_value)
if (!isNaN(val)) {
value[key] = val
}
} else if (typeof existing_value === 'string') {
value[key] = '' + new_value
}
}
}
setNewValue(
value,
namespace.slice(1).concat(name),
existing_value,
new_value,
)
updateAttrs(hash, key, value, all)
}
useEffect(() => {
if (hash && getAttrs) {
getAttrs(hash).then((res) => {
setVal(res)
})
} else {
onCancel()
getAttrs()
}
}, [getAttrs, hash, onCancel])
return (
<Drawer mask={false} onClose={onCancel} visible={hash}>
<ReactJson
style={{ fontSize: 12 }}
onAdd={change}
onEdit={change}
onDelete={change}
src={val}
/>
</Drawer>
)
}
const buildTreeData = (data = {}, isRoot) => {
const node = {
title: data.name || data.shape || data.type,
type: data.type,
key: data.hash,
name: data.name,
id: data.id,
hash: data.hash,
count: data.count,
num: data.children?.length || 0,
}
if (data.children) {
node.children = data.children.map((e) => buildTreeData(e))
}
if (isRoot) {
node.type = 'svg'
node.title = 'svg'
// node.key = node.hash || 'svg'
return node
}
return node
}
const GTree = (props) => {
const { data, actions = {} } = props
const [selectedKey, setSelected] = useState()
useEffect(() => {
actions.showRect(selectedKey, '__x6_select__', 'rgba(29, 57, 196, 0.5)')
return () => {
actions.cleanRect('__x6_select__')
}
}, [actions, selectedKey])
if (!data) {
return <Empty />
}
const treeData = buildTreeData(data, true)
return (
<div>
<Tree
selectedKeys={[selectedKey]}
onSelect={(keys) => setSelected(keys[0])}
showLine={{ showLeafIcon: false }}
height={document.body.clientHeight - 45}
titleRender={(node) => (
<div
onMouseEnter={() => {
actions.showRect(node.key, '__x6_hover__')
}}
onMouseLeave={() => {
actions.cleanRect('__x6_hover__')
}}
>
<Space>
{iconMap[node.type]}
{node.title}
{node.name && (
<Typography.Text type="secondary">
name:{node.name}
</Typography.Text>
)}
{node.id && (
<Typography.Text type="secondary">id:{node.id}</Typography.Text>
)}
{node.num > 0 && (
<Typography.Text type="secondary">
({node.num} children / {node.count || 0} descendants)
</Typography.Text>
)}
</Space>
</div>
)}
treeData={[treeData]}
/>
<AttrsDrawer
hash={selectedKey}
onCancel={() => setSelected()}
getAttrs={actions.getAttrs}
updateAttrs={actions.updateAttrs}
/>
</div>
)
}
export default GTree

View File

@ -1,8 +0,0 @@
import React from 'react'
import ReactDOM from 'react-dom'
import Devtool from './components/Devtool'
import 'antd/dist/antd.css'
window.mount = (data = [], container, actions = {}) => {
ReactDOM.render(<Devtool data={data} actions={actions} />, container)
}

View File

@ -1,30 +0,0 @@
const path = require('path')
module.exports = {
entry: './ui/index.js',
output: {
path: path.resolve(__dirname, 'devtools', 'ui'),
filename: 'ui.js',
},
optimization: {
usedExports: true,
},
module: {
rules: [
{
test: /\.css$/i,
use: ['style-loader', 'css-loader'],
},
{
test: /\.jsx?$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env', '@babel/preset-react'],
},
},
},
],
},
}

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

View File

@ -10,8 +10,7 @@
"files": [
"dist",
"es",
"lib",
"src"
"lib"
],
"keywords": [
"geometry",

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

View File

@ -10,8 +10,7 @@
"files": [
"dist",
"es",
"lib",
"src"
"lib"
],
"keywords": [
"plugin",

View File

@ -7,7 +7,7 @@ declare module '@antv/x6/lib/graph/graph' {
enableClipboard: () => Graph
disableClipboard: () => Graph
toggleClipboard: (enabled?: boolean) => Graph
isClipboardEmpty: (options?: Clipboard.Options) => boolean
isClipboardEmpty: () => boolean
getCellsInClipboard: () => Cell[]
cleanClipboard: () => Graph
copy: (cells: Cell[], options?: Clipboard.CopyOptions) => Graph
@ -55,10 +55,10 @@ Graph.prototype.toggleClipboard = function (enabled?: boolean) {
return this
}
Graph.prototype.isClipboardEmpty = function (options?: Clipboard.Options) {
Graph.prototype.isClipboardEmpty = function () {
const clipboard = this.getPlugin('clipboard') as Clipboard
if (clipboard) {
return clipboard.isEmpty(options)
return clipboard.isEmpty()
}
return true
}

View File

@ -89,12 +89,7 @@ export class ClipboardImpl {
}
}
isEmpty(options: ClipboardImpl.Options = {}) {
if (options.useLocalStorage) {
// With useLocalStorage turned on, no real cells can be obtained without deserialize first
// https://github.com/antvis/X6/issues/2573
this.deserialize(options)
}
isEmpty() {
return this.cells.length <= 0
}

View File

@ -11,7 +11,7 @@ export class Clipboard
public options: Clipboard.Options
public name = 'clipboard'
constructor(options: Clipboard.Options = { enabled: true }) {
constructor(options: Clipboard.Options) {
super()
this.options = options
}
@ -32,12 +32,14 @@ export class Clipboard
if (this.disabled) {
this.options.enabled = true
}
return this
}
disable() {
if (!this.disabled) {
this.options.enabled = false
}
return this
}
toggleEnabled(enabled?: boolean) {
@ -58,8 +60,8 @@ export class Clipboard
return this
}
isEmpty(options: Clipboard.Options = {}) {
return this.clipboardImpl.isEmpty(options)
isEmpty() {
return this.clipboardImpl.isEmpty()
}
getCellsInClipboard() {

View File

@ -1,10 +1,3 @@
## @antv/x6-plugin-dnd [2.0.4](https://github.com/antvis/x6/compare/@antv/x6-plugin-dnd@2.0.3...@antv/x6-plugin-dnd@2.0.4) (2023-01-13)
### Bug Fixes
* change dragging container options ([#3139](https://github.com/antvis/x6/issues/3139)) ([7b091f3](https://github.com/antvis/x6/commit/7b091f35dee147c5e7bf97577e14e11ceb7e8e3d))
## @antv/x6-plugin-dnd [2.0.3](https://github.com/antvis/x6/compare/@antv/x6-plugin-dnd@2.0.2...@antv/x6-plugin-dnd@2.0.3) (2022-11-25)

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

View File

@ -1,6 +1,6 @@
{
"name": "@antv/x6-plugin-dnd",
"version": "2.0.4",
"version": "2.0.3",
"description": "dnd plugin for X6",
"main": "lib/index.js",
"module": "es/index.js",
@ -10,8 +10,7 @@
"files": [
"dist",
"es",
"lib",
"src"
"lib"
],
"keywords": [
"plugin",

View File

@ -81,10 +81,7 @@ export class Dnd extends View {
this.targetModel.startBatch('dnd')
Dom.addClass(this.container, 'dragging')
Dom.appendTo(
this.container,
this.options.draggingContainer || document.body,
)
Dom.appendTo(this.container, this.options.containerParent || document.body)
this.sourceNode = node
this.prepareDragging(node, e.clientX, e.clientY)
@ -484,7 +481,7 @@ export namespace Dnd {
// duration?: number
// easing?: string
// }
draggingContainer?: HTMLElement
containerParent?: HTMLElement
/**
* dnd tool box container.
*/

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

Some files were not shown because too many files have changed in this diff Show More