From be9df1d2bfc2fdc43b8c5c66716db929b91be31e Mon Sep 17 00:00:00 2001 From: Mario Nebl Date: Mon, 28 May 2018 17:54:36 +0200 Subject: [PATCH] feat: indicate elements that do not accept children --- src/components/element/demo.tsx | 104 +++++++++---- src/components/element/index.tsx | 137 +++++++----------- .../element-list/element-container.tsx | 39 ++++- src/container/element-list/element-list.tsx | 13 +- 4 files changed, 170 insertions(+), 123 deletions(-) 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 {