-
Notifications
You must be signed in to change notification settings - Fork 71
feat(drag-n-drop): add item dragging into grid #111
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
Draft
skutam
wants to merge
18
commits into
katoid:main
Choose a base branch
from
skutam:drag-and-drop
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from 2 commits
Commits
Show all changes
18 commits
Select commit
Hold shift + click to select a range
2fdba29
refactor(grid): use pointer events instead of mouse and touch events …
llorenspujol 48a174c
feat(drag-n-drop): add item dragging into grid
7834a91
feat(drag-n-drop): add item dragging into grid
3fad45a
feat: use pointer events instead of mouse/touch one's
llorenspujol 9f527e4
feat: added 'drag-from-outside' example
llorenspujol c92c282
feat(drag-n-drop): add item dragging into grid
950ee44
feat(drag-n-drop): resize bug fix
skutam 53323cb
feat(drag-n-drop): wip, TODO Drag outside weird snap
skutam 3d38c81
feat(drag-n-drop): snap bug fixed
skutam ec9693c
feat(drag-n-drop): fixed drag-from-outside example, plus some bugs wh…
skutam 505c326
feat(drag-n-drop): fixed drag between grids, WIP
skutam 06d222a
feat(drag-n-drop): fixed drag between grids
skutam 035e0b4
fix: resize drop outside grid leaves resize mid state
skutam 0343ca7
fix: function ktdGridCompact does not copy data attribute
skutam 4038890
fix: ktd-drag does not propagate width and height
skutam 74a6e14
fix: drag from outside does not resize height
skutam c9b06fe
fix: drag disable fix
skutam 54cf431
fix: drag/resize null problem fix
skutam File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
212 changes: 212 additions & 0 deletions
212
projects/angular-grid-layout/src/lib/directives/ktd-drag.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,212 @@ | ||
| import { | ||
| AfterContentInit, ContentChild, ContentChildren, | ||
| Directive, ElementRef, | ||
| InjectionToken, Input, OnDestroy, Output, QueryList | ||
| } from '@angular/core'; | ||
| import {coerceBooleanProperty} from "../coercion/boolean-property"; | ||
| import {BehaviorSubject, Observable, Observer, Subscription} from "rxjs"; | ||
| import {coerceNumberProperty} from "../coercion/number-property"; | ||
| import {KtdRegistryService} from "../ktd-registry.service"; | ||
| import {KTD_GRID_DRAG_HANDLE, KtdGridDragHandle} from "./drag-handle"; | ||
| import {DragRef} from "../utils/drag-ref"; | ||
| import {KTD_GRID_ITEM_PLACEHOLDER, KtdGridItemPlaceholder} from "./placeholder"; | ||
| import {ktdPointerClientX, ktdPointerClientY} from "../utils/pointer.utils"; | ||
| import {takeUntil} from "rxjs/operators"; | ||
|
|
||
|
|
||
| export const KTD_DRAG = new InjectionToken<KtdDrag<any>>('KtdDrag'); | ||
|
|
||
| @Directive({ | ||
| selector: '[ktdDrag]', | ||
| host: { | ||
| '[class.ktd-draggable]': '_dragHandles.length === 0 && draggable', | ||
| '[class.ktd-dragging]': '_dragRef.isDragging', | ||
| '[class.ktd-drag-disabled]': 'disabled', | ||
| }, | ||
| providers: [{provide: KTD_DRAG, useExisting: KtdDrag}] | ||
| }) | ||
| export class KtdDrag<T> implements AfterContentInit, OnDestroy { | ||
| /** Elements that can be used to drag the draggable item. */ | ||
| @ContentChildren(KTD_GRID_DRAG_HANDLE, {descendants: true}) _dragHandles: QueryList<KtdGridDragHandle>; | ||
|
|
||
| /** Template ref for placeholder */ | ||
| @ContentChild(KTD_GRID_ITEM_PLACEHOLDER) placeholder: KtdGridItemPlaceholder; | ||
|
|
||
| @Input() | ||
| get disabled(): boolean { | ||
| return this._disabled; | ||
| } | ||
| set disabled(value: boolean) { | ||
| this._disabled = coerceBooleanProperty(value); | ||
| this._dragRef.draggable = !this._disabled; | ||
| } | ||
| private _disabled: boolean = false; | ||
|
|
||
| /** Minimum amount of pixels that the user should move before it starts the drag sequence. */ | ||
| @Input() | ||
| get dragStartThreshold(): number { | ||
| return this._dragStartThreshold; | ||
| } | ||
| set dragStartThreshold(val: number) { | ||
| this._dragStartThreshold = coerceNumberProperty(val); | ||
| } | ||
| private _dragStartThreshold: number = 0; | ||
|
|
||
| /** Whether the item is draggable or not. Defaults to true. Does not affect manual dragging using the startDragManually method. */ | ||
| @Input() | ||
| get draggable(): boolean { | ||
| return this._draggable; | ||
| } | ||
| set draggable(val: boolean) { | ||
| this._draggable = coerceBooleanProperty(val); | ||
| this._dragRef.draggable = this._draggable; | ||
| this._draggable$.next(this._draggable); | ||
| } | ||
| private _draggable: boolean = true; | ||
| private _draggable$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(this._draggable); | ||
|
|
||
| @Input() | ||
| get id(): string { | ||
| return this._dragRef.id; | ||
| } | ||
| set id(val: string) { | ||
| this._dragRef.id = val; | ||
| } | ||
|
|
||
| /** | ||
| * Width of the draggable item, in cols. When set to 0 we will use the width of grid. | ||
| */ | ||
| @Input() | ||
| get width(): number { | ||
| return this._dragRef.width; | ||
| } | ||
| set width(val: number) { | ||
| this._dragRef.width = coerceNumberProperty(val); | ||
| } | ||
|
|
||
| /** | ||
| * Height of the draggable item, in cols. When set to 0 we will use the height of grid. | ||
| */ | ||
| @Input() | ||
| get height(): number { | ||
| return this._dragRef.height; | ||
| } | ||
| set height(val: number) { | ||
| this._dragRef.height = coerceNumberProperty(val); | ||
| } | ||
|
|
||
| /** | ||
| * TODO: Add support for custom drag data. | ||
| */ | ||
| @Input('ktdDragData') data: T; | ||
|
|
||
| @Output('dragStart') | ||
| readonly dragStart: Observable<{source: DragRef<T>, event: MouseEvent | TouchEvent}> = new Observable( | ||
| (observer: Observer<{source: DragRef<T>, event: MouseEvent | TouchEvent}>) => { | ||
| const subscription = this._dragRef.dragStart$ | ||
| .subscribe(observer); | ||
|
|
||
| return () => { | ||
| subscription.unsubscribe(); | ||
| }; | ||
| }, | ||
| ); | ||
|
|
||
| @Output('dragMove') | ||
| readonly dragMove: Observable<{source: DragRef<T>, event: MouseEvent | TouchEvent}> = new Observable( | ||
| (observer: Observer<{source: DragRef<T>, event: MouseEvent | TouchEvent}>) => { | ||
| const subscription = this._dragRef.dragMove$ | ||
| .subscribe(observer); | ||
|
|
||
| return () => { | ||
| subscription.unsubscribe(); | ||
| }; | ||
| }, | ||
| ); | ||
|
|
||
| @Output('dragEnd') | ||
| readonly dragEnd: Observable<{source: DragRef<T>, event: MouseEvent | TouchEvent}> = new Observable( | ||
| (observer: Observer<{source: DragRef<T>, event: MouseEvent | TouchEvent}>) => { | ||
| const subscription = this._dragRef.dragEnd$ | ||
| .subscribe(observer); | ||
|
|
||
| return () => { | ||
| subscription.unsubscribe(); | ||
| }; | ||
| }, | ||
| ); | ||
|
|
||
| public _dragRef: DragRef<T>; | ||
| private subscriptions: Subscription[] = []; | ||
| private element: HTMLElement; | ||
|
|
||
| constructor( | ||
| /** Element that the draggable is attached to. */ | ||
| public elementRef: ElementRef, | ||
| private registryService: KtdRegistryService, | ||
| ) { | ||
| this._dragRef = this.registryService.createKtgDrag(this.elementRef, this, this.data); | ||
| } | ||
|
|
||
| ngAfterContentInit(): void { | ||
| this.element = this.elementRef.nativeElement as HTMLElement; | ||
| this.registryService.registerKtgDragItem(this); | ||
| this.initDrag(); | ||
| } | ||
|
|
||
| ngOnDestroy(): void { | ||
| this.registryService.unregisterKtgDragItem(this); | ||
| this.registryService.destroyKtgDrag(this._dragRef); | ||
| this.subscriptions.forEach(subscription => subscription.unsubscribe()); | ||
| } | ||
|
|
||
| /** | ||
| * Initialize the drag of ktd-drag element, placeholder dragging is handled by ktd-grid. | ||
| * The element will be freely draggable, when drag ends it will snap back to its initial place. | ||
| */ | ||
| private initDrag() { | ||
| this._dragRef.dragStartThreshold = this.dragStartThreshold; | ||
| this._dragRef.placeholder = this.placeholder; | ||
| this._dragRef.draggable = this.draggable; | ||
| this._dragRef.dragHandles = this._dragHandles.toArray(); | ||
|
|
||
| const handlesSub$ = this._dragHandles.changes.subscribe(() => { | ||
| console.log(this._dragHandles.toArray()); | ||
| this._dragRef.dragHandles = this._dragHandles.toArray(); | ||
| }); | ||
|
|
||
| const dragStart$ = this.dragStart.subscribe(({event}) => { | ||
| let currentX = 0, | ||
| currentY = 0; | ||
|
|
||
| const initialX = ktdPointerClientX(event) - currentX; | ||
| const initialY = ktdPointerClientY(event) - currentY; | ||
|
|
||
| this.dragMove.pipe( | ||
| takeUntil(this.dragEnd), | ||
| ).subscribe(({event}) => { | ||
| event.preventDefault(); | ||
|
|
||
| // Calculate the new cursor position: | ||
| currentX = ktdPointerClientX(event) - initialX; | ||
| currentY = ktdPointerClientY(event) - initialY; | ||
|
|
||
| /** | ||
| * TODO: Add support handling scroll offset | ||
| * | ||
| * Possible solution would be to add for each scrollParent element observable that emits scroll offset, | ||
| * and use the offset when we are on top of the scrollParent. | ||
| * Still dont know how we would handle nested scrollParents, generated by nested grids. | ||
| */ | ||
|
|
||
| this.element.style.transform = `translate3d(${currentX}px, ${currentY}px, 0)`; | ||
| }); | ||
| }); | ||
|
|
||
| const dragEnd$ = this.dragEnd.subscribe(() => { | ||
| this.element.style.transform = 'none'; | ||
| }); | ||
|
|
||
| this.subscriptions.push(handlesSub$, dragStart$, dragEnd$); | ||
| } | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Currently, ktdDrag manages its drag movement, whereas the drag movement of the ktd-grid-item is controlled by the ktd-grid. This creates a bit of inconsistency. I believe that the approach you're taking here is correct, so we should enable the ktd-grid-item to handle its drag movement as well.
This will also open the door for drag-and-drop functionality between grids in the near future.