Skip to content

Commit

Permalink
feat(cdk:drag-drop): add useDraggable and CdkDraggable (#939)
Browse files Browse the repository at this point in the history
  • Loading branch information
danranVm committed Jun 6, 2022
1 parent 201106d commit 7161d96
Show file tree
Hide file tree
Showing 25 changed files with 479 additions and 49 deletions.
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

0 comments on commit 7161d96

Please sign in to comment.