diff --git a/src/components/element/demo.tsx b/src/components/element/demo.tsx
index 3a54f41af..d4286157a 100644
--- a/src/components/element/demo.tsx
+++ b/src/components/element/demo.tsx
@@ -1,6 +1,6 @@
import DemoContainer from '../demo-container';
import { IconName, IconRegistry } from '../icons';
-import Element from './index';
+import { Element, ElementState } from './index';
import * as React from 'react';
import styled from 'styled-components';
@@ -10,19 +10,33 @@ const StyledTestDiv = styled.div`
padding: 20px 10px;
`;
+// tslint:disable-next-line:no-empty
+const NOOP = () => {};
+
+const CHILD = (
+
+);
+
const ElementDemo: React.StatelessComponent = (): JSX.Element => (
Default
@@ -31,66 +45,98 @@ const ElementDemo: React.StatelessComponent = (): JSX.Element => (
Active
- With Child and open
+ Highlighted
+
+
+
+ Placeholder Highlighted
+
+
+
+ Editable
+
+
+
+ May open, closed
- Child
+ {CHILD}
- With child and active
+ May open, opened
- Child
+ {CHILD}
With child, active and open
- Child
+ {CHILD}
diff --git a/src/components/element/index.tsx b/src/components/element/index.tsx
index 2730cc159..11b3ee8f5 100644
--- a/src/components/element/index.tsx
+++ b/src/components/element/index.tsx
@@ -13,53 +13,41 @@ export const ElementAnchors = {
placeholder: 'data-element-placeholder'
};
+export enum ElementState {
+ Default = 'default',
+ Editable = 'editable',
+ Active = 'active',
+ Disabled = 'disabled',
+ Highlighted = 'highlighted'
+}
+
export interface ElementProps {
- active: boolean;
draggable: boolean;
dragging: boolean;
- editable: boolean;
- highlight: boolean;
- highlightPlaceholder: boolean;
id: string;
mayOpen: boolean;
- onChange?: React.FormEventHandler;
- onClick?: React.MouseEventHandler;
- onContextMenu?: React.MouseEventHandler;
- onDragDrop?: React.DragEventHandler;
- onDragDropForChild?: React.DragEventHandler;
- onDragEnter?: React.DragEventHandler;
- onDragEnterForChild?: React.DragEventHandler;
- onDragLeave?: React.DragEventHandler;
- onDragLeaveForChild?: React.DragEventHandler;
- onDragStart?: React.DragEventHandler;
+ onChange: React.FormEventHandler;
open: boolean;
+ placeholderHighlighted?: boolean;
+ state: ElementState;
title: string;
}
interface StyledElementLabelProps {
- active?: boolean;
- highlight?: boolean;
+ state: ElementState;
}
interface StyledIconProps {
- active?: boolean;
- id?: string;
- open?: boolean;
+ active: boolean;
+ open: boolean;
}
interface LabelContentProps {
- active?: boolean;
+ active: boolean;
}
export interface StyledElementChildProps {
- open?: boolean;
-}
-
-export interface StyledPlaceholder {
- highlightPlaceholder?: boolean;
- onDragDropForChild?: React.DragEventHandler;
- onDragEnterForChild?: React.DragEventHandler;
- onDragLeaveForChild?: React.DragEventHandler;
+ open: boolean;
}
const StyledElement = styled.div`
@@ -70,23 +58,27 @@ const StyledElement = styled.div`
const div = tag('div').omit(['active', 'highlight']);
const LABEL_COLOR = (props: StyledElementLabelProps): string => {
- if (props.active) {
- return colors.blue.toString();
+ switch (props.state) {
+ case ElementState.Active:
+ case ElementState.Editable:
+ return colors.blue.toString();
+ case ElementState.Disabled:
+ return colors.grey60.toString();
+ default:
+ return 'inherit';
}
-
- return 'inherit';
};
const LABEL_BACKGROUND = (props: StyledElementLabelProps): string => {
- if (props.active) {
- return colors.blue80.toString();
+ switch (props.state) {
+ case ElementState.Active:
+ case ElementState.Editable:
+ return colors.blue80.toString();
+ case ElementState.Highlighted:
+ return colors.grey90.toString();
+ default:
+ return 'transparent';
}
-
- if (props.highlight) {
- return colors.grey90.toString();
- }
-
- return 'transparent';
};
const StyledElementLabel = styled(div)`
@@ -113,8 +105,13 @@ const StyledElementLabel = styled(div)`
}
`;
-const placeholderDiv = tag('div').omit(['highlightPlaceholder']);
-const StyledPlaceholder = styled(placeholderDiv)`
+interface StyledPlaceholderProps {
+ visible: boolean;
+}
+
+const PLACEHOLDER_SCALE = (props: StyledPlaceholderProps): number => (props.visible ? 1 : 0);
+
+const StyledPlaceholder = styled.div`
position: relative;
height: ${getSpace(SpaceSize.S)}px;
width: 100%;
@@ -132,7 +129,7 @@ const StyledPlaceholder = styled(placeholderDiv)`
top: 3px;
border-radius: 3px;
background: ${colors.blue40.toString()};
- transform: scale(0);
+ transform: scale(${PLACEHOLDER_SCALE});
transition: transform 0.2s;
z-index: 20;
}
@@ -146,30 +143,17 @@ const StyledPlaceholder = styled(placeholderDiv)`
left: ${getSpace(SpaceSize.XS)};
top: 5px;
background: ${colors.blue40.toString()};
- transform: scaleY(0);
+ transform: scaleY(${PLACEHOLDER_SCALE});
transition: transform 0.2s;
z-index: 20;
}
-
- ${(props: StyledPlaceholder) =>
- props.highlightPlaceholder
- ? `
- &::before {
- transform: scale(1);
- }
-
- &::after {
- transform: scaleY(1);
- }
- `
- : ''};
`;
const elementDiv = tag('div').omit(['open']);
-const StyledElementChild = styled(elementDiv)`
+
+const StyledElementChildren = styled(elementDiv)`
flex-basis: 100%;
padding-left: ${getSpace(SpaceSize.L)}px;
- ${(props: StyledElementChildProps) => (props.open ? 'display: block;' : 'display: none;')};
`;
const StyledIcon = styled(Icon)`
@@ -242,28 +226,14 @@ class SeamlessInput extends React.Component {
}
export const Element: React.StatelessComponent = props => (
-
+
{props.dragging && (
)}
-
+
{props.mayOpen && (
= props => (
size={IconSize.XXS}
color={colors.grey60}
open={props.open}
- active={props.active}
+ active={props.state === ElementState.Active}
/>
)}
- {props.editable ? (
+ {props.state === ElementState.Editable ? (
= props => (
autoSelect
/>
) : (
-
+
{props.title}
)}
- {props.children && (
- {props.children}
- )}
+ {props.open && props.children ? (
+ {props.children}
+ ) : null}
);
diff --git a/src/container/element-list/element-container.tsx b/src/container/element-list/element-container.tsx
index 51d2353a3..4ce8e7139 100644
--- a/src/container/element-list/element-container.tsx
+++ b/src/container/element-list/element-container.tsx
@@ -4,6 +4,7 @@ import { ElementContentContainer } from './element-content-container';
import * as MobxReact from 'mobx-react';
import * as Model from '../../model';
import * as React from 'react';
+import { ViewStore } from '../../store';
export interface ElementContainerProps {
element: Model.Element;
@@ -12,21 +13,27 @@ export interface ElementContainerProps {
@MobxReact.observer
export class ElementContainer extends React.Component {
public render(): JSX.Element | null {
+ const store = ViewStore.getInstance();
const { props } = this;
const open =
props.element.getOpen() || props.element.getDescendants().some(e => e.getSelected());
+
+ // Ensure mobx registers
+ props.element.getSelected();
+ props.element.isNameEditable();
+ props.element.getHighlighted();
+ props.element.acceptsChildren();
+
return (
{open
@@ -40,3 +47,25 @@ export class ElementContainer extends React.Component {
);
}
}
+
+const getState = (element: Model.Element): Components.ElementState => {
+ const store = ViewStore.getInstance();
+
+ if (element.getSelected() && element.isNameEditable()) {
+ return Components.ElementState.Editable;
+ }
+
+ if (element.getSelected()) {
+ return Components.ElementState.Active;
+ }
+
+ if (store.getDragging() && !element.acceptsChildren()) {
+ return Components.ElementState.Disabled;
+ }
+
+ if (element.getHighlighted()) {
+ return Components.ElementState.Highlighted;
+ }
+
+ return Components.ElementState.Default;
+};
diff --git a/src/container/element-list/element-list.tsx b/src/container/element-list/element-list.tsx
index 406103630..99f566e05 100644
--- a/src/container/element-list/element-list.tsx
+++ b/src/container/element-list/element-list.tsx
@@ -9,10 +9,6 @@ import * as Store from '../../store';
import styled from 'styled-components';
import * as Types from '../../model/types';
-export interface ElementListState {
- dragging: boolean;
-}
-
const DRAG_IMG_STYLE = `
position: fixed;
top: 100vh;
@@ -180,9 +176,6 @@ export class ElementList extends React.Component {
return;
}
- draggedElement.setDragged(true);
- store.setSelectedElement(draggedElement);
-
const dragImg = document.createElement('div');
dragImg.textContent = draggedElement.getName();
dragImg.setAttribute('style', DRAG_IMG_STYLE);
@@ -191,6 +184,12 @@ export class ElementList extends React.Component {
e.dataTransfer.effectAllowed = 'copy';
e.dataTransfer.setDragImage(dragImg, 75, 15);
this.dragImg = dragImg;
+
+ // tslint:disable-next-line:no-any
+ (window as any).requestIdleCallback(() => {
+ draggedElement.setDragged(true);
+ store.setSelectedElement(draggedElement);
+ });
}
private handleDrop(e: React.DragEvent): void {