diff --git a/components/core/DataView.js b/components/core/DataView.js index 385904ea..b2d58c45 100644 --- a/components/core/DataView.js +++ b/components/core/DataView.js @@ -16,6 +16,7 @@ import { CheckBox } from "~/components/system/components/CheckBox"; import { Table } from "~/components/core/Table"; import { FileTypeIcon } from "~/components/core/FileTypeIcon"; import { ButtonPrimary, ButtonWarning } from "~/components/system/components/Buttons"; +import { GroupSelectable, Selectable } from "~/components/core/Selectable/"; import SlateMediaObjectPreview from "~/components/core/SlateMediaObjectPreview"; import FilePreviewBubble from "~/components/core/FilePreviewBubble"; @@ -283,6 +284,31 @@ export default class DataView extends React.Component { } }; + _addSelectedItemsOnDrag = (e) => { + let selectedItems = {}; + for (const i of e) { + selectedItems[i] = true; + } + this.setState({ checked: { ...this.state.checked, ...selectedItems } }); + }; + + _removeSelectedItemsOnDrag = (e) => { + const selectedItems = { ...this.state.checked }; + for (const i in selectedItems) { + selectedItems[i] = selectedItems[i] && !e.includes(+i); + if (!selectedItems[i]) delete selectedItems[i]; + } + this.setState({ checked: selectedItems, ...selectedItems }); + }; + + _handleDragAndSelect = (e, { isAltDown }) => { + if (isAltDown) { + this._removeSelectedItemsOnDrag(e); + return; + } + this._addSelectedItemsOnDrag(e); + }; + _handleKeyUp = (e) => { if (e.keyCode === 16 && this.isShiftDown) { this.isShiftDown = false; @@ -379,6 +405,14 @@ export default class DataView extends React.Component { }); }; + _handleCheckBoxMouseEnter = (i) => { + this.setState({ hover: i }); + }; + + _handleCheckBoxMouseLeave = (i) => { + this.setState({ hover: null }); + }; + _handleCopy = (e, value) => { e.stopPropagation(); this._handleHide(); @@ -491,135 +525,138 @@ export default class DataView extends React.Component { if (this.props.view === 0) { return ( -
- {this.props.items.slice(0, this.state.viewLimit).map((each, i) => { - const cid = each.cid; - return ( -
+
+ {this.props.items.slice(0, this.state.viewLimit).map((each, i) => { + const cid = each.cid; + return ( + this._handleSelect(i)} - onMouseEnter={() => this.setState({ hover: i })} - onMouseLeave={() => this.setState({ hover: null })} - > - - - {numChecked || this.state.hover === i || this.state.menu === each.id ? ( - -
{ - e.stopPropagation(); - this.setState({ - menu: this.state.menu === each.id ? null : each.id, - }); - }} - > - - {this.state.menu === each.id ? ( - - {this.props.isOwner ? ( - this._handleCopy(e, cid), - }, - { - text: "Copy link", - onClick: (e) => - this._handleCopy(e, Strings.getCIDGatewayURL(cid)), - }, - { - text: "Delete", - onClick: (e) => { - e.stopPropagation(); - this.setState({ menu: null }, () => - this._handleDelete(cid, each.id) - ); + : "", + }} + onClick={() => this._handleSelect(i)} + onMouseEnter={() => this._handleCheckBoxMouseEnter(i)} + onMouseLeave={() => this._handleCheckBoxMouseLeave(i)} + > + + + {numChecked || this.state.hover === i || this.state.menu === each.id ? ( + +
{ + e.stopPropagation(); + this.setState({ + menu: this.state.menu === each.id ? null : each.id, + }); + }} + > + + {this.state.menu === each.id ? ( + + {this.props.isOwner ? ( + this._handleCopy(e, cid), }, - }, - ]} - /> - ) : ( - this._handleCopy(e, cid), - }, - { - text: "Copy link", - onClick: (e) => - this._handleCopy(e, Strings.getCIDGatewayURL(cid)), - }, - ]} - /> - )} - - ) : null} -
+ { + text: "Copy link", + onClick: (e) => + this._handleCopy(e, Strings.getCIDGatewayURL(cid)), + }, + { + text: "Delete", + onClick: (e) => { + e.stopPropagation(); + this.setState({ menu: null }, () => + this._handleDelete(cid, each.id) + ); + }, + }, + ]} + /> + ) : ( + this._handleCopy(e, cid), + }, + { + text: "Copy link", + onClick: (e) => + this._handleCopy(e, Strings.getCIDGatewayURL(cid)), + }, + ]} + /> + )} +
+ ) : null} +
-
this._handleCheckBox(e, i)}> - -
-
- ) : null} -
-
- ); - })} - {[0, 1, 2, 3].map((i) => ( -
- ))} -
+
this._handleCheckBox(e, i)}> + +
+ + ) : null} + + + ); + })} + {[0, 1, 2, 3].map((i) => ( +
+ ))} +
+ {footer} { @@ -688,14 +725,16 @@ export default class DataView extends React.Component {
), name: ( - -
this._handleSelect(index)}> -
- + + +
this._handleSelect(index)}> +
+ +
+
{each.file || each.name}
-
{each.file || each.name}
-
- + + ), size:
{Strings.bytesToSize(each.size)}
, more: ( @@ -752,22 +791,32 @@ export default class DataView extends React.Component { return ( - this.setState({ hover: i })} - onMouseLeave={() => this.setState({ hover: null })} - isShiftDown={this.isShiftDown} - /> + + {({ isSelecting }) => ( +
{ + if (isSelecting) return; + this._handleCheckBoxMouseEnter(i); + }} + onMouseLeave={() => { + if (isSelecting) return; + this._handleCheckBoxMouseEnter(); + }} + isShiftDown={this.isShiftDown} + /> + )} + {footer} { diff --git a/components/core/Selectable/doObjectsCollide.js b/components/core/Selectable/doObjectsCollide.js new file mode 100644 index 00000000..3d3f34b0 --- /dev/null +++ b/components/core/Selectable/doObjectsCollide.js @@ -0,0 +1,41 @@ +const getBoundsForNode = (node) => { + const rect = node.getBoundingClientRect(); + return { + top: rect.top + document.body.scrollTop, + left: rect.left + document.body.scrollLeft, + offsetWidth: node.offsetWidth, + offsetHeight: node.offsetHeight, + }; +}; + +const coordsCollide = (aTop, aLeft, bTop, bLeft, aWidth, aHeight, bWidth, bHeight, tolerance) => { + return !( + // 'a' bottom doesn't touch 'b' top + ( + aTop + aHeight - tolerance < bTop || + // 'a' top doesn't touch 'b' bottom + aTop + tolerance > bTop + bHeight || + // 'a' right doesn't touch 'b' left + aLeft + aWidth - tolerance < bLeft || + // 'a' left doesn't touch 'b' right + aLeft + tolerance > bLeft + bWidth + ) + ); +}; + +export default (a, b, tolerance = 0) => { + const aObj = a instanceof HTMLElement ? getBoundsForNode(a) : a; + const bObj = b instanceof HTMLElement ? getBoundsForNode(b) : b; + + return coordsCollide( + aObj.top, + aObj.left, + bObj.top, + bObj.left, + aObj.offsetWidth, + aObj.offsetHeight, + bObj.offsetWidth, + bObj.offsetHeight, + tolerance + ); +}; diff --git a/components/core/Selectable/groupSelectable.js b/components/core/Selectable/groupSelectable.js new file mode 100644 index 00000000..d848c25d --- /dev/null +++ b/components/core/Selectable/groupSelectable.js @@ -0,0 +1,225 @@ +import * as React from "react"; +import * as Constants from "~/common/constants"; +import { css } from "@emotion/react"; + +import doObjectsCollide from "./doObjectsCollide"; + +const Context = React.createContext(); + +export const useSelectable = () => { + return React.useContext(Context); +}; + +const GROUP_WRAPPER = css` + position: relative; + overflow: visible; +`; + +const SELECTION_BOX_WRAPPER = css` + z-index: 9000; + position: absolute; + cursor: default; +`; + +const SELECTION_BOX_INNER = css` + background-color: rgba(255, 255, 255, 0.45); + border: 1px dashed ${Constants.system.border}; + width: 100%; + height: 100%; + float: left; +`; + +export default function GroupSelectable({ + onSelection, + onSelectionStarted, + children, + enabled: enabledProp = true, + ...props +}) { + const ref = React.useRef(); + const selectBoxRef = React.useRef(); + + const isShiftDown = useKeyDown(16); + const isAltDown = useKeyDown(18); + const enabled = enabledProp && (isShiftDown || isAltDown); + + const { _registerSelectable, _unregisterUnselectable, registery } = useRegistery(); + const { isBoxSelecting, boxLeft, boxTop, boxWidth, boxHeight } = useGroupSelectable({ + ref, + selectBoxRef, + enabled, + onSelection: (e) => onSelection(e, { isShiftDown, isAltDown }), + onSelectionStarted, + registery, + }); + + return ( + +
+ {isBoxSelecting ? ( +
+ +
+ ) : null} +
{typeof children === "function" ? children({ isSelecting: enabled }) : children}
+
+
+ ); +} + +const useKeyDown = (id) => { + const [isKeyDown, setKeyDownBool] = React.useState(false); + const _handleKeyDown = (e) => { + if (e.keyCode === id) setKeyDownBool(true); + }; + const _handleKeyUp = (e) => { + if (e.keyCode === id) setKeyDownBool(false); + }; + React.useEffect(() => { + window.addEventListener("keydown", _handleKeyDown); + window.addEventListener("keyup", _handleKeyUp); + return () => { + window.removeEventListener("keydown", _handleKeyDown); + window.removeEventListener("keyup", _handleKeyUp); + }; + }, []); + return isKeyDown; +}; + +const useRegistery = () => { + const data = React.useRef({ registery: [] }); + const _registerSelectable = (key, domNode) => { + data.current.registery.push({ key, domNode }); + }; + const _unregisterUnselectable = (key) => + data.current.registery.filter((item) => item.key !== key); + return { _registerSelectable, _unregisterUnselectable, registery: data.current.registery }; +}; + +const _getInitialCoords = (element) => { + const style = window.getComputedStyle(document.body); + const t = style.getPropertyValue("margin-top"); + const l = style.getPropertyValue("margin-left"); + const mLeft = parseInt(l.slice(0, l.length - 2), 10); + const mTop = parseInt(t.slice(0, t.length - 2), 10); + + const bodyRect = document.body.getBoundingClientRect(); + const elemRect = element.getBoundingClientRect(); + return { + x: Math.round(elemRect.left - bodyRect.left + mLeft), + y: Math.round(elemRect.top - bodyRect.top + mTop), + }; +}; + +const useGroupSelectable = ({ + ref, + selectBoxRef, + enabled, + onSelection, + onSelectionStarted, + registery, +}) => { + const [state, setState] = React.useState({ + isBoxSelecting: false, + boxHeight: 0, + boxWidth: 0, + }); + + const data = React.useRef({ + mouseDataDown: null, + rect: null, + }); + + React.useEffect(() => { + if (!ref.current) return; + _applyMouseDown(enabled); + data.current.rect = _getInitialCoords(ref.current); + + return () => { + if (!ref.current) return; + _applyMouseDown(false); + }; + }, [enabled]); + + const _applyMouseDown = (enabled) => { + const fncName = enabled ? "addEventListener" : "removeEventListener"; + ref.current[fncName]("mousedown", _mousedown); + }; + + const _drawBox = (e) => { + const w = Math.abs(data.current.mouseDataDown.initialW - e.pageX + data.current.rect.x); + const h = Math.abs(data.current.mouseDataDown.initialH - e.pageY + data.current.rect.y); + + setState({ + isBoxSelecting: true, + boxWidth: w, + boxHeight: h, + boxLeft: Math.min(e.pageX - data.current.rect.x, data.current.mouseDataDown.initialW), + boxTop: Math.min(e.pageY - data.current.rect.y, data.current.mouseDataDown.initialH), + }); + }; + + const _selectElements = (e) => { + const currentItems = []; + const _selectbox = selectBoxRef.current; + if (!_selectbox) return; + registery.forEach((item) => { + if ( + item.domNode && + doObjectsCollide(_selectbox, item.domNode) && + !currentItems.includes(item.key) + ) { + currentItems.push(item.key); + } + }); + onSelection(currentItems, e); + }; + + const _mousedown = (e) => { + e.preventDefault(); + + if (typeof onSelectionStarted === "function") onSelectionStarted(e); + + window.addEventListener("mouseup", _mouseUp); + + // Right clicks + if (e.which === 3 || e.button === 2) return; + + data.current.rect = _getInitialCoords(ref.current); + data.current.mouseDataDown = { + boxLeft: e.pageX - data.current.rect.x, + boxTop: e.pageY - data.current.rect.y, + initialW: e.pageX - data.current.rect.x, + initialH: e.pageY - data.current.rect.y, + }; + window.addEventListener("mousemove", _drawBox); + }; + + const _mouseUp = (e) => { + e.stopPropagation(); + + window.removeEventListener("mousemove", _drawBox); + window.removeEventListener("mouseup", _mouseUp); + + if (!data.current.mouseDataDown) return; + _selectElements(e, true); + data.current.mouseDataDown = null; + setState({ + isBoxSelecting: false, + boxWidth: 0, + boxHeight: 0, + }); + }; + + return state; +}; diff --git a/components/core/Selectable/index.js b/components/core/Selectable/index.js new file mode 100644 index 00000000..f52e6c82 --- /dev/null +++ b/components/core/Selectable/index.js @@ -0,0 +1,2 @@ +export { default as GroupSelectable } from "./groupSelectable"; +export { default as Selectable } from "./selectable"; diff --git a/components/core/Selectable/selectable.js b/components/core/Selectable/selectable.js new file mode 100644 index 00000000..ede2c039 --- /dev/null +++ b/components/core/Selectable/selectable.js @@ -0,0 +1,26 @@ +import * as React from "react"; +import { useSelectable } from "./groupSelectable"; + +export default function Selectable({ children, selectableKey, style, ...props }) { + const ref = React.useRef(); + const selectable = useSelectable(); + React.useEffect(() => { + if (selectable) { + selectable.register(selectableKey, ref.current); + return () => selectable.unregister(selectableKey); + } + }); + return ( +
+ {children} +
+ ); +} diff --git a/components/core/SlateLayout.js b/components/core/SlateLayout.js index f3b11d8d..6faabdc9 100644 --- a/components/core/SlateLayout.js +++ b/components/core/SlateLayout.js @@ -23,6 +23,7 @@ import { ButtonDisabled, ButtonWarning, } from "~/components/system/components/Buttons"; +import { GroupSelectable, Selectable } from "~/components/core/Selectable/"; //NOTE(martina): sets 200px as the standard width for a 1080px wide layout with 20px margin btwn images. //If the container is larger or smaller, it scales accordingly by that factor @@ -752,6 +753,31 @@ export class SlateLayout extends React.Component { } }; + _addSelectedItemsOnDrag = (e) => { + let selectedItems = {}; + for (const i of e) { + selectedItems[i] = true; + } + this.setState({ checked: { ...this.state.checked, ...selectedItems } }); + }; + + _removeSelectedItemsOnDrag = (e) => { + const selectedItems = { ...this.state.checked }; + for (const i in selectedItems) { + selectedItems[i] = selectedItems[i] && !e.includes(+i); + if (!selectedItems[i]) delete selectedItems[i]; + } + this.setState({ checked: selectedItems, ...selectedItems }); + }; + + _handleDragAndSelect = (e, { isAltDown }) => { + if (isAltDown) { + this._removeSelectedItemsOnDrag(e); + return; + } + this._addSelectedItemsOnDrag(e); + }; + _handleMouseDown = (e, i) => { e.stopPropagation(); e.preventDefault(); @@ -1098,687 +1124,700 @@ export class SlateLayout extends React.Component { let unit = this.state.unit; return (
- {this.props.isOwner ? ( - this.state.editing ? ( -
-
-
- - Display titles - -
- -
-
- {this.state.defaultLayout ? ( - - Reset layout - - ) : ( - - Reset layout - - )} - {this.state.prevLayouts.length ? ( - - Undo - - ) : ( - - Undo - - )} - {!this.state.keyboardTooltip ? ( -
this.setState({ keyboardTooltip: true })}> - this.setState({ keyboardTooltip: true })} - /> -
- ) : ( -
+ + {this.props.isOwner ? ( + this.state.editing ? ( +
+
+
this.setState({ keyboardTooltip: false })} + style={{ + fontFamily: Constants.font.semiBold, + fontSize: 14, + letterSpacing: "0.2px", + }} > - + Display titles -
-
-

- Keyboard shortcuts -

-
-
-

shift + click and drag

-

- keep x value or y value while moving file -

-
-
-

shift + click and resize

-

- keep aspect ratio while resizing -

-
-
-

ctrl + click and drag

-

- move without snapping to the dot grid -

-
-
-

ctrl + click and resize

-

- resize without snapping to the dot grid -

-
+
+
- )} -
-
- this._toggleEditing(e, true)} - style={{ cursor: "pointer", marginLeft: 16 }} - > - Cancel - - - Save - -
-
- ) : ( -
- - Edit - -
- ) - ) : null} -
-
{ - this._ref = c; - }} - > - {this.state.show ? ( - this.state.layout.map((pos, i) => ( -
this.setState({ hover: i })} - onMouseLeave={() => this.setState({ hover: null })} - onMouseDown={this.state.editing ? (e) => this._handleMouseDown(e, i) : () => {}} - onClick={this.state.editing ? () => {} : () => this.props.onSelect(i)} - style={{ - top: pos.y * unit, - left: pos.x * unit, - width: pos.w * unit, - height: this.state.fileNames ? (pos.h + TAG_HEIGHT) * unit : pos.h * unit, - zIndex: pos.z, - boxShadow: this.state.dragIndex === i ? `0 0 44px 0 rgba(0, 0, 0, 0.25)` : null, - backgroundColor: Constants.system.white, - }} - > - - {numChecked || this.state.hover === i ? ( -
- {this.props.external ? null : ( -
{ - this._stopProp(e); - let checked = this.state.checked; - if (checked[i]) { - delete checked[i]; - } else { - checked[i] = true; - } - this.setState({ checked }); + {this.state.defaultLayout ? ( + + Reset layout + + ) : ( + + Reset layout + + )} + {this.state.prevLayouts.length ? ( + + Undo + + ) : ( + + Undo + + )} + {!this.state.keyboardTooltip ? ( +
this.setState({ keyboardTooltip: true })}> + this.setState({ keyboardTooltip: true })} + /> +
+ ) : ( +
+ this.setState({ keyboardTooltip: false })} + > + - + +
+
+

+ > + Keyboard shortcuts +

- )} - {this.state.hover !== i ? null : this.state.editing ? ( - - {this.state.tooltip && this.state.tooltip.startsWith(`${i}-`) ? ( - - {this.state.tooltip === `${i}-remove` - ? "Remove from slate" - : this.state.tooltip === `${i}-view` - ? "View file" - : this.state.tooltip === `${i}-download` - ? "Download" - : "Delete file"} - - ) : null} +
+

shift + click and drag

+

+ keep x value or y value while moving file +

+
+
+

shift + click and resize

+

+ keep aspect ratio while resizing +

+
+
+

ctrl + click and drag

+

+ move without snapping to the dot grid +

+
+
+

ctrl + click and resize

+

+ resize without snapping to the dot grid +

+
+
+
+ )} +
+
+ this._toggleEditing(e, true)} + style={{ cursor: "pointer", marginLeft: 16 }} + > + Cancel + + + Save + +
+
+ ) : ( +
+ + Edit + +
+ ) + ) : null} +
+
{ + this._ref = c; + }} + > + {this.state.show ? ( + this.state.layout.map((pos, i) => ( + this.setState({ hover: i })} + onMouseLeave={() => this.setState({ hover: null })} + onMouseDown={this.state.editing ? (e) => this._handleMouseDown(e, i) : () => {}} + onClick={this.state.editing ? () => {} : () => this.props.onSelect(i)} + style={{ + top: pos.y * unit, + left: pos.x * unit, + width: pos.w * unit, + height: this.state.fileNames ? (pos.h + TAG_HEIGHT) * unit : pos.h * unit, + zIndex: pos.z, + boxShadow: + this.state.dragIndex === i ? `0 0 44px 0 rgba(0, 0, 0, 0.25)` : null, + backgroundColor: Constants.system.white, + }} + > + + {numChecked || this.state.hover === i ? ( +
+ {this.props.external ? null : (
this.setState({ tooltip: `${i}-remove` })} - onMouseLeave={() => this.setState({ tooltip: null })} onClick={(e) => { - this._handleRemoveFromSlate(e, i); - }} - style={{ - position: "absolute", - top: 8, - right: 8, - cursor: "pointer", - margin: 0, - }} - css={STYLES_ICON_CIRCLE} - > - -
-
-
this.setState({ tooltip: `${i}-view` })} - onMouseLeave={() => this.setState({ tooltip: null })} - onClick={(e) => { - this._stopProp(e); - this.props.onSelect(i); - }} - > - -
-
this.setState({ tooltip: `${i}-download` })} - onMouseLeave={() => this.setState({ tooltip: null })} - onClick={(e) => { - this._handleDownload(e, i); - }} - > - -
-
this.setState({ tooltip: `${i}-delete` })} - onMouseLeave={() => this.setState({ tooltip: null })} - onClick={ - this.state.items[i].ownerId === this.props.viewer.id - ? (e) => { - this._handleDeleteFiles(e, i); - } - : () => {} + this._stopProp(e); + let checked = this.state.checked; + if (checked[i]) { + delete checked[i]; + } else { + checked[i] = true; } - style={{ - cursor: - this.state.items[i].ownerId === this.props.viewer.id - ? "pointer" - : "not-allowed", - }} - > - -
-
- - ) : ( - - {this.state.tooltip && this.state.tooltip.startsWith(`${i}-`) ? ( - - {this.state.tooltip === `${i}-add` - ? "Add to slate" - : this.state.tooltip === `${i}-copy` - ? "Copy link" - : this.state.tooltip === `${i}-download` - ? "Download" - : this.state.tooltip === `${i}-preview` - ? "Make preview image" - : "Save copy"} - - ) : null} -
this.setState({ tooltip: `${i}-add` })} - onMouseLeave={() => this.setState({ tooltip: null })} - onClick={ - this.props.external - ? this._handleLoginModal - : (e) => { - this._handleAddToSlate(e, i); - } - } - style={{ - position: "absolute", - top: 8, - right: 8, - cursor: "pointer", - margin: 0, - }} - css={STYLES_ICON_CIRCLE} - > - -
-
- { - this._handleCopy(e, this.state.items[i].url); - }} - onMouseDown={this._stopProp} - onMouseUp={this._stopProp} - onMouseEnter={() => this.setState({ tooltip: `${i}-copy` })} - onMouseLeave={() => this.setState({ tooltip: null })} - successState={ - - } - style={{ + - - + style={{ + position: "absolute", + top: 8, + left: 8, + pointerEvents: "auto", + }} + /> +
+ )} + {this.state.hover !== i ? null : this.state.editing ? ( + + {this.state.tooltip && this.state.tooltip.startsWith(`${i}-`) ? ( + + {this.state.tooltip === `${i}-remove` + ? "Remove from slate" + : this.state.tooltip === `${i}-view` + ? "View file" + : this.state.tooltip === `${i}-download` + ? "Download" + : "Delete file"} + + ) : null}
this.setState({ tooltip: `${i}-download` })} + onMouseEnter={() => this.setState({ tooltip: `${i}-remove` })} + onMouseLeave={() => this.setState({ tooltip: null })} + onClick={(e) => { + this._handleRemoveFromSlate(e, i); + }} + style={{ + position: "absolute", + top: 8, + right: 8, + cursor: "pointer", + margin: 0, + }} + css={STYLES_ICON_CIRCLE} + > + +
+
+
this.setState({ tooltip: `${i}-view` })} + onMouseLeave={() => this.setState({ tooltip: null })} + onClick={(e) => { + this._stopProp(e); + this.props.onSelect(i); + }} + > + +
+
this.setState({ tooltip: `${i}-download` })} + onMouseLeave={() => this.setState({ tooltip: null })} + onClick={(e) => { + this._handleDownload(e, i); + }} + > + +
+
this.setState({ tooltip: `${i}-delete` })} + onMouseLeave={() => this.setState({ tooltip: null })} + onClick={ + this.state.items[i].ownerId === this.props.viewer.id + ? (e) => { + this._handleDeleteFiles(e, i); + } + : () => {} + } + style={{ + cursor: + this.state.items[i].ownerId === this.props.viewer.id + ? "pointer" + : "not-allowed", + }} + > + +
+
+
+ ) : ( + + {this.state.tooltip && this.state.tooltip.startsWith(`${i}-`) ? ( + + {this.state.tooltip === `${i}-add` + ? "Add to slate" + : this.state.tooltip === `${i}-copy` + ? "Copy link" + : this.state.tooltip === `${i}-download` + ? "Download" + : this.state.tooltip === `${i}-preview` + ? "Make preview image" + : "Save copy"} + + ) : null} +
this.setState({ tooltip: `${i}-add` })} onMouseLeave={() => this.setState({ tooltip: null })} onClick={ this.props.external ? this._handleLoginModal : (e) => { - this._handleDownload(e, i); + this._handleAddToSlate(e, i); } } + style={{ + position: "absolute", + top: 8, + right: 8, + cursor: "pointer", + margin: 0, + }} + css={STYLES_ICON_CIRCLE} > - +
- {this.props.isOwner ? ( +
+ { + this._handleCopy(e, this.state.items[i].url); + }} + onMouseDown={this._stopProp} + onMouseUp={this._stopProp} + onMouseEnter={() => this.setState({ tooltip: `${i}-copy` })} + onMouseLeave={() => this.setState({ tooltip: null })} + successState={ + + } + style={{ + height: 24, + width: 24, + borderRadius: "50%", + backgroundColor: "rgba(248, 248, 248, 0.6)", + color: "#4b4a4d", + display: "flex", + alignItems: "center", + justifyContent: "center", + cursor: "pointer", + margin: "0 8px", + WebkitBackdropFilter: "blur(25px)", + backdropFilter: "blur(25px)", + }} + > + +
this.setState({ tooltip: `${i}-preview` })} + onMouseEnter={() => this.setState({ tooltip: `${i}-download` })} onMouseLeave={() => this.setState({ tooltip: null })} onClick={ this.props.external ? this._handleLoginModal - : this.state.items[i].type && - Validations.isPreviewableImage(this.state.items[i].type) && - this.state.items[i].size && - this.state.items[i].size < SIZE_LIMIT - ? (e) => this._handleSetPreview(e, i) - : () => {} - } - style={ - this.props.preview === this.state.items[i].url - ? { - backgroundColor: "rgba(0, 97, 187, 0.75)", - } - : this.state.items[i].type && - Validations.isPreviewableImage(this.state.items[i].type) && - this.state.items[i].size && - this.state.items[i].size < SIZE_LIMIT - ? {} - : { - color: "#999999", - cursor: "not-allowed", + : (e) => { + this._handleDownload(e, i); } } > - {this.props.preview === - this.state.items[i].url.replace("https://undefined", "https://") ? ( - - ) : ( - - )} +
- ) : ( -
this.setState({ tooltip: `${i}-save` })} - onMouseLeave={() => this.setState({ tooltip: null })} - onClick={ - this.props.external - ? this._handleLoginModal - : (e) => this._handleSaveCopy(e, i) - } - > - -
- )} -
-
- )} -
- ) : null} - {this.state.fileNames ? ( -
- - {this.state.items[i].title || this.state.items[i].name} - - - {Strings.getFileExtension(this.state.items[i].file)} - -
- ) : null} - {this.state.editing ? ( -
this._handleMouseDownResize(e, i)} - style={{ - display: - this.state.hover === i || this.state.dragIndex === i ? "block" : "none", - }} - > - -
- ) : null} -
- )) - ) : ( -
- -
- )} -
-
- {numChecked ? ( -
-
-
- - {numChecked} file{numChecked > 1 ? "s" : ""} selected - -
- {this.props.isOwner ? ( -
- - - Add to slate - - - {/* - Download - */} - - Remove - - - - Delete files - -
this.setState({ checked: {} })}> - -
-
+ {this.props.isOwner ? ( +
this.setState({ tooltip: `${i}-preview` })} + onMouseLeave={() => this.setState({ tooltip: null })} + onClick={ + this.props.external + ? this._handleLoginModal + : this.state.items[i].type && + Validations.isPreviewableImage(this.state.items[i].type) && + this.state.items[i].size && + this.state.items[i].size < SIZE_LIMIT + ? (e) => this._handleSetPreview(e, i) + : () => {} + } + style={ + this.props.preview === this.state.items[i].url + ? { + backgroundColor: "rgba(0, 97, 187, 0.75)", + } + : this.state.items[i].type && + Validations.isPreviewableImage(this.state.items[i].type) && + this.state.items[i].size && + this.state.items[i].size < SIZE_LIMIT + ? {} + : { + color: "#999999", + cursor: "not-allowed", + } + } + > + {this.props.preview === + this.state.items[i].url.replace( + "https://undefined", + "https://" + ) ? ( + + ) : ( + + )} +
+ ) : ( +
this.setState({ tooltip: `${i}-save` })} + onMouseLeave={() => this.setState({ tooltip: null })} + onClick={ + this.props.external + ? this._handleLoginModal + : (e) => this._handleSaveCopy(e, i) + } + > + +
+ )} +
+ + )} +
+ ) : null} + {this.state.fileNames ? ( +
+ + {this.state.items[i].title || this.state.items[i].name} + + + {Strings.getFileExtension(this.state.items[i].file)} + +
+ ) : null} + {this.state.editing ? ( +
this._handleMouseDownResize(e, i)} + style={{ + display: + this.state.hover === i || this.state.dragIndex === i ? "block" : "none", + }} + > + +
+ ) : null} + + )) ) : ( -
- - Add to slate - - - Save copy - - {/* - Download - */} -
this.setState({ checked: {} })}> - -
+
+
)}
- ) : null} - { - this._input = c; - }} - readOnly - value={this.state.copyValue} - css={STYLES_COPY_INPUT} - /> - {this.props.external && this.state.signInModal && ( -
- this.setState({ signInModal: false })} - viewer={this.props.viewer} - open={this.state.signInModal} - redirectURL={`/_${Strings.createQueryParams({ - scene: "NAV_SLATE", - user: this.props.creator.username, - slate: this.props.slate.slatename, - })}`} - /> -
- )} + {numChecked ? ( +
+
+
+ + {numChecked} file{numChecked > 1 ? "s" : ""} selected + +
+ {this.props.isOwner ? ( +
+ + + Add to slate + + + {/* + Download + */} + + Remove + + + + Delete files + +
this.setState({ checked: {} })}> + +
+
+ ) : ( +
+ + Add to slate + + + Save copy + + {/* + Download + */} +
this.setState({ checked: {} })}> + +
+
+ )} +
+
+ ) : null} + { + this._input = c; + }} + readOnly + value={this.state.copyValue} + css={STYLES_COPY_INPUT} + /> + {this.props.external && this.state.signInModal && ( +
+ this.setState({ signInModal: false })} + viewer={this.props.viewer} + open={this.state.signInModal} + redirectURL={`/_${Strings.createQueryParams({ + scene: "NAV_SLATE", + user: this.props.creator.username, + slate: this.props.slate.slatename, + })}`} + /> +
+ )} +
); }