Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support both mouse selection with the left mouse button and panning with the middle mouse button in the graph #3965

Merged
Merged
4 changes: 4 additions & 0 deletions examples/x6-example-features/src/pages/selection/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ export default class Example extends React.Component {
width: 800,
height: 600,
grid: true,
panning: {
enabled: true,
eventTypes: ['mouseWheelDown'],
},
})

const keyboard = new Keyboard()
Expand Down
41 changes: 33 additions & 8 deletions packages/x6-plugin-selection/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import {
ModifierKey,
CssLoader,
Dom,
ObjectExt,
Cell,
EventArgs,
Graph,
Expand Down Expand Up @@ -42,11 +41,12 @@ export class Selection

constructor(options: Selection.Options = {}) {
super()
this.options = ObjectExt.merge(
{ enabled: true },
Selection.defaultOptions,
options,
)
this.options = {
enabled: true,
...Selection.defaultOptions,
...options,
}
aMoonkin marked this conversation as resolved.
Show resolved Hide resolved

CssLoader.ensure(this.name, content)
}

Expand Down Expand Up @@ -304,21 +304,45 @@ export class Selection
}

protected startListening() {
this.graph.on('blank:mousedown', this.onBlankMouseDown, this)
if (this.options.eventTypes?.includes('leftMouseDown')) {
aMoonkin marked this conversation as resolved.
Show resolved Hide resolved
this.graph.on('blank:mousedown', this.onBlankLeftMouseDown, this)
}
if (this.options.eventTypes?.includes('mouseWheelDown')) {
this.graph.on('blank:mousedown', this.onBlankMouseWheelDown, this)
}
this.graph.on('blank:click', this.onBlankClick, this)
this.graph.on('cell:mousemove', this.onCellMouseMove, this)
this.graph.on('cell:mouseup', this.onCellMouseUp, this)
this.selectionImpl.on('box:mousedown', this.onBoxMouseDown, this)
}

protected stopListening() {
this.graph.off('blank:mousedown', this.onBlankMouseDown, this)
if (this.options.eventTypes?.includes('leftMouseDown')) {
this.graph.off('blank:mousedown', this.onBlankLeftMouseDown, this)
}
if (this.options.eventTypes?.includes('mouseWheelDown')) {
this.graph.off('blank:mousedown', this.onBlankMouseWheelDown, this)
}
this.graph.off('blank:click', this.onBlankClick, this)
this.graph.off('cell:mousemove', this.onCellMouseMove, this)
this.graph.off('cell:mouseup', this.onCellMouseUp, this)
this.selectionImpl.off('box:mousedown', this.onBoxMouseDown, this)
}

protected onBlankLeftMouseDown(mouseDownEvent: EventArgs['blank:mousedown']) {
if (mouseDownEvent.e.button === 0) {
this.onBlankMouseDown(mouseDownEvent)
}
}

protected onBlankMouseWheelDown(
mouseDownEvent: EventArgs['blank:mousedown'],
) {
if (mouseDownEvent.e.button === 1) {
this.onBlankMouseDown(mouseDownEvent)
}
}

protected onBlankMouseDown({ e }: EventArgs['blank:mousedown']) {
const allowGraphPanning = this.graph.panning.allowPanning(e, true)
const scroller = this.graph.getPlugin<any>('scroller')
Expand Down Expand Up @@ -475,5 +499,6 @@ export namespace Selection {
selectEdgeOnMoved: false,
following: true,
content: null,
eventTypes: ['leftMouseDown', 'mouseWheelDown'],
}
}
5 changes: 5 additions & 0 deletions packages/x6-plugin-selection/src/selection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -950,6 +950,8 @@ export class SelectionImpl extends View<SelectionImpl.EventArgs> {
}

