Skip to content

ListView: Draggable Rows #2593

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

Merged
merged 83 commits into from
Feb 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
83 commits
Select commit Hold shift + click to select a range
80e8863
initial setup
reidbarber Nov 11, 2021
fc59801
updating ListView types and improving defaults for drag state
LFDanLu Nov 13, 2021
27ad57f
adding ListView drag story and supporting individual row drag disabling
LFDanLu Nov 15, 2021
617145d
fixing ListViewItem outdated selection maanger
LFDanLu Nov 16, 2021
772ac53
fixing disabling individual rows in drag and drop
LFDanLu Nov 16, 2021
c08a0dc
add drag handle UI
reidbarber Nov 16, 2021
138247c
cleanup formatting
reidbarber Nov 16, 2021
0da64d9
fix list item padding
reidbarber Nov 16, 2021
bded88c
improve drag handle focus ring
reidbarber Nov 16, 2021
a62be4d
initial render preview styles
reidbarber Nov 17, 2021
80a689a
improve render preview styles
reidbarber Nov 17, 2021
8d7a067
fix render preview border radius
reidbarber Nov 17, 2021
92f7a95
fix logic to show handle
reidbarber Nov 19, 2021
c584521
add support for custom dragIcon
reidbarber Nov 23, 2021
1c117fe
fix lint
reidbarber Nov 23, 2021
d1044cb
only drop draggable items if multiple selected
reidbarber Nov 23, 2021
9d1e854
fix drag handle styles
reidbarber Nov 23, 2021
76907f4
add border radius to drag handle
reidbarber Nov 23, 2021
008cfc3
add isDraggable to item type
reidbarber Nov 23, 2021
a45f340
skip failing draggable tests for now
reidbarber Nov 24, 2021
2f630e1
Merge branch 'main' into ListView-DnD
reidbarber Nov 24, 2021
d63382a
prevent selection when clicking draggable row
reidbarber Nov 30, 2021
fef5ffe
updates from design review
LFDanLu Dec 2, 2021
8ac5a97
Merge branch 'main' of github.com:adobe/react-spectrum into ListView-DnD
LFDanLu Dec 2, 2021
54ba449
removing skips from useDraggableCollection tests
LFDanLu Dec 2, 2021
6b69aad
adding tests for dnd listview
LFDanLu Dec 2, 2021
02cc4c1
removing stray comment
LFDanLu Dec 2, 2021
c156684
adding test to check for drag preview
LFDanLu Dec 2, 2021
4057928
improve default render preview
reidbarber Dec 2, 2021
72e1d9b
Merge branch 'ListView-DnD' of github.com:adobe/react-spectrum into L…
reidbarber Dec 2, 2021
586538e
fixing lint and tests
LFDanLu Dec 2, 2021
7b45217
fix render preview padding
reidbarber Dec 2, 2021
ed652c2
Merge branch 'ListView-DnD' of github.com:adobe/react-spectrum into L…
LFDanLu Dec 2, 2021
8126c22
adding aria-label to render preview checkbox
LFDanLu Dec 3, 2021
5f3c759
Merge branch 'main' into ListView-DnD
reidbarber Dec 22, 2021
185e272
keep incoming onAction change
reidbarber Dec 22, 2021
973ca32
Merge branch 'main' into ListView-DnD
devongovett Dec 23, 2021
484246a
Merge branch 'ListView-DnD' of github.com:adobe/react-spectrum into L…
reidbarber Jan 4, 2022
7122c7b
tentative fix to stop row selection on drag start
LFDanLu Jan 7, 2022
59ec5a5
Merge branch 'main' of github.com:adobe/react-spectrum into ListView-DnD
LFDanLu Jan 7, 2022
0abadce
fixing lint and adding tests for checkbox/menu clicks
LFDanLu Jan 7, 2022
a3b416e
fixing usePress tests
LFDanLu Jan 7, 2022
c3f3995
adding allowsDifferentPressOrigin to useSelectableItem
LFDanLu Jan 7, 2022
7e28276
fixing lint
LFDanLu Jan 7, 2022
7e3abda
Merge branch 'main' into ListView-DnD
reidbarber Jan 14, 2022
6eaf932
Merge branch 'main' into ListView-DnD
reidbarber Jan 31, 2022
e4a2113
add useDragHooks
reidbarber Feb 3, 2022
41e1130
fix test setup
reidbarber Feb 3, 2022
905b0a3
Merge branch 'main' into ListView-DnD
reidbarber Feb 7, 2022
2e38865
Merge branch 'main' of github.com:adobe/react-spectrum into ListView-DnD
LFDanLu Feb 17, 2022
641384c
adding code comment and small cleanup
LFDanLu Feb 17, 2022
cb24f67
fix warning for changes hooks provided
reidbarber Feb 17, 2022
f96277d
add default itemAllowsDragging
reidbarber Feb 18, 2022
81efd9b
add default getItems
reidbarber Feb 18, 2022
a774328
remove extra checkbox hit area for drag handle
reidbarber Feb 18, 2022
052515f
fix lint
reidbarber Feb 18, 2022
97dc2a8
addressing some review comments
LFDanLu Feb 24, 2022
6ec8e77
updating tests and hiding drag handle via display: none
LFDanLu Feb 24, 2022
951fe6e
making drag handle use visually hidden styles for screen readers
LFDanLu Feb 24, 2022
7d16e8a
fixing weird safari drag handle positioning
LFDanLu Feb 24, 2022
822dd2a
removing fallback
LFDanLu Feb 24, 2022
a9a9a22
move defaults to useDraggableCollectionState
reidbarber Feb 25, 2022
a9ed083
Merge branch 'main' of github.com:adobe/react-spectrum into ListView-DnD
LFDanLu Feb 25, 2022
a69fb51
optimize drag handle svg
reidbarber Feb 25, 2022
1b23709
fix the changing hooks warning logic
reidbarber Feb 25, 2022
c36c3af
change itemAllowsDragging to allowsDraggingItem
reidbarber Feb 25, 2022
cecae00
add useDraggableItem to dragHooks
reidbarber Feb 25, 2022
e560660
make getItems required and remove default
reidbarber Feb 25, 2022
bcd0183
make getItems required on DragOptions
reidbarber Feb 25, 2022
ff9c6e7
fix getItems type required
reidbarber Feb 25, 2022
9dcd709
add @react-spectrum/dnd package with useDragHooks
reidbarber Feb 25, 2022
18bce37
fix import for draghooks
reidbarber Feb 25, 2022
2a91432
remove old comments
reidbarber Feb 25, 2022
8da0243
remove private: true from package.json
reidbarber Feb 25, 2022
706debd
fix circular dep
reidbarber Feb 25, 2022
4438f2b
fix react-aria/dnd dep in listview
reidbarber Feb 25, 2022
e9c7596
Revert "fix react-aria/dnd dep in listview"
reidbarber Feb 25, 2022
59d2b11
fix DragHooks type
reidbarber Feb 26, 2022
75967d2
Merge branch 'main' into ListView-DnD
reidbarber Feb 26, 2022
de099f2
Merge branch 'ListView-DnD' of github.com:adobe/react-spectrum into L…
reidbarber Feb 26, 2022
d36779c
making aria/stately dnd packages dev deps for list
LFDanLu Feb 26, 2022
ec9f91f
make getItems required
reidbarber Feb 28, 2022
99a6ff0
fix type of dragHooks props
reidbarber Feb 28, 2022
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 bin/imports.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const substrings = ['-', '+'];

