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(cdk:drag-drop): add useDraggable and CdkDraggable #939

Merged
merged 1 commit into from
Jun 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@
"less-plugin-clean-css": "^1.5.1",
"less-plugin-npm-import": "^2.1.0",
"lint-staged": "^12.4.0",
"lodash": "^4.17.21",
"lodash-es": "^4.17.0",
"markdownlint-cli": "^0.30.0",
"marked": "^4.0.14",
"npm-run-all": "^4.1.5",
Expand Down
14 changes: 14 additions & 0 deletions packages/cdk/drag-drop/demo/Basic.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
order: 0
title:
zh: 基本使用
en: Basic usage
---

## zh

最简单的用法。

## en

The simplest usage.
36 changes: 36 additions & 0 deletions packages/cdk/drag-drop/demo/Basic.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<template>
<div ref="elementRef" class="draggable-basic" :style="`left:${position.left}px;top:${position.top}px;`">
Draggable dom!
</div>
<br />
<br />
<br />
<CdkDraggable class="draggable-basic" @start="onStart" @move="onMove" @end="onEnd"> CdkDraggable! </CdkDraggable>
<br />
</template>

<script setup lang="ts">
import { ref } from 'vue'

import { DraggableEvent, useDraggable } from '@idux/cdk/drag-drop'

const elementRef = ref()

const onStart: DraggableEvent = (position, evt) => console.log('onStart', position, evt)
const onMove: DraggableEvent = (position, evt) => console.log('onMove', position, evt)
const onEnd: DraggableEvent = (position, evt) => console.log('onEnd', position, evt)

const { position } = useDraggable(elementRef, { onStart, onMove, onEnd })
</script>

<style scoped lang="less">
.draggable-basic {
position: fixed;
z-index: 10;
cursor: move;
user-select: none;
padding: 8px;
border: 1px solid #c3c3c3;
box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.2);
}
</style>
3 changes: 3 additions & 0 deletions packages/cdk/drag-drop/docs/Design.en.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
## Description

## Usage scenarios
3 changes: 3 additions & 0 deletions packages/cdk/drag-drop/docs/Design.zh.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
## 组件定义

## 使用场景
29 changes: 29 additions & 0 deletions packages/cdk/drag-drop/docs/Index.en.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
---
category: cdk
type:
order: 0
title: DragDrop
subtitle:
---

## API

### IxDragDrop

#### DragDropProps

| Name | Description | Type | Default | Global Config | Remark |
| --- | --- | --- | --- | --- | --- |
| - | - | - | - | ✅ | - |

#### DragDropSlots

| Name | Description | Parameter Type | Remark |
| --- | --- | --- | --- |
| - | - | - | - |

#### DragDropMethods

| Name | Description | Parameter Type | Remark |
| --- | --- | --- | --- |
| - | - | - | - |
61 changes: 61 additions & 0 deletions packages/cdk/drag-drop/docs/Index.zh.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
---
category: cdk
type:
order: 0
title: DragDrop
subtitle: 拖拽
---

## API

### useDraggable

```ts
/**
* 使一个元素可以被拖拽
*
* @param target 目标元素,可以是一个 Ref 或 组件实例
* @param options 拖拽的配置
* @returns 返回一个用于停止监听的函数, 和当前的拖拽状态
*/
export function useDraggable(
target: MaybeElementRef,
options: DraggableOptions,
): {
position: ComputedRef<DragPosition>
isDragging: ComputedRef<boolean>
stop: () => void
}

export interface DragPosition {
// 元素当前的 left 位置
left: number
// 元素当前的 top 位置
top: number
// 元素被拖拽后水平偏移量
offsetX: number
// 元素被拖拽后垂直偏移量
offsetY: number
}

export type DraggableEvent = (position: DragPosition, evt: PointerEvent) => void

export interface DraggableOptions {
onStart?: DraggableEvent
onMove?: DraggableEvent
onEnd?: DraggableEvent
}

```

### CdkDraggable

#### DraggableProps

| 名称 | 说明 | 类型 | 默认值 | 全局配置 | 备注 |
| --- | --- | --- | --- | --- | --- |
| `disabled` | 是否禁用 | `boolean` | - | - | - |
| `is` | 拖拽的元素或者组件 | `string | Component` | `'div'` | - | - |
| `onStart` | 拖拽开始事件 | - | `DraggableEvent` | - | - |
| `onMove` | 拖拽过程中事件 | - | `DraggableEvent` | - | - |
| `onEnd` | 拖拽结束事件 | - | `DraggableEvent` | - | - |
22 changes: 22 additions & 0 deletions packages/cdk/drag-drop/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/**
* @license
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://github.com/IDuxFE/idux/blob/main/LICENSE
*/

export * from './src/draggable/useDraggable'

import type { DraggableComponent } from './src/draggable/types'

import Draggable from './src/draggable/Draggable'

const CdkDraggable = Draggable as unknown as DraggableComponent

export { CdkDraggable }

export type {
DraggableInstance,
DraggableComponent,
DraggablePublicProps as DraggableProps,
} from './src/draggable/types'
64 changes: 64 additions & 0 deletions packages/cdk/drag-drop/src/draggable/Draggable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/**
* @license
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://github.com/IDuxFE/idux/blob/main/LICENSE
*/

import { defineComponent, h, normalizeClass, onBeforeUnmount, ref, shallowRef, watch } from 'vue'

import { callEmit } from '@idux/cdk/utils'

import { draggableProps } from './types'
import { type DragPosition, useDraggable } from './useDraggable'