export namespace SelectionImpl {
type SelectionEventType = 'leftMouseDown' | 'mouseWheelDown'

export interface CommonOptions {
model?: Model
collection?: Collection
Expand Down Expand Up @@ -977,6 +979,9 @@ export namespace SelectionImpl {

// Whether to respond event on the selectionBox
pointerEvents?: 'none' | 'auto'

// with which mouse button the selection can be started
eventTypes?: SelectionEventType[]
}

export interface Options extends CommonOptions {
Expand Down
21 changes: 20 additions & 1 deletion packages/x6/src/graph/panning.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ export class PanningManager extends Base {
this.onRightMouseDown = this.onRightMouseDown.bind(this)
Dom.Event.on(this.graph.container, 'mousedown', this.onRightMouseDown)
}

if (eventTypes.includes('mouseWheelDown')) {
aMoonkin marked this conversation as resolved.
Show resolved Hide resolved
this.onMouseWheelDown = this.onMouseWheelDown.bind(this)
Dom.Event.on(this.graph.container, 'mousedown', this.onMouseWheelDown)
}

if (eventTypes.includes('mouseWheel')) {
this.mousewheelHandle = new Dom.MouseWheelHandle(
this.graph.container,
Expand All @@ -57,6 +63,9 @@ export class PanningManager extends Base {
if (eventTypes.includes('rightMouseDown')) {
Dom.Event.off(this.graph.container, 'mousedown', this.onRightMouseDown)
}
if (eventTypes.includes('mouseWheelDown')) {
Dom.Event.off(this.graph.container, 'mousedown', this.onMouseWheelDown)
}
if (eventTypes.includes('mouseWheel')) {
if (this.mousewheelHandle) {
this.mousewheelHandle.disable()
Expand Down Expand Up @@ -137,6 +146,12 @@ export class PanningManager extends Base {
}
}

protected onMouseWheelDown(e: Dom.MouseDownEvent) {
if (e.button === 1 && this.allowPanning(e, true)) {
this.startPanning(e)
}
}

protected allowMouseWheel(e: WheelEvent) {
return this.pannable && !e.ctrlKey
}
Expand Down Expand Up @@ -195,7 +210,11 @@ export class PanningManager extends Base {
}

export namespace PanningManager {
type EventType = 'leftMouseDown' | 'rightMouseDown' | 'mouseWheel'
type EventType =
| 'leftMouseDown'
aMoonkin marked this conversation as resolved.
Show resolved Hide resolved
| 'rightMouseDown'
| 'mouseWheel'
| 'mouseWheelDown'
export interface Options {
enabled?: boolean
modifiers?: string | ModifierKey[] | null
Expand Down
5 changes: 3 additions & 2 deletions sites/x6-sites/docs/api/graph/panning.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ const graph = new Graph({
interface Options {
enabled?: boolean
modifiers?: ModifierKey
eventTypes?: ('leftMouseDown' | 'rightMouseDown' | 'mouseWheel')[]
eventTypes?: ('leftMouseDown' | 'rightMouseDown' | 'mouseWheel', 'mouseWheelDown')[]
}
```

Expand Down Expand Up @@ -70,7 +70,8 @@ type ModifierKey = string | ('alt' | 'ctrl' | 'meta' | 'shift')[] | null

- `leftMouseDown`: 按下鼠标左键移动进行拖拽
- `rightMouseDown`: 按下鼠标右键移动进行拖拽
- `mouseWheel`: 使用鼠标滚轮拖拽
- `mouseWheel`: 使用鼠标滚轮滚动拖拽
- `mouseWheelDown`: 按下鼠标滚轮进行拖拽

## 方法

Expand Down
40 changes: 26 additions & 14 deletions sites/x6-sites/docs/tutorial/plugins/selection.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,20 +57,21 @@ graph.use(

## 配置

| 属性名 | 类型 | 默认值 | 必选 | 描述 |
|----------------------------|----------------|--------------------|------|------------------------------------------------------------------------------------------------------------------------------------------|
| className | string | - | | 附加样式名,用于定制样式 |
| multiple | boolean | `true` | | 是否启用点击多选,启用后按住 `ctrl` 或 `command` 键点击节点实现多选 |
| multipleSelectionModifiers | ModifierKey | `['ctrl', 'meta']` | | 用于设置上面点击多选配套的修饰键 |
| rubberband | boolean | `false` | | 是否启用框选节点功能 |
| modifiers | ModifierKey | - | | 用于设置上面框选配套的修饰键 |
| strict | boolean | `false` | | 选框是否需要完全包围节点时才选中节点 |
| movable | boolean | `true` | | 拖动选框时框选的节点是否一起移动 |
| content | string | - | | 设置附加显示的内容 |
| filter | Filter | - | | 节点过滤器 |
| showNodeSelectionBox | boolean | `false` | | 是否显示节点的选择框 |
| showEdgeSelectionBox | boolean | `false` | | 是否显示边的选择框 |
| pointerEvents | `node \| auto` | `auto` | | 如果打开 `showNodeSelectionBox` 时,会在节点上方盖一层元素,导致节点的事件无法响应,此时可以配置 `pointerEvents: none` 来解决,默认值是 `auto` |
| 属性名 | 类型 | 默认值 | 必选 | 描述 |
| -------------------------- | -------------------- | ------------------------------------- | ---- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| className | string | - | | 附加样式名,用于定制样式 |
| multiple | boolean | `true` | | 是否启用点击多选,启用后按住 `ctrl` 或 `command` 键点击节点实现多选 |
| multipleSelectionModifiers | ModifierKey | `['ctrl', 'meta']` | | 用于设置上面点击多选配套的修饰键 |
| rubberband | boolean | `false` | | 是否启用框选节点功能 |
| modifiers | ModifierKey | - | | 用于设置上面框选配套的修饰键 |
| strict | boolean | `false` | | 选框是否需要完全包围节点时才选中节点 |
| movable | boolean | `true` | | 拖动选框时框选的节点是否一起移动 |
| content | string | - | | 设置附加显示的内容 |
| filter | Filter | - | | 节点过滤器 |
| showNodeSelectionBox | boolean | `false` | | 是否显示节点的选择框 |
| showEdgeSelectionBox | boolean | `false` | | 是否显示边的选择框 |
| pointerEvents | `node \| auto` | `auto` | | 如果打开 `showNodeSelectionBox` 时,会在节点上方盖一层元素,导致节点的事件无法响应,此时可以配置 `pointerEvents: none` 来解决,默认值是 `auto` |
| eventTypes | SelectionEventType[] | `['leftMouseDown', 'mouseWheelDown']` | | 用于设置框选的触发事件类型 |

`Filter` 的类型定义如下:

Expand All @@ -96,6 +97,17 @@ X6 中修饰键包括 `alt`、`ctrl`、`meta`、`shift` 四个,设置修饰键
- `alt&ctrl` 表示同时按下 `alt` 和 `ctrl`。
- `alt|ctrl&shift` 表示同时按下 `alt` 和 `shift` 或者同时按下 `ctrl` 和 `shift`。

`SelectionEventType` 的类型定义如下:

```ts
type SelectionEventType = 'leftMouseDown' | 'mouseWheelDown';
```
触发框选的交互方式。支持2种形式或者他们之间的组合:

- `leftMouseDown`: 按下鼠标左键移动进行拖拽
- `mouseWheelDown`: 按下鼠标滚轮进行拖拽


## API

### graph.select(...)
Expand Down
Loading