Skip to content
This repository has been archived by the owner on Oct 23, 2023. It is now read-only.

Commit

Permalink
fix: avoid side effect froms drop areas on click interaction
Browse files Browse the repository at this point in the history
  • Loading branch information
marionebl committed Apr 23, 2018
1 parent c6c52ff commit 4f6f148
Show file tree
Hide file tree
Showing 5 changed files with 263 additions and 190 deletions.
251 changes: 168 additions & 83 deletions src/component/container/element-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,18 @@ import { Pattern } from '../../store/styleguide/pattern';
import { PropertyValue } from '../../store/page/property-value';
import * as React from 'react';
import { Store } from '../../store/store';
import * as uuid from 'uuid';

export interface ElementListState {
dragging: boolean;
}

@observer
export class ElementList extends React.Component {
export class ElementList extends React.Component<{}, ElementListState> {
public state = {
dragging: false
};

public componentDidMount(): void {
createMenu();
}
Expand All @@ -25,57 +34,26 @@ export class ElementList extends React.Component {
key: string,
element: PageElement,
selectedElement?: PageElement
): ListItemProps {
): ElementNodeProps {
const pattern: Pattern | undefined = element.getPattern();

if (!pattern) {
return {
label: key,
value: '(invalid)',
children: []
title: '(invalid)',
id: uuid.v4(),
children: [],
dragging: this.state.dragging
};
}

const items: ListItemProps[] = [];
const children: PageElement[] = element.getChildren() || [];
children.forEach((value: PageElement, index: number) => {
items.push(
this.createItemFromProperty(
children.length > 1 ? `Child ${index + 1}` : 'Child',
value,
selectedElement
)
);
});

const updatePageElement: React.MouseEventHandler<HTMLElement> = event => {
event.stopPropagation();
Store.getInstance().setSelectedElement(element);
Store.getInstance().setElementFocussed(true);
};

return {
label: key,
value: element.getName(),
onClick: updatePageElement,
onContextMenu: () => elementMenu(element),
handleDragStart: (e: React.DragEvent<HTMLElement>) => {
Store.getInstance().setDraggedElement(element);
e.dataTransfer.effectAllowed = 'move';

let dragElement = e.currentTarget.querySelector('div');

// restyle the drag image and move it somewhere invisible
if (dragElement) {
let dragImg = dragElement.cloneNode(true) as HTMLElement;
dragImg.setAttribute(
'style',
'position: absolute; background-color: #fff; color: #000; padding: 6px 18px; border-radius: 3px; font-size: 12px; opacity: 1; top: 0; left: -500px;'
);
document.body.appendChild(dragImg);
e.dataTransfer.setDragImage(dragImg, 75, 15);
}
},
handleDragDropForChild: (e: React.DragEvent<HTMLElement>) => {
title: element.getName(),
dragging: this.state.dragging,
id: element.getId(),
onDragDropForChild: (e: React.DragEvent<HTMLElement>) => {
this.handleDragEnd(e);
const patternId = e.dataTransfer.getData('patternId');

const newParent = element.getParent();
Expand Down Expand Up @@ -114,7 +92,8 @@ export class ElementList extends React.Component {
store.execute(ElementLocationCommand.addChild(newParent, draggedElement, newIndex));
store.setSelectedElement(draggedElement);
},
handleDragDrop: (e: React.DragEvent<HTMLElement>) => {
onDragDrop: (e: React.DragEvent<HTMLElement>) => {
this.handleDragEnd(e);
const patternId = e.dataTransfer.getData('patternId');

let draggedElement: PageElement | undefined;
Expand Down Expand Up @@ -142,7 +121,15 @@ export class ElementList extends React.Component {
store.execute(ElementLocationCommand.addChild(element, draggedElement));
store.setSelectedElement(draggedElement);
},
children: items,
children: element
.getChildren()
.map((child, index, items) =>
this.createItemFromProperty(
items.length > 1 ? `Child ${index + 1}` : 'Child',
child,
selectedElement
)
),
active: element === selectedElement
};
}
Expand All @@ -151,65 +138,163 @@ export class ElementList extends React.Component {
key: string,
value: PropertyValue,
selectedElement?: PageElement
): ListItemProps {
if (value instanceof Array) {
const items: ListItemProps[] = [];
(value as (string | number)[]).forEach((child, index: number) => {
items.push(this.createItemFromProperty(String(index + 1), child));
});
return { value: key, children: items };
): ElementNodeProps {
if (Array.isArray(value)) {
return {
title: key,
children: (value as (number | string)[]).map((child, index) =>
this.createItemFromProperty(String(index + 1), child)
),
dragging: this.state.dragging,
id: uuid.v4()
};
}

if (value === undefined || value === null || typeof value !== 'object') {
return { label: key, value: String(value) };
return { label: key, title: String(value), dragging: this.state.dragging, id: uuid.v4() };
}

if (value instanceof PageElement) {
return this.createItemFromElement(key, value, selectedElement);
} else {
const items: ListItemProps[] = [];
Object.keys(value).forEach((childKey: string) => {
// tslint:disable-next-line:no-any
items.push(this.createItemFromProperty(childKey, (value as any)[childKey]));
});
return { value: key, children: items };
}

return {
title: key,
children: Object.entries(value).map(entry =>
this.createItemFromProperty(entry[0], entry[1])
),
dragging: this.state.dragging,
id: uuid.v4()
};
}

private handleClick(e: React.MouseEvent<HTMLElement>): void {
const element = elementFromTarget(e.target);
e.stopPropagation();
Store.getInstance().setSelectedElement(element);
Store.getInstance().setElementFocussed(true);
}

private handleContextMenu(e: React.MouseEvent<HTMLElement>): void {
const element = elementFromTarget(e.target);
if (element) {
elementMenu(element);
}
}

private handleDragEnd(e: React.DragEvent<HTMLElement>): void {
this.setState({ dragging: false });
}

private handleDragStart(e: React.DragEvent<HTMLElement>): void {
this.setState({ dragging: true });
const element = elementFromTarget(e.target);

if (element) {
Store.getInstance().setDraggedElement(element);
}

const dragElement = e.currentTarget.querySelector('div');

// restyle the drag image and move it somewhere invisible
if (dragElement) {
const dragImg = dragElement.cloneNode(true) as HTMLElement;
dragImg.setAttribute(
'style',
'position: absolute; background-color: #fff; color: #000; padding: 6px 18px; border-radius: 3px; font-size: 12px; opacity: 1; top: 0; left: -500px;'
);
document.body.appendChild(dragImg);
e.dataTransfer.setDragImage(dragImg, 75, 15);
}
}

public render(): JSX.Element | null {
const store = Store.getInstance();
const page: Page | undefined = store.getCurrentPage();
if (page) {
const rootElement = page.getRoot();

if (!rootElement) {
return null;
}
if (!page) {
return null;
}

const selectedElement = store.getSelectedElement();
const rootElement = page.getRoot();

return this.renderList(this.createItemFromElement('Root', rootElement, selectedElement));
} else {
if (!rootElement) {
return null;
}
}

public renderList(item: ListItemProps, key?: number): JSX.Element {
const selectedElement = store.getSelectedElement();
const item = this.createItemFromElement('Root', rootElement, selectedElement);

return (
<ElementWrapper
title={item.value}
key={key}
handleClick={item.onClick}
handleContextMenu={item.onContextMenu}
active={item.active}
handleDragStart={item.handleDragStart}
handleDragDropForChild={item.handleDragDropForChild}
handleDragDrop={item.handleDragDrop}
<div
onClick={e => this.handleClick(e)}
onContextMenu={e => this.handleContextMenu(e)}
onDragStart={e => this.handleDragStart(e)}
onDragEnd={e => this.handleDragEnd(e)}
>
{item.children &&
item.children.length > 0 &&
item.children.map((child, index) => this.renderList(child, index))}
</ElementWrapper>
<ElementTree {...item} dragging={this.state.dragging} />
</div>
);
}
}

export interface ElementNodeProps extends ListItemProps {
children?: ElementNodeProps[];
dragging: boolean;
id: string;
}

function ElementTree(props: ElementNodeProps): JSX.Element {
const children = Array.isArray(props.children) ? props.children : [];

return (
<ElementWrapper {...props} dragging={props.dragging}>
{children.map(child => (
<ElementTree {...child} key={child.id} dragging={props.dragging} />
))}
</ElementWrapper>
);
}

function above(node: EventTarget, selector: string): HTMLElement | null {
let el = node as HTMLElement;
let ended = false;

while (el && !ended) {
if (el.matches(selector)) {
break;
}

if (el.parentElement !== null) {
el = el.parentElement;
} else {
ended = true;
break;
}
}

return ended ? null : el;
}

function elementFromTarget(target: EventTarget): PageElement | undefined {
const el = above(target, '[data-id]');

if (!el) {
return;
}

const id = el.getAttribute('data-id');

if (typeof id !== 'string') {
return;
}

const store = Store.getInstance();
const page = store.getCurrentPage();

if (!page) {
return;
}

return page.getElementById(id);
}
Loading

0 comments on commit 4f6f148

Please sign in to comment.