From ef5388ed4ab2cb3e5436895bc0f2fa6cadded598 Mon Sep 17 00:00:00 2001 From: vector Date: Mon, 26 Jun 2023 19:39:35 +0800 Subject: [PATCH] docs: optimize docs (#3703) * docs: optimize connector docs * docs: optimize docs for node-anchor * docs: add demo for edge anchor * docs: add demo for connection point * docs: add demo for attrs * docs: add demos for filter * docs: add demos for port layout * docs: add demos for port label layout --- sites/x6-sites/.dumirc.ts | 4 +- sites/x6-sites/docs/api/mvc/model.zh.md | 2 +- sites/x6-sites/docs/api/mvc/view.zh.md | 44 +--- sites/x6-sites/docs/api/registry/attr.zh.md | 66 ++++-- .../docs/api/registry/connection-point.zh.md | 77 ++++--- .../docs/api/registry/connector.zh.md | 92 ++++---- .../docs/api/registry/edge-anchor.zh.md | 61 +++--- sites/x6-sites/docs/api/registry/filter.zh.md | 50 ++--- .../docs/api/registry/highlighter.zh.md | 68 +++--- .../docs/api/registry/node-anchor.zh.md | 151 +++++++------- .../docs/api/registry/port-label-layout.zh.md | 60 +----- .../docs/api/registry/port-layout.zh.md | 58 +----- .../src/api/attrs/text-anchor/index.less | 43 ++++ .../src/api/attrs/text-anchor/index.tsx | 124 +++++++++++ .../src/api/attrs/text-anchor/settings.tsx | 131 ++++++++++++ .../src/api/attrs/text-wrap/index.less | 14 ++ .../src/api/attrs/text-wrap/index.tsx | 185 +++++++++++++++++ .../x6-sites/src/api/attrs/x-align/index.less | 43 ++++ .../x6-sites/src/api/attrs/x-align/index.tsx | 119 +++++++++++ .../src/api/attrs/x-align/settings.tsx | 127 ++++++++++++ .../connection-point/playground/index.less | 43 ++++ .../api/connection-point/playground/index.tsx | 181 ++++++++++++++++ .../connection-point/playground/settings.tsx | 109 ++++++++++ .../src/api/connector/jumpover/index.less | 44 ++++ .../src/api/connector/jumpover/index.tsx | 126 +++++++++++ .../src/api/connector/jumpover/settings.tsx | 89 ++++++++ .../src/api/connector/normal/index.less | 14 ++ .../src/api/connector/normal/index.tsx | 82 ++++++++ .../src/api/connector/rounded/index.less | 43 ++++ .../src/api/connector/rounded/index.tsx | 91 ++++++++ .../src/api/connector/rounded/settings.tsx | 48 +++++ .../src/api/connector/smooth/index.less | 14 ++ .../src/api/connector/smooth/index.tsx | 82 ++++++++ .../src/api/connector/wobble/index.less | 14 ++ .../src/api/connector/wobble/index.tsx | 121 +++++++++++ .../src/api/edge-anchor/playground/index.less | 43 ++++ .../src/api/edge-anchor/playground/index.tsx | 98 +++++++++ .../api/edge-anchor/playground/settings.tsx | 73 +++++++ sites/x6-sites/src/api/filter/blur/index.less | 43 ++++ sites/x6-sites/src/api/filter/blur/index.tsx | 73 +++++++ .../x6-sites/src/api/filter/blur/settings.tsx | 99 +++++++++ .../src/api/filter/brightness/index.less | 43 ++++ .../src/api/filter/brightness/index.tsx | 73 +++++++ .../src/api/filter/brightness/settings.tsx | 76 +++++++ .../src/api/filter/contrast/index.less | 43 ++++ .../src/api/filter/contrast/index.tsx | 73 +++++++ .../src/api/filter/contrast/settings.tsx | 76 +++++++ .../src/api/filter/drop-shadow/index.less | 43 ++++ .../src/api/filter/drop-shadow/index.tsx | 73 +++++++ .../src/api/filter/drop-shadow/settings.tsx | 145 +++++++++++++ .../src/api/filter/gray-scale/index.less | 43 ++++ .../src/api/filter/gray-scale/index.tsx | 73 +++++++ .../src/api/filter/gray-scale/settings.tsx | 76 +++++++ .../src/api/filter/highlight/index.less | 43 ++++ .../src/api/filter/highlight/index.tsx | 73 +++++++ .../src/api/filter/highlight/settings.tsx | 123 +++++++++++ .../src/api/filter/hue-rotate/index.less | 43 ++++ .../src/api/filter/hue-rotate/index.tsx | 73 +++++++ .../src/api/filter/hue-rotate/settings.tsx | 76 +++++++ .../x6-sites/src/api/filter/invert/index.less | 43 ++++ .../x6-sites/src/api/filter/invert/index.tsx | 73 +++++++ .../src/api/filter/invert/settings.tsx | 76 +++++++ .../src/api/filter/outline/index.less | 43 ++++ .../x6-sites/src/api/filter/outline/index.tsx | 73 +++++++ .../src/api/filter/outline/settings.tsx | 123 +++++++++++ .../src/api/filter/saturate/index.less | 43 ++++ .../src/api/filter/saturate/index.tsx | 73 +++++++ .../src/api/filter/saturate/settings.tsx | 76 +++++++ .../x6-sites/src/api/filter/sepia/index.less | 43 ++++ sites/x6-sites/src/api/filter/sepia/index.tsx | 73 +++++++ .../src/api/filter/sepia/settings.tsx | 76 +++++++ .../src/api/node-anchor/playground/index.less | 43 ++++ .../src/api/node-anchor/playground/index.tsx | 147 +++++++++++++ .../api/node-anchor/playground/settings.tsx | 51 +++++ .../inside-outside/index.less | 42 ++++ .../inside-outside/index.tsx | 98 +++++++++ .../inside-outside/settings.tsx | 103 +++++++++ .../api/port-label-layout/radial/index.less | 42 ++++ .../api/port-label-layout/radial/index.tsx | 98 +++++++++ .../api/port-label-layout/radial/settings.tsx | 97 +++++++++ .../src/api/port-label-layout/side/index.less | 42 ++++ .../src/api/port-label-layout/side/index.tsx | 108 ++++++++++ .../api/port-label-layout/side/settings.tsx | 94 +++++++++ .../src/api/port-layout/absolute/index.less | 42 ++++ .../src/api/port-layout/absolute/index.tsx | 129 ++++++++++++ .../src/api/port-layout/absolute/settings.tsx | 100 +++++++++ .../api/port-layout/ellipse-spread/index.less | 42 ++++ .../api/port-layout/ellipse-spread/index.tsx | 122 +++++++++++ .../port-layout/ellipse-spread/settings.tsx | 171 +++++++++++++++ .../src/api/port-layout/ellipse/index.less | 42 ++++ .../src/api/port-layout/ellipse/index.tsx | 147 +++++++++++++ .../src/api/port-layout/ellipse/settings.tsx | 196 ++++++++++++++++++ .../src/api/port-layout/line/index.less | 42 ++++ .../src/api/port-layout/line/index.tsx | 127 ++++++++++++ .../src/api/port-layout/line/settings.tsx | 122 +++++++++++ .../src/api/port-layout/side/index.less | 42 ++++ .../src/api/port-layout/side/index.tsx | 110 ++++++++++ .../src/api/port-layout/side/settings.tsx | 151 ++++++++++++++ .../src/api/port-layout/sin/index.less | 14 ++ .../src/api/port-layout/sin/index.tsx | 80 +++++++ 100 files changed, 7339 insertions(+), 437 deletions(-) create mode 100644 sites/x6-sites/src/api/attrs/text-anchor/index.less create mode 100644 sites/x6-sites/src/api/attrs/text-anchor/index.tsx create mode 100644 sites/x6-sites/src/api/attrs/text-anchor/settings.tsx create mode 100644 sites/x6-sites/src/api/attrs/text-wrap/index.less create mode 100644 sites/x6-sites/src/api/attrs/text-wrap/index.tsx create mode 100644 sites/x6-sites/src/api/attrs/x-align/index.less create mode 100644 sites/x6-sites/src/api/attrs/x-align/index.tsx create mode 100644 sites/x6-sites/src/api/attrs/x-align/settings.tsx create mode 100644 sites/x6-sites/src/api/connection-point/playground/index.less create mode 100644 sites/x6-sites/src/api/connection-point/playground/index.tsx create mode 100644 sites/x6-sites/src/api/connection-point/playground/settings.tsx create mode 100644 sites/x6-sites/src/api/connector/jumpover/index.less create mode 100644 sites/x6-sites/src/api/connector/jumpover/index.tsx create mode 100644 sites/x6-sites/src/api/connector/jumpover/settings.tsx create mode 100644 sites/x6-sites/src/api/connector/normal/index.less create mode 100644 sites/x6-sites/src/api/connector/normal/index.tsx create mode 100644 sites/x6-sites/src/api/connector/rounded/index.less create mode 100644 sites/x6-sites/src/api/connector/rounded/index.tsx create mode 100644 sites/x6-sites/src/api/connector/rounded/settings.tsx create mode 100644 sites/x6-sites/src/api/connector/smooth/index.less create mode 100644 sites/x6-sites/src/api/connector/smooth/index.tsx create mode 100644 sites/x6-sites/src/api/connector/wobble/index.less create mode 100644 sites/x6-sites/src/api/connector/wobble/index.tsx create mode 100644 sites/x6-sites/src/api/edge-anchor/playground/index.less create mode 100644 sites/x6-sites/src/api/edge-anchor/playground/index.tsx create mode 100644 sites/x6-sites/src/api/edge-anchor/playground/settings.tsx create mode 100644 sites/x6-sites/src/api/filter/blur/index.less create mode 100644 sites/x6-sites/src/api/filter/blur/index.tsx create mode 100644 sites/x6-sites/src/api/filter/blur/settings.tsx create mode 100644 sites/x6-sites/src/api/filter/brightness/index.less create mode 100644 sites/x6-sites/src/api/filter/brightness/index.tsx create mode 100644 sites/x6-sites/src/api/filter/brightness/settings.tsx create mode 100644 sites/x6-sites/src/api/filter/contrast/index.less create mode 100644 sites/x6-sites/src/api/filter/contrast/index.tsx create mode 100644 sites/x6-sites/src/api/filter/contrast/settings.tsx create mode 100644 sites/x6-sites/src/api/filter/drop-shadow/index.less create mode 100644 sites/x6-sites/src/api/filter/drop-shadow/index.tsx create mode 100644 sites/x6-sites/src/api/filter/drop-shadow/settings.tsx create mode 100644 sites/x6-sites/src/api/filter/gray-scale/index.less create mode 100644 sites/x6-sites/src/api/filter/gray-scale/index.tsx create mode 100644 sites/x6-sites/src/api/filter/gray-scale/settings.tsx create mode 100644 sites/x6-sites/src/api/filter/highlight/index.less create mode 100644 sites/x6-sites/src/api/filter/highlight/index.tsx create mode 100644 sites/x6-sites/src/api/filter/highlight/settings.tsx create mode 100644 sites/x6-sites/src/api/filter/hue-rotate/index.less create mode 100644 sites/x6-sites/src/api/filter/hue-rotate/index.tsx create mode 100644 sites/x6-sites/src/api/filter/hue-rotate/settings.tsx create mode 100644 sites/x6-sites/src/api/filter/invert/index.less create mode 100644 sites/x6-sites/src/api/filter/invert/index.tsx create mode 100644 sites/x6-sites/src/api/filter/invert/settings.tsx create mode 100644 sites/x6-sites/src/api/filter/outline/index.less create mode 100644 sites/x6-sites/src/api/filter/outline/index.tsx create mode 100644 sites/x6-sites/src/api/filter/outline/settings.tsx create mode 100644 sites/x6-sites/src/api/filter/saturate/index.less create mode 100644 sites/x6-sites/src/api/filter/saturate/index.tsx create mode 100644 sites/x6-sites/src/api/filter/saturate/settings.tsx create mode 100644 sites/x6-sites/src/api/filter/sepia/index.less create mode 100644 sites/x6-sites/src/api/filter/sepia/index.tsx create mode 100644 sites/x6-sites/src/api/filter/sepia/settings.tsx create mode 100644 sites/x6-sites/src/api/node-anchor/playground/index.less create mode 100644 sites/x6-sites/src/api/node-anchor/playground/index.tsx create mode 100644 sites/x6-sites/src/api/node-anchor/playground/settings.tsx create mode 100644 sites/x6-sites/src/api/port-label-layout/inside-outside/index.less create mode 100644 sites/x6-sites/src/api/port-label-layout/inside-outside/index.tsx create mode 100644 sites/x6-sites/src/api/port-label-layout/inside-outside/settings.tsx create mode 100644 sites/x6-sites/src/api/port-label-layout/radial/index.less create mode 100644 sites/x6-sites/src/api/port-label-layout/radial/index.tsx create mode 100644 sites/x6-sites/src/api/port-label-layout/radial/settings.tsx create mode 100644 sites/x6-sites/src/api/port-label-layout/side/index.less create mode 100644 sites/x6-sites/src/api/port-label-layout/side/index.tsx create mode 100644 sites/x6-sites/src/api/port-label-layout/side/settings.tsx create mode 100644 sites/x6-sites/src/api/port-layout/absolute/index.less create mode 100644 sites/x6-sites/src/api/port-layout/absolute/index.tsx create mode 100644 sites/x6-sites/src/api/port-layout/absolute/settings.tsx create mode 100644 sites/x6-sites/src/api/port-layout/ellipse-spread/index.less create mode 100644 sites/x6-sites/src/api/port-layout/ellipse-spread/index.tsx create mode 100644 sites/x6-sites/src/api/port-layout/ellipse-spread/settings.tsx create mode 100644 sites/x6-sites/src/api/port-layout/ellipse/index.less create mode 100644 sites/x6-sites/src/api/port-layout/ellipse/index.tsx create mode 100644 sites/x6-sites/src/api/port-layout/ellipse/settings.tsx create mode 100644 sites/x6-sites/src/api/port-layout/line/index.less create mode 100644 sites/x6-sites/src/api/port-layout/line/index.tsx create mode 100644 sites/x6-sites/src/api/port-layout/line/settings.tsx create mode 100644 sites/x6-sites/src/api/port-layout/side/index.less create mode 100644 sites/x6-sites/src/api/port-layout/side/index.tsx create mode 100644 sites/x6-sites/src/api/port-layout/side/settings.tsx create mode 100644 sites/x6-sites/src/api/port-layout/sin/index.less create mode 100644 sites/x6-sites/src/api/port-layout/sin/index.tsx diff --git a/sites/x6-sites/.dumirc.ts b/sites/x6-sites/.dumirc.ts index d5d77a6e7fc..8e970bfddb8 100644 --- a/sites/x6-sites/.dumirc.ts +++ b/sites/x6-sites/.dumirc.ts @@ -233,8 +233,8 @@ export default defineConfig({ { slug: 'api/registry', title: { - zh: '注册', - en: 'Registry', + zh: '扩展', + en: 'Extension', }, order: 4, }, diff --git a/sites/x6-sites/docs/api/mvc/model.zh.md b/sites/x6-sites/docs/api/mvc/model.zh.md index 9eab2a3ee03..9f24b0dffc9 100644 --- a/sites/x6-sites/docs/api/mvc/model.zh.md +++ b/sites/x6-sites/docs/api/mvc/model.zh.md @@ -1,5 +1,5 @@ --- -title: Model +title: 模型 order: 1 redirect_from: - /zh/docs diff --git a/sites/x6-sites/docs/api/mvc/view.zh.md b/sites/x6-sites/docs/api/mvc/view.zh.md index 735b210ebbc..6798fb46aa0 100644 --- a/sites/x6-sites/docs/api/mvc/view.zh.md +++ b/sites/x6-sites/docs/api/mvc/view.zh.md @@ -1,5 +1,5 @@ --- -title: View +title: 视图 order: 7 redirect_from: - /zh/docs @@ -111,45 +111,7 @@ const graph = new Graph({ | container | Element | ✓ | 文本标签容器。 | | selectors | Markup.Selectors | ✓ | 文本标签 Markup 渲染后的选择器键值对。 | -例如,我们可以在标签上渲染任何想要的元素。 - -```tsx -const graph = new Graph({ - container: this.container, - onEdgeLabelRendered(args) { - const { label, container, selectors } = args - const data = label.data - - if (data) { - // 在 Label 容器中渲染一个 foreignObject 来承载 HTML 元素和 React 组件 - const content = this.appendForeignObject(container) - - if (data === 1) { - // 渲染一个 Div 元素 - const txt = document.createTextNode('text node') - content.style.border = '1px solid #f0f0f0' - content.style.borderRadius = '4px' - content.appendChild(txt) - } else if (data === 2) { - // 渲染一个 HTML 按钮 - const btn = document.createElement('button') - btn.appendChild(document.createTextNode('HTML Button')) - btn.style.height = '30px' - btn.style.lineHeight = '1' - btn.addEventListener('click', () => { - alert('clicked') - }) - content.appendChild(btn) - } else if (data === 3) { - // 渲染一个 Atnd 的按钮 - ReactDOM.render(, content) - } - } - }, -}) -``` - -我们也可以在定义 Label 的 Markup 时添加 `` 元素来支持 HTML 和 React 的渲染能力。 +我们可以在定义 Label 的 Markup 时添加 `` 元素来支持 HTML 和 React 的渲染能力。 ```tsx const graph = new Graph({ @@ -171,7 +133,7 @@ const graph = new Graph({ ### createCellView ```ts -;(this: Graph, cell: Cell) => CellView | null | undefined +(this: Graph, cell: Cell) => CellView | null | undefined ``` 自定义元素的视图,可以返回一个 `CellView`,会替换默认的视图,如果返回 `null`,则不会渲染,如果返回 `undefined`,会按照默认方式渲染。 diff --git a/sites/x6-sites/docs/api/registry/attr.zh.md b/sites/x6-sites/docs/api/registry/attr.zh.md index 9b2a63f6c22..3fbb9244aa9 100644 --- a/sites/x6-sites/docs/api/registry/attr.zh.md +++ b/sites/x6-sites/docs/api/registry/attr.zh.md @@ -1,25 +1,51 @@ --- -title: Attr -order: 11 +title: 属性 +order: 13 redirect_from: - /zh/docs - /zh/docs/api - /zh/docs/api/registry --- -我们在 `Attr` 命名空间中提供了注册和管理特殊属性的方法,以及所有特殊属性的定义。 +X6 提供了非常多的内置属性以及自定义属性方式。 -## presets - -我们在 `Registry.Attr.presets` 命名空间下挂载了所有特殊属性的定义。 +## 内置属性 ### ref -指向一个元素的 CSS 选择器,指代的元素是那些以 `ref` 开头的属性的参照元素。 +指向一个元素的选择器,指向的元素作为参考元素。 + +```typescript +graph.addNode({ + ..., + markup: [ + { + tagName: 'rect', + selector: 'body,' + }, + { + tagName: 'rect', + selector: 'custom,' + } + ], + attrs: { + body: { + width: 100, + height: 50, + }, + // custom 元素的长度和宽度是 body 的一半 + custom: { + ref: 'body', + refWidth: 0.5, + refHeight: 0.5, + } + } +}) +``` ### refX -设置元素 `x` 坐标,目标 `x` 坐标相对于 [`ref`](#ref) 指代的参照元素的左上角 `x` 坐标(参照 `x` 坐标) +设置元素 `x` 坐标,目标 `x` 坐标相对于 [`ref`](#ref) 指代的元素的左上角 `x` 坐标。 - 当其值在 `[0, 1]` 之间或为百分比(如 `50%`)时,表示目标 `x` 坐标是参照 `x` 坐标相对于参照元素宽度百分比的相对偏移量。例如 `refX: 0.5` 表示,目标 `x` 坐标在参照 `x` 坐标基础上,向右偏移参照宽度的 `50%`。 - 当其值 `<0` 或 `>1` 时,表示目标 `x` 坐标是参照 `x` 坐标的绝对偏移量。例如 `refX: 20` 表示,目标 `x` 坐标在参照 `x` 坐标基础上,向右偏移 `20px`。 @@ -79,7 +105,7 @@ redirect_from: - 当其值 `<0` 或 `>1` 时,表示元素的宽度在参照宽度的基础上减少或增加多少。例如 `refWidth: 20` 表示元素比相对元素宽 `20px`。 :::warning{title=注意} -需要注意的是,该属性只适用于那些支持宽度 `width` 和高度 `height` 的元素,如 `` 元素。 +该属性只适用于那些支持宽度 `width` 和高度 `height` 的元素,如 `` 元素。 ::: ### refWidth2 @@ -103,7 +129,7 @@ redirect_from: - 当其值 `<0` 或 `>1` 时,表示元素的高度在参照高度的基础上减少或增加多少。例如 `refHeight: 20` 表示元素比相对元素高 `20px`。 :::warning{title=注意} -需要注意的是,该属性只适用于那些支持宽度 `width` 和高度 `height` 的元素,如 `` 元素。 +该属性只适用于那些支持宽度 `width` 和高度 `height` 的元素,如 `` 元素。 ::: ### refHeight2 @@ -127,7 +153,7 @@ redirect_from: - 当其值 `<0` 或 `>1` 时,表示元素的 `cx` 是在参照宽度的基础上减少或增加多少。例如 `refCx: 20` 表示元素中心 `x` 坐标位于参照宽度加 `20px` 处。 :::warning{title=注意} -需要注意的是,该属性只适用于那些支持 `cx` 和 `cy` 属性的元素,如 `` 元素。 +该属性只适用于那些支持 `cx` 和 `cy` 属性的元素,如 `` 元素。 ::: ### refCy @@ -138,7 +164,7 @@ redirect_from: - 当其值 `<0` 或 `>1` 时,表示元素的 `cy` 是在参照宽度的基础上减少或增加多少。例如 `refCy: 20` 表示元素中心 `y` 坐标位于参照高度加 `20px` 处。 :::warning{title=注意} -需要注意的是,该属性只适用于那些支持 `cx` 和 `cy` 属性的元素,如 `` 元素。 +该属性只适用于那些支持 `cx` 和 `cy` 属性的元素,如 `` 元素。 ::: ### refRx @@ -149,7 +175,7 @@ redirect_from: - 当其值 `<0` 或 `>1` 时,表示元素的 `rx` 是在参照宽度的基础上减少或增加多少。例如 `refRx: 20` 表示元素的 `rx` 是参照宽度加 `20px`。 :::warning{title=注意} -需要注意的是,该属性只适用于那些支持 `rx` 和 `ry` 属性的元素,如 `` 元素。 +该属性只适用于那些支持 `rx` 和 `ry` 属性的元素,如 `` 元素。 ::: ### refRy @@ -160,7 +186,7 @@ redirect_from: - 当其值 `<0` 或 `>1` 时,表示元素的 `ry` 是在参照宽度的基础上减少或增加多少。例如 `refRy: 20` 表示元素的 `ry` 是参照高度加 `20px`。 :::warning{title=注意} -需要注意的是,该属性只适用于那些支持 `rx` 和 `ry` 属性的元素,如 `` 元素。 +该属性只适用于那些支持 `rx` 和 `ry` 属性的元素,如 `` 元素。 ::: ### refRCircumscribed @@ -171,7 +197,7 @@ redirect_from: - 当其值 `<0` 或 `>1` 时,表示 `r` 是在参照长度的基础上减少或增加多少。例如 `refRCircumscribed: 20` 表示 `r` 是参照长度加 `20px`。 :::warning{title=注意} -需要注意的是,该属性只适用于那些支持 `r` 属性的元素,如 `` 元素。 +该属性只适用于那些支持 `r` 属性的元素,如 `` 元素。 ::: ### refRInscribed @@ -184,7 +210,7 @@ _简称_:**`refR`** - 当其值 `<0` 或 `>1` 时,表示 `r` 是在参照长度的基础上减少或增加多少。例如 `refRInscribed: 20` 表示 `r` 是参照长度加 `20px`。 :::warning{title=注意} -需要注意的是,该属性只适用于那些支持 `r` 属性的元素,如 `` 元素。 +该属性只适用于那些支持 `r` 属性的元素,如 `` 元素。 ::: ### refDKeepOffset @@ -356,7 +382,7 @@ console.log(view.findOne('polygon').getAttribute('points')) - `'middle'` 目标元素的中心与 `y` 对齐。 - `'bottom'` 目标元素与的底部与 `y` 对齐。 - + ### fill @@ -434,7 +460,7 @@ textWrap: { } ``` - + ### textPath @@ -456,7 +482,7 @@ textWrap: { - `'middle'` 目标元素的中心与 `y` 对齐。 - `'bottom'` 目标元素与的底部与 `y` 对齐。 - + ### connection @@ -570,7 +596,7 @@ edge.attr('connection/sourceMarker', { 适用于所有 `` 元素,在路径的终点添加一个 SVG 元素(如终点箭头),并自动旋转该元素,使其与根据路径方向保持一致。了解更多详情请参考[这篇教程](/api/model/marker)。 :::warning{title=注意} -需要注意的是,该元素初始时就被旋转了 `180` 度,在此基础上再自动调整旋转角度,并与路径的方向保持一致。例如,对于一个水平的直线,我们为其起点指定了一个向左的箭头,我们也可以为其重点指定相同的箭头,这个箭头会自动指向右侧(自动旋转了 `180` 度)。 +该元素初始时就被旋转了 `180` 度,在此基础上再自动调整旋转角度,并与路径的方向保持一致。例如,对于一个水平的直线,我们为其起点指定了一个向左的箭头,我们也可以为其重点指定相同的箭头,这个箭头会自动指向右侧(自动旋转了 `180` 度)。 ::: ### vertexMarker diff --git a/sites/x6-sites/docs/api/registry/connection-point.zh.md b/sites/x6-sites/docs/api/registry/connection-point.zh.md index 11f37e1d76e..93875414352 100644 --- a/sites/x6-sites/docs/api/registry/connection-point.zh.md +++ b/sites/x6-sites/docs/api/registry/connection-point.zh.md @@ -1,5 +1,5 @@ --- -title: ConnectionPoint +title: 连接点 order: 10 redirect_from: - /zh/docs @@ -9,17 +9,17 @@ redirect_from: 连接点 ConnectionPoint 与锚点 [Anchor](/zh/docs/api/registry/node-anchor) 共同确定了边的起点或终点。 -- 起点:从第一个路径点或目标节点的中心(没有路径点时)画一条参考线到源节点的锚点,然后根据 [connectionPoint](/zh/docs/api/model/edge#source-和-target) 指定的交点计算方法,计算参考线与图形的交点,该交点就是边的起点。 -- 终点:从最后一个路径点或源节点的中心(没有路径点时)画一条参考线到目标节点的锚点,然后根据 [connectionPoint](/zh/docs/api/model/edge#source-和-target) 指定的交点计算方法,计算参考线与图形的交点,该交点就是边的终点。 +- 起点:从第一个路径点或目标节点的中心(没有路径点时)画一条参考线到源节点的锚点,然后根据 `connectionPoint` 指定的交点计算方法,计算参考线与图形的交点,该交点就是边的起点。 +- 终点:从最后一个路径点或源节点的中心(没有路径点时)画一条参考线到目标节点的锚点,然后根据 `connectionPoint` 指定的交点计算方法,计算参考线与图形的交点,该交点就是边的终点。 -我们在 `Registry.ConnectionPoint.presets` 命名空间中提供了以下几种连接点计算方法。 +X6 内置了以下几种连接点计算方法。 - [boundary](#boundary) 默认值,与链接图形的边框的交点。 - [bbox](#bbox) 与链接元素的包围盒的交点。 - [rect](#rect) 与链接元素的旋转后的矩形区域的交点。 - [anchor](#anchor) 使用锚点作为连接点。 - + 可以在创建边时指定连接点: @@ -80,7 +80,7 @@ new Graph({ }) ``` -## presets +## 内置连接点 ### boundary @@ -88,43 +88,43 @@ new Graph({ 支持的参数如下表: -| 参数名 | 参数类型 | 是否必选 | 默认值 | 参数说明 | -| --- | --- | :-: | --- | --- | -| offset | number \| Point.PointLike | 否 | `0` | 连接点的偏移量。 | -| stroked | boolean | 否 | `true` | 是否考虑图形的边框宽度。 | -| insideout | boolean | 否 | `true` | 当参考线位于图形内部且没有交点时,是否延长参考线求交点,默认为 `true`。 | -| extrapolate | boolean | 否 | `false` | 当参考线位于图形外部且没有交点时,是否延长参考线求交点,延长后也可能没有交点,默认为 `false`。此参数的优先级高于 `sticky`。 | -| sticky | boolean | 否 | `false` | 当参考线位于图形外部且没有交点时,是否使用边框上离参考线最最近的点作为交点,默认为 `false`。 | -| precision | number | 否 | `2` | 交点计算的精度。 | -| selector | string | 否 | `undefined` | 选择器,用于标识一个元素,使用该元素的边框来计算交点。默认使用节点中第一个不在 `` 元素内的子元素。 | +| 参数名 | 参数类型 | 是否必选 | 默认值 | 参数说明 | +|-------------|---------------------------|:-------:|-------------|--------------------------------------------------------------------------------------------------------------------| +| offset | number \| Point.PointLike | 否 | `0` | 连接点的偏移量。 | +| stroked | boolean | 否 | `true` | 是否考虑图形的边框宽度。 | +| insideout | boolean | 否 | `true` | 当参考线位于图形内部且没有交点时,是否延长参考线求交点,默认为 `true`。 | +| extrapolate | boolean | 否 | `false` | 当参考线位于图形外部且没有交点时,是否延长参考线求交点,延长后也可能没有交点,默认为 `false`。此参数的优先级高于 `sticky`。 | +| sticky | boolean | 否 | `false` | 当参考线位于图形外部且没有交点时,是否使用边框上离参考线最最近的点作为交点,默认为 `false`。 | +| precision | number | 否 | `2` | 交点计算的精度。 | +| selector | string | 否 | `undefined` | 选择器,用于标识一个元素,使用该元素的边框来计算交点。默认使用节点中第一个不在 `` 元素内的子元素。 | ### anchor 将锚点作为连接点,支持如下参数: -| 参数名 | 参数类型 | 是否必选 | 默认值 | 参数说明 | -| ------ | ------------------------- | :------: | ------ | ---------------- | +| 参数名 | 参数类型 | 是否必选 | 默认值 | 参数说明 | +|--------|---------------------------|:-------:|--------|-------------| | offset | number \| Point.PointLike | 否 | `0` | 连接点的偏移量。 | ### bbox 图形的包围盒与参考线的交点,支持如下参数: -| 参数名 | 参数类型 | 是否必选 | 默认值 | 参数说明 | -| --- | --- | :-: | --- | --- | -| offset | number \| Point.PointLike | 否 | `0` | 连接点的偏移量。 | -| stroked | boolean | 否 | `false` | 是否考虑图形的边框宽度。 | +| 参数名 | 参数类型 | 是否必选 | 默认值 | 参数说明 | +|---------|---------------------------|:-------:|---------|---------------------| +| offset | number \| Point.PointLike | 否 | `0` | 连接点的偏移量。 | +| stroked | boolean | 否 | `false` | 是否考虑图形的边框宽度。 | ### rect 图形旋转后的矩形区域与参考线的交点,支持如下参数: -| 参数名 | 参数类型 | 是否必选 | 默认值 | 参数说明 | -| --- | --- | :-: | --- | --- | -| offset | number \| Point.PointLike | 否 | `0` | 连接点的偏移量。 | -| stroked | boolean | 否 | `false` | 是否考虑图形的边框宽度。 | +| 参数名 | 参数类型 | 是否必选 | 默认值 | 参数说明 | +|---------|---------------------------|:-------:|---------|---------------------| +| offset | number \| Point.PointLike | 否 | `0` | 连接点的偏移量。 | +| stroked | boolean | 否 | `false` | 是否考虑图形的边框宽度。 | -## registry +## 自定义连接点 连接点定义是一个具有如下签名的函数,返回连接点。 @@ -137,28 +137,25 @@ export type Definition = ( ) => Point ``` -| 参数名 | 参数类型 | 参数说明 | -| -------- | ---------- | -------------------- | +| 参数名 | 参数类型 | 参数说明 | +|----------|------------|-------------------| | line | Line | 参考线。 | | nodeView | NodeView | 连接的节点视图。 | | magnet | SVGElement | 连接的节点上的元素。 | | args | T | 参数。 | -并在 `Registry.ConnectionPoint.registry` 对象上提供了 [`register`](#register) 和 [`unregister`](#unregister) 两个方法来注册和取消注册连接点定义,同时也将这两个方法分别挂载为 Graph 上的两个静态方法 `Graph.registerConnectionPoint` 和 `Graph.unregisterConnectionPoint`。 - -### register +完成连接点定义后,我们先注册连接点: ```ts -register(entities: { [name: string]: Definition }, force?: boolean): void -register(name: string, entity: Definition, force?: boolean): Definition +Graph.registerConnectionPoint('custom-connection-point', ...) ``` -注册连连接点定义。 - -### unregister +注册以后我们就可以通过连接点名称来使用: ```ts -unregister(name: string): Definition | null -``` - -取消注册连接点定义。 +new Graph({ + connecting: { + connectionPoint: 'custom-connection-point' + }, +}) +``` \ No newline at end of file diff --git a/sites/x6-sites/docs/api/registry/connector.zh.md b/sites/x6-sites/docs/api/registry/connector.zh.md index 66119a4e14a..66d2bbf322f 100644 --- a/sites/x6-sites/docs/api/registry/connector.zh.md +++ b/sites/x6-sites/docs/api/registry/connector.zh.md @@ -1,5 +1,5 @@ --- -title: Connector +title: 连接器 order: 6 redirect_from: - /zh/docs @@ -7,13 +7,13 @@ redirect_from: - /zh/docs/api/registry --- -连接器将起点、路由返回的点、终点加工为 `` 元素的 [`d`](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/d) 属性,决定了边渲染到画布后的样式。我们在 `Registry.Connector.presets` 命名空间中提供了以下几种连接器。 +连接器将起点、路由返回的点、终点加工为 `` 元素的 [`d`](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/d) 属性,决定了边渲染到画布后的样式。X6 中内置了以下几种连接器。 -| 连接器 | 说明 | -| --- | --- | -| normal | [简单连接器](#normal),用直线连接起点、路由点和终点。 | -| smooth | [平滑连接器](#smooth),用三次贝塞尔曲线线连接起点、路由点和终点。 | -| rounded | [圆角连接器](#rounded),用直线连接起点、路由点和终点,并在线段连接处用圆弧链接(倒圆角)。 | +| 连接器 | 说明 | +|----------|-------------------------------------------------------------------------------------| +| normal | [简单连接器](#normal),用直线连接起点、路由点和终点。 | +| smooth | [平滑连接器](#smooth),用三次贝塞尔曲线线连接起点、路由点和终点。 | +| rounded | [圆角连接器](#rounded),用直线连接起点、路由点和终点,并在线段连接处用圆弧链接(倒圆角)。 | | jumpover | [跳线连接器](#jumpover),用直线连接起点、路由点和终点,并在边与边的交叉处用跳线符号链接。 | 可以为某条边设置路由: @@ -74,7 +74,7 @@ new Graph({ 下面我们一起来看看如何使用内置连接器,以及如何自定并注册自定义连接器。 -## presets +## 内置连接器 ### normal @@ -82,11 +82,11 @@ new Graph({ 支持的参数如下表: -| 参数名 | 参数类型 | 是否必选 | 默认值 | 参数说明 | -| --- | --- | :-: | --- | --- | -| raw | boolean | 否 | `false` | 是否返回一个 `Path` 对象,默认值为 `false` 返回序列化后的字符串。 | +| 参数名 | 参数类型 | 是否必选 | 默认值 | 参数说明 | +|--------|----------|:-------:|---------|-------------------------------------------------------------| +| raw | boolean | 否 | `false` | 是否返回一个 `Path` 对象,默认值为 `false` 返回序列化后的字符串。 | - + ### smooth @@ -94,10 +94,10 @@ new Graph({ 支持的参数如下表: -| 参数名 | 参数类型 | 是否必选 | 默认值 | 参数说明 | -| --- | --- | :-: | --- | --- | -| raw | boolean | 否 | `false` | 是否返回一个 `Path` 对象,默认值为 `false` 返回序列化后的字符串。 | -| direction | `H` \| `V` | 否 | - | 保持水平连接或者保持垂直连接,不设置会根据起点和终点位置动态计算。 | +| 参数名 | 参数类型 | 是否必选 | 默认值 | 参数说明 | +|-----------|------------|:-------:|---------|--------------------------------------------------------------| +| raw | boolean | 否 | `false` | 是否返回一个 `Path` 对象,默认值为 `false` 返回序列化后的字符串。 | +| direction | `H` \| `V` | 否 | - | 保持水平连接或者保持垂直连接,不设置会根据起点和终点位置动态计算。 | 例如: @@ -113,7 +113,7 @@ graph.addEdge({ }) ``` - + ### rounded @@ -121,10 +121,10 @@ graph.addEdge({ 支持的参数如下表: -| 参数名 | 参数类型 | 是否必选 | 默认值 | 参数说明 | -| --- | --- | :-: | --- | --- | -| radius | number | 否 | `10` | 倒角半径。 | -| raw | boolean | 否 | `false` | 是否返回一个 `Path` 对象,默认值为 `false` 返回序列化后的字符串。 | +| 参数名 | 参数类型 | 是否必选 | 默认值 | 参数说明 | +|--------|----------|:-------:|---------|-------------------------------------------------------------| +| radius | number | 否 | `10` | 倒角半径。 | +| raw | boolean | 否 | `false` | 是否返回一个 `Path` 对象,默认值为 `false` 返回序列化后的字符串。 | 例如: @@ -145,7 +145,7 @@ graph.addEdge({ }) ``` - + ### jumpover @@ -153,16 +153,16 @@ graph.addEdge({ 支持的参数如下表: -| 参数名 | 参数类型 | 是否必选 | 默认值 | 参数说明 | -| --- | --- | :-: | --- | --- | -| type | 'arc' \| 'gap' \| 'cubic' | 否 | `'arc'` | 跳线类型。 | -| size | number | 否 | `5` | 跳线大小。 | -| radius | number | 否 | `0` | 倒角半径。 | -| raw | boolean | 否 | `false` | 是否返回一个 `Path` 对象,默认值为 `false` 返回序列化后的字符串。 | +| 参数名 | 参数类型 | 是否必选 | 默认值 | 参数说明 | +|--------|---------------------------|:-------:|---------|-------------------------------------------------------------| +| type | 'arc' \| 'gap' \| 'cubic' | 否 | `'arc'` | 跳线类型。 | +| size | number | 否 | `5` | 跳线大小。 | +| radius | number | 否 | `0` | 倒角半径。 | +| raw | boolean | 否 | `false` | 是否返回一个 `Path` 对象,默认值为 `false` 返回序列化后的字符串。 | - + -## registry +## 自定义连接器 连接器是一个具有如下签名的函数,返回 `Path` 对象或序列化后的字符串。 @@ -177,8 +177,8 @@ export type Definition = ( ) => Path | string ``` -| 参数名 | 参数类型 | 参数说明 | -| ----------- | ----------------- | -------------- | +| 参数名 | 参数类型 | 参数说明 | +|-------------|-------------------|-------------| | this | EdgeView | 边的视图。 | | sourcePoint | Point.PointLike | 起点。 | | targetPoint | Point.PointLike | 终点。 | @@ -186,28 +186,7 @@ export type Definition = ( | args | T | 连接器参数。 | | edgeView | EdgeView | 边的视图。 | -并在 `Registry.Connector.registry` 对象上提供了 [`register`](#register) 和 [`unregister`](#unregister) 两个方法来注册和取消注册连接器。 - -### register - -```ts -register(entities: { [name: string]: Definition }, force?: boolean): void -register(name: string, entity: Definition, force?: boolean): Definition -``` - -注册连接器。 - -### unregister - -```ts -unregister(name: string): Definition | null -``` - -取消注册连接器。 - -### 自定义连接器 - -按照上面的规则,我来定义一个 `wobble` 连接器: +我来定义一个 `wobble` 连接器: ```ts export interface WobbleArgs { @@ -249,7 +228,7 @@ function wobble( } ``` -实际上,我们将 `Registry.Connector.registry` 对象的 `register` 和 `unregister` 方法分别挂载为 `Graph` 的两个静态方法 `Graph.registerConnector` 和 `Graph.unregisterConnector`,所以我们可以像下面这样来注册连接器: +然后注册连接器: ```ts Graph.registerConnector('wobble', wobble) @@ -260,5 +239,4 @@ Graph.registerConnector('wobble', wobble) ```ts edge.setConnector('wobble', { spread: 16 }) ``` - - + diff --git a/sites/x6-sites/docs/api/registry/edge-anchor.zh.md b/sites/x6-sites/docs/api/registry/edge-anchor.zh.md index db187e171d8..6229db7dc70 100644 --- a/sites/x6-sites/docs/api/registry/edge-anchor.zh.md +++ b/sites/x6-sites/docs/api/registry/edge-anchor.zh.md @@ -1,5 +1,5 @@ --- -title: EdgeAnchor +title: 边的锚点 order: 9 redirect_from: - /zh/docs @@ -9,35 +9,35 @@ redirect_from: 当边连接到边时,可以通过 EdgeAnchor 来指定被连接的边上的锚点,锚点与连接点 [ConnectionPoint](/zh/docs/api/registry/connection-point) 共同确定了边的起点和终点。 -- 起点:从第一个路径点或目标节点的中心(没有路径点时)画一条参考线到源节点的锚点,然后根据 [connectionPoint](/zh/docs/api/model/edge#source-和-target) 指定的交点计算方法,计算参考线与图形的交点,该交点就是边的起点。 -- 终点:从最后一个路径点或源节点的中心(没有路径点时)画一条参考线到目标节点的锚点,然后根据 [connectionPoint](/zh/docs/api/model/edge#source-和-target) 指定的交点计算方法,计算参考线与图形的交点,该交点就是边的终点。 +- 起点:从第一个路径点或目标节点的中心(没有路径点时)画一条参考线到源节点的锚点,然后根据 [connectionPoint](/zh/docs/api/registry/connection-point) 指定的交点计算方法,计算参考线与图形的交点,该交点就是边的起点。 +- 终点:从最后一个路径点或源节点的中心(没有路径点时)画一条参考线到目标节点的锚点,然后根据 [connectionPoint](/zh/docs/api/registry/connection-point) 指定的交点计算方法,计算参考线与图形的交点,该交点就是边的终点。 -我们在 `Registry.EdgeAnchor.presets` 命名空间中提供了以下几种锚点定义。 +X6 内置了以下几种锚点定义。 - [ratio](#ratio) 默认值,锚点位于被连接的边的指定比例处。 - [length](#length) 锚点位于被连接的边的指定长度处。 - [closest](#closest) 使用距离参照点最近的点作为锚点。 - [orth](#orth) 正交锚点。 - + -## presets +## 内置锚点 ### ratio 锚点位于被连接的边的指定比例处。支持如下参数: -| 参数名 | 参数类型 | 是否必选 | 默认值 | 参数说明 | -| --- | --- | :-: | --- | --- | -| ratio | number | 否 | `0.5` | 距离边起点多少比例位置处,默认位于边长度的中心。 | +| 参数名 | 参数类型 | 是否必选 | 默认值 | 参数说明 | +|--------|----------|:-------:|--------|--------------------------------------------| +| ratio | number | 否 | `0.5` | 距离边起点多少比例位置处,默认位于边长度的中心。 | ### length 锚点位于被连接的边的指定长度处。支持如下参数: -| 参数名 | 参数类型 | 是否必选 | 默认值 | 参数说明 | -| --- | --- | :-: | --- | --- | -| length | number | 否 | `20` | 距离边的起点多少长度位置处,默认位于偏离起点 `20px` 处。 | +| 参数名 | 参数类型 | 是否必选 | 默认值 | 参数说明 | +|--------|----------|:-------:|--------|----------------------------------------------------| +| length | number | 否 | `20` | 距离边的起点多少长度位置处,默认位于偏离起点 `20px` 处。 | ### closest @@ -47,13 +47,13 @@ redirect_from: 正交锚点。支持如下参数: -| 参数名 | 参数类型 | 是否必选 | 默认值 | 参数说明 | -| --- | --- | :-: | --- | --- | -| fallbackAt | number \| string | 否 | `undefined` | 当没有正交点时,使用 `fallbackAt` 指定的点作为锚点。
当 `fallbackAt` 为百分比字符串时,表示锚点位于距离起点多少比例位置处。
当 `fallbackAt` 为数字时,表示锚点位于距离起点多少长度位置处。 | +| 参数名 | 参数类型 | 是否必选 | 默认值 | 参数说明 | +|------------|------------------|:-------:|-------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| fallbackAt | number \| string | 否 | `undefined` | 当没有正交点时,使用 `fallbackAt` 指定的点作为锚点。
当 `fallbackAt` 为百分比字符串时,表示锚点位于距离起点多少比例位置处。
当 `fallbackAt` 为数字时,表示锚点位于距离起点多少长度位置处。 | -## registry +## 自定义锚点 -锚点定义是一个具有如下签名的函数,返回锚点。 +边的锚点定义是一个具有如下签名的函数,返回锚点。 ```ts export type Definition = ( @@ -65,29 +65,28 @@ export type Definition = ( ) => Point ``` -| 参数名 | 参数类型 | 参数说明 | -| ------ | ----------------------------- | ---------------- | +| 参数名 | 参数类型 | 参数说明 | +|--------|-------------------------------|---------------| | this | EdgeView | 边的视图。 | | view | EdgeView | 连接的边的视图。 | | magnet | SVGElement | 连接的边的元素。 | | ref | Point.PointLike \| SVGElement | 参照点/元素。 | | args | T | 参数。 | -并在 `Registry.Connector.registry` 对象上提供了 [`register`](#register) 和 [`unregister`](#unregister) 两个方法来注册和取消注册锚点定义,同时也将这两个方法分别挂载为 Graph 上的两个静态方法 `Graph.registerEdgeAnchor` 和 `Graph.unregisterEdgeAnchor`。 - -### register +完成锚点定义后,我们先注册锚点: ```ts -register(entities: { [name: string]: Definition }, force?: boolean): void -register(name: string, entity: Definition, force?: boolean): Definition +Graph.registerEdgeAnchor('custom-anchor', ...) ``` -注册连锚点定义。 - -### unregister +注册以后我们就可以通过锚点名称来使用: ```ts -unregister(name: string): Definition | null -``` - -取消注册锚点定义。 +new Graph({ + connecting: { + edgeAnchor: { + name: 'custom-anchor', + }, + }, +}) +``` \ No newline at end of file diff --git a/sites/x6-sites/docs/api/registry/filter.zh.md b/sites/x6-sites/docs/api/registry/filter.zh.md index a1f026748cd..b4fb161e220 100644 --- a/sites/x6-sites/docs/api/registry/filter.zh.md +++ b/sites/x6-sites/docs/api/registry/filter.zh.md @@ -1,6 +1,6 @@ --- -title: Filter -order: 12 +title: 滤镜 +order: 15 redirect_from: - /zh/docs - /zh/docs/api @@ -56,9 +56,9 @@ const filterId = graph.defineFilter({ rect.attr('body/filter', `#${filterId}`) ``` -通过上面的简单介绍,我们了解了如何使用滤镜,下面我们就分别来看看在 `Registry.Filter.presets` 命名空间中预定义了哪些滤镜。 +通过上面的简单介绍,我们了解了如何使用滤镜,下面我们就分别来看看在 X6 中预定义了哪些滤镜。 -## presets +## 内置滤镜 ### dropShadow @@ -71,7 +71,7 @@ rect.attr('body/filter', `#${filterId}`) | blur | number | `0` | 阴影的模糊半径。 | | opacity | number | `1` | 阴影的透明度。 | - + ### blur @@ -82,7 +82,7 @@ rect.attr('body/filter', `#${filterId}`) | x | number | `2` | X 轴方向的模糊程度。 | | y | number | - | Y 轴方向的模糊程度,缺省时与 X 轴保持一致。 | - + ### grayScale @@ -92,7 +92,7 @@ rect.attr('body/filter', `#${filterId}`) |--------|--------|--------|------------------------------------------------------| | amount | number | `1` | 灰阶程度。取值从 `[0-1]`,`0` 表示没有灰度,`1` 表示全灰。 | - + ### sepia @@ -102,7 +102,7 @@ rect.attr('body/filter', `#${filterId}`) |--------|--------|--------|--------------------------------------------------------------| | amount | number | `1` | 褐色程度。取值从 `[0-1]`,`0` 表示褐色程度为 `0`,`1` 表示全褐色。 | - + ### saturate @@ -112,7 +112,7 @@ rect.attr('body/filter', `#${filterId}`) |--------|--------|--------|----------------------| | amount | number | `1` | 饱和度。取值从 `[0-1]`。 | - + ### hueRotate @@ -122,6 +122,8 @@ rect.attr('body/filter', `#${filterId}`) |--------|--------|--------|-------------| | angle | number | `0` | 色相旋转角度。 | + + ### invert 反色滤镜。参考 [CSS invert()](https://developer.mozilla.org/en-US/docs/Web/CSS/filter-function/invert) 滤镜。 @@ -130,7 +132,7 @@ rect.attr('body/filter', `#${filterId}`) |--------|--------|--------|--------------------------------------------------------| | amount | number | `1` | 反色度。取值从 `[0-1]`,`0` 表示没有反色,`1` 表示完全反色。 | - + ### brightness @@ -140,7 +142,7 @@ rect.attr('body/filter', `#${filterId}`) |--------|--------|--------|------------------------------------------------| | amount | number | `1` | 明亮度。取值从 `[0-1]`,`0` 表示全暗,`1` 表示全亮。 | - + ### contrast @@ -150,7 +152,7 @@ rect.attr('body/filter', `#${filterId}`) |--------|--------|--------|------------------------------------------------| | amount | number | `1` | 对比度。取值从 `[0-1]`,`0` 表示全暗,`1` 表示全亮。 | - + ### highlight @@ -163,7 +165,7 @@ rect.attr('body/filter', `#${filterId}`) | blur | number | `0` | 模糊半径。 | | opacity | number | `1` | 透明度。 | - + ### outline @@ -176,9 +178,9 @@ rect.attr('body/filter', `#${filterId}`) | margin | number | `2` | 边距。 | | opacity | number | `1` | 透明度。 | - + -## registry +## 自定义滤镜 滤镜定义是一个具有如下签名的函数,返回 `` 标签字符串。 @@ -206,27 +208,9 @@ export function blur(args: BlurArgs = {}) { } ``` -同时,我们在 `Registry.Filter.registry` 对象上提供了 [`register`](#register) 和 [`unregister`](#unregister) 两个方法来注册和取消注册网格定义,同时也将这两个方法分别挂载为 Graph 上的两个静态方法 `Graph.registerFilter` 和 `Graph.unregisterFilter`。 - -### register - -```ts -register(entities: { [name: string]: Definition }, force?: boolean): void -register(name: string, entity: Definition, force?: boolean): Definition -``` - -注册滤镜。 我们可以调用 `Graph.registerFilter(...)` 方法来注册滤镜。 ```ts Graph.registerFilter('blur', blur) ``` - -### unregister - -```ts -unregister(name: string): Definition | null -``` - -取消注册滤镜。 diff --git a/sites/x6-sites/docs/api/registry/highlighter.zh.md b/sites/x6-sites/docs/api/registry/highlighter.zh.md index 0b4ffb5aac8..a2c4f7f800b 100644 --- a/sites/x6-sites/docs/api/registry/highlighter.zh.md +++ b/sites/x6-sites/docs/api/registry/highlighter.zh.md @@ -1,16 +1,16 @@ --- -title: Highlighter -order: 11 +title: 高亮器 +order: 14 redirect_from: - /zh/docs - /zh/docs/api - /zh/docs/api/registry --- -节点/边的高亮器,用于高亮指定的元素。我们在 `Registry.Highlighter.presets` 命名空间中提供了以下几种高亮器。 +节点/边的高亮器,用于高亮指定的元素。X6 内置了以下几种高亮器。 -| 名称 | 说明 | -| --------- | ------------------------------------------------------------ | +| 名称 | 说明 | +|-----------|----------------------------------------------------------| | stroke | [边框高亮器](#stroke),沿元素的包围盒渲染一个高亮的边框。 | | className | [样式名高亮器](#classname),通过添加额外的样式名来高亮元素。 | @@ -42,28 +42,28 @@ new Graph({ - `'magnetAvailable'` 连线过程中,连接桩可以被链接时被使用。 - `'magnetAdsorbed'` 连线过程中,自动吸附到连接桩时被使用。 -## presets +## 内置高亮器 ### stroke 边框高亮器,沿元素的包围盒渲染一个高亮的边框。 -| 参数名 | 类型 | 默认值 | 说明 | -| --- | --- | --- | --- | -| rx | number | `0` | 边框倒角。 | -| ry | number | `0` | 边框倒角。 | -| padding | number | `3` | 边距。 | -| attrs | object | `{ 'stroke-width': 3, stroke: '#FEB663' }` | 边框元素属性。 | +| 参数名 | 类型 | 默认值 | 说明 | +|---------|--------|--------------------------------------------|-------------| +| rx | number | `0` | 边框倒角。 | +| ry | number | `0` | 边框倒角。 | +| padding | number | `3` | 边距。 | +| attrs | object | `{ 'stroke-width': 3, stroke: '#FEB663' }` | 边框元素属性。 | ### className 样式名高亮器,通过添加额外的样式名来高亮元素。 -| 参数名 | 类型 | 默认值 | 说明 | -| --------- | ------ | ---------------- | -------- | +| 参数名 | 类型 | 默认值 | 说明 | +|-----------|--------|------------------|-------| | className | string | `x6-highlighted` | 样式名。 | -## registry +## 自定义高亮器 高亮器是一个具有如下签名的对象,该对象中包含 `highlight` 和 `unhighlight` 两个方法,分别用于高亮和取消高亮元素。 @@ -74,32 +74,12 @@ export interface Definition { } ``` -| 参数名 | 类型 | 默认值 | 说明 | -| -------- | -------- | ------ | -------------- | +| 参数名 | 类型 | 默认值 | 说明 | +|----------|----------|--------|-------------| | cellView | CellView | | 视图。 | | magnet | Element | | 被高亮的元素。 | | options | T | | 高亮选项。 | -同时我们在 `Registry.Highlighter.registry` 对象上提供了 [register](#register) 和 [unregister](#unregister) 两个方法来注册和取消注册高亮器。 - -### register - -```ts -register(entities: { [name: string]: Definition }, force?: boolean): void -register(name: string, entity: Definition, force?: boolean): Definition -``` - -注册高亮器。 - -### unregister - -```ts -unregister(name: string): Definition | null -``` - -取消注册高亮器。 - -### 自定义高亮器 下面我们来定义一个名为 `opacity` 的高亮器,该高亮器为元素添加一个 `'highlight-opacity'` 样式名。 @@ -119,8 +99,20 @@ export const opacity: Highlighter.Definition = { } ``` -完成定义后就可以注册我们的高亮器,实际上,我们将 `Registry.Highlighter.registry` 对象的 `register` 和 `unregister` 方法分别挂载为 `Graph` 的两个静态方法 `Graph.registerHighlighter` 和 `Graph.unregisterHighlighter`,所以我们可以像下面这样来注册高亮器: +完成定义后就可以注册我们的高亮器: ```ts Graph.registerHighlighter('opacity', opacity, true) ``` + +然后我们就可以通过 `opacity` 字符串来使用该高亮器了: + +```ts +new Graph({ + highlighting: { + magnetAvailable: { + name: 'opacity', + }, + }, +}) +``` \ No newline at end of file diff --git a/sites/x6-sites/docs/api/registry/node-anchor.zh.md b/sites/x6-sites/docs/api/registry/node-anchor.zh.md index 941d5df6521..b1bd78b755f 100644 --- a/sites/x6-sites/docs/api/registry/node-anchor.zh.md +++ b/sites/x6-sites/docs/api/registry/node-anchor.zh.md @@ -1,5 +1,5 @@ --- -title: NodeAnchor +title: 节点的锚点 order: 8 redirect_from: - /zh/docs @@ -7,12 +7,12 @@ redirect_from: - /zh/docs/api/registry --- -当连接到节点时,可以通过 NodeAnchor 来指定被连接的节点的锚点,锚点与连接点 [ConnectionPoint](/zh/docs/api/registry/connection-point) 共同确定了边的起点和终点。 +当连接到节点时,可以通过 `NodeAnchor` 来指定被连接的节点的锚点,锚点与连接点 [ConnectionPoint](/zh/docs/api/registry/connection-point) 共同确定了边的起点和终点。 -- 起点:从第一个路径点或目标节点的中心(没有路径点时)画一条参考线到源节点的锚点,然后根据 [connectionPoint](/zh/docs/api/model/edge#source-和-target) 指定的交点计算方法,计算参考线与图形的交点,该交点就是边的起点。 -- 终点:从最后一个路径点或源节点的中心(没有路径点时)画一条参考线到目标节点的锚点,然后根据 [connectionPoint](/zh/docs/api/model/edge#source-和-target) 指定的交点计算方法,计算参考线与图形的交点,该交点就是边的终点。 +- 起点:从第一个路径点或目标节点的中心(没有路径点时)画一条参考线到源节点的锚点,然后根据 [connectionPoint](/zh/docs/api/registry/connection-point) 指定的交点计算方法,计算参考线与图形的交点,该交点就是边的起点。 +- 终点:从最后一个路径点或源节点的中心(没有路径点时)画一条参考线到目标节点的锚点,然后根据 [connectionPoint](/zh/docs/api/registry/connection-point) 指定的交点计算方法,计算参考线与图形的交点,该交点就是边的终点。 -我们在 `Registry.NodeAnchor.presets` 命名空间中提供了以下几种锚点定义。 +X6 内置了以下几种锚点定义。 - [center](#center) 边链接的元素的中心点(默认值)。 - [top](#top) 边链接的元素的顶部中心点。 @@ -27,7 +27,7 @@ redirect_from: - [orth](#orth) 正交点。 - [nodeCenter](#nodecenter) 节点的中心点。 - + 可以在创建边时指定锚点: @@ -88,126 +88,126 @@ new Graph({ }) ``` -## presets +## 内置锚点 ### center 元素中心点,支持如下参数: -| 参数名 | 参数类型 | 是否必选 | 默认值 | 参数说明 | -| --- | --- | :-: | --- | --- | -| dx | number \| string | 否 | `0` | X 轴偏移量,支持数字绝对偏移量和百分比相对偏移量。 | -| dy | number \| string | 否 | `0` | Y 轴偏移量,支持数字绝对偏移量和百分比相对偏移量。 | -| rotate | boolean | 否 | `false` | 是否使用元素跟随节点旋转后的包围盒,默认不考虑旋转角度。 | +| 参数名 | 参数类型 | 是否必选 | 默认值 | 参数说明 | +|--------|------------------|:-------:|---------|----------------------------------------------------| +| dx | number \| string | 否 | `0` | X 轴偏移量,支持数字绝对偏移量和百分比相对偏移量。 | +| dy | number \| string | 否 | `0` | Y 轴偏移量,支持数字绝对偏移量和百分比相对偏移量。 | +| rotate | boolean | 否 | `false` | 是否使用元素跟随节点旋转后的包围盒,默认不考虑旋转角度。 | ### top 元素顶部中心点,支持如下参数: -| 参数名 | 参数类型 | 是否必选 | 默认值 | 参数说明 | -| --- | --- | :-: | --- | --- | -| dx | number \| string | 否 | `0` | X 轴偏移量,支持数字绝对偏移量和百分比相对偏移量。 | -| dy | number \| string | 否 | `0` | Y 轴偏移量,支持数字绝对偏移量和百分比相对偏移量。 | -| rotate | boolean | 否 | `false` | 是否使用元素跟随节点旋转后的包围盒,默认不考虑旋转角度。 | +| 参数名 | 参数类型 | 是否必选 | 默认值 | 参数说明 | +|--------|------------------|:-------:|---------|----------------------------------------------------| +| dx | number \| string | 否 | `0` | X 轴偏移量,支持数字绝对偏移量和百分比相对偏移量。 | +| dy | number \| string | 否 | `0` | Y 轴偏移量,支持数字绝对偏移量和百分比相对偏移量。 | +| rotate | boolean | 否 | `false` | 是否使用元素跟随节点旋转后的包围盒,默认不考虑旋转角度。 | ### bottom 元素底部中心点,支持如下参数: -| 参数名 | 参数类型 | 是否必选 | 默认值 | 参数说明 | -| --- | --- | :-: | --- | --- | -| dx | number \| string | 否 | `0` | X 轴偏移量,支持数字绝对偏移量和百分比相对偏移量。 | -| dy | number \| string | 否 | `0` | Y 轴偏移量,支持数字绝对偏移量和百分比相对偏移量。 | -| rotate | boolean | 否 | `false` | 是否使用元素跟随节点旋转后的包围盒,默认不考虑旋转角度。 | +| 参数名 | 参数类型 | 是否必选 | 默认值 | 参数说明 | +|--------|------------------|:-------:|---------|----------------------------------------------------| +| dx | number \| string | 否 | `0` | X 轴偏移量,支持数字绝对偏移量和百分比相对偏移量。 | +| dy | number \| string | 否 | `0` | Y 轴偏移量,支持数字绝对偏移量和百分比相对偏移量。 | +| rotate | boolean | 否 | `false` | 是否使用元素跟随节点旋转后的包围盒,默认不考虑旋转角度。 | ### left 元素左侧中心点,支持如下参数: -| 参数名 | 参数类型 | 是否必选 | 默认值 | 参数说明 | -| --- | --- | :-: | --- | --- | -| dx | number \| string | 否 | `0` | X 轴偏移量,支持数字绝对偏移量和百分比相对偏移量。 | -| dy | number \| string | 否 | `0` | Y 轴偏移量,支持数字绝对偏移量和百分比相对偏移量。 | -| rotate | boolean | 否 | `false` | 是否使用元素跟随节点旋转后的包围盒,默认不考虑旋转角度。 | +| 参数名 | 参数类型 | 是否必选 | 默认值 | 参数说明 | +|--------|------------------|:-------:|---------|----------------------------------------------------| +| dx | number \| string | 否 | `0` | X 轴偏移量,支持数字绝对偏移量和百分比相对偏移量。 | +| dy | number \| string | 否 | `0` | Y 轴偏移量,支持数字绝对偏移量和百分比相对偏移量。 | +| rotate | boolean | 否 | `false` | 是否使用元素跟随节点旋转后的包围盒,默认不考虑旋转角度。 | ### right 元素右侧中心点,支持如下参数: -| 参数名 | 参数类型 | 是否必选 | 默认值 | 参数说明 | -| --- | --- | :-: | --- | --- | -| dx | number \| string | 否 | `0` | X 轴偏移量,支持数字绝对偏移量和百分比相对偏移量。 | -| dy | number \| string | 否 | `0` | Y 轴偏移量,支持数字绝对偏移量和百分比相对偏移量。 | -| rotate | boolean | 否 | `false` | 是否使用元素跟随节点旋转后的包围盒,默认不考虑旋转角度。 | +| 参数名 | 参数类型 | 是否必选 | 默认值 | 参数说明 | +|--------|------------------|:-------:|---------|----------------------------------------------------| +| dx | number \| string | 否 | `0` | X 轴偏移量,支持数字绝对偏移量和百分比相对偏移量。 | +| dy | number \| string | 否 | `0` | Y 轴偏移量,支持数字绝对偏移量和百分比相对偏移量。 | +| rotate | boolean | 否 | `false` | 是否使用元素跟随节点旋转后的包围盒,默认不考虑旋转角度。 | ### midSide 最靠近边的那一侧中心点,支持如下参数: -| 参数名 | 参数类型 | 是否必选 | 默认值 | 参数说明 | -| --- | --- | :-: | --- | --- | -| padding | number | 否 | `0` | 通过放大元素的包围盒 `padding` 像素,来偏移中心点。 | -| rotate | boolean | 否 | `false` | 是否使用元素跟随节点旋转后的包围盒,默认不考虑旋转角度。 | -| direction | 'H' \| 'V' | 否 | - | 连接点的方向,比如如果设置为 `H`,只能连接到节点的左侧或者右侧的中心点,会根据位置自动判断。 | +| 参数名 | 参数类型 | 是否必选 | 默认值 | 参数说明 | +|-----------|------------|:-------:|---------|--------------------------------------------------------------------------------------| +| padding | number | 否 | `0` | 通过放大元素的包围盒 `padding` 像素,来偏移中心点。 | +| rotate | boolean | 否 | `false` | 是否使用元素跟随节点旋转后的包围盒,默认不考虑旋转角度。 | +| direction | 'H' \| 'V' | 否 | - | 连接点的方向,比如如果设置为 `H`,只能连接到节点的左侧或者右侧的中心点,会根据位置自动判断。 | ### topLeft 元素左上角,支持如下参数: -| 参数名 | 参数类型 | 是否必选 | 默认值 | 参数说明 | -| --- | --- | :-: | --- | --- | -| dx | number \| string | 否 | `0` | X 轴偏移量,支持数字绝对偏移量和百分比相对偏移量。 | -| dy | number \| string | 否 | `0` | Y 轴偏移量,支持数字绝对偏移量和百分比相对偏移量。 | -| rotate | boolean | 否 | `false` | 是否使用元素跟随节点旋转后的包围盒,默认不考虑旋转角度。 | +| 参数名 | 参数类型 | 是否必选 | 默认值 | 参数说明 | +|--------|------------------|:-------:|---------|----------------------------------------------------| +| dx | number \| string | 否 | `0` | X 轴偏移量,支持数字绝对偏移量和百分比相对偏移量。 | +| dy | number \| string | 否 | `0` | Y 轴偏移量,支持数字绝对偏移量和百分比相对偏移量。 | +| rotate | boolean | 否 | `false` | 是否使用元素跟随节点旋转后的包围盒,默认不考虑旋转角度。 | ### topRight 元素右上角,支持如下参数: -| 参数名 | 参数类型 | 是否必选 | 默认值 | 参数说明 | -| --- | --- | :-: | --- | --- | -| dx | number \| string | 否 | `0` | X 轴偏移量,支持数字绝对偏移量和百分比相对偏移量。 | -| dy | number \| string | 否 | `0` | Y 轴偏移量,支持数字绝对偏移量和百分比相对偏移量。 | -| rotate | boolean | 否 | `false` | 是否使用元素跟随节点旋转后的包围盒,默认不考虑旋转角度。 | +| 参数名 | 参数类型 | 是否必选 | 默认值 | 参数说明 | +|--------|------------------|:-------:|---------|----------------------------------------------------| +| dx | number \| string | 否 | `0` | X 轴偏移量,支持数字绝对偏移量和百分比相对偏移量。 | +| dy | number \| string | 否 | `0` | Y 轴偏移量,支持数字绝对偏移量和百分比相对偏移量。 | +| rotate | boolean | 否 | `false` | 是否使用元素跟随节点旋转后的包围盒,默认不考虑旋转角度。 | ### bottomLeft 元素左下角,支持如下参数: -| 参数名 | 参数类型 | 是否必选 | 默认值 | 参数说明 | -| --- | --- | :-: | --- | --- | -| dx | number \| string | 否 | `0` | X 轴偏移量,支持数字绝对偏移量和百分比相对偏移量。 | -| dy | number \| string | 否 | `0` | Y 轴偏移量,支持数字绝对偏移量和百分比相对偏移量。 | -| rotate | boolean | 否 | `false` | 是否使用元素跟随节点旋转后的包围盒,默认不考虑旋转角度。 | +| 参数名 | 参数类型 | 是否必选 | 默认值 | 参数说明 | +|--------|------------------|:-------:|---------|----------------------------------------------------| +| dx | number \| string | 否 | `0` | X 轴偏移量,支持数字绝对偏移量和百分比相对偏移量。 | +| dy | number \| string | 否 | `0` | Y 轴偏移量,支持数字绝对偏移量和百分比相对偏移量。 | +| rotate | boolean | 否 | `false` | 是否使用元素跟随节点旋转后的包围盒,默认不考虑旋转角度。 | ### bottomRight 元素右下角,支持如下参数: -| 参数名 | 参数类型 | 是否必选 | 默认值 | 参数说明 | -| --- | --- | :-: | --- | --- | -| dx | number \| string | 否 | `0` | X 轴偏移量,支持数字绝对偏移量和百分比相对偏移量。 | -| dy | number \| string | 否 | `0` | Y 轴偏移量,支持数字绝对偏移量和百分比相对偏移量。 | -| rotate | boolean | 否 | `false` | 是否使用元素跟随节点旋转后的包围盒,默认不考虑旋转角度。 | +| 参数名 | 参数类型 | 是否必选 | 默认值 | 参数说明 | +|--------|------------------|:-------:|---------|----------------------------------------------------| +| dx | number \| string | 否 | `0` | X 轴偏移量,支持数字绝对偏移量和百分比相对偏移量。 | +| dy | number \| string | 否 | `0` | Y 轴偏移量,支持数字绝对偏移量和百分比相对偏移量。 | +| rotate | boolean | 否 | `false` | 是否使用元素跟随节点旋转后的包围盒,默认不考虑旋转角度。 | ### orth 正交点,支持如下参数: -| 参数名 | 参数类型 | 是否必选 | 默认值 | 参数说明 | -| ------- | -------- | :------: | ------ | ------------ | +| 参数名 | 参数类型 | 是否必选 | 默认值 | 参数说明 | +|---------|----------|:-------:|--------|---------| | padding | number | 否 | `0` | X 轴偏移量。 | ### nodeCenter 节点的中心,支持如下参数: -| 参数名 | 参数类型 | 是否必选 | 默认值 | 参数说明 | -| ------ | -------- | :------: | ------ | ------------ | +| 参数名 | 参数类型 | 是否必选 | 默认值 | 参数说明 | +|--------|----------|:-------:|--------|---------| | dx | number | 否 | `0` | X 轴偏移量。 | | dy | number | 否 | `0` | Y 轴偏移量。 | -## registry +## 自定义锚点 锚点定义是一个具有如下签名的函数,返回锚点。 @@ -222,8 +222,8 @@ export type Definition = ( ) => Point ``` -| 参数名 | 参数类型 | 参数说明 | -| -------- | ----------------------------- | -------------------- | +| 参数名 | 参数类型 | 参数说明 | +|----------|-------------------------------|-------------------| | this | EdgeView | 边的视图。 | | nodeView | NodeView | 连接的节点视图。 | | magnet | SVGElement | 连接的节点上的元素。 | @@ -231,21 +231,20 @@ export type Definition = ( | args | T | 参数。 | | type | Edge.TerminalType | 边的终端类型。 | -并在 `Registry.NodeAnchor.registry` 对象上提供了 [`register`](#register) 和 [`unregister`](#unregister) 两个方法来注册和取消注册锚点定义,同时也将这两个方法分别挂载为 Graph 上的两个静态方法 `Graph.registerAnchor` 和 `Graph.unregisterAnchor`。 - -### register +完成锚点定义后,我们先注册锚点: ```ts -register(entities: { [name: string]: Definition }, force?: boolean): void -register(name: string, entity: Definition, force?: boolean): Definition +Graph.registerAnchor('custom-anchor', ...) ``` -注册连锚点定义。 - -### unregister +注册以后我们就可以通过锚点名称来使用: ```ts -unregister(name: string): Definition | null -``` - -取消注册锚点定义。 +new Graph({ + connecting: { + anchor: { + name: 'custom-anchor', + }, + }, +}) +``` \ No newline at end of file diff --git a/sites/x6-sites/docs/api/registry/port-label-layout.zh.md b/sites/x6-sites/docs/api/registry/port-label-layout.zh.md index 5df5125fb1c..e8323690ced 100644 --- a/sites/x6-sites/docs/api/registry/port-label-layout.zh.md +++ b/sites/x6-sites/docs/api/registry/port-label-layout.zh.md @@ -1,6 +1,6 @@ --- -title: PortLabelLayout -order: 14 +title: 连接桩标签布局 +order: 12 redirect_from: - /zh/docs - /zh/docs/api @@ -59,11 +59,9 @@ graph.addNode( 下面我们一起来看看如何使用内置的标签布局算法,以及如何自定并注册自定义布局算法。 -## presets +## 内置连接桩标签布局 -在 `Registry.PortLabelLayout.presets` 命令空间中提供了以下几个布局算法。 - -### _Side_ +### Side 标签位于连接桩的某一侧。 @@ -107,9 +105,9 @@ label: { } ``` - + -### _Inside/Outside_ +### Inside/Outside 标签位于节点的内部或者外部,支持一下四种布局: @@ -147,9 +145,9 @@ label: { } ``` - + -### _Radial_ +### Radial 将标签放在圆形或椭圆形节点的外围。支持一下两种布局: @@ -182,9 +180,9 @@ label: { } ``` - + -## registry +## 自定义连接桩标签布局 连接桩标签定位是一个具有如下签名的函数,返回标签相对于连接桩的位置和旋转角度。 @@ -220,48 +218,10 @@ function bottomRight(portPosition, elemBBox, args) { 布局算法实现后,需要注册到系统,注册后就可以像内置布局算法那样来使用。 -### register - -```ts -/** - * - */ -register(entities: { [name: string]: Definition }, force?: boolean): void -register(name: string, entity: Definition, force?: boolean): Definition -``` - -注册自定义布局算法。 - -### unregister - -```ts -unregister(name: string): Definition | null -``` - -删除注册的自定义布局算法。 - -实际上,我们将该命名空间的中 `register` 和 `unregister` 两个方法分别挂载为 Graph 的两个静态方法 `Graph.registerPortLabelLayout` 和 `Graph.unregisterPortLabelLayout`,所以我们可以像下面这样来注册刚刚定义的布局算法: - ```ts Graph.registerPortLabelLayout('bottomRight', bottomRight) ``` -或者: - -```ts -Graph.registerPortLayout('bottomRight', (portPosition, elemBBox, args) => { - const dx = args.dx || 10 - const dy = args.dy || 10 - - return { - position: { - portPosition.x + dx, - portPosition.y + dy, - } - } -}) -``` - 注册以后,我们就可以像内置布局算法那样来使用: ```ts diff --git a/sites/x6-sites/docs/api/registry/port-layout.zh.md b/sites/x6-sites/docs/api/registry/port-layout.zh.md index 97d017ca42d..65f4b01229a 100644 --- a/sites/x6-sites/docs/api/registry/port-layout.zh.md +++ b/sites/x6-sites/docs/api/registry/port-layout.zh.md @@ -1,6 +1,6 @@ --- -title: PortLayout -order: 13 +title: 连接桩布局 +order: 11 redirect_from: - /zh/docs - /zh/docs/api @@ -51,9 +51,7 @@ graph.addNode( 下面我们一起来看看如何使用内置的连接桩布局算法,以及如何自定并注册自定义布局算法。 -## presets - -在 `Registry.PortLayout.presets` 命名空间下提供了以下几个内置的布局算法。 +## 内置布局 ### absolute @@ -100,7 +98,7 @@ graph.addNode({ }) ``` - + ### left, right, top, bottom @@ -145,7 +143,7 @@ graph.addNode({ }) ``` - + ### line @@ -200,7 +198,7 @@ graph.addNode({ }) ``` - + ### ellipse @@ -257,7 +255,7 @@ Array.from({ length: 10 }).forEach((_, index) => { }) ``` - + ### ellipseSpread @@ -312,9 +310,9 @@ Array.from({ length: 36 }).forEach(function (_, index) { }) ``` - + -## registry +## 自定义连接桩布局 连接桩布局算法是一个函数具有如下签名的函数,返回每个连接桩相对于节点的相对位置。例如,某节点在画布的位置是 `{ x: 30, y: 40 }`,如果返回的某个连接桩的位置是 `{ x: 2, y: 4 }`,那么该连接桩渲染到画布后的位置是 `{ x: 32, y: 44 }`。 @@ -351,47 +349,11 @@ function sin(portsPositionArgs, elemBBox) { 布局算法实现后,需要注册到系统,注册后就可以像内置布局算法那样来使用。 -### register - -```ts -register(entities: { [name: string]: Definition }, force?: boolean): void -register(name: string, entity: Definition, force?: boolean): Definition -``` - -注册自定义布局算法。 - -### unregister - -```ts -unregister(name: string): Definition | null -``` - -删除注册的自定义布局算法。 - -实际上,我们将 `registry` 的 `register` 和 `unregister` 方法分别挂载为 `Graph` 的两个静态方法 `Graph.registerPortLayout` 和 `Graph.unregisterPortLayout`,所以我们定义的正弦布局可以像下面这样注册到系统: ```ts Graph.registerPortLayout('sin', sin) ``` -或者: - -```ts -Graph.registerPortLayout('sin', (portsPositionArgs, elemBBox) => { - return portsPositionArgs.map((_, index) => { - const step = -Math.PI / 8 - const y = Math.sin(index * step) * 50 - return { - position: { - x: index * 12, - y: y + elemBBox.height, - }, - angle: 0, - } - }) -}) -``` - 注册以后,我们就可以像内置布局算法那样来使用: ```ts @@ -429,4 +391,4 @@ Array.from({ length: 24 }).forEach(() => { }) ``` - + diff --git a/sites/x6-sites/src/api/attrs/text-anchor/index.less b/sites/x6-sites/src/api/attrs/text-anchor/index.less new file mode 100644 index 00000000000..e39b4f6bd08 --- /dev/null +++ b/sites/x6-sites/src/api/attrs/text-anchor/index.less @@ -0,0 +1,43 @@ +.attrs-text-anchor-app { + display: flex; + padding: 0; + padding: 16px 8px; + font-family: sans-serif; + + .app-left { + bottom: 0; + width: 336px; + padding: 0 8px; + } + + .app-content { + flex: 1; + margin-right: 8px; + margin-left: 8px; + box-shadow: 0 0 10px 1px #e9e9e9; + } + + .ant-card { + box-shadow: 0 0 10px 1px #e9e9e9; + } + + .ant-card-head-title { + text-align: center; + } + + .ant-row { + margin: 16px 0; + text-align: left; + } + + .slider-value { + display: inline-block; + margin-left: 8px; + padding: 3px 7px; + color: #333; + font-size: 12px; + line-height: 1.25; + background: #eee; + border-radius: 10px; + } +} diff --git a/sites/x6-sites/src/api/attrs/text-anchor/index.tsx b/sites/x6-sites/src/api/attrs/text-anchor/index.tsx new file mode 100644 index 00000000000..c6943fa031c --- /dev/null +++ b/sites/x6-sites/src/api/attrs/text-anchor/index.tsx @@ -0,0 +1,124 @@ +import * as React from 'react' +import { Graph, Cell } from '@antv/x6' +import { Settings, State } from './settings' +import './index.less' + +export default class Example extends React.Component { + private container: HTMLDivElement + private node: Cell + + componentDidMount() { + const graph = new Graph({ + container: this.container, + background: { + color: '#F2F7FA', + }, + }) + + this.node = graph.addNode({ + x: 120, + y: 80, + width: 200, + height: 80, + markup: [ + { + tagName: 'rect', + selector: 'body', + }, + { + tagName: 'path', + selector: 'hLine', + }, + { + tagName: 'path', + selector: 'vLine', + }, + { + tagName: 'rect', + selector: 'bg', + }, + { + tagName: 'text', + selector: 'label', + }, + { + tagName: 'circle', + selector: 'dot', + }, + ], + attrs: { + body: { + refWidth: '100%', + refHeight: '100%', + fill: '#ffffff', + stroke: '#8f8f8f', + strokeWidth: 1, + }, + dot: { + r: 2, + fill: '#8f8f8f', + stroke: 'none', + refX: 0.5, + refY: 0.5, + }, + bg: { + ref: 'label', + fill: '#8f8f8f', + stroke: '#8f8f8f', + rx: 2, + ry: 2, + refWidth: 1, + refHeight: 1, + refX: 0, + refY: 0, + }, + label: { + fontSize: 14, + refX: 0.5, + refY: 0.5, + textAnchor: 'start', + textVerticalAnchor: 'top', + fill: '#fff', + stroke: '#fff', + fontFamily: 'Arial, helvetica, sans-serif', + text: 'Hello World', + }, + hLine: { + refY: 0.5, + d: 'M -40 0 240 0', + stroke: '#8f8f8f', + strokeDasharray: '5 5', + }, + vLine: { + refX: 0.5, + d: 'M 0 -40 0 120', + stroke: '#8f8f8f', + strokeDasharray: '5 5', + }, + }, + }) + } + + onGridChanged = (attrs: State) => { + this.node.attr({ + label: attrs, + hLine: { refY: attrs.refY }, + vLine: { refX: attrs.refX }, + } as any) + } + + refContainer = (container: HTMLDivElement) => { + this.container = container + } + + render() { + return ( +
+
+ +
+
+
+ ) + } +} diff --git a/sites/x6-sites/src/api/attrs/text-anchor/settings.tsx b/sites/x6-sites/src/api/attrs/text-anchor/settings.tsx new file mode 100644 index 00000000000..44afed37036 --- /dev/null +++ b/sites/x6-sites/src/api/attrs/text-anchor/settings.tsx @@ -0,0 +1,131 @@ +import React from 'react' +import { Radio, Slider, Card, Row, Col } from 'antd' + +export interface Props { + onChange: (state: State) => void +} + +export interface State { + refX: number + refY: number + textAnchor?: string + textVerticalAnchor?: string +} + +export class Settings extends React.Component { + state: State = { + refX: 0.5, + refY: 0.5, + textAnchor: 'start', + textVerticalAnchor: 'top', + } + + notifyChange() { + this.props.onChange(this.state) + } + + onRefXChanged = (refX: number) => { + this.setState({ refX }, () => { + this.notifyChange() + }) + } + + onRefYChanged = (refY: number) => { + this.setState({ refY }, () => { + this.notifyChange() + }) + } + + onTextAnchorAlignChange = (e: any) => { + this.setState( + { + textAnchor: e.target.value, + }, + () => { + this.notifyChange() + }, + ) + } + + onTextVerticalAnchorAlignChange = (e: any) => { + this.setState( + { + textVerticalAnchor: e.target.value, + }, + () => { + this.notifyChange() + }, + ) + } + + render() { + return ( + + + refX + + + + +
+ {(this.state.refX * 100).toFixed(0)}% +
+ +
+ + refY + + + + +
+ {(this.state.refY * 100).toFixed(0)}% +
+ +
+ + textAnchor + + + + + Start + Middle + end + + + + + textVerticalAnchor + + + + + Top + Middle + Bottom + + + +
+ ) + } +} diff --git a/sites/x6-sites/src/api/attrs/text-wrap/index.less b/sites/x6-sites/src/api/attrs/text-wrap/index.less new file mode 100644 index 00000000000..a31605cc7de --- /dev/null +++ b/sites/x6-sites/src/api/attrs/text-wrap/index.less @@ -0,0 +1,14 @@ +.attrs-text-wrap-app { + display: flex; + width: 100%; + padding: 0; + font-family: sans-serif; + + .app-content { + flex: 1; + height: 216px; + margin-right: 8px; + margin-left: 8px; + box-shadow: 0 0 10px 1px #e9e9e9; + } +} diff --git a/sites/x6-sites/src/api/attrs/text-wrap/index.tsx b/sites/x6-sites/src/api/attrs/text-wrap/index.tsx new file mode 100644 index 00000000000..408a77d9b55 --- /dev/null +++ b/sites/x6-sites/src/api/attrs/text-wrap/index.tsx @@ -0,0 +1,185 @@ +import * as React from 'react' +import { Graph } from '@antv/x6' +import './index.less' + +export default class Example extends React.Component { + private container: HTMLDivElement + + componentDidMount() { + const graph = new Graph({ + container: this.container, + background: { + color: '#F2F7FA', + }, + }) + + graph.addNode({ + x: 40, + y: 40, + width: 160, + height: 48, + attrs: { + body: { + fill: '#ffffff', + stroke: '#8f8f8f', + strokeWidth: 1, + rx: 5, + ry: 5, + }, + label: { + textAnchor: 'left', + refX: 8, + text: 'hello test foo bar lint css jsvascript typescript', + textWrap: { + width: 144, + height: 40, + ellipsis: true, + }, + }, + }, + }) + + graph.addNode({ + x: 240, + y: 40, + width: 160, + height: 48, + attrs: { + body: { + fill: '#ffffff', + stroke: '#8f8f8f', + strokeWidth: 1, + rx: 5, + ry: 5, + }, + label: { + textAnchor: 'left', + refX: 8, + text: 'hello test foo bar 中文字符 lint css jsvascript typescript', + textWrap: { + width: 144, + height: 40, + ellipsis: true, + }, + }, + }, + }) + + graph.addNode({ + x: 440, + y: 40, + width: 160, + height: 48, + attrs: { + body: { + fill: '#ffffff', + stroke: '#8f8f8f', + strokeWidth: 1, + rx: 5, + ry: 5, + }, + label: { + textAnchor: 'left', + refX: 8, + text: 'hello-test-foo-bar-lint-css-jsvascript-typescript', + textWrap: { + width: 144, + height: 40, + ellipsis: true, + }, + }, + }, + }) + + graph.addNode({ + x: 40, + y: 128, + width: 160, + height: 48, + attrs: { + body: { + fill: '#ffffff', + stroke: '#8f8f8f', + strokeWidth: 1, + rx: 5, + ry: 5, + }, + label: { + textAnchor: 'left', + refX: 8, + text: 'Thisissomeveryveryverylong word. Words will break according to usual rules.', + textWrap: { + width: 144, + height: 40, + ellipsis: true, + }, + }, + }, + }) + + graph.addNode({ + x: 240, + y: 128, + width: 160, + height: 48, + attrs: { + body: { + fill: '#ffffff', + stroke: '#8f8f8f', + strokeWidth: 1, + rx: 5, + ry: 5, + }, + label: { + textAnchor: 'left', + refX: 8, + text: 'Thisissomeveryveryverylong word. Words will break according to usual rules.', + textWrap: { + width: 144, + height: 40, + ellipsis: true, + breakWord: false, + }, + }, + }, + }) + + graph.addNode({ + x: 440, + y: 128, + width: 160, + height: 48, + attrs: { + body: { + fill: '#ffffff', + stroke: '#8f8f8f', + strokeWidth: 1, + rx: 5, + ry: 5, + }, + label: { + textAnchor: 'left', + refX: 8, + text: 'hello test foo bar lint css jsvascript typescript', + textWrap: { + width: 144, + height: 20, + ellipsis: true, + }, + }, + }, + }) + } + + refContainer = (container: HTMLDivElement) => { + this.container = container + } + + render() { + return ( +
+
+
+ ) + } +} diff --git a/sites/x6-sites/src/api/attrs/x-align/index.less b/sites/x6-sites/src/api/attrs/x-align/index.less new file mode 100644 index 00000000000..32490fd096e --- /dev/null +++ b/sites/x6-sites/src/api/attrs/x-align/index.less @@ -0,0 +1,43 @@ +.attrs-x-align-app { + display: flex; + padding: 0; + padding: 16px 8px; + font-family: sans-serif; + + .app-left { + bottom: 0; + width: 336px; + padding: 0 8px; + } + + .app-content { + flex: 1; + margin-right: 8px; + margin-left: 8px; + box-shadow: 0 0 10px 1px #e9e9e9; + } + + .ant-card { + box-shadow: 0 0 10px 1px #e9e9e9; + } + + .ant-card-head-title { + text-align: center; + } + + .ant-row { + margin: 16px 0; + text-align: left; + } + + .slider-value { + display: inline-block; + margin-left: 8px; + padding: 3px 7px; + color: #333; + font-size: 12px; + line-height: 1.25; + background: #eee; + border-radius: 10px; + } +} diff --git a/sites/x6-sites/src/api/attrs/x-align/index.tsx b/sites/x6-sites/src/api/attrs/x-align/index.tsx new file mode 100644 index 00000000000..f67b66cef7d --- /dev/null +++ b/sites/x6-sites/src/api/attrs/x-align/index.tsx @@ -0,0 +1,119 @@ +import React from 'react' +import { Graph, Cell } from '@antv/x6' +import { Settings, State } from './settings' +import './index.less' + +export default class Example extends React.Component { + private container: HTMLDivElement + private node: Cell + + componentDidMount() { + const graph = new Graph({ + container: this.container, + width: 400, + height: 250, + background: { + color: '#F2F7FA', + }, + }) + + this.node = graph.addNode({ + x: 60, + y: 60, + width: 280, + height: 120, + markup: [ + { + tagName: 'rect', + selector: 'body', + }, + { + tagName: 'path', + selector: 'hLine', + }, + { + tagName: 'path', + selector: 'vLine', + }, + { + tagName: 'rect', + selector: 'ref', + }, + { + tagName: 'circle', + selector: 'dot', + }, + { + tagName: 'text', + selector: 'label', + }, + ], + attrs: { + body: { + refWidth: '100%', + refHeight: '100%', + fill: '#ffffff', + stroke: '#8f8f8f', + strokeWidth: 1, + }, + dot: { + r: 3, + fill: '#8f8f8f', + stroke: 'none', + refX: 0.5, + refY: 0.5, + }, + ref: { + width: 70, + height: 30, + refX: 0.5, + refY: 0.5, + // xAlign: 'middle', + // yAlign: 'middle', + fill: '#8f8f8f', + stroke: '#8f8f8f', + }, + label: { + fontSize: 14, + fill: '#333333', + fontFamily: 'Arial, helvetica, sans-serif', + }, + hLine: { + refY: 0.5, + d: 'M -30 0 310 0', + stroke: '#8f8f8f', + strokeDasharray: '5 5', + }, + vLine: { + refX: 0.5, + d: 'M 0 -30 0 150', + stroke: '#8f8f8f', + strokeDasharray: '5 5', + }, + }, + }) + } + + onAttrsChanged = (attrs: State) => { + this.node.attr({ + ref: attrs, + hLine: { refY: attrs.refY }, + vLine: { refX: attrs.refX }, + } as any) + } + + refContainer = (container: HTMLDivElement) => { + this.container = container + } + + render() { + return ( +
+
+ +
+
+
+ ) + } +} diff --git a/sites/x6-sites/src/api/attrs/x-align/settings.tsx b/sites/x6-sites/src/api/attrs/x-align/settings.tsx new file mode 100644 index 00000000000..94644087d9c --- /dev/null +++ b/sites/x6-sites/src/api/attrs/x-align/settings.tsx @@ -0,0 +1,127 @@ +import React from 'react' +import { Radio, Slider, Card, Row, Col } from 'antd' + +export interface Props { + onChange: (state: State) => void +} + +export interface State { + refX: number + refY: number + xAlign?: string + yAlign?: string +} + +export class Settings extends React.Component { + state: State = { + refX: 0.5, + refY: 0.5, + xAlign: 'left', + yAlign: 'top', + } + + notifyChange() { + this.props.onChange(this.state) + } + + onRefXChanged = (refX: number) => { + this.setState({ refX }, () => { + this.notifyChange() + }) + } + + onRefYChanged = (refY: number) => { + this.setState({ refY }, () => { + this.notifyChange() + }) + } + + onXAlignChange = (e: any) => { + this.setState( + { + xAlign: e.target.value, + }, + () => { + this.notifyChange() + }, + ) + } + + onYAlignChange = (e: any) => { + this.setState( + { + yAlign: e.target.value, + }, + () => { + this.notifyChange() + }, + ) + } + + render() { + return ( + + + refX + + + + +
+ {(this.state.refX * 100).toFixed(0)}% +
+ +
+ + refY + + + + +
+ {(this.state.refY * 100).toFixed(0)}% +
+ +
+ + xAlign + + + Left + Middle + Right + + + + + yAlign + + + Top + Middle + Bottom + + + +
+ ) + } +} diff --git a/sites/x6-sites/src/api/connection-point/playground/index.less b/sites/x6-sites/src/api/connection-point/playground/index.less new file mode 100644 index 00000000000..59eefaba382 --- /dev/null +++ b/sites/x6-sites/src/api/connection-point/playground/index.less @@ -0,0 +1,43 @@ +.connection-point-app { + display: flex; + padding: 0; + padding: 16px 8px; + font-family: sans-serif; + + .app-left { + bottom: 0; + width: 336px; + padding: 0 8px; + } + + .app-content { + flex: 1; + margin-right: 8px; + margin-left: 8px; + box-shadow: 0 0 10px 1px #e9e9e9; + } + + .ant-card { + box-shadow: 0 0 10px 1px #e9e9e9; + } + + .ant-card-head-title { + text-align: center; + } + + .ant-row { + margin: 16px 0; + text-align: left; + } + + .slider-value { + display: inline-block; + margin-left: 8px; + padding: 3px 7px; + color: #333; + font-size: 12px; + line-height: 1.25; + background: #eee; + border-radius: 10px; + } +} diff --git a/sites/x6-sites/src/api/connection-point/playground/index.tsx b/sites/x6-sites/src/api/connection-point/playground/index.tsx new file mode 100644 index 00000000000..214bc908ae9 --- /dev/null +++ b/sites/x6-sites/src/api/connection-point/playground/index.tsx @@ -0,0 +1,181 @@ +import React from 'react' +import { Graph, Edge, Node } from '@antv/x6' +import { Settings, State } from './settings' +import './index.less' + +export default class Example extends React.Component { + private container: HTMLDivElement + private ellipse: Node + private edge1: Edge + private edge2: Edge + + componentDidMount() { + const graph = new Graph({ + container: this.container, + background: { + color: '#F2F7FA', + }, + interacting: false, + }) + + const rect = graph.addNode({ + x: 160, + y: 160, + width: 100, + height: 40, + attrs: { + body: { + stroke: '#8f8f8f', + strokeWidth: 1, + fill: '#fff', + rx: 6, + ry: 6, + }, + }, + }) + + const ellipse = graph.addNode({ + shape: 'ellipse', + x: 460, + y: 160, + width: 100, + height: 40, + angle: 45, + markup: [ + { tagName: 'ellipse', selector: 'body' }, + { tagName: 'rect', selector: 'outline' }, + ], + attrs: { + body: { + refWidth: '100%', + refHeight: '100%', + stroke: '#8f8f8f', + strokeWidth: 1, + }, + outline: { + refWidth: '100%', + refHeight: '100%', + strokeWidth: 1, + strokeDasharray: '5 5', + stroke: '#8f8f8f', + fill: 'none', + }, + }, + }) + + let bbox: Node + const updateBBox = () => { + if (bbox) { + bbox.remove() + } + const rect = ellipse.findView(graph)!.getBBox() + bbox = graph.addNode({ + x: rect.x, + y: rect.y, + width: rect.width, + height: rect.height, + attrs: { + body: { + strokeWidth: 1, + strokeDasharray: '5 5', + stroke: '#8f8f8f', + fill: 'none', + }, + }, + }) + } + + ellipse.on('change:angle', updateBBox) + + updateBBox() + + const edge1 = graph.addEdge({ + source: { x: 100, y: 180 }, + target: { cell: rect.id }, + attrs: { + line: { + stroke: '#8f8f8f', + strokeWidth: 1, + targetMarker: 'classic', + }, + }, + }) + + const edge2 = graph.addEdge({ + source: { x: 320, y: 180 }, + target: { cell: ellipse.id }, + attrs: { + line: { + stroke: '#8f8f8f', + strokeWidth: 1, + targetMarker: 'classic', + }, + }, + }) + + function animate() { + edge1.transition('source', 9.36 / 60, { + duration: 5000, + interp: (start, startTime) => { + const corr = startTime * (2 * Math.PI) - Math.PI / 2 + const origin = { x: 210, y: 180 } + const radius = 120 + return function (t) { + return { + x: origin.x + radius * Math.cos(t * 2 * Math.PI + corr), + y: origin.y + radius * Math.sin(t * 2 * Math.PI + corr), + } + } + }, + }) + + edge2.transition('source', 9.36 / 60, { + duration: 5000, + interp: (start, startTime) => { + const corr = startTime * (2 * Math.PI) - Math.PI / 2 + const origin = { x: 485, y: 180 } + const radius = 120 + return function (t) { + return { + x: origin.x + radius * Math.cos(t * 2 * Math.PI + corr), + y: origin.y + radius * Math.sin(t * 2 * Math.PI + corr), + } + } + }, + }) + } + + animate() + + edge1.on('transition:complete', animate) + + this.edge1 = edge1 + this.edge2 = edge2 + this.ellipse = ellipse + } + + updateBBox() {} + + onAttrsChanged = ({ anchor, connectionPoint, angle }: State) => { + this.edge1.prop('target/anchor', { name: anchor }) + this.edge1.prop('target/connectionPoint', { name: connectionPoint }) + this.edge2.prop('target/anchonr', { name: anchor }) + this.edge2.prop('target/connectionPoint', { name: connectionPoint }) + this.ellipse.rotate(angle, { absolute: true }) + } + + refContainer = (container: HTMLDivElement) => { + this.container = container + } + + render() { + return ( +
+
+ +
+
+
+ ) + } +} diff --git a/sites/x6-sites/src/api/connection-point/playground/settings.tsx b/sites/x6-sites/src/api/connection-point/playground/settings.tsx new file mode 100644 index 00000000000..e0c4a2e65ee --- /dev/null +++ b/sites/x6-sites/src/api/connection-point/playground/settings.tsx @@ -0,0 +1,109 @@ +import React from 'react' +import { Radio, Slider, Card, Row, Col } from 'antd' + +export interface Props { + onChange: (state: State) => void +} + +export interface State { + anchor: string + connectionPoint: string + angle: number +} + +export class Settings extends React.Component { + state: State = { + anchor: 'center', + connectionPoint: 'boundary', + angle: 45, + } + + notifyChange() { + this.props.onChange(this.state) + } + + onAngleChanged = (angle: number) => { + this.setState({ angle }, () => { + this.notifyChange() + }) + } + + onAnchorChange = (e: any) => { + this.setState({ anchor: e.target.value }, () => { + this.notifyChange() + }) + } + + onConnectionPointChange = (e: any) => { + this.setState({ connectionPoint: e.target.value }, () => { + this.notifyChange() + }) + } + + render() { + return ( + + + anchor + + + + + center + nodeCenter + orth + midSide + top + bottom + left + right + topLeft + topRight + bottomLeft + bottomRight + + + + + connectionPoint + + + + + boundary + anchor + bbox + rect + + + + + angle + + + + +
{this.state.angle}°
+ +
+
+ ) + } +} diff --git a/sites/x6-sites/src/api/connector/jumpover/index.less b/sites/x6-sites/src/api/connector/jumpover/index.less new file mode 100644 index 00000000000..2e4edc015b0 --- /dev/null +++ b/sites/x6-sites/src/api/connector/jumpover/index.less @@ -0,0 +1,44 @@ +.connector-jumpover-app { + display: flex; + padding: 0; + padding: 16px 8px; + font-family: sans-serif; + + .app-left { + bottom: 0; + width: 336px; + padding: 0 8px; + } + + .app-content { + flex: 1; + height: 320px; + margin-right: 8px; + margin-left: 8px; + box-shadow: 0 0 10px 1px #e9e9e9; + } + + .ant-card { + box-shadow: 0 0 10px 1px #e9e9e9; + } + + .ant-card-head-title { + text-align: center; + } + + .ant-row { + margin: 16px 0; + text-align: left; + } + + .slider-value { + display: inline-block; + margin-left: 8px; + padding: 3px 7px; + color: #333; + font-size: 12px; + line-height: 1.25; + background: #eee; + border-radius: 10px; + } +} diff --git a/sites/x6-sites/src/api/connector/jumpover/index.tsx b/sites/x6-sites/src/api/connector/jumpover/index.tsx new file mode 100644 index 00000000000..75593c183d4 --- /dev/null +++ b/sites/x6-sites/src/api/connector/jumpover/index.tsx @@ -0,0 +1,126 @@ +import React from 'react' +import { Graph, Edge } from '@antv/x6' +import { Settings, State } from './settings' +import './index.less' + +export default class Example extends React.Component { + private container: HTMLDivElement + private edge: Edge + + componentDidMount() { + const graph = new Graph({ + container: this.container, + background: { + color: '#F2F7FA', + }, + }) + + const source = graph.addNode({ + x: 24, + y: 70, + width: 100, + height: 40, + attrs: { + body: { + stroke: '#8f8f8f', + strokeWidth: 1, + fill: '#fff', + rx: 6, + ry: 6, + }, + }, + }) + + const target = graph.addNode({ + x: 24, + y: 160, + width: 100, + height: 40, + attrs: { + body: { + stroke: '#8f8f8f', + strokeWidth: 1, + fill: '#fff', + rx: 6, + ry: 6, + }, + }, + }) + + this.edge = graph.addEdge({ + source, + target, + attrs: { + line: { + stroke: '#8f8f8f', + strokeWidth: 1, + }, + }, + vertices: [ + { x: 760, y: 150 }, + { x: 760, y: 240 }, + ], + connector: { + name: 'jumpover', + args: { + type: 'arc', + size: 5, + radius: 0, + }, + }, + }) + + const rect = graph.addNode({ + x: 160, + y: 24, + width: 70, + height: 30, + attrs: { + body: { + stroke: '#8f8f8f', + strokeWidth: 1, + fill: '#fff', + rx: 6, + ry: 6, + }, + }, + }) + + for (let i = 0; i < 6; i += 1) { + const source = rect.clone().translate(i * 100, i * 10) + graph.addNode(source) + + const target = source.clone().translate(0, 200) + graph.addNode(target) + graph.addEdge({ + source, + target, + attrs: { + line: { + stroke: '#8f8f8f', + strokeWidth: 1, + }, + }, + }) + } + } + + onAttrsChanged = (args: State) => { + this.edge.setConnector('jumpover', args) + } + + refContainer = (container: HTMLDivElement) => { + this.container = container + } + + render() { + return ( +
+
+ +
+
+
+ ) + } +} diff --git a/sites/x6-sites/src/api/connector/jumpover/settings.tsx b/sites/x6-sites/src/api/connector/jumpover/settings.tsx new file mode 100644 index 00000000000..f02bf9db391 --- /dev/null +++ b/sites/x6-sites/src/api/connector/jumpover/settings.tsx @@ -0,0 +1,89 @@ +import React from 'react' +import { Slider, Radio, Card, Row, Col } from 'antd' + +export interface Props { + onChange: (state: State) => void +} + +export interface State { + type: string + size: number + radius: number +} + +export class Settings extends React.Component { + state: State = { + type: 'arc', + size: 5, + radius: 0, + } + + notifyChange() { + this.props.onChange(this.state) + } + + onSizeChanged = (size: number) => { + this.setState({ size }, () => { + this.notifyChange() + }) + } + + onRadiusChanged = (radius: number) => { + this.setState({ radius }, () => { + this.notifyChange() + }) + } + + onTypeChange = (e: any) => { + this.setState({ type: e.target.value }, () => { + this.notifyChange() + }) + } + + render() { + return ( + + + type + + + arc + cubic + gap + + + + + size + + + + +
{this.state.size}
+ +
+ + radius + + + + +
{this.state.radius}
+ +
+
+ ) + } +} diff --git a/sites/x6-sites/src/api/connector/normal/index.less b/sites/x6-sites/src/api/connector/normal/index.less new file mode 100644 index 00000000000..c734a06866b --- /dev/null +++ b/sites/x6-sites/src/api/connector/normal/index.less @@ -0,0 +1,14 @@ +.connector-normal-app { + display: flex; + width: 100%; + padding: 0; + font-family: sans-serif; + + .app-content { + flex: 1; + height: 320px; + margin-right: 8px; + margin-left: 8px; + box-shadow: 0 0 10px 1px #e9e9e9; + } +} diff --git a/sites/x6-sites/src/api/connector/normal/index.tsx b/sites/x6-sites/src/api/connector/normal/index.tsx new file mode 100644 index 00000000000..1f9b924e6d9 --- /dev/null +++ b/sites/x6-sites/src/api/connector/normal/index.tsx @@ -0,0 +1,82 @@ +import React from 'react' +import { Graph } from '@antv/x6' +import './index.less' + +export default class Example extends React.Component { + private container: HTMLDivElement + + componentDidMount() { + const graph = new Graph({ + container: this.container, + background: { + color: '#F2F7FA', + }, + }) + + const rect1 = graph.addNode({ + x: 30, + y: 30, + width: 100, + height: 40, + label: 'hello', + attrs: { + body: { + stroke: '#8f8f8f', + strokeWidth: 1, + fill: '#fff', + rx: 6, + ry: 6, + }, + }, + }) + + const rect2 = graph.addNode({ + x: 300, + y: 240, + width: 100, + height: 40, + label: 'world', + attrs: { + body: { + stroke: '#8f8f8f', + strokeWidth: 1, + fill: '#fff', + rx: 6, + ry: 6, + }, + }, + }) + + graph.addEdge({ + source: rect1, + target: rect2, + vertices: [ + { x: 100, y: 200 }, + { x: 300, y: 120 }, + ], + attrs: { + line: { + stroke: '#8f8f8f', + strokeWidth: 1, + }, + }, + connector: { + name: 'normal', + }, + }) + + graph.centerContent() + } + + refContainer = (container: HTMLDivElement) => { + this.container = container + } + + render() { + return ( +
+
+
+ ) + } +} diff --git a/sites/x6-sites/src/api/connector/rounded/index.less b/sites/x6-sites/src/api/connector/rounded/index.less new file mode 100644 index 00000000000..2370241f8a2 --- /dev/null +++ b/sites/x6-sites/src/api/connector/rounded/index.less @@ -0,0 +1,43 @@ +.connector-rounded-app { + display: flex; + padding: 0; + padding: 16px 8px; + font-family: sans-serif; + + .app-left { + bottom: 0; + width: 320px; + padding: 0 8px; + } + + .app-content { + flex: 1; + margin-right: 8px; + margin-left: 8px; + box-shadow: 0 0 10px 1px #e9e9e9; + } + + .ant-card { + box-shadow: 0 0 10px 1px #e9e9e9; + } + + .ant-card-head-title { + text-align: center; + } + + .ant-row { + margin: 16px 0; + text-align: left; + } + + .slider-value { + display: inline-block; + margin-left: 8px; + padding: 3px 7px; + color: #333; + font-size: 12px; + line-height: 1.25; + background: #eee; + border-radius: 10px; + } +} diff --git a/sites/x6-sites/src/api/connector/rounded/index.tsx b/sites/x6-sites/src/api/connector/rounded/index.tsx new file mode 100644 index 00000000000..dd7cbfc00ec --- /dev/null +++ b/sites/x6-sites/src/api/connector/rounded/index.tsx @@ -0,0 +1,91 @@ +import React from 'react' +import { Graph, Edge } from '@antv/x6' +import { Settings, State } from './settings' +import './index.less' + +export default class Example extends React.Component { + private container: HTMLDivElement + private edge: Edge + + componentDidMount() { + const graph = new Graph({ + container: this.container, + background: { + color: '#F2F7FA', + }, + }) + + const rect1 = graph.addNode({ + x: 30, + y: 30, + width: 100, + height: 40, + label: 'hello', + attrs: { + body: { + stroke: '#8f8f8f', + strokeWidth: 1, + fill: '#fff', + rx: 6, + ry: 6, + }, + }, + }) + + const rect2 = graph.addNode({ + x: 300, + y: 220, + width: 100, + height: 40, + label: 'world', + attrs: { + body: { + stroke: '#8f8f8f', + strokeWidth: 1, + fill: '#fff', + rx: 6, + ry: 6, + }, + }, + }) + + this.edge = graph.addEdge({ + source: rect1, + target: rect2, + attrs: { + line: { + stroke: '#8f8f8f', + strokeWidth: 1, + }, + }, + vertices: [ + { x: 100, y: 200 }, + { x: 300, y: 120 }, + ], + connector: { + name: 'rounded', + }, + }) + + graph.centerContent() + } + + onAttrsChanged = (args: State) => { + this.edge.setConnector('rounded', args) + } + + refContainer = (container: HTMLDivElement) => { + this.container = container + } + + render() { + return ( +
+
+ +
+
+
+ ) + } +} diff --git a/sites/x6-sites/src/api/connector/rounded/settings.tsx b/sites/x6-sites/src/api/connector/rounded/settings.tsx new file mode 100644 index 00000000000..87daa7f35cf --- /dev/null +++ b/sites/x6-sites/src/api/connector/rounded/settings.tsx @@ -0,0 +1,48 @@ +import React from 'react' +import { Slider, Card, Row, Col } from 'antd' + +export interface Props { + onChange: (state: State) => void +} + +export interface State { + radius: number +} + +export class Settings extends React.Component { + state: State = { + radius: 10, + } + + notifyChange() { + this.props.onChange(this.state) + } + + onRadiusChanged = (radius: number) => { + this.setState({ radius }, () => { + this.notifyChange() + }) + } + + render() { + return ( + + + radius + + + + +
{this.state.radius}
+ +
+
+ ) + } +} diff --git a/sites/x6-sites/src/api/connector/smooth/index.less b/sites/x6-sites/src/api/connector/smooth/index.less new file mode 100644 index 00000000000..23dfd92949c --- /dev/null +++ b/sites/x6-sites/src/api/connector/smooth/index.less @@ -0,0 +1,14 @@ +.connector-smooth-app { + display: flex; + width: 100%; + padding: 0; + font-family: sans-serif; + + .app-content { + flex: 1; + height: 320px; + margin-right: 8px; + margin-left: 8px; + box-shadow: 0 0 10px 1px #e9e9e9; + } +} diff --git a/sites/x6-sites/src/api/connector/smooth/index.tsx b/sites/x6-sites/src/api/connector/smooth/index.tsx new file mode 100644 index 00000000000..6b3432ee0f5 --- /dev/null +++ b/sites/x6-sites/src/api/connector/smooth/index.tsx @@ -0,0 +1,82 @@ +import React from 'react' +import { Graph } from '@antv/x6' +import './index.less' + +export default class Example extends React.Component { + private container: HTMLDivElement + + componentDidMount() { + const graph = new Graph({ + container: this.container, + background: { + color: '#F2F7FA', + }, + }) + + const rect1 = graph.addNode({ + x: 30, + y: 30, + width: 100, + height: 40, + label: 'hello', + attrs: { + body: { + stroke: '#8f8f8f', + strokeWidth: 1, + fill: '#fff', + rx: 6, + ry: 6, + }, + }, + }) + + const rect2 = graph.addNode({ + x: 300, + y: 240, + width: 100, + height: 40, + label: 'world', + attrs: { + body: { + stroke: '#8f8f8f', + strokeWidth: 1, + fill: '#fff', + rx: 6, + ry: 6, + }, + }, + }) + + graph.addEdge({ + source: rect1, + target: rect2, + vertices: [ + { x: 100, y: 200 }, + { x: 300, y: 120 }, + ], + attrs: { + line: { + stroke: '#8f8f8f', + strokeWidth: 1, + }, + }, + connector: { + name: 'smooth', + }, + }) + + graph.centerContent() + } + + refContainer = (container: HTMLDivElement) => { + this.container = container + } + + render() { + return ( +
+
+
+ ) + } +} diff --git a/sites/x6-sites/src/api/connector/wobble/index.less b/sites/x6-sites/src/api/connector/wobble/index.less new file mode 100644 index 00000000000..63faab0c954 --- /dev/null +++ b/sites/x6-sites/src/api/connector/wobble/index.less @@ -0,0 +1,14 @@ +.connector-wobble-app { + display: flex; + width: 100%; + padding: 0; + font-family: sans-serif; + + .app-content { + flex: 1; + height: 320px; + margin-right: 8px; + margin-left: 8px; + box-shadow: 0 0 10px 1px #e9e9e9; + } +} diff --git a/sites/x6-sites/src/api/connector/wobble/index.tsx b/sites/x6-sites/src/api/connector/wobble/index.tsx new file mode 100644 index 00000000000..3948f302a39 --- /dev/null +++ b/sites/x6-sites/src/api/connector/wobble/index.tsx @@ -0,0 +1,121 @@ +import React from 'react' +import { Graph, Path, Point } from '@antv/x6' +import './index.less' + +export interface WobbleArgs { + spread?: number + raw?: boolean +} + +Graph.registerConnector( + 'wobble', + (sourcePoint, targetPoint, vertices, args: WobbleArgs) => { + const spread = args.spread || 20 + const points = [...vertices, targetPoint].map((p) => Point.create(p)) + let prev = Point.create(sourcePoint) + const path = new Path() + path.appendSegment(Path.createSegment('M', prev)) + + for (let i = 0, n = points.length; i < n; i += 1) { + const next = points[i] + const distance = prev.distance(next) + let d = spread + + while (d < distance) { + const current = prev.clone().move(next, -d) + current.translate( + Math.floor(7 * Math.random()) - 3, + Math.floor(7 * Math.random()) - 3, + ) + path.appendSegment(Path.createSegment('L', current)) + d += spread + } + + path.appendSegment(Path.createSegment('L', next)) + prev = next + } + + return path + }, + true, +) + +export default class Example extends React.Component { + private container: HTMLDivElement + + componentDidMount() { + const graph = new Graph({ + container: this.container, + background: { + color: '#F2F7FA', + }, + }) + + const rect1 = graph.addNode({ + x: 30, + y: 30, + width: 100, + height: 40, + label: 'hello', + attrs: { + body: { + stroke: '#8f8f8f', + strokeWidth: 1, + fill: '#fff', + rx: 6, + ry: 6, + }, + }, + }) + + const rect2 = graph.addNode({ + x: 300, + y: 240, + width: 100, + height: 40, + label: 'world', + attrs: { + body: { + stroke: '#8f8f8f', + strokeWidth: 1, + fill: '#fff', + rx: 6, + ry: 6, + }, + }, + }) + + graph.addEdge({ + source: rect1, + target: rect2, + attrs: { + line: { + stroke: '#8f8f8f', + strokeWidth: 1, + }, + }, + vertices: [ + { x: 100, y: 200 }, + { x: 300, y: 120 }, + ], + connector: { + name: 'wobble', + args: { + spread: 16, + }, + }, + }) + } + + refContainer = (container: HTMLDivElement) => { + this.container = container + } + + render() { + return ( +
+
+
+ ) + } +} diff --git a/sites/x6-sites/src/api/edge-anchor/playground/index.less b/sites/x6-sites/src/api/edge-anchor/playground/index.less new file mode 100644 index 00000000000..f096226f9eb --- /dev/null +++ b/sites/x6-sites/src/api/edge-anchor/playground/index.less @@ -0,0 +1,43 @@ +.edge-anchor-app { + display: flex; + padding: 0; + padding: 16px 8px; + font-family: sans-serif; + + .app-left { + bottom: 0; + width: 336px; + padding: 0 8px; + } + + .app-content { + flex: 1; + margin-right: 8px; + margin-left: 8px; + box-shadow: 0 0 10px 1px #e9e9e9; + } + + .ant-card { + box-shadow: 0 0 10px 1px #e9e9e9; + } + + .ant-card-head-title { + text-align: center; + } + + .ant-row { + margin: 16px 0; + text-align: left; + } + + .slider-value { + display: inline-block; + margin-left: 8px; + padding: 3px 7px; + color: #333; + font-size: 12px; + line-height: 1.25; + background: #eee; + border-radius: 10px; + } +} diff --git a/sites/x6-sites/src/api/edge-anchor/playground/index.tsx b/sites/x6-sites/src/api/edge-anchor/playground/index.tsx new file mode 100644 index 00000000000..3c8639a35b1 --- /dev/null +++ b/sites/x6-sites/src/api/edge-anchor/playground/index.tsx @@ -0,0 +1,98 @@ +import React from 'react' +import { Graph, Edge } from '@antv/x6' +import { Settings, State } from './settings' +import './index.less' + +export default class Example extends React.Component { + private container: HTMLDivElement + private edge: Edge + + componentDidMount() { + const graph = new Graph({ + container: this.container, + background: { + color: '#F2F7FA', + }, + interacting: false, + }) + + const rect = graph.addEdge({ + source: { x: 40, y: 80 }, + target: { x: 360, y: 80 }, + vertices: [ + { x: 120, y: 120 }, + { x: 200, y: 40 }, + { x: 280, y: 120 }, + ], + attrs: { + line: { + stroke: '#8f8f8f', + strokeWidth: 1, + }, + }, + }) + + const edge = graph.addEdge({ + source: { x: 100, y: 100 }, + target: { cell: rect.id }, + attrs: { + line: { + stroke: '#8f8f8f', + strokeWidth: 1, + targetMarker: 'classic', + }, + }, + }) + + function animate() { + edge.transition('source', 9.36 / 60, { + duration: 5000, + interp: (start, startTime) => { + const corr = startTime * (2 * Math.PI) - Math.PI / 2 + const origin = { x: 200, y: 100 } + const radius = 140 + return function (t) { + return { + x: origin.x + radius * Math.cos(t * 2 * Math.PI + corr), + y: origin.y + radius * Math.sin(t * 2 * Math.PI + corr), + } + } + }, + }) + } + + animate() + + edge.on('transition:complete', animate) + + this.edge = edge + } + + onAttrsChanged = ({ type, value }: State) => { + const anchor = + type === 'ratio' || type === 'length' + ? { + name: type, + args: { + [type]: value, + }, + } + : { name: type } + this.edge.prop('target/anchor', anchor) + } + + refContainer = (container: HTMLDivElement) => { + this.container = container + } + + render() { + return ( +
+
+ +
+
+
+ ) + } +} diff --git a/sites/x6-sites/src/api/edge-anchor/playground/settings.tsx b/sites/x6-sites/src/api/edge-anchor/playground/settings.tsx new file mode 100644 index 00000000000..7c9a4c6b61b --- /dev/null +++ b/sites/x6-sites/src/api/edge-anchor/playground/settings.tsx @@ -0,0 +1,73 @@ +import React from 'react' +import { Radio, Slider, Card, Row, Col } from 'antd' + +export interface Props { + onChange: (state: State) => void +} + +export interface State { + type: string + value: number +} + +export class Settings extends React.Component { + state: State = { + type: 'ratio', + value: 50, + } + + notifyChange() { + this.props.onChange(this.state) + } + + onAngleChanged = (value: number) => { + this.setState({ value }, () => { + this.notifyChange() + }) + } + + onChange = (e: any) => { + this.setState({ type: e.target.value }, () => { + this.notifyChange() + }) + } + + render() { + return ( + + + + + ratio + length + orth + closest + + + + {(this.state.type === 'ratio' || this.state.type === 'length') && ( + + + {this.state.type === 'ratio' ? 'ratio' : 'length'} + + + + + +
+ {this.state.value} + {this.state.type === 'ratio' ? '%' : ''} +
+ +
+ )} +
+ ) + } +} diff --git a/sites/x6-sites/src/api/filter/blur/index.less b/sites/x6-sites/src/api/filter/blur/index.less new file mode 100644 index 00000000000..ec6b472c032 --- /dev/null +++ b/sites/x6-sites/src/api/filter/blur/index.less @@ -0,0 +1,43 @@ +.filter-blur-app { + display: flex; + padding: 0; + padding: 16px 8px; + font-family: sans-serif; + + .app-left { + bottom: 0; + width: 336px; + padding: 0 8px; + } + + .app-content { + flex: 1; + margin-right: 8px; + margin-left: 8px; + box-shadow: 0 0 10px 1px #e9e9e9; + } + + .ant-card { + box-shadow: 0 0 10px 1px #e9e9e9; + } + + .ant-card-head-title { + text-align: center; + } + + .ant-row { + margin: 16px 0; + text-align: left; + } + + .slider-value { + display: inline-block; + margin-left: 8px; + padding: 3px 7px; + color: #333; + font-size: 12px; + line-height: 1.25; + background: #eee; + border-radius: 10px; + } +} diff --git a/sites/x6-sites/src/api/filter/blur/index.tsx b/sites/x6-sites/src/api/filter/blur/index.tsx new file mode 100644 index 00000000000..d90df2e9925 --- /dev/null +++ b/sites/x6-sites/src/api/filter/blur/index.tsx @@ -0,0 +1,73 @@ +import React from 'react' +import { Graph, Node, Color } from '@antv/x6' +import { Settings, State, defaults } from './settings' +import './index.less' + +export default class Example extends React.Component { + private container: HTMLDivElement + private graph: Graph + private node: Node + + componentDidMount() { + this.graph = new Graph({ + container: this.container, + background: { + color: '#F2F7FA', + }, + }) + + this.node = this.graph.addNode({ + x: 80, + y: 80, + width: 240, + height: 80, + attrs: { + body: { + stroke: '#8f8f8f', + strokeWidth: 1, + fill: '#fff', + rx: 6, + ry: 6, + }, + }, + }) + + this.onChanged(defaults) + } + + getText(args: State) { + return `blur(${args.x}${args.y > 0 ? `,${args.y}` : ''})` + } + + onChanged = (args: State) => { + this.node.attr({ + label: { + text: this.getText(args), + fill: Color.invert(args.color, true), + }, + body: { + stroke: args.color, + fill: Color.lighten(args.color, 40), + filter: { + name: 'blur', + args: { x: args.x, y: args.y }, + }, + }, + }) + } + + refContainer = (container: HTMLDivElement) => { + this.container = container + } + + render() { + return ( +
+
+ +
+
+
+ ) + } +} diff --git a/sites/x6-sites/src/api/filter/blur/settings.tsx b/sites/x6-sites/src/api/filter/blur/settings.tsx new file mode 100644 index 00000000000..9d7db7bd8b4 --- /dev/null +++ b/sites/x6-sites/src/api/filter/blur/settings.tsx @@ -0,0 +1,99 @@ +import React from 'react' +import { Input, Slider, Card, Row, Col } from 'antd' + +export interface Props { + onChange: (state: State) => void +} + +export interface State { + color: string + x: number + y: number +} + +export const defaults: State = { + color: '#4943a3', + x: 10, + y: 10, +} + +export class Settings extends React.Component { + state: State = defaults + + notifyChange() { + this.props.onChange({ + ...this.state, + }) + } + + onColorChanged = (e: any) => { + this.setState({ color: e.target.value }, () => { + this.notifyChange() + }) + } + + onXChanged = (dx: number) => { + this.setState({ x: dx }, () => { + this.notifyChange() + }) + } + + onYChanged = (dy: number) => { + this.setState({ y: dy }, () => { + this.notifyChange() + }) + } + + render() { + return ( + + + fill + + + + + + x + + + + +
{this.state.x.toFixed(2)}
+ +
+ + y + + + + +
{this.state.y.toFixed(2)}
+ +
+
+ ) + } +} diff --git a/sites/x6-sites/src/api/filter/brightness/index.less b/sites/x6-sites/src/api/filter/brightness/index.less new file mode 100644 index 00000000000..15a4c1884a8 --- /dev/null +++ b/sites/x6-sites/src/api/filter/brightness/index.less @@ -0,0 +1,43 @@ +.filter-brightness-app { + display: flex; + padding: 0; + padding: 16px 8px; + font-family: sans-serif; + + .app-left { + bottom: 0; + width: 336px; + padding: 0 8px; + } + + .app-content { + flex: 1; + margin-right: 8px; + margin-left: 8px; + box-shadow: 0 0 10px 1px #e9e9e9; + } + + .ant-card { + box-shadow: 0 0 10px 1px #e9e9e9; + } + + .ant-card-head-title { + text-align: center; + } + + .ant-row { + margin: 16px 0; + text-align: left; + } + + .slider-value { + display: inline-block; + margin-left: 8px; + padding: 3px 7px; + color: #333; + font-size: 12px; + line-height: 1.25; + background: #eee; + border-radius: 10px; + } +} diff --git a/sites/x6-sites/src/api/filter/brightness/index.tsx b/sites/x6-sites/src/api/filter/brightness/index.tsx new file mode 100644 index 00000000000..85574d019b4 --- /dev/null +++ b/sites/x6-sites/src/api/filter/brightness/index.tsx @@ -0,0 +1,73 @@ +import React from 'react' +import { Graph, Node, Color } from '@antv/x6' +import { Settings, State, defaults } from './settings' +import './index.less' + +export default class Example extends React.Component { + private container: HTMLDivElement + private graph: Graph + private node: Node + + componentDidMount() { + this.graph = new Graph({ + container: this.container, + background: { + color: '#F2F7FA', + }, + }) + + this.node = this.graph.addNode({ + x: 40, + y: 40, + width: 240, + height: 80, + attrs: { + body: { + stroke: '#8f8f8f', + strokeWidth: 1, + fill: '#fff', + rx: 6, + ry: 6, + }, + }, + }) + + this.onChanged(defaults) + } + + getText(args: State) { + return `brightness(${args.amount})` + } + + onChanged = (args: State) => { + this.node.attr({ + label: { + text: this.getText(args), + fill: Color.invert(args.color, true), + }, + body: { + stroke: args.color, + fill: Color.lighten(args.color, 40), + filter: { + name: 'brightness', + args: { amount: args.amount }, + }, + }, + }) + } + + refContainer = (container: HTMLDivElement) => { + this.container = container + } + + render() { + return ( +
+
+ +
+
+
+ ) + } +} diff --git a/sites/x6-sites/src/api/filter/brightness/settings.tsx b/sites/x6-sites/src/api/filter/brightness/settings.tsx new file mode 100644 index 00000000000..3726a5c70f0 --- /dev/null +++ b/sites/x6-sites/src/api/filter/brightness/settings.tsx @@ -0,0 +1,76 @@ +import React from 'react' +import { Input, Slider, Card, Row, Col } from 'antd' + +export interface Props { + onChange: (state: State) => void +} + +export interface State { + color: string + amount: number +} + +export const defaults: State = { + color: '#4943a3', + amount: 0.3, +} + +export class Settings extends React.Component { + state: State = defaults + + notifyChange() { + this.props.onChange({ + ...this.state, + }) + } + + onColorChanged = (e: any) => { + this.setState({ color: e.target.value }, () => { + this.notifyChange() + }) + } + + onAmountChanged = (dx: number) => { + this.setState({ amount: dx }, () => { + this.notifyChange() + }) + } + + render() { + return ( + + + fill + + + + + + amount + + + + +
{this.state.amount.toFixed(2)}
+ +
+
+ ) + } +} diff --git a/sites/x6-sites/src/api/filter/contrast/index.less b/sites/x6-sites/src/api/filter/contrast/index.less new file mode 100644 index 00000000000..6c6222b0ae2 --- /dev/null +++ b/sites/x6-sites/src/api/filter/contrast/index.less @@ -0,0 +1,43 @@ +.filter-contrast-app { + display: flex; + padding: 0; + padding: 16px 8px; + font-family: sans-serif; + + .app-left { + bottom: 0; + width: 336px; + padding: 0 8px; + } + + .app-content { + flex: 1; + margin-right: 8px; + margin-left: 8px; + box-shadow: 0 0 10px 1px #e9e9e9; + } + + .ant-card { + box-shadow: 0 0 10px 1px #e9e9e9; + } + + .ant-card-head-title { + text-align: center; + } + + .ant-row { + margin: 16px 0; + text-align: left; + } + + .slider-value { + display: inline-block; + margin-left: 8px; + padding: 3px 7px; + color: #333; + font-size: 12px; + line-height: 1.25; + background: #eee; + border-radius: 10px; + } +} diff --git a/sites/x6-sites/src/api/filter/contrast/index.tsx b/sites/x6-sites/src/api/filter/contrast/index.tsx new file mode 100644 index 00000000000..f804cc3567a --- /dev/null +++ b/sites/x6-sites/src/api/filter/contrast/index.tsx @@ -0,0 +1,73 @@ +import React from 'react' +import { Graph, Node, Color } from '@antv/x6' +import { Settings, State, defaults } from './settings' +import './index.less' + +export default class Example extends React.Component { + private container: HTMLDivElement + private graph: Graph + private node: Node + + componentDidMount() { + this.graph = new Graph({ + container: this.container, + background: { + color: '#F2F7FA', + }, + }) + + this.node = this.graph.addNode({ + x: 40, + y: 40, + width: 240, + height: 80, + attrs: { + body: { + stroke: '#8f8f8f', + strokeWidth: 1, + fill: '#fff', + rx: 6, + ry: 6, + }, + }, + }) + + this.onChanged(defaults) + } + + getText(args: State) { + return `contrast(${args.amount})` + } + + onChanged = (args: State) => { + this.node.attr({ + label: { + text: this.getText(args), + fill: Color.invert(args.color, true), + }, + body: { + stroke: args.color, + fill: Color.lighten(args.color, 40), + filter: { + name: 'contrast', + args: { amount: args.amount }, + }, + }, + }) + } + + refContainer = (container: HTMLDivElement) => { + this.container = container + } + + render() { + return ( +
+
+ +
+
+
+ ) + } +} diff --git a/sites/x6-sites/src/api/filter/contrast/settings.tsx b/sites/x6-sites/src/api/filter/contrast/settings.tsx new file mode 100644 index 00000000000..3726a5c70f0 --- /dev/null +++ b/sites/x6-sites/src/api/filter/contrast/settings.tsx @@ -0,0 +1,76 @@ +import React from 'react' +import { Input, Slider, Card, Row, Col } from 'antd' + +export interface Props { + onChange: (state: State) => void +} + +export interface State { + color: string + amount: number +} + +export const defaults: State = { + color: '#4943a3', + amount: 0.3, +} + +export class Settings extends React.Component { + state: State = defaults + + notifyChange() { + this.props.onChange({ + ...this.state, + }) + } + + onColorChanged = (e: any) => { + this.setState({ color: e.target.value }, () => { + this.notifyChange() + }) + } + + onAmountChanged = (dx: number) => { + this.setState({ amount: dx }, () => { + this.notifyChange() + }) + } + + render() { + return ( + + + fill + + + + + + amount + + + + +
{this.state.amount.toFixed(2)}
+ +
+
+ ) + } +} diff --git a/sites/x6-sites/src/api/filter/drop-shadow/index.less b/sites/x6-sites/src/api/filter/drop-shadow/index.less new file mode 100644 index 00000000000..11b7c717927 --- /dev/null +++ b/sites/x6-sites/src/api/filter/drop-shadow/index.less @@ -0,0 +1,43 @@ +.filter-drop-shadow-app { + display: flex; + padding: 0; + padding: 16px 8px; + font-family: sans-serif; + + .app-left { + bottom: 0; + width: 336px; + padding: 0 8px; + } + + .app-content { + flex: 1; + margin-right: 8px; + margin-left: 8px; + box-shadow: 0 0 10px 1px #e9e9e9; + } + + .ant-card { + box-shadow: 0 0 10px 1px #e9e9e9; + } + + .ant-card-head-title { + text-align: center; + } + + .ant-row { + margin: 16px 0; + text-align: left; + } + + .slider-value { + display: inline-block; + margin-left: 8px; + padding: 3px 7px; + color: #333; + font-size: 12px; + line-height: 1.25; + background: #eee; + border-radius: 10px; + } +} diff --git a/sites/x6-sites/src/api/filter/drop-shadow/index.tsx b/sites/x6-sites/src/api/filter/drop-shadow/index.tsx new file mode 100644 index 00000000000..ce6aeb6c005 --- /dev/null +++ b/sites/x6-sites/src/api/filter/drop-shadow/index.tsx @@ -0,0 +1,73 @@ +import React from 'react' +import { Graph, Node, Color } from '@antv/x6' +import { Settings, State, defaults } from './settings' +import './index.less' + +export default class Example extends React.Component { + private container: HTMLDivElement + private graph: Graph + private node: Node + + componentDidMount() { + this.graph = new Graph({ + container: this.container, + background: { + color: '#F2F7FA', + }, + }) + + this.node = this.graph.addNode({ + x: 40, + y: 80, + width: 400, + height: 160, + attrs: { + body: { + stroke: '#8f8f8f', + strokeWidth: 1, + fill: '#fff', + rx: 6, + ry: 6, + }, + }, + }) + + this.onChanged(defaults) + } + + getText(args: State) { + return `dropShadow\n\ncolor: ${args.color}\ndx: ${args.dx} \ndy: ${args.dy} \nblur: ${args.blur} \nopacity: ${args.opacity} \n` + } + + onChanged = (args: State) => { + this.node.attr({ + label: { + text: this.getText(args), + fill: Color.invert(args.color, true), + }, + body: { + stroke: args.color, + fill: Color.lighten(args.color, 40), + filter: { + name: 'dropShadow', + args: { ...args }, + }, + }, + }) + } + + refContainer = (container: HTMLDivElement) => { + this.container = container + } + + render() { + return ( +
+
+ +
+
+
+ ) + } +} diff --git a/sites/x6-sites/src/api/filter/drop-shadow/settings.tsx b/sites/x6-sites/src/api/filter/drop-shadow/settings.tsx new file mode 100644 index 00000000000..666dca7929e --- /dev/null +++ b/sites/x6-sites/src/api/filter/drop-shadow/settings.tsx @@ -0,0 +1,145 @@ +import React from 'react' +import { Input, Slider, Card, Row, Col } from 'antd' + +export interface Props { + onChange: (state: State) => void +} + +export interface State { + color: string + dx: number + dy: number + blur: number + opacity: number +} + +export const defaults: State = { + color: '#4943a3', + dx: 10, + dy: 10, + blur: 10, + opacity: 0.5, +} + +export class Settings extends React.Component { + state: State = defaults + + notifyChange() { + this.props.onChange({ + ...this.state, + }) + } + + onColorChanged = (e: any) => { + this.setState({ color: e.target.value }, () => { + this.notifyChange() + }) + } + + onDxChanged = (dx: number) => { + this.setState({ dx }, () => { + this.notifyChange() + }) + } + + onDyChanged = (dy: number) => { + this.setState({ dy }, () => { + this.notifyChange() + }) + } + + onBlurChanged = (blur: number) => { + this.setState({ blur }, () => { + this.notifyChange() + }) + } + + onOpacityChanged = (opacity: number) => { + this.setState({ opacity }, () => { + this.notifyChange() + }) + } + + render() { + return ( + + + color + + + + + + dx + + + + +
{this.state.dx.toFixed(2)}
+ +
+ + dy + + + + +
{this.state.dy.toFixed(2)}
+ +
+ + blur + + + + +
{this.state.blur.toFixed(2)}
+ +
+ + opacity + + + + +
{this.state.opacity.toFixed(2)}
+ +
+
+ ) + } +} diff --git a/sites/x6-sites/src/api/filter/gray-scale/index.less b/sites/x6-sites/src/api/filter/gray-scale/index.less new file mode 100644 index 00000000000..f02f0a39276 --- /dev/null +++ b/sites/x6-sites/src/api/filter/gray-scale/index.less @@ -0,0 +1,43 @@ +.filter-gray-scale-app { + display: flex; + padding: 0; + padding: 16px 8px; + font-family: sans-serif; + + .app-left { + bottom: 0; + width: 336px; + padding: 0 8px; + } + + .app-content { + flex: 1; + margin-right: 8px; + margin-left: 8px; + box-shadow: 0 0 10px 1px #e9e9e9; + } + + .ant-card { + box-shadow: 0 0 10px 1px #e9e9e9; + } + + .ant-card-head-title { + text-align: center; + } + + .ant-row { + margin: 16px 0; + text-align: left; + } + + .slider-value { + display: inline-block; + margin-left: 8px; + padding: 3px 7px; + color: #333; + font-size: 12px; + line-height: 1.25; + background: #eee; + border-radius: 10px; + } +} diff --git a/sites/x6-sites/src/api/filter/gray-scale/index.tsx b/sites/x6-sites/src/api/filter/gray-scale/index.tsx new file mode 100644 index 00000000000..3f405b0e09a --- /dev/null +++ b/sites/x6-sites/src/api/filter/gray-scale/index.tsx @@ -0,0 +1,73 @@ +import React from 'react' +import { Graph, Node, Color } from '@antv/x6' +import { Settings, State, defaults } from './settings' +import './index.less' + +export default class Example extends React.Component { + private container: HTMLDivElement + private graph: Graph + private node: Node + + componentDidMount() { + this.graph = new Graph({ + container: this.container, + background: { + color: '#F2F7FA', + }, + }) + + this.node = this.graph.addNode({ + x: 40, + y: 40, + width: 240, + height: 80, + attrs: { + body: { + stroke: '#8f8f8f', + strokeWidth: 1, + fill: '#fff', + rx: 6, + ry: 6, + }, + }, + }) + + this.onChanged(defaults) + } + + getText(args: State) { + return `grayScale(${args.amount})` + } + + onChanged = (args: State) => { + this.node.attr({ + label: { + text: this.getText(args), + fill: Color.invert(args.color, true), + }, + body: { + stroke: args.color, + fill: Color.lighten(args.color, 40), + filter: { + name: 'grayScale', + args: { amount: args.amount }, + }, + }, + }) + } + + refContainer = (container: HTMLDivElement) => { + this.container = container + } + + render() { + return ( +
+
+ +
+
+
+ ) + } +} diff --git a/sites/x6-sites/src/api/filter/gray-scale/settings.tsx b/sites/x6-sites/src/api/filter/gray-scale/settings.tsx new file mode 100644 index 00000000000..3726a5c70f0 --- /dev/null +++ b/sites/x6-sites/src/api/filter/gray-scale/settings.tsx @@ -0,0 +1,76 @@ +import React from 'react' +import { Input, Slider, Card, Row, Col } from 'antd' + +export interface Props { + onChange: (state: State) => void +} + +export interface State { + color: string + amount: number +} + +export const defaults: State = { + color: '#4943a3', + amount: 0.3, +} + +export class Settings extends React.Component { + state: State = defaults + + notifyChange() { + this.props.onChange({ + ...this.state, + }) + } + + onColorChanged = (e: any) => { + this.setState({ color: e.target.value }, () => { + this.notifyChange() + }) + } + + onAmountChanged = (dx: number) => { + this.setState({ amount: dx }, () => { + this.notifyChange() + }) + } + + render() { + return ( + + + fill + + + + + + amount + + + + +
{this.state.amount.toFixed(2)}
+ +
+
+ ) + } +} diff --git a/sites/x6-sites/src/api/filter/highlight/index.less b/sites/x6-sites/src/api/filter/highlight/index.less new file mode 100644 index 00000000000..fd2b067891c --- /dev/null +++ b/sites/x6-sites/src/api/filter/highlight/index.less @@ -0,0 +1,43 @@ +.filter-highlight-app { + display: flex; + padding: 0; + padding: 16px 8px; + font-family: sans-serif; + + .app-left { + bottom: 0; + width: 336px; + padding: 0 8px; + } + + .app-content { + flex: 1; + margin-right: 8px; + margin-left: 8px; + box-shadow: 0 0 10px 1px #e9e9e9; + } + + .ant-card { + box-shadow: 0 0 10px 1px #e9e9e9; + } + + .ant-card-head-title { + text-align: center; + } + + .ant-row { + margin: 16px 0; + text-align: left; + } + + .slider-value { + display: inline-block; + margin-left: 8px; + padding: 3px 7px; + color: #333; + font-size: 12px; + line-height: 1.25; + background: #eee; + border-radius: 10px; + } +} diff --git a/sites/x6-sites/src/api/filter/highlight/index.tsx b/sites/x6-sites/src/api/filter/highlight/index.tsx new file mode 100644 index 00000000000..f142f2765c9 --- /dev/null +++ b/sites/x6-sites/src/api/filter/highlight/index.tsx @@ -0,0 +1,73 @@ +import React from 'react' +import { Graph, Node, Color } from '@antv/x6' +import { Settings, State, defaults } from './settings' +import './index.less' + +export default class Example extends React.Component { + private container: HTMLDivElement + private graph: Graph + private node: Node + + componentDidMount() { + this.graph = new Graph({ + container: this.container, + background: { + color: '#F2F7FA', + }, + }) + + this.node = this.graph.addNode({ + x: 40, + y: 55, + width: 400, + height: 160, + attrs: { + body: { + stroke: '#8f8f8f', + strokeWidth: 1, + fill: '#fff', + rx: 6, + ry: 6, + }, + }, + }) + + this.onChanged(defaults) + } + + getText(args: State) { + return `highlight\n\ncolor: ${args.color}\ndx: ${args.width} \ndy: ${args.blur} \nopacity: ${args.opacity} \n` + } + + onChanged = (args: State) => { + this.node.attr({ + label: { + text: this.getText(args), + fill: Color.invert(args.color, true), + }, + body: { + stroke: args.color, + fill: Color.lighten(args.color, 40), + filter: { + name: 'highlight', + args: { ...args }, + }, + }, + }) + } + + refContainer = (container: HTMLDivElement) => { + this.container = container + } + + render() { + return ( +
+
+ +
+
+
+ ) + } +} diff --git a/sites/x6-sites/src/api/filter/highlight/settings.tsx b/sites/x6-sites/src/api/filter/highlight/settings.tsx new file mode 100644 index 00000000000..8139ecaa8b6 --- /dev/null +++ b/sites/x6-sites/src/api/filter/highlight/settings.tsx @@ -0,0 +1,123 @@ +import React from 'react' +import { Input, Slider, Card, Row, Col } from 'antd' + +export interface Props { + onChange: (state: State) => void +} + +export interface State { + color: string + width: number + blur: number + opacity: number +} + +export const defaults: State = { + color: '#4943a3', + width: 10, + blur: 1, + opacity: 0.5, +} + +export class Settings extends React.Component { + state: State = defaults + + notifyChange() { + this.props.onChange({ + ...this.state, + }) + } + + onColorChanged = (e: any) => { + this.setState({ color: e.target.value }, () => { + this.notifyChange() + }) + } + + onWidthChanged = (width: number) => { + this.setState({ width }, () => { + this.notifyChange() + }) + } + + onBlurChanged = (blur: number) => { + this.setState({ blur }, () => { + this.notifyChange() + }) + } + + onOpacityChanged = (opacity: number) => { + this.setState({ opacity }, () => { + this.notifyChange() + }) + } + + render() { + return ( + + + color + + + + + + width + + + + +
{this.state.width.toFixed(2)}
+ +
+ + blur + + + + +
{this.state.blur.toFixed(2)}
+ +
+ + + opacity + + + + +
{this.state.opacity.toFixed(2)}
+ +
+
+ ) + } +} diff --git a/sites/x6-sites/src/api/filter/hue-rotate/index.less b/sites/x6-sites/src/api/filter/hue-rotate/index.less new file mode 100644 index 00000000000..c18abccef76 --- /dev/null +++ b/sites/x6-sites/src/api/filter/hue-rotate/index.less @@ -0,0 +1,43 @@ +.filter-hue-rotate-app { + display: flex; + padding: 0; + padding: 16px 8px; + font-family: sans-serif; + + .app-left { + bottom: 0; + width: 336px; + padding: 0 8px; + } + + .app-content { + flex: 1; + margin-right: 8px; + margin-left: 8px; + box-shadow: 0 0 10px 1px #e9e9e9; + } + + .ant-card { + box-shadow: 0 0 10px 1px #e9e9e9; + } + + .ant-card-head-title { + text-align: center; + } + + .ant-row { + margin: 16px 0; + text-align: left; + } + + .slider-value { + display: inline-block; + margin-left: 8px; + padding: 3px 7px; + color: #333; + font-size: 12px; + line-height: 1.25; + background: #eee; + border-radius: 10px; + } +} diff --git a/sites/x6-sites/src/api/filter/hue-rotate/index.tsx b/sites/x6-sites/src/api/filter/hue-rotate/index.tsx new file mode 100644 index 00000000000..85fcf5dcdb4 --- /dev/null +++ b/sites/x6-sites/src/api/filter/hue-rotate/index.tsx @@ -0,0 +1,73 @@ +import React from 'react' +import { Graph, Node, Color } from '@antv/x6' +import { Settings, State, defaults } from './settings' +import './index.less' + +export default class Example extends React.Component { + private container: HTMLDivElement + private graph: Graph + private node: Node + + componentDidMount() { + this.graph = new Graph({ + container: this.container, + background: { + color: '#F2F7FA', + }, + }) + + this.node = this.graph.addNode({ + x: 40, + y: 40, + width: 240, + height: 80, + attrs: { + body: { + stroke: '#8f8f8f', + strokeWidth: 1, + fill: '#fff', + rx: 6, + ry: 6, + }, + }, + }) + + this.onChanged(defaults) + } + + getText(args: State) { + return `hueRotate(${args.angle})` + } + + onChanged = (args: State) => { + this.node.attr({ + label: { + text: this.getText(args), + fill: Color.invert(args.color, true), + }, + body: { + stroke: args.color, + fill: Color.lighten(args.color, 40), + filter: { + name: 'hueRotate', + args: { angle: args.angle }, + }, + }, + }) + } + + refContainer = (container: HTMLDivElement) => { + this.container = container + } + + render() { + return ( +
+
+ +
+
+
+ ) + } +} diff --git a/sites/x6-sites/src/api/filter/hue-rotate/settings.tsx b/sites/x6-sites/src/api/filter/hue-rotate/settings.tsx new file mode 100644 index 00000000000..e69ac8e75c1 --- /dev/null +++ b/sites/x6-sites/src/api/filter/hue-rotate/settings.tsx @@ -0,0 +1,76 @@ +import React from 'react' +import { Input, Slider, Card, Row, Col } from 'antd' + +export interface Props { + onChange: (state: State) => void +} + +export interface State { + color: string + angle: number +} + +export const defaults: State = { + color: '#4943a3', + angle: 0, +} + +export class Settings extends React.Component { + state: State = defaults + + notifyChange() { + this.props.onChange({ + ...this.state, + }) + } + + onColorChanged = (e: any) => { + this.setState({ color: e.target.value }, () => { + this.notifyChange() + }) + } + + onAngleChanged = (dx: number) => { + this.setState({ angle: dx }, () => { + this.notifyChange() + }) + } + + render() { + return ( + + + fill + + + + + + angle + + + + +
{this.state.angle.toFixed(2)}
+ +
+
+ ) + } +} diff --git a/sites/x6-sites/src/api/filter/invert/index.less b/sites/x6-sites/src/api/filter/invert/index.less new file mode 100644 index 00000000000..b4b6220a0a2 --- /dev/null +++ b/sites/x6-sites/src/api/filter/invert/index.less @@ -0,0 +1,43 @@ +.filter-invert-app { + display: flex; + padding: 0; + padding: 16px 8px; + font-family: sans-serif; + + .app-left { + bottom: 0; + width: 336px; + padding: 0 8px; + } + + .app-content { + flex: 1; + margin-right: 8px; + margin-left: 8px; + box-shadow: 0 0 10px 1px #e9e9e9; + } + + .ant-card { + box-shadow: 0 0 10px 1px #e9e9e9; + } + + .ant-card-head-title { + text-align: center; + } + + .ant-row { + margin: 16px 0; + text-align: left; + } + + .slider-value { + display: inline-block; + margin-left: 8px; + padding: 3px 7px; + color: #333; + font-size: 12px; + line-height: 1.25; + background: #eee; + border-radius: 10px; + } +} diff --git a/sites/x6-sites/src/api/filter/invert/index.tsx b/sites/x6-sites/src/api/filter/invert/index.tsx new file mode 100644 index 00000000000..7ecb91c9449 --- /dev/null +++ b/sites/x6-sites/src/api/filter/invert/index.tsx @@ -0,0 +1,73 @@ +import React from 'react' +import { Graph, Node, Color } from '@antv/x6' +import { Settings, State, defaults } from './settings' +import './index.less' + +export default class Example extends React.Component { + private container: HTMLDivElement + private graph: Graph + private node: Node + + componentDidMount() { + this.graph = new Graph({ + container: this.container, + background: { + color: '#F2F7FA', + }, + }) + + this.node = this.graph.addNode({ + x: 40, + y: 40, + width: 240, + height: 80, + attrs: { + body: { + stroke: '#8f8f8f', + strokeWidth: 1, + fill: '#fff', + rx: 6, + ry: 6, + }, + }, + }) + + this.onChanged(defaults) + } + + getText(args: State) { + return `invert(${args.amount})` + } + + onChanged = (args: State) => { + this.node.attr({ + label: { + text: this.getText(args), + fill: Color.invert(args.color, true), + }, + body: { + stroke: args.color, + fill: Color.lighten(args.color, 40), + filter: { + name: 'invert', + args: { amount: args.amount }, + }, + }, + }) + } + + refContainer = (container: HTMLDivElement) => { + this.container = container + } + + render() { + return ( +
+
+ +
+
+
+ ) + } +} diff --git a/sites/x6-sites/src/api/filter/invert/settings.tsx b/sites/x6-sites/src/api/filter/invert/settings.tsx new file mode 100644 index 00000000000..3726a5c70f0 --- /dev/null +++ b/sites/x6-sites/src/api/filter/invert/settings.tsx @@ -0,0 +1,76 @@ +import React from 'react' +import { Input, Slider, Card, Row, Col } from 'antd' + +export interface Props { + onChange: (state: State) => void +} + +export interface State { + color: string + amount: number +} + +export const defaults: State = { + color: '#4943a3', + amount: 0.3, +} + +export class Settings extends React.Component { + state: State = defaults + + notifyChange() { + this.props.onChange({ + ...this.state, + }) + } + + onColorChanged = (e: any) => { + this.setState({ color: e.target.value }, () => { + this.notifyChange() + }) + } + + onAmountChanged = (dx: number) => { + this.setState({ amount: dx }, () => { + this.notifyChange() + }) + } + + render() { + return ( + + + fill + + + + + + amount + + + + +
{this.state.amount.toFixed(2)}
+ +
+
+ ) + } +} diff --git a/sites/x6-sites/src/api/filter/outline/index.less b/sites/x6-sites/src/api/filter/outline/index.less new file mode 100644 index 00000000000..c99f42b7df7 --- /dev/null +++ b/sites/x6-sites/src/api/filter/outline/index.less @@ -0,0 +1,43 @@ +.filter-outline-app { + display: flex; + padding: 0; + padding: 16px 8px; + font-family: sans-serif; + + .app-left { + bottom: 0; + width: 336px; + padding: 0 8px; + } + + .app-content { + flex: 1; + margin-right: 8px; + margin-left: 8px; + box-shadow: 0 0 10px 1px #e9e9e9; + } + + .ant-card { + box-shadow: 0 0 10px 1px #e9e9e9; + } + + .ant-card-head-title { + text-align: center; + } + + .ant-row { + margin: 16px 0; + text-align: left; + } + + .slider-value { + display: inline-block; + margin-left: 8px; + padding: 3px 7px; + color: #333; + font-size: 12px; + line-height: 1.25; + background: #eee; + border-radius: 10px; + } +} diff --git a/sites/x6-sites/src/api/filter/outline/index.tsx b/sites/x6-sites/src/api/filter/outline/index.tsx new file mode 100644 index 00000000000..ce5b1543ebd --- /dev/null +++ b/sites/x6-sites/src/api/filter/outline/index.tsx @@ -0,0 +1,73 @@ +import React from 'react' +import { Graph, Node, Color } from '@antv/x6' +import { Settings, State, defaults } from './settings' +import './index.less' + +export default class Example extends React.Component { + private container: HTMLDivElement + private graph: Graph + private node: Node + + componentDidMount() { + this.graph = new Graph({ + container: this.container, + background: { + color: '#F2F7FA', + }, + }) + + this.node = this.graph.addNode({ + x: 40, + y: 55, + width: 400, + height: 160, + attrs: { + body: { + stroke: '#8f8f8f', + strokeWidth: 1, + fill: '#fff', + rx: 6, + ry: 6, + }, + }, + }) + + this.onChanged(defaults) + } + + getText(args: State) { + return `outline\n\ncolor: ${args.color}\nwidth: ${args.width} \nmargin: ${args.margin} \nopacity: ${args.opacity} \n` + } + + onChanged = (args: State) => { + this.node.attr({ + label: { + text: this.getText(args), + fill: Color.invert(args.color, true), + }, + body: { + stroke: args.color, + fill: Color.lighten(args.color, 40), + filter: { + name: 'outline', + args: { ...args }, + }, + }, + }) + } + + refContainer = (container: HTMLDivElement) => { + this.container = container + } + + render() { + return ( +
+
+ +
+
+
+ ) + } +} diff --git a/sites/x6-sites/src/api/filter/outline/settings.tsx b/sites/x6-sites/src/api/filter/outline/settings.tsx new file mode 100644 index 00000000000..d3d21eebe25 --- /dev/null +++ b/sites/x6-sites/src/api/filter/outline/settings.tsx @@ -0,0 +1,123 @@ +import React from 'react' +import { Input, Slider, Card, Row, Col } from 'antd' + +export interface Props { + onChange: (state: State) => void +} + +export interface State { + color: string + width: number + margin: number + opacity: number +} + +export const defaults: State = { + color: '#4943a3', + width: 2, + margin: 2, + opacity: 0.5, +} + +export class Settings extends React.Component { + state: State = defaults + + notifyChange() { + this.props.onChange({ + ...this.state, + }) + } + + onColorChanged = (e: any) => { + this.setState({ color: e.target.value }, () => { + this.notifyChange() + }) + } + + onWidthChanged = (width: number) => { + this.setState({ width }, () => { + this.notifyChange() + }) + } + + onMarginChanged = (margin: number) => { + this.setState({ margin }, () => { + this.notifyChange() + }) + } + + onOpacityChanged = (opacity: number) => { + this.setState({ opacity }, () => { + this.notifyChange() + }) + } + + render() { + return ( + + + color + + + + + + width + + + + +
{this.state.width.toFixed(2)}
+ +
+ + margin + + + + +
{this.state.margin.toFixed(2)}
+ +
+ + + opacity + + + + +
{this.state.opacity.toFixed(2)}
+ +
+
+ ) + } +} diff --git a/sites/x6-sites/src/api/filter/saturate/index.less b/sites/x6-sites/src/api/filter/saturate/index.less new file mode 100644 index 00000000000..b3adad1335f --- /dev/null +++ b/sites/x6-sites/src/api/filter/saturate/index.less @@ -0,0 +1,43 @@ +.filter-saturate-app { + display: flex; + padding: 0; + padding: 16px 8px; + font-family: sans-serif; + + .app-left { + bottom: 0; + width: 336px; + padding: 0 8px; + } + + .app-content { + flex: 1; + margin-right: 8px; + margin-left: 8px; + box-shadow: 0 0 10px 1px #e9e9e9; + } + + .ant-card { + box-shadow: 0 0 10px 1px #e9e9e9; + } + + .ant-card-head-title { + text-align: center; + } + + .ant-row { + margin: 16px 0; + text-align: left; + } + + .slider-value { + display: inline-block; + margin-left: 8px; + padding: 3px 7px; + color: #333; + font-size: 12px; + line-height: 1.25; + background: #eee; + border-radius: 10px; + } +} diff --git a/sites/x6-sites/src/api/filter/saturate/index.tsx b/sites/x6-sites/src/api/filter/saturate/index.tsx new file mode 100644 index 00000000000..33772df0018 --- /dev/null +++ b/sites/x6-sites/src/api/filter/saturate/index.tsx @@ -0,0 +1,73 @@ +import React from 'react' +import { Graph, Node, Color } from '@antv/x6' +import { Settings, State, defaults } from './settings' +import './index.less' + +export default class Example extends React.Component { + private container: HTMLDivElement + private graph: Graph + private node: Node + + componentDidMount() { + this.graph = new Graph({ + container: this.container, + background: { + color: '#F2F7FA', + }, + }) + + this.node = this.graph.addNode({ + x: 40, + y: 40, + width: 240, + height: 80, + attrs: { + body: { + stroke: '#8f8f8f', + strokeWidth: 1, + fill: '#fff', + rx: 6, + ry: 6, + }, + }, + }) + + this.onChanged(defaults) + } + + getText(args: State) { + return `saturate(${args.amount})` + } + + onChanged = (args: State) => { + this.node.attr({ + label: { + text: this.getText(args), + fill: Color.invert(args.color, true), + }, + body: { + stroke: args.color, + fill: Color.lighten(args.color, 40), + filter: { + name: 'saturate', + args: { amount: args.amount }, + }, + }, + }) + } + + refContainer = (container: HTMLDivElement) => { + this.container = container + } + + render() { + return ( +
+
+ +
+
+
+ ) + } +} diff --git a/sites/x6-sites/src/api/filter/saturate/settings.tsx b/sites/x6-sites/src/api/filter/saturate/settings.tsx new file mode 100644 index 00000000000..2c5292a3887 --- /dev/null +++ b/sites/x6-sites/src/api/filter/saturate/settings.tsx @@ -0,0 +1,76 @@ +import React from 'react' +import { Input, Slider, Card, Row, Col } from 'antd' + +export interface Props { + onChange: (state: State) => void +} + +export interface State { + color: string + amount: number +} + +export const defaults: State = { + color: '#4943a3', + amount: 0.3, +} + +export class Settings extends React.Component { + state: State = defaults + + notifyChange() { + this.props.onChange({ + ...this.state, + }) + } + + onColorChanged = (e: any) => { + this.setState({ color: e.target.value }, () => { + this.notifyChange() + }) + } + + onAmountChanged = (dx: number) => { + this.setState({ amount: dx }, () => { + this.notifyChange() + }) + } + + render() { + return ( + + + color + + + + + + amount + + + + +
{this.state.amount.toFixed(2)}
+ +
+
+ ) + } +} diff --git a/sites/x6-sites/src/api/filter/sepia/index.less b/sites/x6-sites/src/api/filter/sepia/index.less new file mode 100644 index 00000000000..ea0fa997ff4 --- /dev/null +++ b/sites/x6-sites/src/api/filter/sepia/index.less @@ -0,0 +1,43 @@ +.filter-sepia-app { + display: flex; + padding: 0; + padding: 16px 8px; + font-family: sans-serif; + + .app-left { + bottom: 0; + width: 336px; + padding: 0 8px; + } + + .app-content { + flex: 1; + margin-right: 8px; + margin-left: 8px; + box-shadow: 0 0 10px 1px #e9e9e9; + } + + .ant-card { + box-shadow: 0 0 10px 1px #e9e9e9; + } + + .ant-card-head-title { + text-align: center; + } + + .ant-row { + margin: 16px 0; + text-align: left; + } + + .slider-value { + display: inline-block; + margin-left: 8px; + padding: 3px 7px; + color: #333; + font-size: 12px; + line-height: 1.25; + background: #eee; + border-radius: 10px; + } +} diff --git a/sites/x6-sites/src/api/filter/sepia/index.tsx b/sites/x6-sites/src/api/filter/sepia/index.tsx new file mode 100644 index 00000000000..36b0579b034 --- /dev/null +++ b/sites/x6-sites/src/api/filter/sepia/index.tsx @@ -0,0 +1,73 @@ +import React from 'react' +import { Graph, Node, Color } from '@antv/x6' +import { Settings, State, defaults } from './settings' +import './index.less' + +export default class Example extends React.Component { + private container: HTMLDivElement + private graph: Graph + private node: Node + + componentDidMount() { + this.graph = new Graph({ + container: this.container, + background: { + color: '#F2F7FA', + }, + }) + + this.node = this.graph.addNode({ + x: 40, + y: 40, + width: 240, + height: 80, + attrs: { + body: { + stroke: '#8f8f8f', + strokeWidth: 1, + fill: '#fff', + rx: 6, + ry: 6, + }, + }, + }) + + this.onChanged(defaults) + } + + getText(args: State) { + return `sepia(${args.amount})` + } + + onChanged = (args: State) => { + this.node.attr({ + label: { + text: this.getText(args), + fill: Color.invert(args.color, true), + }, + body: { + stroke: args.color, + fill: Color.lighten(args.color, 40), + filter: { + name: 'sepia', + args: { amount: args.amount }, + }, + }, + }) + } + + refContainer = (container: HTMLDivElement) => { + this.container = container + } + + render() { + return ( +
+
+ +
+
+
+ ) + } +} diff --git a/sites/x6-sites/src/api/filter/sepia/settings.tsx b/sites/x6-sites/src/api/filter/sepia/settings.tsx new file mode 100644 index 00000000000..3726a5c70f0 --- /dev/null +++ b/sites/x6-sites/src/api/filter/sepia/settings.tsx @@ -0,0 +1,76 @@ +import React from 'react' +import { Input, Slider, Card, Row, Col } from 'antd' + +export interface Props { + onChange: (state: State) => void +} + +export interface State { + color: string + amount: number +} + +export const defaults: State = { + color: '#4943a3', + amount: 0.3, +} + +export class Settings extends React.Component { + state: State = defaults + + notifyChange() { + this.props.onChange({ + ...this.state, + }) + } + + onColorChanged = (e: any) => { + this.setState({ color: e.target.value }, () => { + this.notifyChange() + }) + } + + onAmountChanged = (dx: number) => { + this.setState({ amount: dx }, () => { + this.notifyChange() + }) + } + + render() { + return ( + + + fill + + + + + + amount + + + + +
{this.state.amount.toFixed(2)}
+ +
+
+ ) + } +} diff --git a/sites/x6-sites/src/api/node-anchor/playground/index.less b/sites/x6-sites/src/api/node-anchor/playground/index.less new file mode 100644 index 00000000000..57417bb2d22 --- /dev/null +++ b/sites/x6-sites/src/api/node-anchor/playground/index.less @@ -0,0 +1,43 @@ +.node-anchor-app { + display: flex; + padding: 0; + padding: 16px 8px; + font-family: sans-serif; + + .app-left { + bottom: 0; + width: 336px; + padding: 0 8px; + } + + .app-content { + flex: 1; + margin-right: 8px; + margin-left: 8px; + box-shadow: 0 0 10px 1px #e9e9e9; + } + + .ant-card { + box-shadow: 0 0 10px 1px #e9e9e9; + } + + .ant-card-head-title { + text-align: center; + } + + .ant-row { + margin: 16px 0; + text-align: left; + } + + .slider-value { + display: inline-block; + margin-left: 8px; + padding: 3px 7px; + color: #333; + font-size: 12px; + line-height: 1.25; + background: #eee; + border-radius: 10px; + } +} diff --git a/sites/x6-sites/src/api/node-anchor/playground/index.tsx b/sites/x6-sites/src/api/node-anchor/playground/index.tsx new file mode 100644 index 00000000000..d05d5bb71c2 --- /dev/null +++ b/sites/x6-sites/src/api/node-anchor/playground/index.tsx @@ -0,0 +1,147 @@ +import React from 'react' +import { Graph, Edge } from '@antv/x6' +import { Settings, State } from './settings' +import './index.less' + +export default class Example extends React.Component { + private container: HTMLDivElement + private edge1: Edge + private edge2: Edge + + componentDidMount() { + const graph = new Graph({ + container: this.container, + background: { + color: '#F2F7FA', + }, + interacting: false, + }) + + const rect1 = graph.addNode({ + x: 160, + y: 80, + width: 100, + height: 40, + attrs: { + body: { + stroke: '#8f8f8f', + strokeWidth: 1, + fill: '#fff', + rx: 6, + ry: 6, + }, + }, + }) + + const rect2 = graph.addNode({ + x: 460, + y: 80, + width: 100, + height: 40, + markup: [ + { tagName: 'rect', selector: 'body' }, + { tagName: 'rect', selector: 'port' }, + ], + attrs: { + body: { + stroke: '#8f8f8f', + strokeWidth: 1, + fill: '#fff', + rx: 6, + ry: 6, + }, + port: { + x: 10, + y: 10, + width: 30, + height: 10, + strokeWidth: 1, + stroke: '#fe8550', + }, + }, + }) + + const edge1 = graph.addEdge({ + source: { x: 100, y: 100 }, + target: { cell: rect1.id }, + attrs: { + line: { + stroke: '#8f8f8f', + strokeWidth: 1, + targetMarker: 'classic', + }, + }, + }) + + const edge2 = graph.addEdge({ + source: { x: 320, y: 100 }, + target: { cell: rect2, selector: 'port' }, + attrs: { + line: { + stroke: '#8f8f8f', + strokeWidth: 1, + targetMarker: 'classic', + }, + }, + }) + + function animate() { + edge1.transition('source', 9.36 / 60, { + duration: 5000, + interp: (start, startTime) => { + const corr = startTime * (2 * Math.PI) - Math.PI / 2 + const origin = { x: 210, y: 100 } + const radius = 140 + return function (t) { + return { + x: origin.x + radius * Math.cos(t * 2 * Math.PI + corr), + y: origin.y + radius * Math.sin(t * 2 * Math.PI + corr), + } + } + }, + }) + + edge2.transition('source', 9.36 / 60, { + duration: 5000, + interp: (start, startTime) => { + const corr = startTime * (2 * Math.PI) - Math.PI / 2 + const origin = { x: 485, y: 95 } + const radius = 120 + return function (t) { + return { + x: origin.x + radius * Math.cos(t * 2 * Math.PI + corr), + y: origin.y + radius * Math.sin(t * 2 * Math.PI + corr), + } + } + }, + }) + } + + animate() + + edge1.on('transition:complete', animate) + + this.edge1 = edge1 + this.edge2 = edge2 + } + + onAttrsChanged = ({ type }: State) => { + this.edge1.prop('target/anchor', { name: type }) + this.edge2.prop('target/anchor', { name: type }) + } + + refContainer = (container: HTMLDivElement) => { + this.container = container + } + + render() { + return ( +
+
+ +
+
+
+ ) + } +} diff --git a/sites/x6-sites/src/api/node-anchor/playground/settings.tsx b/sites/x6-sites/src/api/node-anchor/playground/settings.tsx new file mode 100644 index 00000000000..8fe2e8518f8 --- /dev/null +++ b/sites/x6-sites/src/api/node-anchor/playground/settings.tsx @@ -0,0 +1,51 @@ +import React from 'react' +import { Radio, Card, Row, Col } from 'antd' + +export interface Props { + onChange: (state: State) => void +} + +export interface State { + type: string +} + +export class Settings extends React.Component { + state: State = { + type: 'center', + } + + notifyChange() { + this.props.onChange(this.state) + } + + onChange = (e: any) => { + this.setState({ type: e.target.value }, () => { + this.notifyChange() + }) + } + + render() { + return ( + + + + + center + nodeCenter + orth + midSide + top + bottom + left + right + topLeft + topRight + bottomLeft + bottomRight + + + + + ) + } +} diff --git a/sites/x6-sites/src/api/port-label-layout/inside-outside/index.less b/sites/x6-sites/src/api/port-label-layout/inside-outside/index.less new file mode 100644 index 00000000000..419153c6251 --- /dev/null +++ b/sites/x6-sites/src/api/port-label-layout/inside-outside/index.less @@ -0,0 +1,42 @@ +.port-label-inside-outside-app { + display: flex; + padding: 0; + padding: 16px 8px; + font-family: sans-serif; + + .app-side { + bottom: 0; + padding: 0 8px; + } + + .app-content { + flex: 1; + margin-right: 8px; + margin-left: 8px; + box-shadow: 0 0 10px 1px #e9e9e9; + } + + .ant-card { + box-shadow: 0 0 10px 1px #e9e9e9; + } + + .ant-card-head-title { + text-align: center; + } + + .ant-row { + margin: 16px 0; + text-align: left; + } + + .slider-value { + display: inline-block; + margin-left: 8px; + padding: 3px 7px; + color: #333; + font-size: 12px; + line-height: 1.25; + background: #eee; + border-radius: 10px; + } +} diff --git a/sites/x6-sites/src/api/port-label-layout/inside-outside/index.tsx b/sites/x6-sites/src/api/port-label-layout/inside-outside/index.tsx new file mode 100644 index 00000000000..bb2a4cc83ef --- /dev/null +++ b/sites/x6-sites/src/api/port-label-layout/inside-outside/index.tsx @@ -0,0 +1,98 @@ +import React from 'react' +import { Graph, Node } from '@antv/x6' +import { Settings, State } from './settings' +import './index.less' + +export default class Example extends React.Component { + private container: HTMLDivElement + private node: Node + + componentDidMount() { + const graph = new Graph({ + container: this.container, + background: { + color: '#F2F7FA', + }, + }) + + this.node = graph.addNode({ + shape: 'ellipse', + x: 70, + y: 85, + width: 260, + height: 100, + attrs: { + label: { + text: 'outside', + fill: '#888', + fontSize: 12, + }, + body: { + stroke: '#8f8f8f', + strokeWidth: 1, + fill: '#fff', + rx: 6, + ry: 6, + }, + }, + ports: { + groups: { + a: { + position: { + name: 'ellipseSpread', + args: { + compensateRotate: true, + }, + }, + label: { + position: { + name: 'outside', + }, + }, + attrs: { + circle: { + fill: '#ffffff', + stroke: '#8f8f8f', + strokeWidth: 1, + r: 10, + magnet: true, + }, + text: { + fill: '#6a6c8a', + fontSize: 12, + }, + }, + }, + }, + }, + }) + + Array.from({ length: 10 }).forEach((_, index) => { + this.node.addPort({ attrs: { text: { text: `P ${index}` } }, group: 'a' }) + }) + } + + onAttrsChanged = ({ position, offset }: State) => { + this.node.prop('ports/groups/a/label/position', { + name: position, + args: { offset }, + }) + + this.node.attr('label/text', position) + } + + refContainer = (container: HTMLDivElement) => { + this.container = container + } + + render() { + return ( +
+
+ +
+
+
+ ) + } +} diff --git a/sites/x6-sites/src/api/port-label-layout/inside-outside/settings.tsx b/sites/x6-sites/src/api/port-label-layout/inside-outside/settings.tsx new file mode 100644 index 00000000000..2b58eb40b1e --- /dev/null +++ b/sites/x6-sites/src/api/port-label-layout/inside-outside/settings.tsx @@ -0,0 +1,103 @@ +import React from 'react' +import { Radio, Slider, Card, Row, Col } from 'antd' + +export interface Props { + onChange: (state: State) => void +} + +export interface State { + position: string + offset: number +} + +export class Settings extends React.Component { + state: State = { + position: 'outside', + offset: 15, + } + + notifyChange() { + this.props.onChange(this.state) + } + + onOffsetChanged = (offset: number) => { + this.setState({ offset }, () => { + this.notifyChange() + }) + } + + onPositionChange = (e: any) => { + this.setState( + { + position: e.target.value, + }, + () => { + this.notifyChange() + }, + ) + } + + render() { + const radioStyle = { + display: 'block', + height: '36px', + lineHeight: '36px', + } + + return ( + + + + + + inside + + + outside + + + insideOriented + + + outsideOriented + + + + + + + offset + + + + + +
{this.state.offset}
+ +
+
+ ) + } +} diff --git a/sites/x6-sites/src/api/port-label-layout/radial/index.less b/sites/x6-sites/src/api/port-label-layout/radial/index.less new file mode 100644 index 00000000000..51d1104836b --- /dev/null +++ b/sites/x6-sites/src/api/port-label-layout/radial/index.less @@ -0,0 +1,42 @@ +.port-label-radial-app { + display: flex; + padding: 0; + padding: 16px 8px; + font-family: sans-serif; + + .app-side { + bottom: 0; + padding: 0 8px; + } + + .app-content { + flex: 1; + margin-right: 8px; + margin-left: 8px; + box-shadow: 0 0 10px 1px #e9e9e9; + } + + .ant-card { + box-shadow: 0 0 10px 1px #e9e9e9; + } + + .ant-card-head-title { + text-align: center; + } + + .ant-row { + margin: 16px 0; + text-align: left; + } + + .slider-value { + display: inline-block; + margin-left: 8px; + padding: 3px 7px; + color: #333; + font-size: 12px; + line-height: 1.25; + background: #eee; + border-radius: 10px; + } +} diff --git a/sites/x6-sites/src/api/port-label-layout/radial/index.tsx b/sites/x6-sites/src/api/port-label-layout/radial/index.tsx new file mode 100644 index 00000000000..afccd64d79f --- /dev/null +++ b/sites/x6-sites/src/api/port-label-layout/radial/index.tsx @@ -0,0 +1,98 @@ +import React from 'react' +import { Graph, Node } from '@antv/x6' +import { Settings, State } from './settings' +import './index.less' + +export default class Example extends React.Component { + private container: HTMLDivElement + private node: Node + + componentDidMount() { + const graph = new Graph({ + container: this.container, + background: { + color: '#F2F7FA', + }, + }) + + this.node = graph.addNode({ + shape: 'ellipse', + x: 70, + y: 50, + width: 260, + height: 100, + attrs: { + label: { + text: 'outside', + fill: '#888', + fontSize: 12, + }, + body: { + stroke: '#8f8f8f', + strokeWidth: 1, + fill: '#fff', + rx: 6, + ry: 6, + }, + }, + ports: { + groups: { + a: { + position: { + name: 'ellipseSpread', + args: { + compensateRotate: true, + }, + }, + label: { + position: { + name: 'outside', + }, + }, + attrs: { + circle: { + fill: '#ffffff', + stroke: '#8f8f8f', + strokeWidth: 1, + r: 10, + magnet: true, + }, + text: { + fill: '#6a6c8a', + fontSize: 12, + }, + }, + }, + }, + }, + }) + + Array.from({ length: 10 }).forEach((_, index) => { + this.node.addPort({ attrs: { text: { text: `P ${index}` } }, group: 'a' }) + }) + } + + onAttrsChanged = ({ position, offset }: State) => { + this.node.prop('ports/groups/a/label/position', { + name: position, + args: { offset }, + }) + + this.node.attr('label/text', position) + } + + refContainer = (container: HTMLDivElement) => { + this.container = container + } + + render() { + return ( +
+
+ +
+
+
+ ) + } +} diff --git a/sites/x6-sites/src/api/port-label-layout/radial/settings.tsx b/sites/x6-sites/src/api/port-label-layout/radial/settings.tsx new file mode 100644 index 00000000000..291df7bfd1e --- /dev/null +++ b/sites/x6-sites/src/api/port-label-layout/radial/settings.tsx @@ -0,0 +1,97 @@ +import React from 'react' +import { Radio, Slider, Card, Row, Col } from 'antd' + +export interface Props { + onChange: (state: State) => void +} + +export interface State { + position: string + offset: number +} + +export class Settings extends React.Component { + state: State = { + position: 'radial', + offset: 20, + } + + notifyChange() { + this.props.onChange(this.state) + } + + onOffsetChanged = (offset: number) => { + this.setState({ offset }, () => { + this.notifyChange() + }) + } + + onPositionChange = (e: any) => { + this.setState( + { + position: e.target.value, + }, + () => { + this.notifyChange() + }, + ) + } + + render() { + const radioStyle = { + display: 'block', + height: '36px', + lineHeight: '36px', + } + + return ( + + + + + + radial + + + radialOriented + + + + + + + offset + + + + + +
{this.state.offset}
+ +
+
+ ) + } +} diff --git a/sites/x6-sites/src/api/port-label-layout/side/index.less b/sites/x6-sites/src/api/port-label-layout/side/index.less new file mode 100644 index 00000000000..dda8d3dbf27 --- /dev/null +++ b/sites/x6-sites/src/api/port-label-layout/side/index.less @@ -0,0 +1,42 @@ +.port-label-side-app { + display: flex; + padding: 0; + padding: 16px 8px; + font-family: sans-serif; + + .app-side { + bottom: 0; + padding: 0 8px; + } + + .app-content { + flex: 1; + margin-right: 8px; + margin-left: 8px; + box-shadow: 0 0 10px 1px #e9e9e9; + } + + .ant-card { + box-shadow: 0 0 10px 1px #e9e9e9; + } + + .ant-card-head-title { + text-align: center; + } + + .ant-row { + margin: 16px 0; + text-align: left; + } + + .slider-value { + display: inline-block; + margin-left: 8px; + padding: 3px 7px; + color: #333; + font-size: 12px; + line-height: 1.25; + background: #eee; + border-radius: 10px; + } +} diff --git a/sites/x6-sites/src/api/port-label-layout/side/index.tsx b/sites/x6-sites/src/api/port-label-layout/side/index.tsx new file mode 100644 index 00000000000..fba03ce290d --- /dev/null +++ b/sites/x6-sites/src/api/port-label-layout/side/index.tsx @@ -0,0 +1,108 @@ +import React from 'react' +import { Graph, Node } from '@antv/x6' +import { Settings, State } from './settings' +import './index.less' + +export default class Example extends React.Component { + private container: HTMLDivElement + private node: Node + + componentDidMount() { + const graph = new Graph({ + container: this.container, + background: { + color: '#F2F7FA', + }, + }) + + this.node = graph.addNode({ + shape: 'rect', + x: 80, + y: 68, + width: 240, + height: 80, + attrs: { + label: { + text: 'left', + fill: '#6a6c8a', + }, + body: { + stroke: '#8f8f8f', + strokeWidth: 1, + fill: '#fff', + rx: 6, + ry: 6, + }, + }, + ports: { + groups: { + a: { + position: { + name: 'top', + args: { + dr: 0, + dx: 0, + dy: -10, + }, + }, + label: { + position: { + name: 'left', + }, + }, + attrs: { + circle: { + fill: '#ffffff', + stroke: '#8f8f8f', + strokeWidth: 1, + r: 10, + }, + text: { + fill: '#6a6c8a', + }, + }, + }, + }, + }, + }) + + Array.from({ length: 3 }).forEach((_, index) => { + const label = + index === 2 + ? { + position: { args: { x: 20, y: -20 } }, + } + : {} + const stroke = index === 2 ? { stroke: 'red' } : {} + const fill = index === 2 ? { fill: 'red' } : {} + this.node.addPort({ + label, + group: 'a', + attrs: { + circle: { magnet: true, ...stroke }, + text: { text: `P${index}`, ...fill }, + }, + }) + }) + } + + onAttrsChanged = ({ position }: State) => { + this.node.prop('ports/groups/a/label/position/name', position) + this.node.attr('label/text', position) + } + + refContainer = (container: HTMLDivElement) => { + this.container = container + } + + render() { + return ( +
+
+ +
+
+
+ ) + } +} diff --git a/sites/x6-sites/src/api/port-label-layout/side/settings.tsx b/sites/x6-sites/src/api/port-label-layout/side/settings.tsx new file mode 100644 index 00000000000..a6ca02615f3 --- /dev/null +++ b/sites/x6-sites/src/api/port-label-layout/side/settings.tsx @@ -0,0 +1,94 @@ +import React from 'react' +import { Radio, Card, Row, Col } from 'antd' + +export interface Props { + onChange: (state: State) => void +} + +export interface State { + position: string + dx: number + dy: number + angle: number +} + +export class Settings extends React.Component { + state: State = { + position: 'left', + dx: 0, + dy: 0, + angle: 45, + } + + notifyChange() { + this.props.onChange(this.state) + } + + onDxChanged = (dx: number) => { + this.setState({ dx }, () => { + this.notifyChange() + }) + } + + onDyChanged = (dy: number) => { + this.setState({ dy }, () => { + this.notifyChange() + }) + } + + onAngleChanged = (angle: number) => { + this.setState({ angle }, () => { + this.notifyChange() + }) + } + + onPositionChange = (e: any) => { + this.setState( + { + position: e.target.value, + }, + () => { + this.notifyChange() + }, + ) + } + + render() { + const radioStyle = { + display: 'block', + height: '36px', + lineHeight: '36px', + } + + return ( + + + + + + left + + + right + + + top + + + bottom + + + + + + ) + } +} diff --git a/sites/x6-sites/src/api/port-layout/absolute/index.less b/sites/x6-sites/src/api/port-layout/absolute/index.less new file mode 100644 index 00000000000..14d47bea90a --- /dev/null +++ b/sites/x6-sites/src/api/port-layout/absolute/index.less @@ -0,0 +1,42 @@ +.port-absolute-app { + display: flex; + padding: 0; + padding: 16px 8px; + font-family: sans-serif; + + .app-side { + bottom: 0; + padding: 0 8px; + } + + .app-content { + flex: 1; + margin-right: 8px; + margin-left: 8px; + box-shadow: 0 0 10px 1px #e9e9e9; + } + + .ant-card { + box-shadow: 0 0 10px 1px #e9e9e9; + } + + .ant-card-head-title { + text-align: center; + } + + .ant-row { + margin: 16px 0; + text-align: left; + } + + .slider-value { + display: inline-block; + margin-left: 8px; + padding: 3px 7px; + color: #333; + font-size: 12px; + line-height: 1.25; + background: #eee; + border-radius: 10px; + } +} diff --git a/sites/x6-sites/src/api/port-layout/absolute/index.tsx b/sites/x6-sites/src/api/port-layout/absolute/index.tsx new file mode 100644 index 00000000000..71d7e57e47e --- /dev/null +++ b/sites/x6-sites/src/api/port-layout/absolute/index.tsx @@ -0,0 +1,129 @@ +import React from 'react' +import { Graph, Node } from '@antv/x6' +import { Settings, State } from './settings' +import './index.less' + +export default class Example extends React.Component { + private container: HTMLDivElement + private node: Node + + componentDidMount() { + const graph = new Graph({ + container: this.container, + background: { + color: '#F2F7FA', + }, + }) + + this.node = graph.addNode({ + x: 120, + y: 48, + width: 280, + height: 120, + markup: [ + { + tagName: 'rect', + selector: 'body', + }, + ], + attrs: { + body: { + refWidth: '100%', + refHeight: '100%', + fill: '#fff', + stroke: '#8f8f8f', + strokeWidth: 1, + }, + }, + ports: { + groups: { + group1: { + attrs: { + circle: { + r: 6, + magnet: true, + stroke: '#8f8f8f', + strokeWidth: 1, + fill: '#fff', + }, + text: { + fontSize: 12, + fill: '#888', + }, + }, + position: { + name: 'absolute', + }, + }, + }, + items: [ + { + id: 'port1', + group: 'group1', + args: { x: 0, y: 60 }, + attrs: { + text: { text: '{ x: 0, y: 60 }' }, + }, + }, + { + id: 'port2', + group: 'group1', + args: { x: 0.6, y: 32, angle: 45 }, + markup: [ + { + tagName: 'path', + selector: 'path', + }, + ], + zIndex: 10, + attrs: { + path: { + d: 'M -6 -8 L 0 8 L 6 -8 Z', + magnet: true, + fill: '#8f8f8f', + }, + text: { text: '{ x: 0.6, y: 32, angle: 45 }', fill: '#888' }, + }, + }, + { + id: 'port3', + group: 'group1', + args: { x: '100%', y: '100%' }, + attrs: { + text: { text: "{ x: '100%', y: '100%' }" }, + }, + label: { + position: { + name: 'right', + }, + }, + }, + ], + }, + }) + } + + onAttrsChanged = (args: State) => { + this.node.portProp('port2', { + args, + attrs: { + text: { text: `{ x: ${args.x}, y: ${args.y}, angle: ${args.angle} }` }, + }, + } as any) + } + + refContainer = (container: HTMLDivElement) => { + this.container = container + } + + render() { + return ( +
+
+ +
+
+
+ ) + } +} diff --git a/sites/x6-sites/src/api/port-layout/absolute/settings.tsx b/sites/x6-sites/src/api/port-layout/absolute/settings.tsx new file mode 100644 index 00000000000..78186ad5deb --- /dev/null +++ b/sites/x6-sites/src/api/port-layout/absolute/settings.tsx @@ -0,0 +1,100 @@ +import React from 'react' +import { Slider, Card, Row, Col } from 'antd' + +export interface Props { + onChange: (state: State) => void +} + +export interface State { + x: number + y: number + angle: number +} + +export class Settings extends React.Component { + state: State = { + x: 0.6, + y: 32, + angle: 45, + } + + notifyChange() { + this.props.onChange(this.state) + } + + onXChanged = (x: number) => { + this.setState({ x }, () => { + this.notifyChange() + }) + } + + onYChanged = (y: number) => { + this.setState({ y }, () => { + this.notifyChange() + }) + } + + onAngleChanged = (angle: number) => { + this.setState({ angle }, () => { + this.notifyChange() + }) + } + + render() { + return ( + + + + x + + + + + +
{this.state.x}
+ +
+ + + y + + + + + +
{this.state.y}
+ +
+ + + angle + + + + + +
{this.state.angle}
+ +
+
+ ) + } +} diff --git a/sites/x6-sites/src/api/port-layout/ellipse-spread/index.less b/sites/x6-sites/src/api/port-layout/ellipse-spread/index.less new file mode 100644 index 00000000000..c774001d8f1 --- /dev/null +++ b/sites/x6-sites/src/api/port-layout/ellipse-spread/index.less @@ -0,0 +1,42 @@ +.port-ellipse-spread-app { + display: flex; + padding: 0; + padding: 16px 8px; + font-family: sans-serif; + + .app-side { + bottom: 0; + padding: 0 8px; + } + + .app-content { + flex: 1; + margin-right: 8px; + margin-left: 8px; + box-shadow: 0 0 10px 1px #e9e9e9; + } + + .ant-card { + box-shadow: 0 0 10px 1px #e9e9e9; + } + + .ant-card-head-title { + text-align: center; + } + + .ant-row { + margin: 16px 0; + text-align: left; + } + + .slider-value { + display: inline-block; + margin-left: 8px; + padding: 3px 7px; + color: #333; + font-size: 12px; + line-height: 1.25; + background: #eee; + border-radius: 10px; + } +} diff --git a/sites/x6-sites/src/api/port-layout/ellipse-spread/index.tsx b/sites/x6-sites/src/api/port-layout/ellipse-spread/index.tsx new file mode 100644 index 00000000000..93e40851f24 --- /dev/null +++ b/sites/x6-sites/src/api/port-layout/ellipse-spread/index.tsx @@ -0,0 +1,122 @@ +import React from 'react' +import { Graph, Node } from '@antv/x6' +import { Settings, State } from './settings' +import './index.less' + +export default class Example extends React.Component { + private container: HTMLDivElement + private node: Node + + componentDidMount() { + const graph = new Graph({ + container: this.container, + background: { + color: '#F2F7FA', + }, + }) + + this.node = graph.addNode({ + x: 120, + y: 90, + width: 360, + height: 200, + shape: 'ellipse', + + attrs: { + body: { + refWidth: '100%', + refHeight: '100%', + fill: '#fff', + stroke: '#8f8f8f', + strokeWidth: 1, + }, + }, + ports: { + groups: { + group1: { + markup: [ + { + tagName: 'rect', + selector: 'rect', + }, + { + tagName: 'circle', + selector: 'dot', + }, + ], + attrs: { + rect: { + magnet: true, + stroke: '#8f8f8f', + fill: 'rgba(255,255,255,0.8)', + strokeWidth: 1, + width: 16, + height: 16, + x: -8, + y: -8, + }, + dot: { + fill: '#8f8f8f', + r: 2, + }, + text: { + fontSize: 12, + fill: '#888', + }, + }, + label: { + position: 'radial', + }, + position: { + name: 'ellipseSpread', + args: { + start: 45, + }, + }, + }, + }, + }, + }) + + Array.from({ length: 10 }).forEach((_, index) => { + this.node.addPort({ + id: `${index}`, + group: 'group1', + attrs: { text: { text: index } }, + }) + }) + + this.node.portProp('0', { + attrs: { + rect: { stroke: '#8f8f8f' }, + dot: { fill: '#8f8f8f' }, + }, + }) + } + + onAttrsChanged = ({ start, compensateRotate, ...args }: State) => { + this.node.prop('ports/groups/group1/position/args', { + start, + compensateRotate, + }) + + this.node.portProp('0', { + args, + } as any) + } + + refContainer = (container: HTMLDivElement) => { + this.container = container + } + + render() { + return ( +
+
+ +
+
+
+ ) + } +} diff --git a/sites/x6-sites/src/api/port-layout/ellipse-spread/settings.tsx b/sites/x6-sites/src/api/port-layout/ellipse-spread/settings.tsx new file mode 100644 index 00000000000..609a62f8b79 --- /dev/null +++ b/sites/x6-sites/src/api/port-layout/ellipse-spread/settings.tsx @@ -0,0 +1,171 @@ +import React from 'react' +import { Slider, Checkbox, Card, Row, Col } from 'antd' + +export interface Props { + onChange: (state: State) => void +} + +export interface State { + start: number + compensateRotate: boolean + dr: number + dx: number + dy: number + angle: number +} + +export class Settings extends React.Component { + state: State = { + start: 45, + compensateRotate: false, + dr: 0, + dx: 0, + dy: 0, + angle: 45, + } + + notifyChange() { + this.props.onChange(this.state) + } + + onStartChanged = (start: number) => { + this.setState({ start }, () => { + this.notifyChange() + }) + } + + onCompensateRotateChange = (e: any) => { + this.setState({ compensateRotate: e.target.checked }, () => { + this.notifyChange() + }) + } + + onDxChanged = (dx: number) => { + this.setState({ dx }, () => { + this.notifyChange() + }) + } + + onDrChanged = (dr: number) => { + this.setState({ dr }, () => { + this.notifyChange() + }) + } + + onDyChanged = (dy: number) => { + this.setState({ dy }, () => { + this.notifyChange() + }) + } + + onAngleChanged = (angle: number) => { + this.setState({ angle }, () => { + this.notifyChange() + }) + } + + render() { + return ( + + + + start + + + + + +
{this.state.start}
+ +
+ + + + compensateRotate + + + + + + dr + + + + + +
{this.state.dr}
+ +
+ + + dx + + + + + +
{this.state.dx}
+ +
+ + + dy + + + + + +
{this.state.dy}
+ +
+ + + angle + + + + + +
{this.state.angle}
+ +
+
+ ) + } +} diff --git a/sites/x6-sites/src/api/port-layout/ellipse/index.less b/sites/x6-sites/src/api/port-layout/ellipse/index.less new file mode 100644 index 00000000000..86d4864e992 --- /dev/null +++ b/sites/x6-sites/src/api/port-layout/ellipse/index.less @@ -0,0 +1,42 @@ +.port-ellipse-app { + display: flex; + padding: 0; + padding: 16px 8px; + font-family: sans-serif; + + .app-side { + bottom: 0; + padding: 0 8px; + } + + .app-content { + flex: 1; + margin-right: 8px; + margin-left: 8px; + box-shadow: 0 0 10px 1px #e9e9e9; + } + + .ant-card { + box-shadow: 0 0 10px 1px #e9e9e9; + } + + .ant-card-head-title { + text-align: center; + } + + .ant-row { + margin: 16px 0; + text-align: left; + } + + .slider-value { + display: inline-block; + margin-left: 8px; + padding: 3px 7px; + color: #333; + font-size: 12px; + line-height: 1.25; + background: #eee; + border-radius: 10px; + } +} diff --git a/sites/x6-sites/src/api/port-layout/ellipse/index.tsx b/sites/x6-sites/src/api/port-layout/ellipse/index.tsx new file mode 100644 index 00000000000..218b39f8f1f --- /dev/null +++ b/sites/x6-sites/src/api/port-layout/ellipse/index.tsx @@ -0,0 +1,147 @@ +import React from 'react' +import { Graph, Node, Point } from '@antv/x6' +import { Settings, State } from './settings' +import './index.less' + +function getPathData(deg: number) { + const center = new Point(180, 100) + const start = new Point(180, 0) + const ratio = center.x / center.y + const p = start + .clone() + .rotate(90 - deg, center) + .scale(ratio, 1, center) + return `M ${center.x} ${center.y} ${p.x} ${p.y}` +} + +export default class Example extends React.Component { + private container: HTMLDivElement + private node: Node + + componentDidMount() { + const graph = new Graph({ + container: this.container, + background: { + color: '#F2F7FA', + }, + }) + + this.node = graph.addNode({ + x: 120, + y: 90, + width: 360, + height: 200, + shape: 'ellipse', + markup: [ + { tagName: 'ellipse', selector: 'body' }, + { tagName: 'text', selector: 'label' }, + { tagName: 'path', selector: 'line' }, + ], + attrs: { + body: { + refWidth: '100%', + refHeight: '100%', + fill: '#fff', + stroke: '#8f8f8f', + strokeWidth: 1, + }, + line: { + d: getPathData(45), + stroke: '#8f8f8f', + strokeDasharray: '5 5', + }, + }, + ports: { + groups: { + group1: { + markup: [ + { + tagName: 'rect', + selector: 'rect', + }, + { + tagName: 'circle', + selector: 'dot', + }, + ], + attrs: { + rect: { + magnet: true, + stroke: '#8f8f8f', + fill: 'rgba(255,255,255,0.8)', + strokeWidth: 1, + width: 16, + height: 16, + x: -8, + y: -8, + }, + dot: { + fill: '#8f8f8f', + r: 2, + }, + text: { + fontSize: 12, + fill: '#888', + }, + }, + label: { + position: 'radial', + }, + position: { + name: 'ellipse', + args: { + start: 45, + }, + }, + }, + }, + }, + }) + + Array.from({ length: 10 }).forEach((_, index) => { + this.node.addPort({ + id: `${index}`, + group: 'group1', + attrs: { text: { text: index } }, + }) + }) + + this.node.portProp('0', { + attrs: { + rect: { stroke: '#8f8f8f' }, + dot: { fill: '#8f8f8f' }, + }, + }) + } + + onAttrsChanged = ({ start, step, compensateRotate, ...args }: State) => { + this.node.prop('ports/groups/group1/position/args', { + start, + step, + compensateRotate, + }) + + this.node.attr({ + line: { d: getPathData(start) }, + }) + + this.node.portProp('0', { + args, + } as any) + } + + refContainer = (container: HTMLDivElement) => { + this.container = container + } + + render() { + return ( +
+
+ +
+
+
+ ) + } +} diff --git a/sites/x6-sites/src/api/port-layout/ellipse/settings.tsx b/sites/x6-sites/src/api/port-layout/ellipse/settings.tsx new file mode 100644 index 00000000000..267fde79d28 --- /dev/null +++ b/sites/x6-sites/src/api/port-layout/ellipse/settings.tsx @@ -0,0 +1,196 @@ +import React from 'react' +import { Slider, Checkbox, Card, Row, Col } from 'antd' + +export interface Props { + onChange: (state: State) => void +} + +export interface State { + start: number + step: number + compensateRotate: boolean + dr: number + dx: number + dy: number + angle: number +} + +export class Settings extends React.Component { + state: State = { + start: 45, + step: 20, + compensateRotate: false, + dr: 0, + dx: 0, + dy: 0, + angle: 45, + } + + notifyChange() { + this.props.onChange(this.state) + } + + onStartChanged = (start: number) => { + this.setState({ start }, () => { + this.notifyChange() + }) + } + + onStepChanged = (step: number) => { + this.setState({ step }, () => { + this.notifyChange() + }) + } + + onCompensateRotateChange = (e: any) => { + this.setState({ compensateRotate: e.target.checked }, () => { + this.notifyChange() + }) + } + + onDxChanged = (dx: number) => { + this.setState({ dx }, () => { + this.notifyChange() + }) + } + + onDrChanged = (dr: number) => { + this.setState({ dr }, () => { + this.notifyChange() + }) + } + + onDyChanged = (dy: number) => { + this.setState({ dy }, () => { + this.notifyChange() + }) + } + + onAngleChanged = (angle: number) => { + this.setState({ angle }, () => { + this.notifyChange() + }) + } + + render() { + return ( + + + + start + + + + + +
{this.state.start}
+ +
+ + + step + + + + + +
{this.state.step}
+ +
+ + + + compensateRotate + + + + + + dr + + + + + +
{this.state.dr}
+ +
+ + + dx + + + + + +
{this.state.dx}
+ +
+ + + dy + + + + + +
{this.state.dy}
+ +
+ + + angle + + + + + +
{this.state.angle}
+ +
+
+ ) + } +} diff --git a/sites/x6-sites/src/api/port-layout/line/index.less b/sites/x6-sites/src/api/port-layout/line/index.less new file mode 100644 index 00000000000..521da07b3c0 --- /dev/null +++ b/sites/x6-sites/src/api/port-layout/line/index.less @@ -0,0 +1,42 @@ +.port-line-app { + display: flex; + padding: 0; + padding: 16px 8px; + font-family: sans-serif; + + .app-side { + bottom: 0; + padding: 0 8px; + } + + .app-content { + flex: 1; + margin-right: 8px; + margin-left: 8px; + box-shadow: 0 0 10px 1px #e9e9e9; + } + + .ant-card { + box-shadow: 0 0 10px 1px #e9e9e9; + } + + .ant-card-head-title { + text-align: center; + } + + .ant-row { + margin: 16px 0; + text-align: left; + } + + .slider-value { + display: inline-block; + margin-left: 8px; + padding: 3px 7px; + color: #333; + font-size: 12px; + line-height: 1.25; + background: #eee; + border-radius: 10px; + } +} diff --git a/sites/x6-sites/src/api/port-layout/line/index.tsx b/sites/x6-sites/src/api/port-layout/line/index.tsx new file mode 100644 index 00000000000..88aa6bea7fb --- /dev/null +++ b/sites/x6-sites/src/api/port-layout/line/index.tsx @@ -0,0 +1,127 @@ +import React from 'react' +import { Graph, Node } from '@antv/x6' +import { Settings, State } from './settings' +import './index.less' + +export default class Example extends React.Component { + private container: HTMLDivElement + private node: Node + + componentDidMount() { + const graph = new Graph({ + container: this.container, + background: { + color: '#F2F7FA', + }, + }) + + this.node = graph.addNode({ + x: 160, + y: 80, + width: 280, + height: 120, + markup: [ + { + tagName: 'rect', + selector: 'body', + }, + { + tagName: 'path', + selector: 'line', + }, + ], + attrs: { + body: { + refWidth: '100%', + refHeight: '100%', + fill: '#fff', + stroke: '#8f8f8f', + strokeWidth: 1, + }, + line: { + d: 'M 0 0 280 120', + stroke: '#8f8f8f', + strokeDasharray: '5 5', + }, + }, + ports: { + groups: { + group1: { + attrs: { + circle: { + r: 6, + magnet: true, + stroke: '#8f8f8f', + strokeWidth: 2, + fill: '#fff', + }, + text: { + fontSize: 12, + fill: '#888', + }, + }, + position: { + name: 'line', + args: { + start: { x: 0, y: 0 }, + end: { x: 280, y: 120 }, + }, + }, + }, + }, + items: [ + { + id: 'port1', + group: 'group1', + }, + { + id: 'port2', + group: 'group1', + args: { angle: 45 }, + markup: [ + { + tagName: 'path', + selector: 'path', + }, + ], + attrs: { + path: { + d: 'M -6 -8 L 0 8 L 6 -8 Z', + magnet: true, + fill: '#8f8f8f', + }, + }, + }, + { + id: 'port3', + group: 'group1', + args: {}, + }, + ], + }, + }) + } + + onAttrsChanged = ({ strict, ...args }: State) => { + this.node.prop('ports/groups/group1/position', { + name: 'line', + args: { strict }, + }) + this.node.portProp('port2', { args } as any) + } + + refContainer = (container: HTMLDivElement) => { + this.container = container + } + + render() { + return ( +
+
+ +
+
+
+ ) + } +} diff --git a/sites/x6-sites/src/api/port-layout/line/settings.tsx b/sites/x6-sites/src/api/port-layout/line/settings.tsx new file mode 100644 index 00000000000..0c6afdfb979 --- /dev/null +++ b/sites/x6-sites/src/api/port-layout/line/settings.tsx @@ -0,0 +1,122 @@ +import React from 'react' +import { Slider, Switch, Card, Row, Col } from 'antd' + +export interface Props { + onChange: (state: State) => void +} + +export interface State { + strict: boolean + dx: number + dy: number + angle: number +} + +export class Settings extends React.Component { + state: State = { + strict: false, + dx: 0, + dy: 0, + angle: 45, + } + + notifyChange() { + this.props.onChange(this.state) + } + + onStrictChange = (strict: boolean) => { + this.setState({ strict }, () => { + this.notifyChange() + }) + } + + onDxChanged = (dx: number) => { + this.setState({ dx }, () => { + this.notifyChange() + }) + } + + onDyChanged = (dy: number) => { + this.setState({ dy }, () => { + this.notifyChange() + }) + } + + onAngleChanged = (angle: number) => { + this.setState({ angle }, () => { + this.notifyChange() + }) + } + + render() { + return ( + + + + strict + + + + + + + + dx + + + + + +
{this.state.dx}
+ +
+ + + dy + + + + + +
{this.state.dy}
+ +
+ + + angle + + + + + +
{this.state.angle}
+ +
+
+ ) + } +} diff --git a/sites/x6-sites/src/api/port-layout/side/index.less b/sites/x6-sites/src/api/port-layout/side/index.less new file mode 100644 index 00000000000..22bb7935836 --- /dev/null +++ b/sites/x6-sites/src/api/port-layout/side/index.less @@ -0,0 +1,42 @@ +.port-side-app { + display: flex; + padding: 0; + padding: 16px 8px; + font-family: sans-serif; + + .app-side { + bottom: 0; + padding: 0 8px; + } + + .app-content { + flex: 1; + margin-right: 8px; + margin-left: 8px; + box-shadow: 0 0 10px 1px #e9e9e9; + } + + .ant-card { + box-shadow: 0 0 10px 1px #e9e9e9; + } + + .ant-card-head-title { + text-align: center; + } + + .ant-row { + margin: 16px 0; + text-align: left; + } + + .slider-value { + display: inline-block; + margin-left: 8px; + padding: 3px 7px; + color: #333; + font-size: 12px; + line-height: 1.25; + background: #eee; + border-radius: 10px; + } +} diff --git a/sites/x6-sites/src/api/port-layout/side/index.tsx b/sites/x6-sites/src/api/port-layout/side/index.tsx new file mode 100644 index 00000000000..8c7b8fd5084 --- /dev/null +++ b/sites/x6-sites/src/api/port-layout/side/index.tsx @@ -0,0 +1,110 @@ +import React from 'react' +import { Graph, Node } from '@antv/x6' +import { Settings, State } from './settings' +import './index.less' + +export default class Example extends React.Component { + private container: HTMLDivElement + private node: Node + + componentDidMount() { + const graph = new Graph({ + container: this.container, + background: { + color: '#F2F7FA', + }, + }) + + this.node = graph.addNode({ + x: 160, + y: 110, + width: 280, + height: 120, + attrs: { + body: { + refWidth: '100%', + refHeight: '100%', + fill: '#fff', + stroke: '#8f8f8f', + strokeWidth: 1, + }, + label: { text: 'left', fill: '#888' }, + }, + ports: { + groups: { + group1: { + attrs: { + circle: { + r: 6, + magnet: true, + stroke: '#8f8f8f', + strokeWidth: 1, + fill: '#fff', + }, + text: { + fontSize: 12, + fill: '#888', + }, + }, + position: { + name: 'left', + }, + }, + }, + items: [ + { + id: 'port1', + group: 'group1', + }, + { + id: 'port2', + group: 'group1', + args: { angle: 45 }, + markup: [ + { + tagName: 'path', + selector: 'path', + }, + ], + attrs: { + path: { + d: 'M -6 -8 L 0 8 L 6 -8 Z', + magnet: true, + fill: '#8f8f8f', + }, + }, + }, + { + id: 'port3', + group: 'group1', + args: {}, + }, + ], + }, + }) + } + + onAttrsChanged = ({ position, strict, ...args }: State) => { + this.node.prop('ports/groups/group1/position', { + name: position, + args: { strict }, + }) + this.node.attr('label/text', position) + this.node.portProp('port2', { args } as any) + } + + refContainer = (container: HTMLDivElement) => { + this.container = container + } + + render() { + return ( +
+
+ +
+
+
+ ) + } +} diff --git a/sites/x6-sites/src/api/port-layout/side/settings.tsx b/sites/x6-sites/src/api/port-layout/side/settings.tsx new file mode 100644 index 00000000000..0ebdbfff5e2 --- /dev/null +++ b/sites/x6-sites/src/api/port-layout/side/settings.tsx @@ -0,0 +1,151 @@ +import React from 'react' +import { Radio, Switch, Slider, Card, Row, Col } from 'antd' + +export interface Props { + onChange: (state: State) => void +} + +export interface State { + position: string + strict: boolean + dx: number + dy: number + angle: number +} + +export class Settings extends React.Component { + state: State = { + position: 'left', + strict: false, + dx: 0, + dy: 0, + angle: 45, + } + + notifyChange() { + this.props.onChange(this.state) + } + + onDxChanged = (dx: number) => { + this.setState({ dx }, () => { + this.notifyChange() + }) + } + + onDyChanged = (dy: number) => { + this.setState({ dy }, () => { + this.notifyChange() + }) + } + + onAngleChanged = (angle: number) => { + this.setState({ angle }, () => { + this.notifyChange() + }) + } + + onStrictChange = (strict: boolean) => { + this.setState({ strict }, () => { + this.notifyChange() + }) + } + + onPositionChange = (e: any) => { + this.setState( + { + position: e.target.value, + }, + () => { + this.notifyChange() + }, + ) + } + + render() { + return ( + + + + position + + + + left + right + top + bottom + + + + + + strict + + + + + + + + dx + + + + + +
{this.state.dx}
+ +
+ + + dy + + + + + +
{this.state.dy}
+ +
+ + + angle + + + + + +
{this.state.angle}
+ +
+
+ ) + } +} diff --git a/sites/x6-sites/src/api/port-layout/sin/index.less b/sites/x6-sites/src/api/port-layout/sin/index.less new file mode 100644 index 00000000000..0df96e61630 --- /dev/null +++ b/sites/x6-sites/src/api/port-layout/sin/index.less @@ -0,0 +1,14 @@ +.port-sin-app { + display: flex; + width: 100%; + padding: 0; + font-family: sans-serif; + + .app-content { + flex: 1; + height: 240px; + margin-right: 8px; + margin-left: 8px; + box-shadow: 0 0 10px 1px #e9e9e9; + } +} diff --git a/sites/x6-sites/src/api/port-layout/sin/index.tsx b/sites/x6-sites/src/api/port-layout/sin/index.tsx new file mode 100644 index 00000000000..faa6c7c9cfa --- /dev/null +++ b/sites/x6-sites/src/api/port-layout/sin/index.tsx @@ -0,0 +1,80 @@ +import React from 'react' +import { Graph } from '@antv/x6' +import './index.less' + +Graph.registerPortLayout( + 'sin', + (portsPositionArgs, elemBBox) => { + return portsPositionArgs.map((_, index) => { + const step = -Math.PI / 8 + const y = Math.sin(index * step) * 50 + return { + position: { + x: index * 12, + y: y + elemBBox.height, + }, + angle: 0, + } + }) + }, + true, +) + +export default class Example extends React.Component { + private container: HTMLDivElement + + componentDidMount() { + const graph = new Graph({ + container: this.container, + background: { + color: '#F2F7FA', + }, + }) + + const rect = graph.addNode({ + x: 120, + y: 40, + width: 280, + height: 120, + attrs: { + body: { + fill: '#fff', + stroke: '#8f8f8f', + strokeWidth: 1, + }, + }, + ports: { + groups: { + sin: { + attrs: { + circle: { + r: 6, + magnet: true, + stroke: '#8f8f8f', + strokeWidth: 1, + fill: '#fe854f', + }, + }, + position: 'sin', + }, + }, + }, + }) + + Array.from({ length: 24 }).forEach(() => { + rect.addPort({ group: 'sin' }) + }) + } + + refContainer = (container: HTMLDivElement) => { + this.container = container + } + + render() { + return ( +
+
+
+ ) + } +}