From 8fab95d691bb8ad74a472ff06c5e212ca9e3338e Mon Sep 17 00:00:00 2001 From: "@wwwjim" Date: Sat, 22 Aug 2020 02:32:40 -0700 Subject: [PATCH] slates: lands drag and drop concept --- components/core/Application.js | 20 ++- components/core/Slate.js | 30 +++++ components/core/SlateMediaObjectPreview.js | 149 +++++++++++++++++---- scenes/SceneSlate.js | 21 ++- 4 files changed, 187 insertions(+), 33 deletions(-) diff --git a/components/core/Application.js b/components/core/Application.js index f7a82422..fd5456c9 100644 --- a/components/core/Application.js +++ b/components/core/Application.js @@ -114,11 +114,19 @@ export default class ApplicationPage extends React.Component { }; _handleDragEnter = (e) => { + e.preventDefault(); if (this.state.sidebar) { return; } - e.preventDefault(); + // NOTE(jim): Only allow the sidebar to show with file drag and drop. + if ( + e.dataTransfer.items && + e.dataTransfer.items.length && + e.dataTransfer.items[0].kind !== "file" + ) { + return; + } this._handleAction({ type: "SIDEBAR", @@ -137,6 +145,12 @@ export default class ApplicationPage extends React.Component { _handleSidebarLoading = (sidebarLoading) => this.setState({ sidebarLoading }); _handleDrop = async (e) => { + // NOTE(jim): If this is true, then drag and drop came from a slate object. + const data = e.dataTransfer.getData("slate-object-drag-data"); + if (data) { + return; + } + e.preventDefault(); this.setState({ fileLoading: true }); @@ -154,7 +168,7 @@ export default class ApplicationPage extends React.Component { const files = []; let fileLoading = {}; - if (e.dataTransfer.items) { + if (e.dataTransfer.items && e.dataTransfer.items.length) { for (var i = 0; i < e.dataTransfer.items.length; i++) { if (e.dataTransfer.items[i].kind === "file") { var file = e.dataTransfer.items[i].getAsFile(); @@ -445,8 +459,6 @@ export default class ApplicationPage extends React.Component { ); } - console.log(this.state.viewer); - // NOTE(jim): Authenticated. const navigation = NavigationData.generate(this.state.viewer); const next = this.state.history[this.state.currentIndex]; diff --git a/components/core/Slate.js b/components/core/Slate.js index 302f499e..e923281c 100644 --- a/components/core/Slate.js +++ b/components/core/Slate.js @@ -17,15 +17,45 @@ const STYLES_SLATE = css` `; export default class Slate extends React.Component { + state = { index: null, hiddenIndex: null }; + + _handleSetHoveringIndex = ({ index }) => { + if (!this.props.editing) { + return; + } + + if (this.state.index !== index) { + this.setState({ index }); + } + }; + + _handleSetHiddenIndex = ({ index }) => { + if (!this.props.editing) { + return; + } + + if (this.state.index !== index) { + this.setState({ hiddenIndex: index }); + } + }; + render() { return (
{this.props.items.map((each, index) => { return ( this.props.onSelect(index)} + onSetHoveringIndex={this._handleSetHoveringIndex} + onSetHiddenIndex={this._handleSetHiddenIndex} + onMoveIndex={this.props.onMoveIndex} url={each.url} /> ); diff --git a/components/core/SlateMediaObjectPreview.js b/components/core/SlateMediaObjectPreview.js index da12eeb8..ff8d0331 100644 --- a/components/core/SlateMediaObjectPreview.js +++ b/components/core/SlateMediaObjectPreview.js @@ -12,6 +12,7 @@ const STYLES_ITEM = css` justify-content: center; position: relative; transition: 200ms ease all; + transform: translateX(0px); `; const STYLES_IMAGE = css` @@ -21,63 +22,155 @@ const STYLES_IMAGE = css` cursor: pointer; `; -const STYLES_PDF = css` +const STYLES_ENTITY = css` height: 100%; width: 100%; border: 1px solid ${Constants.system.border}; + background-color: ${Constants.system.foreground}; font-size: 24px; display: flex; align-items: center; justify-content: center; cursor: pointer; + user-select: none; +`; + +const STYLES_ROOT = css` + display: flex; `; export default class SlateMediaObjectPreview extends React.Component { + _handleDragStart = (e) => { + if (!this.props.editing) { + return; + } + + e.stopPropagation(); + + e.dataTransfer.setData( + "slate-object-drag-data", + JSON.stringify({ + ...this.props, + dragging: true, + }) + ); + + this.props.onSetHiddenIndex({ index: this.props.index }); + }; + + _handleDragOver = (e) => { + if (!this.props.editing) { + return; + } + + e.preventDefault(); + this.props.onSetHoveringIndex({ index: this.props.index }); + }; + + _handleDragEnd = (e) => { + if (!this.props.editing) { + return; + } + + e.preventDefault(); + this.props.onSetHoveringIndex({ index: null }); + this.props.onSetHiddenIndex({ index: null }); + }; + + _handleDrop = async (e) => { + if (!this.props.editing) { + return; + } + + e.preventDefault(); + this.props.onSetHoveringIndex({ index: null }); + this.props.onSetHiddenIndex({ index: null }); + const data = e.dataTransfer.getData("slate-object-drag-data"); + if (!data) { + return; + } + + let parsed = JSON.parse(data); + + return await this.props.onMoveIndex(parsed, this.props); + }; + render() { + let element = ( +
+ No Preview +
+ ); + if (this.props.type && this.props.type.startsWith("video/")) { - return ( - -
- Video -
-
+ element = ( +
+ Video +
); } if (this.props.type && this.props.type.startsWith("audio/")) { - return ( - -
- Audio -
-
+ element = ( +
+ Audio +
); } if (this.props.type && this.props.type.startsWith("application/epub")) { - return ( - -
- EPub -
-
+ element = ( +
+ EPub +
); } if (this.props.type && this.props.type.startsWith("application/pdf")) { - return ( - -
- PDF -
-
+ element = ( +
+ PDF +
); } + if (this.props.type && this.props.type.startsWith("image/")) { + element = ( + + ); + } + + const translateDirection = this.props.lastIndex + ? `translateX(-228px)` + : `translateX(228px)`; + return ( - - - +
+ + {element} + +
); } } diff --git a/scenes/SceneSlate.js b/scenes/SceneSlate.js index 3e4b0685..220b5e50 100644 --- a/scenes/SceneSlate.js +++ b/scenes/SceneSlate.js @@ -12,6 +12,14 @@ import Slate from "~/components/core/Slate"; import SlateMediaObject from "~/components/core/SlateMediaObject"; import CircleButtonLight from "~/components/core/CircleButtonLight"; +const moveIndex = (set, fromIndex, toIndex) => { + const element = set[fromIndex]; + set.splice(fromIndex, 1); + set.splice(toIndex, 0, element); + + return set; +}; + export default class SceneSlate extends React.Component { state = { slatename: this.props.current.slatename, @@ -56,6 +64,12 @@ export default class SceneSlate extends React.Component { } } + _handleMoveIndex = async (from, to) => { + const objects = moveIndex(this.state.objects, from.index, to.index); + this.setState({ objects }); + await this._handleSave(null, objects); + }; + _handleSave = async (e, objects) => { this.setState({ loading: true }); @@ -217,7 +231,12 @@ export default class SceneSlate extends React.Component { > {body} - + ); }