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

DEP Replace react-dnd with dnd-kit #1298

Merged
merged 1 commit into from
Dec 19, 2024
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 client/dist/js/bundle.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion client/dist/styles/bundle.css

Large diffs are not rendered by default.

85 changes: 23 additions & 62 deletions client/src/components/ElementEditor/Element.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,8 @@ import { submit } from 'redux-form';
import { loadElementFormStateName } from 'state/editor/loadElementFormStateName';
import { loadElementSchemaValue } from 'state/editor/loadElementSchemaValue';
import * as TabsActions from 'state/tabs/TabsActions';
import { DragSource, DropTarget } from 'react-dnd';
import { getEmptyImage } from 'react-dnd-html5-backend';
import { elementDragSource, isOverTop } from 'lib/dragHelpers';
import { useSortable } from '@dnd-kit/sortable';
import { CSS } from '@dnd-kit/utilities';
import * as toastsActions from 'state/toasts/ToastsActions';
import { addFormChanged, removeFormChanged } from 'state/unsavedForms/UnsavedFormsActions';
import { ElementEditorContext } from 'components/ElementEditor/ElementEditor';
Expand Down Expand Up @@ -43,6 +42,20 @@ const Element = (props) => {
const [doDispatchAddFormChanged, setDoDispatchAddFormChanged] = useState(false);
const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false);
const { fetchElements } = useContext(ElementEditorContext);
const {
attributes,
listeners,
setNodeRef,
transform,
transition,
isDragging,
isOver,
} = useSortable({ id: props.element.id });

const style = {
transform: CSS.Transform.toString(transform),
transition,
};

useEffect(() => {
// Note that formDirty from redux can be set to undefined after failed validation
Expand All @@ -65,18 +78,6 @@ const Element = (props) => {
}
}, [props.saveElement, hasUnsavedChanges, props.increment]);

useEffect(() => {
if (props.connectDragPreview) {
// Use empty image as a drag preview so browsers don't draw it
// and we can draw whatever we want on the custom drag layer instead.
props.connectDragPreview(getEmptyImage(), {
// IE fallback: specify that we'd rather screenshot the node
// when it already knows it's being dragged so we can hide it with CSS.
captureDraggingState: true,
});
}
}, []);

