From 560a5a391ae14ce26917efc0d5dce0ba4b04f3bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claud=C3=A9ric=20Demers?= Date: Mon, 31 Jul 2023 17:14:52 -0400 Subject: [PATCH] Latest --- .../stories/Droppable/DroppableExample.tsx | 20 ++----- .../components/Dropzone/Dropzone.module.css | 55 +++++++++++++++++++ .../stories/components/Dropzone/Dropzone.tsx | 33 +++++++++++ .../docs/stories/components/Dropzone/index.ts | 1 + apps/docs/stories/components/index.ts | 1 + apps/docs/stories/icons/DroppableIcon.tsx | 18 ++++++ apps/docs/stories/icons/index.ts | 1 + .../dom-utilities/src/scroll/shouldScroll.ts | 21 +++++-- .../dom/src/plugins/scrolling/AutoScroller.ts | 2 - .../dom/src/plugins/scrolling/Scroller.ts | 40 +++++++++++--- 10 files changed, 163 insertions(+), 29 deletions(-) create mode 100644 apps/docs/stories/components/Dropzone/Dropzone.module.css create mode 100644 apps/docs/stories/components/Dropzone/Dropzone.tsx create mode 100644 apps/docs/stories/components/Dropzone/index.ts create mode 100644 apps/docs/stories/icons/DroppableIcon.tsx diff --git a/apps/docs/stories/Droppable/DroppableExample.tsx b/apps/docs/stories/Droppable/DroppableExample.tsx index f0e5a26b..69077889 100644 --- a/apps/docs/stories/Droppable/DroppableExample.tsx +++ b/apps/docs/stories/Droppable/DroppableExample.tsx @@ -4,7 +4,7 @@ import type {UniqueIdentifier} from '@dnd-kit/types'; import {DndContext, useDraggable, useDroppable} from '@dnd-kit/react'; import {closestCenter, CollisionDetector} from '@dnd-kit/collision'; -import {Button} from '../components'; +import {Button, Dropzone} from '../components'; import {DraggableIcon} from '../icons'; const items = [ @@ -90,20 +90,8 @@ function Droppable({ const {ref, isOver} = useDroppable({id, accept, collisionDetector}); return ( -
-
Container: {id}
- -
{children}
-
+ + {children} + ); } diff --git a/apps/docs/stories/components/Dropzone/Dropzone.module.css b/apps/docs/stories/components/Dropzone/Dropzone.module.css new file mode 100644 index 00000000..a03e5133 --- /dev/null +++ b/apps/docs/stories/components/Dropzone/Dropzone.module.css @@ -0,0 +1,55 @@ +.Dropzone { + display: flex; + align-items: flex-start; + justify-content: center; + position: relative; + padding-top: 80px; + text-align: center; + border-radius: 10px; + width: 340px; + height: 340px; + box-sizing: border-box; + background-color: #fff; + box-shadow: inset rgba(201, 211, 219, 0.5) 0 0 0 2px, + rgba(255, 255, 255, 0) 0 0 0 1px, rgba(201, 211, 219, 0.25) 20px 14px 24px; + transition: box-shadow 250ms ease; + + > svg { + position: absolute; + left: 50%; + top: 50%; + width: 200px; + transform: translate3d(-50%, -50%, 0); + opacity: 0.8; + transition: opacity 300ms ease, transform 200ms ease; + user-select: none; + pointer-events: none; + } +} + + +.lift { + > svg { + opacity: 0.8; + } +} + +.highlight { + box-shadow: inset #1eb99d 0 0 0 3px, rgba(201, 211, 219, 0.5) 20px 14px 24px; + + > svg { + opacity: 1; + } + + .dropped { + box-shadow: inset rgba(201, 211, 219, 0.7) 0 0 0 3px, + rgba(201, 211, 219, 0.5) 20px 14px 24px; + } +} + +.dropped { + > svg { + opacity: 0.2; + transform: translate3d(-50%, 100%, 0) scale(0.8); + } +} diff --git a/apps/docs/stories/components/Dropzone/Dropzone.tsx b/apps/docs/stories/components/Dropzone/Dropzone.tsx new file mode 100644 index 00000000..d9ea7d0b --- /dev/null +++ b/apps/docs/stories/components/Dropzone/Dropzone.tsx @@ -0,0 +1,33 @@ +import React, {forwardRef} from 'react'; + +import {classNames} from '../../utilities'; +import {DroppableIcon} from '../../icons'; + +import styles from './Dropzone.module.css'; + +interface Props { + children: React.ReactNode; + highlight?: boolean; + lift?: boolean; +} + +export const Dropzone = forwardRef(function Dropzone( + {children, lift, highlight}, + ref +) { + return ( +
+ {children} + +
+ ); +}); diff --git a/apps/docs/stories/components/Dropzone/index.ts b/apps/docs/stories/components/Dropzone/index.ts new file mode 100644 index 00000000..ed47facd --- /dev/null +++ b/apps/docs/stories/components/Dropzone/index.ts @@ -0,0 +1 @@ +export {Dropzone} from './Dropzone'; diff --git a/apps/docs/stories/components/index.ts b/apps/docs/stories/components/index.ts index 2cdeff8b..bdd2a1cf 100644 --- a/apps/docs/stories/components/index.ts +++ b/apps/docs/stories/components/index.ts @@ -1 +1,2 @@ export {Button} from './Button'; +export {Dropzone} from './Dropzone'; diff --git a/apps/docs/stories/icons/DroppableIcon.tsx b/apps/docs/stories/icons/DroppableIcon.tsx new file mode 100644 index 00000000..076bb68c --- /dev/null +++ b/apps/docs/stories/icons/DroppableIcon.tsx @@ -0,0 +1,18 @@ +import React from 'react'; + +export const DroppableIcon = () => ( + + + + + +); diff --git a/apps/docs/stories/icons/index.ts b/apps/docs/stories/icons/index.ts index a02c3a51..c4b3b38b 100644 --- a/apps/docs/stories/icons/index.ts +++ b/apps/docs/stories/icons/index.ts @@ -1 +1,2 @@ export {DraggableIcon} from './DraggableIcon'; +export {DroppableIcon} from './DroppableIcon'; diff --git a/packages/dom-utilities/src/scroll/shouldScroll.ts b/packages/dom-utilities/src/scroll/shouldScroll.ts index bcdb8e11..a8b1b413 100644 --- a/packages/dom-utilities/src/scroll/shouldScroll.ts +++ b/packages/dom-utilities/src/scroll/shouldScroll.ts @@ -13,9 +13,15 @@ const defaultThreshold: Record = { y: 0.2, }; +interface ScrollIntent { + x: ScrollDirection; + y: ScrollDirection; +} + export function shouldScroll( scrollableElement: Element, coordinates: Coordinates, + intent?: ScrollIntent, acceleration = 10, thresholdPercentage = defaultThreshold ) { @@ -40,7 +46,11 @@ export function shouldScroll( width: scrollContainerRect.width * thresholdPercentage.x, }; - if (!isTop && coordinates.y <= scrollContainerRect.top + threshold.height) { + if ( + !isTop && + coordinates.y <= scrollContainerRect.top + threshold.height && + intent?.y !== ScrollDirection.Forward + ) { // Scroll Up direction.y = ScrollDirection.Reverse; speed.y = @@ -51,7 +61,8 @@ export function shouldScroll( ); } else if ( !isBottom && - coordinates.y >= scrollContainerRect.bottom - threshold.height + coordinates.y >= scrollContainerRect.bottom - threshold.height && + intent?.y !== ScrollDirection.Reverse ) { // Scroll Down direction.y = ScrollDirection.Forward; @@ -65,7 +76,8 @@ export function shouldScroll( if ( !isRight && - coordinates.x >= scrollContainerRect.right - threshold.width + coordinates.x >= scrollContainerRect.right - threshold.width && + intent?.x !== ScrollDirection.Reverse ) { // Scroll Right direction.x = ScrollDirection.Forward; @@ -77,7 +89,8 @@ export function shouldScroll( ); } else if ( !isLeft && - coordinates.x <= scrollContainerRect.left + threshold.width + coordinates.x <= scrollContainerRect.left + threshold.width && + intent?.x !== ScrollDirection.Forward ) { // Scroll Left direction.x = ScrollDirection.Reverse; diff --git a/packages/dom/src/plugins/scrolling/AutoScroller.ts b/packages/dom/src/plugins/scrolling/AutoScroller.ts index c4afb80f..b86ef89e 100644 --- a/packages/dom/src/plugins/scrolling/AutoScroller.ts +++ b/packages/dom/src/plugins/scrolling/AutoScroller.ts @@ -18,8 +18,6 @@ export class AutoScroller extends Plugin { constructor(manager: DragDropManager, _options?: Options) { super(manager); - const {dragOperation} = manager; - let interval: NodeJS.Timer | null = null; const scrollIntentTracker = ScrollIntentTracker(manager); diff --git a/packages/dom/src/plugins/scrolling/Scroller.ts b/packages/dom/src/plugins/scrolling/Scroller.ts index 60086473..e7c62c88 100644 --- a/packages/dom/src/plugins/scrolling/Scroller.ts +++ b/packages/dom/src/plugins/scrolling/Scroller.ts @@ -3,6 +3,7 @@ import { canScroll, shouldScroll, getScrollableAncestors, + ScrollDirection, } from '@dnd-kit/dom-utilities'; import {computed} from '@dnd-kit/state'; import {isEqual} from '@dnd-kit/utilities'; @@ -53,21 +54,34 @@ export class Scroller extends Plugin { for (const scrollableElement of elements) { const elementCanScroll = canScroll(scrollableElement, {x, y}); - if (elementCanScroll.x || elementCanScroll.y) { + if ((x && elementCanScroll.x) || (y && elementCanScroll.y)) { const shouldScrollElement = shouldScroll( scrollableElement, - currentPosition + currentPosition, + { + x: getScrollIntent(x), + y: getScrollIntent(y), + } ); if ( shouldScrollElement.direction.x || shouldScrollElement.direction.y ) { - const scrollLeft = x * shouldScrollElement.speed.x; - const scrollTop = y * shouldScrollElement.speed.y; - - scrollableElement.scrollBy(scrollLeft, scrollTop); - return true; + const scrollLeftBy = x * shouldScrollElement.speed.x; + const scrollTopBy = y * shouldScrollElement.speed.y; + + if (scrollLeftBy || scrollTopBy) { + const {scrollTop, scrollLeft} = scrollableElement; + scrollableElement.scrollBy(scrollLeftBy, scrollTopBy); + + if ( + scrollTop !== scrollableElement.scrollTop || + scrollLeft !== scrollableElement.scrollLeft + ) { + return true; + } + } } } } @@ -76,3 +90,15 @@ export class Scroller extends Plugin { return false; } } + +function getScrollIntent(value: number) { + if (value > 0) { + return ScrollDirection.Forward; + } + + if (value < 0) { + return ScrollDirection.Reverse; + } + + return ScrollDirection.Idle; +}