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

Commit

Permalink
feat(drag-and-drop): component implementation (#603)
Browse files Browse the repository at this point in the history
* feat(chrome-name): component prep for the new editable title

* feat(chrome-title): include interaction handlers

* feat(editable-title): extract component from preview tile

* feat(editable-title-component): implementation and integration

* feat(editable-title): implementation of the editable title container

* chore(general): fix lint errors from origin

* fix(editable-title): rename the state enum

:

* fix(editable-title): include data-attr directly during rendering

* chore(editable-tigle): rename the editable-title-state enum

* fix(editable-title): remove fontSize props

* feat(editable title): include title type enumerable

* chore(editable-title): extract dinamic styled into own functions

* chore(page-tile): remove unnecessary HTML and styling

* fix(editable-title): remove text truncation on input (edit mode)

* fix(editable-container): move global state into an internal state

* fix: allow primary and secondary type

* fix(editable-title): include required category property

* chore(types): include editable title type in the model types

* fix(editable-title): save name edited name on blur

* feat(drag-area): include component

* feat(drag-area): inlude component methods

* feat(page-tile): adjustments for drag and drop implementation

* chore(utils): implement utils module for reusability

* feat(drag-area): include componentsnecessary event handler

* feat(element-list): include drag area component

* feat(page-list-container): convert funtional component into class

* feat(drag-area): include dropt target signal in the component

* feat(page-tile): include page tile model

* feat(page-tile): WIP create a page tile store

* fix(page-tile): remove page tile model and store

* feat(page-list): include drag and drop functionality

* feat(page-list-container): save reorder page tiles

;

* feat(page-list-container): implement drag and drop functionality

* feat(page-order): include undo after action

* chore(container): remove unused comments

* feat(page-order): include states for the page drop index

* feat(page-order): highlight styling adjustment

* fix(project): dont't remove pages from internal model on reArrangePagesIndex

* fix(element-list): fix drag & drop on element list

* refactor(drag-area): remove unnecessary props

* refactor(drag-element): include anchors interface

* refactor(drag-area): include a unnified interface for the data attr

;

* refactor(utils): include all helpers in a single place

* refactor(drag-area): include getDragAreaAnchors methods
  • Loading branch information
Palmaswell authored Sep 13, 2018
1 parent 14a5706 commit f2f6c3c
Show file tree
Hide file tree
Showing 24 changed files with 644 additions and 305 deletions.
30 changes: 30 additions & 0 deletions src/alva-util/drag-and-drop.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import * as Model from '../model';

export function calculateDropIndex(init: {
dragged: Model.Element;
target: Model.Element;
}): number {
const { dragged, target } = init;

// We definitely know the drop target has a parent, thus an index
const newIndex = target.getIndex() as number;

// The dragged element is dropped into another
// leaf list than it was dragged from.
// True for (1) new elements, (2) elements dragged to other parents
if (dragged.getContainer() !== target.getContainer()) {
return newIndex;
}

// If the dragged element has a parent, it has an index
const currentIndex = dragged.getIndex();

// The dragged element is dropped in the same leaf
// list as it was dragged from.
// Offset the index by the element itself missing from the new list.
if (newIndex > currentIndex) {
return newIndex - 1;
}

return newIndex;
}
4 changes: 3 additions & 1 deletion src/alva-util/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
export * from './compute-difference';
export * from './drag-and-drop';
export * from './ensure-array';
export * from './guess-name';
export * from './noop';
export * from './parse-json';
export * from './to-json';
export * from './set-search';
export * from './target';
export * from './to-json';
90 changes: 90 additions & 0 deletions src/alva-util/target.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import * as Model from '../model';
import * as Store from '../store';
import * as Components from '../components';

export 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;
}

export function pageFromTarget(
target: EventTarget,
store: Store.ViewStore
): Model.Page | undefined {
const el = above(target, `[${Components.PageAnchors.page}]`);
if (!el) {
return;
}
const pageId = el.getAttribute(Components.PageAnchors.page);

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

const page = store.getPageById(pageId);

if (!page) {
return;
}

return page;
}

export function elementFromTarget(
target: EventTarget,
options: { sibling: boolean; store: Store.ViewStore }
): Model.Element | undefined {
const el = above(target, `[${Components.ElementAnchors.element}]`);

if (!el) {
return;
}

const id = el.getAttribute(Components.ElementAnchors.element);

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

const element = options.store.getElementById(id);

if (!element) {
return;
}

return options.sibling ? element.getParent() : element;
}

