Skip to content

Commit

Permalink
DEP Replace react-dnd with dnd-kit
Browse files Browse the repository at this point in the history
  • Loading branch information
GuySartorelli committed Dec 17, 2024
1 parent c4a6f87 commit 6d70016
Show file tree
Hide file tree
Showing 14 changed files with 183 additions and 524 deletions.
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.

108 changes: 51 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 @@ -31,40 +30,46 @@ class ElementEditor extends PureComponent {
isLoading: true,
};

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 +80,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 +128,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 +151,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 +186,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

0 comments on commit 6d70016

Please sign in to comment.