module.exports = function (context) {
let processNode = (node) => {
if (!node.source) {
if (!node.source || node.importKind === 'type') {
return;
}

Expand Down
4 changes: 2 additions & 2 deletions packages/@react-aria/dnd/src/useDraggableItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ import intlMessages from '../intl/*.json';
import {useDrag} from './useDrag';
import {useMessageFormatter} from '@react-aria/i18n';

interface DraggableItemProps {
export interface DraggableItemProps {
key: Key
}

interface DraggableItemResult {
export interface DraggableItemResult {
dragProps: HTMLAttributes<HTMLElement>,
dragButtonProps: AriaButtonProps
}
Expand Down
2 changes: 1 addition & 1 deletion packages/@react-aria/dnd/stories/dnd.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ function Draggable() {
);
}

function Droppable({type, children, actionId = ''}: any) {
export function Droppable({type, children, actionId = ''}: any) {
let ref = React.useRef();
let {dropProps, isDropTarget} = useDrop({
ref,
Expand Down
12 changes: 6 additions & 6 deletions packages/@react-aria/interactions/src/usePress.ts
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@ export function usePress(props: PressHookProps): PressResult {

// Due to browser inconsistencies, especially on mobile browsers, we prevent
// default on pointer down and handle focusing the pressable element ourselves.
if (shouldPreventDefault(e.target as Element)) {
if (shouldPreventDefault(e.currentTarget as HTMLElement)) {
e.preventDefault();
}

Expand Down Expand Up @@ -359,7 +359,7 @@ export function usePress(props: PressHookProps): PressResult {
// Chrome and Firefox on touch Windows devices require mouse down events
// to be canceled in addition to pointer events, or an extra asynchronous
// focus event will be fired.
if (shouldPreventDefault(e.target as Element)) {
if (shouldPreventDefault(e.currentTarget as HTMLElement)) {
e.preventDefault();
}

Expand Down Expand Up @@ -443,7 +443,7 @@ export function usePress(props: PressHookProps): PressResult {

// Due to browser inconsistencies, especially on mobile browsers, we prevent
// default on mouse down and handle focusing the pressable element ourselves.
if (shouldPreventDefault(e.target as Element)) {
if (shouldPreventDefault(e.currentTarget as HTMLElement)) {
e.preventDefault();
}

Expand Down Expand Up @@ -764,9 +764,9 @@ function isOverTarget(point: EventPoint, target: HTMLElement) {
return areRectanglesOverlapping(rect, pointRect);
}

function shouldPreventDefault(target: Element) {
// We cannot prevent default if the target is inside a draggable element.
return !target.closest('[draggable="true"]');
function shouldPreventDefault(target: HTMLElement) {
// We cannot prevent default if the target is a draggable element.
return !target.draggable;
}

function shouldPreventDefaultKeyboard(target: Element) {
Expand Down
32 changes: 28 additions & 4 deletions packages/@react-aria/interactions/test/usePress.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ import {theme} from '@react-spectrum/theme-default';
import {usePress} from '../';

function Example(props) {
let {elementType: ElementType = 'div', style, ...otherProps} = props;
let {elementType: ElementType = 'div', style, draggable, ...otherProps} = props;
let {pressProps} = usePress(otherProps);
return <ElementType {...pressProps} style={style} tabIndex="0">test</ElementType>;
return <ElementType {...pressProps} style={style} tabIndex="0" draggable={draggable}>test</ElementType>;
}

function pointerEvent(type, opts) {
Expand Down Expand Up @@ -507,13 +507,26 @@ describe('usePress', function () {
expect(allowDefault).toBe(false);
});

it('should not prevent default when in a draggable container', function () {
it('should still prevent default when pressing on a non draggable + pressable item in a draggable container', function () {
let res = render(
<div draggable="true">
<Example />
</div>
);

let el = res.getByText('test');
let allowDefault = fireEvent(el, pointerEvent('pointerdown', {pointerId: 1, pointerType: 'mouse'}));
expect(allowDefault).toBe(false);

allowDefault = fireEvent.mouseDown(el);
expect(allowDefault).toBe(false);
});

it('should not prevent default when pressing on a draggable item', function () {
let res = render(
<Example draggable="true" />
);

let el = res.getByText('test');
let allowDefault = fireEvent(el, pointerEvent('pointerdown', {pointerId: 1, pointerType: 'mouse'}));
expect(allowDefault).toBe(true);
Expand Down Expand Up @@ -1036,13 +1049,24 @@ describe('usePress', function () {
expect(allowDefault).toBe(false);
});

it('should not prevent default when in a draggable container', function () {
it('should still prevent default when pressing on a non draggable + pressable item in a draggable container', function () {
let res = render(
<div draggable="true">
<Example />
</div>
);

let el = res.getByText('test');
let allowDefault = fireEvent.mouseDown(el);
expect(allowDefault).toBe(false);
});


it('should not prevent default when pressing on a draggable item', function () {
let res = render(
<Example draggable="true" />
);

let el = res.getByText('test');
let allowDefault = fireEvent.mouseDown(el);
expect(allowDefault).toBe(true);
Expand Down
1 change: 1 addition & 0 deletions packages/@react-aria/listbox/src/useOption.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ export function useOption<T>(props: AriaOptionProps, state: ListState<T>, ref: R
key,
ref,
shouldSelectOnPressUp,
allowsDifferentPressOrigin: shouldSelectOnPressUp,
isVirtualized,
shouldUseVirtualFocus,
isDisabled
Expand Down
3 changes: 2 additions & 1 deletion packages/@react-aria/menu/src/useMenuItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,8 @@ export function useMenuItem<T>(props: AriaMenuItemProps, state: TreeState<T>, re
selectionManager: state.selectionManager,
key,
ref,
shouldSelectOnPressUp: true
shouldSelectOnPressUp: true,
allowsDifferentPressOrigin: true
});

let {pressProps} = usePress({onPressStart, onPressUp, isDisabled});
Expand Down
34 changes: 27 additions & 7 deletions packages/@react-aria/selection/src/useSelectableItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ interface SelectableItemOptions {
* item causes the UI to disappear immediately (e.g. menus).
*/
shouldSelectOnPressUp?: boolean,
/**
* Whether selection requires the pointer/mouse down and up events to occur on the same target or triggers selection on
* the target of the pointer/mouse up event.
*/
allowsDifferentPressOrigin?: boolean,
/**
* Whether the option is contained in a virtual scroller.
*/
Expand Down Expand Up @@ -79,7 +84,8 @@ export function useSelectableItem(options: SelectableItemOptions): SelectableIte
shouldUseVirtualFocus,
focus,
isDisabled,
onAction
onAction,
allowsDifferentPressOrigin
} = options;

let onSelect = (e: PressEvent | LongPressEvent | PointerEvent) => {
Expand Down Expand Up @@ -155,13 +161,27 @@ export function useSelectableItem(options: SelectableItemOptions): SelectableIte
}
};

itemPressProps.onPressUp = (e) => {
if (e.pointerType !== 'keyboard') {
onSelect(e);
}
};
// If allowsDifferentPressOrigin, make selection happen on pressUp (e.g. open menu on press down, selection on menu item happens on press up.)
// Otherwise, have selection happen onPress (prevents listview row selection when clicking on interactable elements in the row)
if (!allowsDifferentPressOrigin) {
itemPressProps.onPress = (e) => {
if (e.pointerType !== 'keyboard') {
onSelect(e);
}

itemPressProps.onPress = hasPrimaryAction ? () => onAction() : null;
if (hasPrimaryAction) {
onAction();
}
};
} else {
itemPressProps.onPressUp = (e) => {
if (e.pointerType !== 'keyboard') {
onSelect(e);
}
};

itemPressProps.onPress = hasPrimaryAction ? () => onAction() : null;
}
} else {
// On touch, it feels strange to select on touch down, so we special case this.
itemPressProps.onPressStart = (e) => {
Expand Down
3 changes: 3 additions & 0 deletions packages/@react-spectrum/dnd/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# @react-spectrum/dnd

This package is part of [react-spectrum](https://github.com/adobe/react-spectrum). See the repo for more details.
13 changes: 13 additions & 0 deletions packages/@react-spectrum/dnd/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/*
* Copyright 2022 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/

export * from './src';
51 changes: 51 additions & 0 deletions packages/@react-spectrum/dnd/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
{
"name": "@react-spectrum/dnd",
"version": "3.0.0-alpha.1",
"description": "Spectrum UI components in React",
"license": "Apache-2.0",
"main": "dist/main.js",
"module": "dist/module.js",
"types": "dist/types.d.ts",
"source": "src/index.ts",
"files": [
"dist",
"src"
],
"sideEffects": [
"*.css"
],
"targets": {
"main": {
"includeNodeModules": [
"@adobe/spectrum-css-temp"
]
},
"module": {
"includeNodeModules": [
"@adobe/spectrum-css-temp"
]
}
},
"repository": {
"type": "git",
"url": "https://github.com/adobe/react-spectrum"
},
"dependencies": {
"@babel/runtime": "^7.6.2",
"@react-aria/dnd": "3.0.0-alpha.5",
"@react-aria/utils": "^3.0.0",
"@react-spectrum/utils": "^3.0.0",
"@react-stately/dnd": "3.0.0-alpha.4",
"@react-types/shared": "^3.0.0"
},
"devDependencies": {
"@adobe/spectrum-css-temp": "3.0.0-alpha.1"
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0-rc.1",
"@react-spectrum/provider": "^3.0.0"
},
"publishConfig": {
"access": "public"
}
}
15 changes: 15 additions & 0 deletions packages/@react-spectrum/dnd/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/*
* Copyright 2022 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/

/// <reference types="css-module-types" />

export * from './useDragHooks';
34 changes: 34 additions & 0 deletions packages/@react-spectrum/dnd/src/useDragHooks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import {DraggableCollectionOptions, DraggableCollectionState, useDraggableCollectionState} from '@react-stately/dnd';
import {DraggableItemProps, DraggableItemResult, useDraggableItem} from '@react-aria/dnd';
import {useMemo} from 'react';

export interface DragHooks {
useDraggableCollectionState(props: Omit<DraggableCollectionOptions, 'getItems'>): DraggableCollectionState,
useDraggableItem(props: DraggableItemProps, state: DraggableCollectionState): DraggableItemResult
}

export type DragHookOptions = Omit<DraggableCollectionOptions, 'collection' | 'selectionManager' | 'isDragging' | 'getKeysForDrag'>

export function useDragHooks(options: DragHookOptions): DragHooks {
return useMemo(() => ({
useDraggableCollectionState(props: DraggableCollectionOptions) {
let {
collection,
selectionManager,
allowsDraggingItem,
getItems,
renderPreview
} = props;

return useDraggableCollectionState({
collection,
selectionManager,
allowsDraggingItem,
getItems,
renderPreview,
...options
});
},
useDraggableItem
}), [options]);
}
8 changes: 7 additions & 1 deletion packages/@react-spectrum/list/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
},
"dependencies": {
"@babel/runtime": "^7.6.2",
"@react-aria/button": "^3.4.1",
"@react-aria/focus": "^3.5.2",
"@react-aria/grid": "^3.2.3",
"@react-aria/i18n": "^3.3.6",
Expand All @@ -40,8 +41,10 @@
"@react-aria/separator": "^3.1.5",
"@react-aria/utils": "^3.11.2",
"@react-aria/virtualizer": "^3.3.7",
"@react-aria/visually-hidden": "^3.2.5",
"@react-spectrum/button": "^3.7.1",
"@react-spectrum/checkbox": "^3.3.1",
"@react-spectrum/dnd": "3.0.0-alpha.1",
"@react-spectrum/layout": "^3.2.3",
"@react-spectrum/listbox": "^3.5.5",
"@react-spectrum/progress": "^3.1.5",
Expand All @@ -54,12 +57,15 @@
"@react-stately/layout": "^3.4.4",
"@react-stately/list": "^3.4.3",
"@react-stately/virtualizer": "^3.1.7",
"@react-types/button": "^3.4.3",
"@react-types/listbox": "^3.2.3",
"@react-types/shared": "^3.11.1",
"@spectrum-icons/ui": "^3.2.3"
},
"devDependencies": {
"@adobe/spectrum-css-temp": "^3.0.0-alpha.1"
"@adobe/spectrum-css-temp": "^3.0.0-alpha.1",
"@react-aria/dnd": "3.0.0-alpha.5",
"@react-stately/dnd": "3.0.0-alpha.4"
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0-rc.1",
Expand Down
30 changes: 30 additions & 0 deletions packages/@react-spectrum/list/src/DragHandle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright 2021 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/

import React from 'react';

export default function DragHandle() {
return (
<svg width="16" height="32" viewBox="0 0 16 32">
<g transform="translate(5.5 10.5)">
<circle cx="1" cy="1" r="1" transform="translate(0 9)" fill="#6e6e6e" />
<circle cx="1" cy="1" r="1" transform="translate(0 6)" fill="#6e6e6e" />
<circle cx="1" cy="1" r="1" transform="translate(0 3)" fill="#6e6e6e" />
<circle cx="1" cy="1" r="1" fill="#6e6e6e" />
<circle cx="1" cy="1" r="1" transform="translate(3 9)" fill="#6e6e6e" />
<circle cx="1" cy="1" r="1" transform="translate(3 6)" fill="#6e6e6e" />
<circle cx="1" cy="1" r="1" transform="translate(3 3)" fill="#6e6e6e" />
<circle cx="1" cy="1" r="1" transform="translate(3)" fill="#6e6e6e" />
</g>
</svg>
);
}
Loading