useEffect(() => {
if (justClickedPublishButton && formHasRendered) {
setJustClickedPublishButton(false);
Expand Down Expand Up @@ -363,11 +364,6 @@ const Element = (props) => {
ContentComponent,
link,
activeTab,
connectDragSource,
connectDropTarget,
isDragging,
isOver,
onDragEnd,
formDirty,
} = props;

Expand All @@ -393,14 +389,19 @@ const Element = (props) => {
onSaveButtonClick: handleSaveButtonClick,
};

const content = connectDropTarget(<div
const content = <div
className={elementClassNames}
onClick={handleExpand}
onKeyUp={handleKeyUp}
role="button"
tabIndex={0}
title={getLinkTitle(type)}
key={element.id}
// sortable properties
ref={setNodeRef}
{...attributes}
{...listeners}
style={style}
>
<ElementContext.Provider value={providerValue}>
<HeaderComponent
Expand All @@ -413,7 +414,6 @@ const Element = (props) => {
handleEditTabsClick={handleTabClick}
activeTab={activeTab}
disableTooltip={isDragging}
onDragEnd={onDragEnd}
/>
<ContentComponent
id={element.id}
Expand All @@ -430,11 +430,7 @@ const Element = (props) => {
onFormInit={() => handleFormInit(activeTab)}
/>
</ElementContext.Provider>
</div>);

if (!previewExpanded) {
return connectDragSource(content);
}
</div>;

return content;
};
Expand Down Expand Up @@ -508,14 +504,7 @@ Element.propTypes = {
activeTab: PropTypes.string,
tabSetName: PropTypes.string,
onActivateTab: PropTypes.func,
connectDragSource: PropTypes.func.isRequired,
connectDragPreview: PropTypes.func.isRequired,
connectDropTarget: PropTypes.func.isRequired,
isDragging: PropTypes.bool.isRequired,
isOver: PropTypes.bool.isRequired,
onDragOver: PropTypes.func, // eslint-disable-line react/no-unused-prop-types
onDragEnd: PropTypes.func, // eslint-disable-line react/no-unused-prop-types
onDragStart: PropTypes.func, // eslint-disable-line react/no-unused-prop-types
saveElement: PropTypes.bool.isRequired,
onBeforeSubmitForm: PropTypes.func.isRequired, // eslint-disable-line react/no-unused-prop-types
onAfterSubmitResponse: PropTypes.func.isRequired,
Expand All @@ -529,35 +518,7 @@ Element.defaultProps = {

export { Element as Component };

const elementTarget = {
drop(props, monitor, component) {
const { element } = props;

return {
target: element.id,
dropSpot: isOverTop(monitor, component) ? 'top' : 'bottom',
};
},

hover(props, monitor, component) {
const { element, onDragOver } = props;

if (onDragOver) {
onDragOver(element, isOverTop(monitor, component));
}
},
};

export default compose(
DropTarget('element', elementTarget, (connector, monitor) => ({
connectDropTarget: connector.dropTarget(),
isOver: monitor.isOver(),
})),
DragSource('element', elementDragSource, (connector, monitor) => ({
connectDragSource: connector.dragSource(),
connectDragPreview: connector.dragPreview(),
isDragging: monitor.isDragging(),
})),
connect(mapStateToProps, mapDispatchToProps),
inject(
['ElementHeader', 'ElementContent'],
Expand Down
52 changes: 0 additions & 52 deletions client/src/components/ElementEditor/ElementDragPreview.js

This file was deleted.

11 changes: 0 additions & 11 deletions client/src/components/ElementEditor/ElementDragPreview.scss

This file was deleted.

109 changes: 52 additions & 57 deletions client/src/components/ElementEditor/ElementEditor.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,13 @@ import { connect } from 'react-redux';
import { inject } from 'lib/Injector';
import { bindActionCreators, compose } from 'redux';
import { elementTypeType } from 'types/elementTypeType';
import { DropTarget } from 'react-dnd';
import ElementDragPreview from 'components/ElementEditor/ElementDragPreview';
import withDragDropContext from 'lib/withDragDropContext';
import backend from 'lib/Backend';
import Config from 'lib/Config';
import { getConfig } from 'state/editor/elementConfig';
import * as toastsActions from 'state/toasts/ToastsActions';
import getJsonErrorMessage from 'lib/getJsonErrorMessage';
import { arrayMove } from '@dnd-kit/sortable';

export const ElementEditorContext = createContext(null);

Expand All @@ -29,42 +28,49 @@ class ElementEditor extends PureComponent {
dragSpot: null,
elements: null,
isLoading: true,
dragging: false,
};

this.handleDragOver = this.handleDragOver.bind(this);
this.handleDragStart = this.handleDragStart.bind(this);
this.handleDragEnd = this.handleDragEnd.bind(this);
this.fetchElements = this.fetchElements.bind(this);
}

/**
* Hook for ReactDND triggered by hovering over a drag _target_.
*
* This tracks the current hover target and whether it's above the top half of the target
* or the bottom half.
*
* @param element
* @param isOverTop
* Hook triggered when a draggable is picked up.
*/
handleDragOver(element = null, isOverTop = null) {
const id = element ? element.id : false;

handleDragStart(event) {
const { active } = event;
this.setState({
dragTargetElementId: id,
dragSpot: isOverTop === false ? 'bottom' : 'top',
dragging: active.id,
});
}

/**
* Hook for ReactDND triggered when a drag source is dropped onto a drag target.
*
* @param sourceId
* @param afterId
* Hook triggered when a draggable is dropped onto a drop target.
*/
handleDragEnd(sourceId, afterId) {
handleDragEnd(event) {
const { active, over } = event;
const { elements } = this.state;

// This happens if letting go of the draggable where it started.
if (active.id === over.id) {
this.setState({
dragging: false,
});
return;
}

const elementIDs = elements.map(e => e.id);
const fromIndex = elementIDs.indexOf(active.id);
const toIndex = elementIDs.indexOf(over.id);
const sortedElements = arrayMove(elements, fromIndex, toIndex);
const afterBlockID = toIndex > 0 ? sortedElements[toIndex - 1].id : 0;

const url = `${getConfig().controllerLink.replace(/\/$/, '')}/api/sort`;
backend.post(url, {
id: sourceId,
afterBlockID: afterId,
id: active.id,
afterBlockID,
}, {
'X-SecurityID': Config.get('SecurityID')
})
Expand All @@ -75,8 +81,10 @@ class ElementEditor extends PureComponent {
});

this.setState({
dragTargetElementId: null,
dragSpot: null,
dragging: false,
// Setting elements ensures there is no "pop" between dropping the element and reloading
// the list with fetchElements above, as the elements will already be rendered in the new order.
elements: sortedElements,
});
}

Expand Down Expand Up @@ -121,13 +129,11 @@ class ElementEditor extends PureComponent {
ListComponent,
areaId,
elementTypes,
isDraggingOver,
connectDropTarget,
allowedElements,
sharedObject,
isLoading,
} = this.props;
const { dragTargetElementId, dragSpot, elements } = this.state;
const { dragging, elements } = this.state;

if (elements === null) {
this.fetchElements(false);
Expand All @@ -146,32 +152,25 @@ class ElementEditor extends PureComponent {
fetchElements: this.fetchElements,
};

return connectDropTarget(
<div className="element-editor">
<ElementEditorContext.Provider value={providerValue}>
<ToolbarComponent
elementTypes={allowedElementTypes}
areaId={areaId}
onDragOver={this.handleDragOver}
/>
<ListComponent
allowedElementTypes={allowedElementTypes}
elementTypes={elementTypes}
areaId={areaId}
onDragOver={this.handleDragOver}
onDragStart={this.handleDragStart}
onDragEnd={this.handleDragEnd}
dragSpot={dragSpot}
isDraggingOver={isDraggingOver}
dragTargetElementId={dragTargetElementId}
sharedObject={sharedObject}
elements={elements}
isLoading={isLoading}
/>
<ElementDragPreview elementTypes={elementTypes} />
</ElementEditorContext.Provider>
</div>
);
return <div className="element-editor">
<ElementEditorContext.Provider value={providerValue}>
<ToolbarComponent
elementTypes={allowedElementTypes}
areaId={areaId}
/>
<ListComponent
allowedElementTypes={allowedElementTypes}
elementTypes={elementTypes}
areaId={areaId}
onDragStart={this.handleDragStart}
onDragEnd={this.handleDragEnd}
dragging={dragging}
sharedObject={sharedObject}
elements={elements}
isLoading={isLoading}
/>
</ElementEditorContext.Provider>
</div>;
}
}

Expand All @@ -188,10 +187,6 @@ export { ElementEditor as Component };

const params = [
withDragDropContext,
DropTarget('element', {}, (connector, monitor) => ({
connectDropTarget: connector.dropTarget(),
isDraggingOver: monitor.isOver(), // isDragging is not available on DropTargetMonitor
})),
inject(
['ElementToolbar', 'ElementList'],
(ToolbarComponent, ListComponent) => ({
Expand Down
Loading
Loading