export function elementContentFromTarget(
target: EventTarget,
options: { store: Store.ViewStore }
): Model.ElementContent | undefined {
const el = above(target, `[${Components.ElementAnchors.content}]`);

if (!el) {
return;
}

const id = el.getAttribute(Components.ElementAnchors.content);

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

return options.store.getContentById(id);
}
2 changes: 1 addition & 1 deletion src/components/add-page-button/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const StyledAddPageButton = styled.button`
border: 1px solid ${Color.Grey80};
border-radius: 6px;
background-color: transparent;
margin: ${getSpace(SpaceSize.S)}px;
margin: ${getSpace(SpaceSize.XS)}px;
display: flex;
align-items: center;
justify-content: center;
Expand Down
24 changes: 24 additions & 0 deletions src/components/drag-area/demo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import * as React from 'react';
import DemoContainer from '../demo-container';

import { DragArea, DragAreaAnchors } from './index';

export const DemoDragArea: React.SFC<{}> = (): JSX.Element => (
<DemoContainer>
<DragArea
anchors={{
[DragAreaAnchors.element]: '12343',
[DragAreaAnchors.content]: '11111'
}}
onDragStart={e => e}
onDragLeave={e => e}
onDragOver={e => e}
onDrop={e => e}
onDragEnter={e => e}
>
<div draggable>Drag Area Element</div>
</DragArea>
</DemoContainer>
);

export default DemoDragArea;
44 changes: 44 additions & 0 deletions src/components/drag-area/drag-area.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import * as React from 'react';
import styled from 'styled-components';

export enum DragAreaAnchors {
element = 'data-id',
content = 'data-content-id'
}

export interface DragAreaAnchorProps {
[DragAreaAnchors.element]: string;
[DragAreaAnchors.content]: string;
}

export interface DragAreaProps {
anchors: DragAreaAnchorProps;
onDragEnter: React.DragEventHandler<HTMLElement>;
onDragStart: React.DragEventHandler<HTMLElement>;
onDragLeave: React.DragEventHandler<HTMLElement>;
onDragOver: React.DragEventHandler<HTMLElement>;
onDrop: React.DragEventHandler<HTMLElement>;
onBlur?: React.FocusEventHandler<HTMLElement>;
onChange?: React.FormEventHandler<HTMLElement>;
onClick?: React.MouseEventHandler<HTMLElement>;
onContextMenu?: React.MouseEventHandler<HTMLElement>;
onKeyDown?: React.KeyboardEventHandler<HTMLElement>;
onMouseLeave?: React.MouseEventHandler<HTMLElement>;
onMouseOver?: React.MouseEventHandler<HTMLElement>;
}

const StyledDragArea = styled.div`
width: 100%;
height: 100%;
`;

export const DragArea: React.SFC<DragAreaProps> = props => (
<StyledDragArea
data-drag-root
data-content-id={props.anchors['data-content-id']}
data-id={props.anchors['data-id']}
{...props}
>
{props.children}
</StyledDragArea>
);
2 changes: 2 additions & 0 deletions src/components/drag-area/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './drag-area';
export * from './target-signal';
7 changes: 7 additions & 0 deletions src/components/drag-area/pattern.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"name": "drag-area",
"displayName": "Drag Area",
"flag": "alpha",
"version": "1.0.0",
"tags": ["globals"]
}
57 changes: 57 additions & 0 deletions src/components/drag-area/target-signal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import * as React from 'react';
import styled from 'styled-components';
import { Color } from '../colors';
import { getSpace, SpaceSize } from '../space';

export interface TargetSignalProps {
visible: boolean;
}

interface StyledTargetSignalProps {
visible: boolean;
}

const TARGET_SIGNAL_SCALE = (props: StyledTargetSignalProps): number => (props.visible ? 1 : 0);

const StyledTargetSignal = styled.div`
position: relative;
height: ${getSpace(SpaceSize.S)}px;
width: 100%;
margin-top: -${getSpace(SpaceSize.XS)}px;
margin-bottom: -${getSpace(SpaceSize.XS)}px;
z-index: 10;
&::before {
content: '';
display: block;
position: absolute;
height: 6px;
width: 6px;
left: 0;
top: 3px;
border-radius: 3px;
background: ${Color.Blue40};
transform: scale(${TARGET_SIGNAL_SCALE});
transition: transform 0.2s;
z-index: 20;
}
&::after {
content: '';
display: block;
position: absolute;
height: 2px;
width: calc(100% - 6px);
left: ${getSpace(SpaceSize.XS)};
top: 5px;
background: ${Color.Blue40};
transform: scaleY(${TARGET_SIGNAL_SCALE});
transition: transform 0.2s;
z-index: 20;
}
`;

export const TargetSignal: React.SFC<TargetSignalProps> = ({
visible,
...dataPlaceHolder
}): JSX.Element => <StyledTargetSignal {...dataPlaceHolder} visible={visible} />;
47 changes: 2 additions & 45 deletions src/components/element/element.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import * as React from 'react';
import { getSpace, SpaceSize } from '../space';
import styled from 'styled-components';
import { tag } from '../tag';
import { TargetSignal } from '../drag-area';

export const ElementAnchors = {
element: 'data-id',
Expand Down Expand Up @@ -114,50 +115,6 @@ const StyledElementLabel = styled(div)`
}
`;

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%;
margin-top: -${getSpace(SpaceSize.XS)}px;
margin-bottom: -${getSpace(SpaceSize.XS)}px;
z-index: 10;
&::before {
content: '';
display: block;
position: absolute;
height: 6px;
width: 6px;
left: 0;
top: 3px;
border-radius: 3px;
background: ${Color.Blue40};
transform: scale(${PLACEHOLDER_SCALE});
transition: transform 0.2s;
z-index: 20;
}
&::after {
content: '';
display: block;
position: absolute;
height: 2px;
width: calc(100% - 6px);
left: ${getSpace(SpaceSize.XS)};
top: 5px;
background: ${Color.Blue40};
transform: scaleY(${PLACEHOLDER_SCALE});
transition: transform 0.2s;
z-index: 20;
}
`;

const elementDiv = tag('div').omit(['open']);

const StyledElementChildren = styled(elementDiv)`
Expand Down Expand Up @@ -283,7 +240,7 @@ export class Element extends React.Component<ElementProps> {
>
{props.placeholder &&
props.dragging && (
<StyledPlaceholder
<TargetSignal
{...{ [ElementAnchors.placeholder]: true }}
visible={Boolean(props.placeholderHighlighted)}
/>
Expand Down
Loading

0 comments on commit f2f6c3c

Please sign in to comment.