Skip to content

Commit

Permalink
Drag and drop handle (#9376)
Browse files Browse the repository at this point in the history
  • Loading branch information
TomasEng authored Dec 8, 2022
1 parent e5944d8 commit 340ae9d
Show file tree
Hide file tree
Showing 8 changed files with 128 additions and 50 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
.handle {
background-color: #00000010;
border-width: 0;
border-top-left-radius: 5px;
border-bottom-left-radius: 5px;
width: 25px;
display: flex;
align-items: center;
justify-content: center;
cursor: move;
height: 100%;
}

.points {
--point-size: 3px;
display: grid;
grid-template: var(--point-size) / var(--point-size) var(--point-size);
gap: var(--point-size);
margin: auto;
}

.points span {
background: #00000040;
width: var(--point-size);
height: var(--point-size);
border-radius: 50%;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React from 'react';
import classes from './DragHandle.module.css';

export const DragHandle = () => (
<div className={classes.handle}>
<span className={classes.points}>
<span/>
<span/>
<span/>
<span/>
<span/>
<span/>
</span>
</div>
);
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { FormLayoutActions } from '../features/formDesigner/formLayout/formLayou
import { EditContainer } from '../containers/EditContainer';
import { makeGetLayoutOrderSelector } from '../selectors/getLayoutData';
import type { FormComponentType, IAppState, IDataModelFieldElement } from '../types/global';
import { ConnectDragSource } from 'react-dnd';

const styles = createStyles({});

Expand All @@ -20,6 +21,7 @@ export interface IProvidedProps {
sendListToParent: any;
singleSelected: boolean;
partOfGroup?: boolean;
dragHandleRef: ConnectDragSource;
}

/**
Expand Down Expand Up @@ -146,6 +148,7 @@ const FormComponent = (props: IFormElementProps) => {
sendItemToParent={handleActiveListChange}
singleSelected={props.singleSelected}
partOfGroup={props.partOfGroup}
dragHandleRef={props.dragHandleRef}
>
<button className={'divider'} onClick={disableEditOnClickForAddedComponent}>
{renderLabel()}
Expand Down Expand Up @@ -180,6 +183,7 @@ const makeMapStateToProps = () => {
validationErrors: null,
textResources: state.appData.textResources.resources,
dataModel: state.appData.dataModel.model,
dragHandleRef: props.dragHandleRef,
});
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -482,9 +482,8 @@ export class ContainerComponent extends Component<IContainerProps, IContainerSta
id='placeholder'
index={0}
containerId={this.props.id}
>
{this.props.language['ux_editor.container_empty']}
</DroppableDraggableComponent>
component={() => this.props.language['ux_editor.container_empty']}
/>
);
};

Expand Down Expand Up @@ -568,17 +567,19 @@ export class ContainerComponent extends Component<IContainerProps, IContainerSta
id={id}
index={index}
key={id}
>
<FormComponentWrapper
activeList={this.props.activeList}
firstInActiveList={firstInActiveList}
id={id}
lastInActiveList={lastInActiveList}
partOfGroup={!this.props.isBaseContainer}
sendListToParent={this.handleActiveListChange}
singleSelected={this.props.activeList.length === 1}
/>
</DroppableDraggableComponent>
component={(dragHandleRef) => (
<FormComponentWrapper
activeList={this.props.activeList}
firstInActiveList={firstInActiveList}
id={id}
lastInActiveList={lastInActiveList}
partOfGroup={!this.props.isBaseContainer}
sendListToParent={this.handleActiveListChange}
singleSelected={this.props.activeList.length === 1}
dragHandleRef={dragHandleRef}
/>
)}
/>
);
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { ReactNode, RefObject } from 'react';
import React, { memo, useRef } from 'react';
import type { DropTargetHookSpec, DropTargetMonitor } from 'react-dnd';
import type { ConnectDragSource, DropTargetHookSpec, DropTargetMonitor } from 'react-dnd';
import { useDrag, useDrop } from 'react-dnd';
import {
dragSourceSpec,
Expand Down Expand Up @@ -43,7 +43,7 @@ export const dropTargetSpec = (

export interface IDroppableDraggableComponentProps {
canDrag: boolean;
children?: ReactNode;
component: (dragHandleRef: ConnectDragSource) => ReactNode;
containerId: string;
dndEvents: EditorDndEvents;
id: string;
Expand All @@ -52,25 +52,29 @@ export interface IDroppableDraggableComponentProps {

export const DroppableDraggableComponent = memo(function DroppableDraggableComponent({
canDrag,
children,
component,
containerId,
dndEvents,
id,
index,
}: IDroppableDraggableComponentProps) {
const ref = useRef<HTMLDivElement>(null);

const wrapperRef = useRef<HTMLDivElement>(null);

const item = { id, containerId, index, type: ItemType.Item };
const [{ isDragging }, drag] = useDrag(dragSourceSpec(item, canDrag, dndEvents.onDropItem));
const [{ isDragging }, drag, dragPreview] = useDrag(dragSourceSpec(item, canDrag, dndEvents.onDropItem));

const [, drop] = useDrop(dropTargetSpec(item, dndEvents, ref));
const opacity = isDragging ? 0 : 1;
const [, drop] = useDrop(dropTargetSpec(item, dndEvents, wrapperRef));
const opacity = isDragging ? 0.25 : 1;
const background = isDragging ? 'inherit !important' : undefined;

drag(drop(ref));
return (
<div style={{ opacity, background }} ref={ref}>
{children}
<div ref={wrapperRef}>
<div ref={drop}>
<div ref={dragPreview} style={{opacity, background}}>
{component(drag)}
</div>
</div>
</div>
);
});
Original file line number Diff line number Diff line change
@@ -1,20 +1,37 @@
.wrapper {
display: flex;
align-items: stretch;
}

.formComponentWithHandle {
display: flex;
flex-direction: row;
align-items: stretch;
flex: 1;
border-top-left-radius: 5px;
border-bottom-left-radius: 5px;
}

.dragHandle {
visibility: hidden;
}

.wrapper:hover .dragHandle {
visibility: visible;
}

.formComponentWithHandle:has(.dragHandle:hover) {
box-shadow: 0 0 0.4rem rgba(0, 0, 0, 0.25);
}

.formComponent {
background-color: #fff;
border: 1px solid #6a6a6a;
color: #022F51;
cursor: move;
flex: 1;
padding: 1rem;
}

.formComponent:hover {
box-shadow: 0 0 0.4rem rgba(0, 0, 0, 0.25);
}

.buttons {
display: flex;
flex-direction: column;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ const render = (props: Partial<IEditContainerProvidedProps> = {}) => {
},
};

const allProps = {
const allProps: IEditContainerProvidedProps = {
component: {
id,
dataModelBindings: {},
Expand All @@ -113,6 +113,8 @@ const render = (props: Partial<IEditContainerProvidedProps> = {}) => {
lastInActiveList: false,
singleSelected: false,
sendItemToParent: jest.fn(),
dragHandleRef: null,
children: null,
...props,
};

Expand All @@ -121,7 +123,7 @@ const render = (props: Partial<IEditContainerProvidedProps> = {}) => {
rtlRender(
<Provider store={mockStore}>
<EditContainer {...allProps}>
<div />
{allProps.children}
</EditContainer>
</Provider>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import classes from './EditContainer.module.css';
import { Button, ButtonColor, ButtonVariant } from '@altinn/altinn-design-system';
import { Cancel, Delete, Edit, Success } from '@navikt/ds-icons';
import cn from 'classnames';
import { ConnectDragSource } from 'react-dnd';
import { DragHandle } from '../components/DragHandle';

export interface IEditContainerProvidedProps {
component: IFormComponent;
Expand All @@ -22,6 +24,7 @@ export interface IEditContainerProvidedProps {
singleSelected: boolean;
partOfGroup?: boolean;
children: any;
dragHandleRef: ConnectDragSource;
}

export function EditContainer(props: IEditContainerProvidedProps) {
Expand Down Expand Up @@ -122,29 +125,34 @@ export function EditContainer(props: IEditContainerProvidedProps) {
const activeListIndex = activeList.findIndex((item: any) => item.id === props.id);
return (
<div className={cn(classes.wrapper, isEditMode && classes.editMode)}>
<div
className={classes.formComponent}
onClick={handleSetActive}
onKeyDown={handleKeyPress}
tabIndex={0}
>
{isEditMode && component ? (
<EditModalContent
component={JSON.parse(JSON.stringify(component))}
handleComponentUpdate={handleComponentUpdate}
/>
) : (
<div className={classes.formComponentTitle}>
<i className={componentIcons[component.type] || 'fa fa-help-circle'}/>
{component.textResourceBindings?.title
? truncate(
<div className={classes.formComponentWithHandle}>
<div ref={props.dragHandleRef} className={classes.dragHandle}>
<DragHandle/>
</div>
<div
className={classes.formComponent}
onClick={handleSetActive}
onKeyDown={handleKeyPress}
tabIndex={0}
>
{isEditMode && component ? (
<EditModalContent
component={JSON.parse(JSON.stringify(component))}
handleComponentUpdate={handleComponentUpdate}
/>
) : (
<div className={classes.formComponentTitle}>
<i className={componentIcons[component.type] || 'fa fa-help-circle'}/>
{component.textResourceBindings?.title
? truncate(
getTextResource(component.textResourceBindings.title, textResources),
80
)
: getComponentTitleByComponentType(component.type, language) ||
: getComponentTitleByComponentType(component.type, language) ||
getLanguageFromKey('ux_editor.component_unknown', language)}
</div>
)}
</div>
)}
</div>
</div>
{!isEditMode && (
<div className={classes.buttons}>
Expand Down

0 comments on commit 340ae9d

Please sign in to comment.