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

Commit

Permalink
perf: remove heftiest drag/mouseover performance cliffs
Browse files Browse the repository at this point in the history
  • Loading branch information
marionebl authored and tilmx committed May 28, 2018
1 parent 64d4afa commit a55a00f
Show file tree
Hide file tree
Showing 10 changed files with 117 additions and 184 deletions.
5 changes: 5 additions & 0 deletions src/components/element/demo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const ElementDemo: React.StatelessComponent<void> = (): JSX.Element => (
editable={false}
highlight={false}
highlightPlaceholder={false}
mayOpen={false}
open={false}
title="Element"
dragging={false}
Expand All @@ -35,6 +36,7 @@ const ElementDemo: React.StatelessComponent<void> = (): JSX.Element => (
editable={false}
highlight={false}
highlightPlaceholder={false}
mayOpen={false}
open={false}
title="Element"
dragging={false}
Expand All @@ -49,6 +51,7 @@ const ElementDemo: React.StatelessComponent<void> = (): JSX.Element => (
editable={false}
highlight={false}
highlightPlaceholder={false}
mayOpen={true}
open
title="Element"
dragging={false}
Expand All @@ -65,6 +68,7 @@ const ElementDemo: React.StatelessComponent<void> = (): JSX.Element => (
editable={false}
highlight={false}
highlightPlaceholder={false}
mayOpen={true}
open={false}
title="Element"
dragging={false}
Expand All @@ -81,6 +85,7 @@ const ElementDemo: React.StatelessComponent<void> = (): JSX.Element => (
editable={false}
highlight={false}
highlightPlaceholder={false}
mayOpen={true}
open
title="Element"
dragging={false}
Expand Down
22 changes: 11 additions & 11 deletions src/components/element/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export interface ElementProps {
highlight: boolean;
highlightPlaceholder: boolean;
id: string;
mayOpen: boolean;
onChange?: React.FormEventHandler<HTMLInputElement>;
onClick?: React.MouseEventHandler<HTMLElement>;
onContextMenu?: React.MouseEventHandler<HTMLElement>;
Expand Down Expand Up @@ -263,17 +264,16 @@ export const Element: React.StatelessComponent<ElementProps> = props => (
onDragLeave={props.onDragLeave}
onDrop={props.onDragDrop}
>
{Array.isArray(props.children) &&
props.children.length > 0 && (
<StyledIcon
dataIcon={props.id}
name={IconName.ArrowFillRight}
size={IconSize.XXS}
color={colors.grey60}
open={props.open}
active={props.active}
/>
)}
{props.mayOpen && (
<StyledIcon
dataIcon={props.id}
name={IconName.ArrowFillRight}
size={IconSize.XXS}
color={colors.grey60}
open={props.open}
active={props.active}
/>
)}
{props.editable ? (
<SeamlessInput
{...{ [ElementAnchors.label]: true }}
Expand Down
40 changes: 40 additions & 0 deletions src/container/element-list/element-container.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import * as Components from '../../components';
import { ElementContentContainer } from './element-content-container';
import * as MobxReact from 'mobx-react';
import * as Model from '../../model';
import * as React from 'react';

export interface ElementContainerProps {
element: Model.Element;
}

@MobxReact.observer
export class ElementContainer extends React.Component<ElementContainerProps> {
public render(): JSX.Element | null {
const { props } = this;
return (
<Components.Element
active={props.element.getSelected()}
draggable={true}
dragging={true}
editable={props.element.isNameEditable()}
highlight={props.element.getHighlighted()}
highlightPlaceholder={props.element.getPlaceholderHighlighted()}
id={props.element.getId()}
mayOpen={props.element.acceptsChildren()}
open={
props.element.getOpen() || props.element.getDescendants().some(e => e.getSelected())
}
title={props.element.getName()}
>
{props.element.getOpen()
? props.element
.getContents()
.map(content => (
<ElementContentContainer key={content.getId()} content={content} />
))
: []}
</Components.Element>
);
}
}
22 changes: 22 additions & 0 deletions src/container/element-list/element-content-container.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { ElementContainer } from './element-container';
import * as MobxReact from 'mobx-react';
import * as Model from '../../model';
import * as React from 'react';

export interface ElementContentContainerProps {
content: Model.ElementContent;
}

@MobxReact.observer
export class ElementContentContainer extends React.Component<ElementContentContainerProps> {
public render(): JSX.Element | null {
const { props } = this;
return (
<div>
{props.content
.getElements()
.map(element => <ElementContainer key={element.getId()} element={element} />)}
</div>
);
}
}
106 changes: 5 additions & 101 deletions src/container/element-list/element-list.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { colors, ElementAnchors, ElementProps } from '../../components';
import { colors, ElementAnchors } from '../../components';
import { elementMenu } from '../../electron/context-menus';
import { ElementWrapper } from './element-wrapper';
import { partition } from 'lodash';
import { ElementContainer } from './element-container';
import * as Mobx from 'mobx';
import * as MobxReact from 'mobx-react';
import * as Model from '../../model';
Expand Down Expand Up @@ -56,70 +55,6 @@ export class ElementList extends React.Component {
}
}

public createItemFromElement(element: Model.Element): ElementNodeProps | undefined {
const store = Store.ViewStore.getInstance();
const pattern = element.getPattern();

if (!pattern) {
return;
}

const createSlot = slot => this.createItemFromSlot(slot, element);

const [[defaultSlotData], slotsData] = partition(
pattern.getSlots(),
slot => slot.getType() === Types.SlotType.Children
);

const defaultSlot = defaultSlotData ? createSlot(defaultSlotData) : { children: [] };
const children = defaultSlot && defaultSlot.children ? Array.from(defaultSlot.children) : [];

const slots = slotsData
.map(createSlot)
.filter((s): s is ElementNodeProps => typeof s !== 'undefined');

return {
active: element.getSelected(),
children: [...slots, ...children],
draggable: !element.isNameEditable(),
dragging: store.getDragging(),
editable: element.isNameEditable(),
highlight: element.getHighlighted(),
highlightPlaceholder: element.getPlaceholderHighlighted(),
id: element.getId(),
title: element.getName(),
open: element.getOpen() || element.getDescendants().some(e => e.getSelected())
};
}

public createItemFromSlot(
slot: Model.PatternSlot,
element: Model.Element
): ElementNodeProps | undefined {
const store = Store.ViewStore.getInstance();
const slotContent = element.getContentBySlotId(slot.getId());

if (!slotContent) {
return;
}

return {
active: false,
children: slotContent
.getElements()
.map(e => this.createItemFromElement(e))
.filter((e): e is ElementNodeProps => typeof e !== 'undefined'),
draggable: false,
dragging: store.getDragging(),
editable: false,
highlight: element.getHighlighted(),
highlightPlaceholder: false,
id: slotContent.getId(),
open: true,
title: slot.getName()
};
}

private handleBlur(e: React.FormEvent<HTMLElement>): void {
const store = Store.ViewStore.getInstance();
const editableElement = store.getNameEditableElement();
Expand Down Expand Up @@ -365,20 +300,15 @@ export class ElementList extends React.Component {
const element = elementFromTarget(e.target as HTMLElement, { sibling: false });
const label = above(e.target, `[${ElementAnchors.label}]`);

// Special case: leaving the hover area of the last
// element, entering the catch-all zone of the page root
if (!label && element && element.isRoot()) {
if ((label && element) || (!label && element && element.isRoot())) {
store
.getProject()
.getElements()
.filter(se => se.getHighlighted())
.forEach(se => se.setHighlighted(false));
}

if (label && element) {
store
.getProject()
.getElements()
.forEach(se => se.setHighlighted(false));
element.setHighlighted(true);
this.setState({ dragging: false });
}
Expand All @@ -398,12 +328,6 @@ export class ElementList extends React.Component {
return null;
}

const item = this.createItemFromElement(rootElement);

if (!item) {
return null;
}

return (
<StyledDragRoot
data-drag-root
Expand All @@ -421,7 +345,7 @@ export class ElementList extends React.Component {
onMouseOver={e => this.handleMouseOver(e)}
innerRef={ref => (this.ref = ref)}
>
<ElementTree {...item} />
<ElementContainer element={rootElement} />
</StyledDragRoot>
);
}
Expand All @@ -432,26 +356,6 @@ const StyledDragRoot = styled.div`
width: 100%;
`;

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

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;
Expand Down
53 changes: 0 additions & 53 deletions src/container/element-list/element-wrapper.tsx

This file was deleted.

Loading

0 comments on commit a55a00f

Please sign in to comment.