export default defineComponent({
name: 'CdkDraggable',
props: draggableProps,
setup(props, { slots }) {
const elementRef = ref()
const onStart = (position: DragPosition, evt: PointerEvent) => callEmit(props.onStart, position, evt)
const onMove = (position: DragPosition, evt: PointerEvent) => callEmit(props.onMove, position, evt)
const onEnd = (position: DragPosition, evt: PointerEvent) => callEmit(props.onEnd, position, evt)

const draggableResult = shallowRef()

const cleanup = () => {
if (draggableResult.value) {
draggableResult.value.stop()
draggableResult.value = undefined
}
}

watch(
[() => props.disabled],
([disabled]) => {
cleanup()
if (!disabled) {
draggableResult.value = useDraggable(elementRef, { onStart, onMove, onEnd })
}
},
{
immediate: true,
flush: 'post',
},
)

onBeforeUnmount(cleanup)

return () => {
if (!draggableResult.value) {
return null
}
const tag = props.is as string
const { isDragging, position } = draggableResult.value
const classes = normalizeClass({
'cdk-draggable': true,
'cdk-dragging': isDragging.value,
})
const { left, top } = position.value
const style = `left:${left}px;top:${top}px;`
return h(tag, { ref: elementRef, class: classes, style }, slots)
}
},
})
27 changes: 27 additions & 0 deletions packages/cdk/drag-drop/src/draggable/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/**
* @license
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://github.com/IDuxFE/idux/blob/main/LICENSE
*/

/* eslint-disable @typescript-eslint/no-explicit-any */

import type { DraggableEvent } from './useDraggable'
import type { ExtractInnerPropTypes, ExtractPublicPropTypes, MaybeArray } from '@idux/cdk/utils'
import type { Component, DefineComponent, HTMLAttributes, PropType } from 'vue'

export const draggableProps = {
disabled: { type: Boolean, default: false },
is: { type: [String, Object] as PropType<string | Component>, default: 'div' },
onStart: [Function, Array] as PropType<MaybeArray<DraggableEvent>>,
onMove: [Function, Array] as PropType<MaybeArray<DraggableEvent>>,
onEnd: [Function, Array] as PropType<MaybeArray<DraggableEvent>>,
} as const

export type DraggableProps = ExtractInnerPropTypes<typeof draggableProps>
export type DraggablePublicProps = ExtractPublicPropTypes<typeof draggableProps>
export type DraggableComponent = DefineComponent<
Omit<HTMLAttributes, keyof DraggablePublicProps> & DraggablePublicProps
>
export type DraggableInstance = InstanceType<DefineComponent<DraggableProps>>
99 changes: 99 additions & 0 deletions packages/cdk/drag-drop/src/draggable/useDraggable.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/**
* @license
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://github.com/IDuxFE/idux/blob/main/LICENSE
*/

import { type ComputedRef, computed, ref, toRaw } from 'vue'

import { type MaybeElementRef, convertElement, useEventListener } from '@idux/cdk/utils'

export interface DragPosition {
left: number
top: number
offsetX: number
offsetY: number
}

export type DraggableEvent = (position: DragPosition, evt: PointerEvent) => void

export interface DraggableOptions {
onStart?: DraggableEvent
onMove?: DraggableEvent
onEnd?: DraggableEvent
}

export function useDraggable(
target: MaybeElementRef,
options: DraggableOptions,
): {
position: ComputedRef<DragPosition>
isDragging: ComputedRef<boolean>
stop: () => void
} {
const startPosition = ref<{ left: number; top: number; pageX: number; pageY: number }>()
const currPosition = ref({} as DragPosition)

const onStart = (evt: PointerEvent) => {
const element = convertElement(target)
if (evt.target !== element) {
return
}

const { pageX, pageY } = evt
const { left, top } = element!.getBoundingClientRect()

startPosition.value = { left, top, pageX, pageY }

const position = { left, top, offsetX: 0, offsetY: 0 }

options.onStart?.(position, evt)
}

const onMove = (evt: PointerEvent) => {
if (!startPosition.value) {
return
}

const { left, top, pageX, pageY } = startPosition.value
const offsetX = evt.pageX - pageX
const offsetY = evt.pageY - pageY

const position = {
left: evt.pageX - pageX + left,
top: evt.pageY - pageY + top,
offsetX,
offsetY,
}
currPosition.value = position

options.onMove?.(position, evt)
}

const onEnd = (evt: PointerEvent) => {
if (!startPosition.value) {
return
}

startPosition.value = undefined

options.onEnd?.(toRaw(currPosition.value), evt)
}

const { stop: stopPointerdown } = useEventListener(target, 'pointerdown', onStart, true)
const { stop: stopPointermove } = useEventListener(document, 'pointermove', onMove, true)
const { stop: stopPointerup } = useEventListener(document, 'pointerup', onEnd, true)

const stop = () => {
stopPointerdown()
stopPointermove()
stopPointerup()
}

return {
isDragging: computed(() => !!startPosition.value),
position: computed(() => currPosition.value),
stop,
}
}
3 changes: 2 additions & 1 deletion packages/cdk/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@
import type { App, Directive } from 'vue'

import { clickOutside } from '@idux/cdk/click-outside'
import { CdkDraggable } from '@idux/cdk/drag-drop'
import { CdkPortal } from '@idux/cdk/portal'
import { CdkResizeObserver } from '@idux/cdk/resize'
import { CdkVirtualScroll } from '@idux/cdk/scroll'
import { version } from '@idux/cdk/version'

const components = [CdkPortal, CdkResizeObserver, CdkVirtualScroll]
const components = [CdkPortal, CdkDraggable, CdkResizeObserver, CdkVirtualScroll]

const directives: Record<string, Directive> = {
clickOutside,
Expand Down
Loading