folders ui updates
parent
307105c96e
commit
8d5218f67c
|
@ -41,22 +41,6 @@ body {
|
|||
display: inline-block;
|
||||
padding: 0.4rem;
|
||||
}
|
||||
.color-picker-item {
|
||||
position: relative;
|
||||
.active-border {
|
||||
display: none;
|
||||
}
|
||||
&.active {
|
||||
.active-border {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
left: 1px;
|
||||
transform: scale(1.5);
|
||||
color: $white;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.bp3-popover.help {
|
||||
|
|
|
@ -238,6 +238,12 @@
|
|||
@media screen and (max-width: 767px) {
|
||||
margin-left: -15px;
|
||||
}
|
||||
.panel-top {
|
||||
margin-left: 1rem !important;
|
||||
button {
|
||||
margin-top: 0.7rem !important;
|
||||
}
|
||||
}
|
||||
.panel-top,
|
||||
.folder-button-cluster {
|
||||
i {
|
||||
|
@ -262,11 +268,41 @@
|
|||
display: flex;
|
||||
i {
|
||||
width: 1.5rem !important;
|
||||
line-height: 2rem !important;
|
||||
}
|
||||
}
|
||||
ul {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.folder-drop-area {
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
padding-left: 2rem;
|
||||
padding-right: 2rem;
|
||||
transition: height 0.5s ease-out,
|
||||
padding-top 0.5s ease-out,
|
||||
padding-bottom 0.5s ease-out;
|
||||
transition-delay: 0.4s;
|
||||
color: $gray;
|
||||
font-weight: bold;
|
||||
background: $white;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
&.visible {
|
||||
transition: height 0.3s ease-in,
|
||||
padding-top 0.3s ease-in,
|
||||
padding-bottom 0.3s ease-in;
|
||||
transition-delay: 0.2s;
|
||||
height: 3rem;
|
||||
padding-top: 0.5rem;
|
||||
padding-bottom: 0.5rem;
|
||||
}
|
||||
&:hover, &.hovered {
|
||||
color: $medium_gray;
|
||||
}
|
||||
}
|
||||
.folders {
|
||||
.folder > div:not(:first-child), ul {
|
||||
margin-left: 1rem;
|
||||
|
@ -277,24 +313,26 @@
|
|||
display: flex;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 3.5rem;
|
||||
border-bottom: 1px solid $light_gray;
|
||||
padding: 0.5rem;
|
||||
cursor: pointer;
|
||||
background: $lighter_gray;
|
||||
border-left: 4px solid transparent;
|
||||
&.active {
|
||||
border-left: 3px solid $dark_gray;
|
||||
border-left: 4px solid $dark_gray;
|
||||
}
|
||||
.fa-chevron-down, .fa-chevron-right {
|
||||
z-index: 2;
|
||||
width: 2rem;
|
||||
position: absolute;
|
||||
width: 3rem;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
.folder-settings-icon,
|
||||
.fa-arrows-v {
|
||||
.fa-bars {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
}
|
||||
.fa-arrows-v, .fa-ellipsis-v {
|
||||
.fa-bars, .fa-ellipsis-v {
|
||||
display: none;
|
||||
}
|
||||
.fa-ellipsis-v {
|
||||
|
@ -303,13 +341,13 @@
|
|||
}
|
||||
}
|
||||
&:hover {
|
||||
.fa-arrows-v, .fa-ellipsis-v {
|
||||
.fa-bars, .fa-ellipsis-v {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
i {
|
||||
margin: 0;
|
||||
line-height: 2.5rem;
|
||||
line-height: 3.5rem;
|
||||
width: 3rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
@ -319,27 +357,52 @@
|
|||
width: 1.2rem;
|
||||
height: 1.2rem;
|
||||
}
|
||||
a {
|
||||
width: 100%;
|
||||
}
|
||||
p {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
font-size: 1.2rem;
|
||||
font-weight: bold;
|
||||
width: 100%;
|
||||
width: 75%;
|
||||
padding: 0.5rem;
|
||||
margin-left: 2rem;
|
||||
padding-left: 0;
|
||||
margin-left: 3rem;
|
||||
line-height: 2.5rem;
|
||||
}
|
||||
.folder-name {
|
||||
width: 100%;
|
||||
margin-right: 3rem;
|
||||
.input {
|
||||
width: 90%;
|
||||
margin-left: 2rem;
|
||||
margin-left: 6rem;
|
||||
p {
|
||||
margin-left: 0;
|
||||
}
|
||||
.folder-name-input {
|
||||
display: flex;
|
||||
button {
|
||||
top: 0.5rem;
|
||||
width: auto;
|
||||
height: 2rem;
|
||||
i {
|
||||
line-height: 0;
|
||||
width: 1rem;
|
||||
}
|
||||
}
|
||||
.input {
|
||||
width: 90%;
|
||||
margin: 0.3rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
.sequence-list-item-icons {
|
||||
display: flex;
|
||||
margin-right: 3rem;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
.fa-hdd-o {
|
||||
margin-right: 3rem;
|
||||
}
|
||||
}
|
||||
button {
|
||||
margin-left: 0.5rem;
|
||||
|
@ -351,24 +414,34 @@
|
|||
background: $white;
|
||||
}
|
||||
}
|
||||
.folder-list-item,
|
||||
.sequence-list-item {
|
||||
padding-left: 2rem;
|
||||
.saucer {
|
||||
.bp3-popover-wrapper.color-picker {
|
||||
position: absolute;
|
||||
line-height: 0;
|
||||
.bp3-popover-target {
|
||||
width: 2rem;
|
||||
height: 3.5rem;
|
||||
}
|
||||
}
|
||||
padding-left: 3rem;
|
||||
.saucer, .icon-saucer {
|
||||
top: 0.55rem;
|
||||
position: relative;
|
||||
margin: auto;
|
||||
margin-top: 0.6rem;
|
||||
}
|
||||
}
|
||||
.folder-list-item {
|
||||
.bp3-popover-wrapper {
|
||||
position: absolute;
|
||||
padding-left: 0;
|
||||
.bp3-popover-wrapper.color-picker {
|
||||
margin-left: 3rem;
|
||||
}
|
||||
.color-picker {
|
||||
.bp3-popover-target {
|
||||
margin-left: 2.5rem;
|
||||
}
|
||||
.icon-saucer {
|
||||
top: 0;
|
||||
left: 1.5rem;
|
||||
line-height: 1.4rem;
|
||||
margin-top: 0;
|
||||
margin-left: 0.4rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { RootFolderNode as Tree } from "./constants";
|
||||
import { cloneAndClimb } from "./climb";
|
||||
import { Color, SpecialStatus } from "farmbot";
|
||||
import { Color, SpecialStatus, TaggedSequence } from "farmbot";
|
||||
import { store } from "../redux/store";
|
||||
import { initSave, destroy, edit, save, init } from "../api/crud";
|
||||
import { Folder } from "farmbot/dist/resources/api_resources";
|
||||
|
@ -11,6 +11,9 @@ import { t } from "../i18next_wrapper";
|
|||
import { push } from "../history";
|
||||
import { urlFriendly } from "../util";
|
||||
import { setActiveSequenceByName } from "../sequences/set_active_sequence_by_name";
|
||||
import { stepGet, STEP_DATATRANSFER_IDENTIFER } from "../draggable/actions";
|
||||
import { joinKindAndId } from "../resources/reducer_support";
|
||||
import { maybeGetSequence } from "../resources/selectors";
|
||||
|
||||
type TreePromise = Promise<Tree>;
|
||||
|
||||
|
@ -80,32 +83,42 @@ export const deleteFolder = (id: number) => {
|
|||
return d(action) as ReturnType<typeof action>;
|
||||
};
|
||||
|
||||
export const updateSearchTerm = (payload: string | undefined) => {
|
||||
store.dispatch({
|
||||
type: Actions.FOLDER_SEARCH,
|
||||
payload
|
||||
});
|
||||
};
|
||||
export const updateSearchTerm = (payload: string | undefined) =>
|
||||
store.dispatch({ type: Actions.FOLDER_SEARCH, payload });
|
||||
|
||||
export const toggleFolderOpenState = (id: number) => Promise
|
||||
.resolve(store.dispatch({ type: Actions.FOLDER_TOGGLE, payload: { id } }));
|
||||
export const toggleFolderOpenState = (id: number) =>
|
||||
store.dispatch({ type: Actions.FOLDER_TOGGLE, payload: { id } });
|
||||
|
||||
export const toggleFolderEditState =
|
||||
(id: number) => Promise.resolve(store.dispatch({
|
||||
type: Actions.FOLDER_TOGGLE_EDIT,
|
||||
payload: { id }
|
||||
}));
|
||||
export const toggleFolderEditState = (id: number) =>
|
||||
store.dispatch({ type: Actions.FOLDER_TOGGLE_EDIT, payload: { id } });
|
||||
|
||||
export const toggleAll = (payload: boolean) => Promise
|
||||
.resolve(store.dispatch({ type: Actions.FOLDER_TOGGLE_ALL, payload }));
|
||||
export const toggleAll = (payload: boolean) =>
|
||||
store.dispatch({ type: Actions.FOLDER_TOGGLE_ALL, payload });
|
||||
|
||||
export const sequenceEditMaybeSave =
|
||||
(sequence: TaggedSequence, update: Partial<TaggedSequence["body"]>) => {
|
||||
const dispatch: Function = store.dispatch;
|
||||
dispatch(edit(sequence, update));
|
||||
if (sequence.specialStatus == SpecialStatus.SAVED) {
|
||||
dispatch(save(sequence.uuid));
|
||||
}
|
||||
};
|
||||
|
||||
export function moveSequence(sequenceUuid: string, folder_id: number) {
|
||||
const d: Function = store.dispatch;
|
||||
const s = store.getState().resources.index.references[sequenceUuid];
|
||||
if (s && s.kind === "Sequence") {
|
||||
d(edit(s, { folder_id }));
|
||||
if (s.specialStatus == SpecialStatus.SAVED) {
|
||||
d(save(sequenceUuid));
|
||||
}
|
||||
sequenceEditMaybeSave(s, { folder_id });
|
||||
}
|
||||
}
|
||||
|
||||
export const dropSequence = (folder_id: number) =>
|
||||
(e: React.DragEvent<HTMLElement>) => {
|
||||
const key = e.dataTransfer.getData(STEP_DATATRANSFER_IDENTIFER);
|
||||
const dispatch: Function = store.dispatch;
|
||||
const dataXferObj = dispatch(stepGet(key));
|
||||
const { sequence_id } = dataXferObj.value.args;
|
||||
const ri = store.getState().resources.index;
|
||||
const seqUuid = ri.byKindAndId[joinKindAndId("Sequence", sequence_id)];
|
||||
const sequence = maybeGetSequence(ri, seqUuid);
|
||||
if (sequence) { sequenceEditMaybeSave(sequence, { folder_id }); }
|
||||
};
|
||||
|
|
|
@ -3,7 +3,6 @@ import {
|
|||
BlurableInput,
|
||||
EmptyStateWrapper,
|
||||
EmptyStateGraphic,
|
||||
Saucer,
|
||||
ColorPicker,
|
||||
} from "../ui";
|
||||
import {
|
||||
|
@ -17,6 +16,10 @@ import {
|
|||
ToggleFolderBtnProps,
|
||||
FolderNodeState,
|
||||
FolderPanelTopProps,
|
||||
SequenceDropAreaProps,
|
||||
FolderButtonClusterProps,
|
||||
FolderNameInputProps,
|
||||
SequenceDropAreaState,
|
||||
} from "./constants";
|
||||
import {
|
||||
createFolder,
|
||||
|
@ -29,6 +32,8 @@ import {
|
|||
addNewSequenceToFolder,
|
||||
moveSequence,
|
||||
setFolderColor,
|
||||
dropSequence,
|
||||
sequenceEditMaybeSave,
|
||||
} from "./actions";
|
||||
import { Link } from "../link";
|
||||
import { urlFriendly, lastUrlChunk } from "../util";
|
||||
|
@ -40,12 +45,13 @@ import { t } from "../i18next_wrapper";
|
|||
import { Content } from "../constants";
|
||||
import { StepDragger, NULL_DRAGGER_ID } from "../draggable/step_dragger";
|
||||
import { variableList } from "../sequences/locals_list/variable_support";
|
||||
import { UUID } from "../resources/interfaces";
|
||||
|
||||
export const FolderListItem = (props: FolderItemProps) => {
|
||||
const { sequence, onClick } = props;
|
||||
const { sequence, movedSequenceUuid } = props;
|
||||
const seqName = sequence.body.name;
|
||||
const url = `/app/sequences/${urlFriendly(seqName) || ""}`;
|
||||
const moveTarget = props.isMoveTarget ? "move-source" : "";
|
||||
const moveSource = movedSequenceUuid === sequence.uuid ? "move-source" : "";
|
||||
const nameWithSaveIndicator = seqName + (sequence.specialStatus ? "*" : "");
|
||||
const active = lastUrlChunk() === urlFriendly(seqName) ? "active" : "";
|
||||
return <StepDragger
|
||||
|
@ -57,32 +63,35 @@ export const FolderListItem = (props: FolderItemProps) => {
|
|||
}}
|
||||
intent="step_splice"
|
||||
draggerId={NULL_DRAGGER_ID}>
|
||||
<Link to={url} key={sequence.uuid} onClick={setActiveSequenceByName}>
|
||||
<li className={`sequence-list-item ${active} ${moveTarget}`}
|
||||
draggable={true}>
|
||||
<Saucer color={sequence.body.color || "gray"} active={false} />
|
||||
<li className={`sequence-list-item ${active} ${moveSource}`}
|
||||
draggable={true}>
|
||||
<ColorPicker
|
||||
current={sequence.body.color || "gray"}
|
||||
onChange={color => sequenceEditMaybeSave(sequence, { color })} />
|
||||
<Link to={url} key={sequence.uuid} onClick={setActiveSequenceByName}>
|
||||
<p>{nameWithSaveIndicator}</p>
|
||||
<div className="sequence-list-item-icons">
|
||||
{props.inUse &&
|
||||
<i className="in-use fa fa-hdd-o" title={t(Content.IN_USE)} />}
|
||||
<i className="fa fa-arrows-v"
|
||||
onClick={() => onClick(sequence.uuid)} />
|
||||
</div>
|
||||
</li>
|
||||
</Link>
|
||||
</StepDragger>;
|
||||
</Link>
|
||||
<div className="sequence-list-item-icons">
|
||||
{props.inUse &&
|
||||
<i className="in-use fa fa-hdd-o" title={t(Content.IN_USE)} />}
|
||||
<i className="fa fa-bars"
|
||||
onMouseDown={() => props.startSequenceMove(sequence.uuid)}
|
||||
onMouseUp={() => props.toggleSequenceMove(sequence.uuid)} />
|
||||
</div>
|
||||
</li>
|
||||
</StepDragger >;
|
||||
};
|
||||
|
||||
const ToggleFolderBtn = (props: ToggleFolderBtnProps) => {
|
||||
return <button className="fb-button gray" onClick={props.onClick}>
|
||||
<i className={`fa fa-${props.expanded ? "plus" : "minus"}-square`} />
|
||||
<i className={`fa fa-chevron-${props.expanded ? "right" : "down"}`} />
|
||||
</button>;
|
||||
};
|
||||
|
||||
const AddFolderBtn = ({ folder }: AddFolderBtn) => {
|
||||
const AddFolderBtn = ({ folder, close }: AddFolderBtn) => {
|
||||
return <button
|
||||
className="fb-button green"
|
||||
onClick={() => createFolder(folder || {})}>
|
||||
onClick={() => { close?.(); createFolder(folder || {}); }}>
|
||||
<div className="fa-stack fa-2x" title={"Create Subfolder"}>
|
||||
<i className="fa fa-folder fa-stack-2x" />
|
||||
<i className="fa fa-plus fa-stack-1x" />
|
||||
|
@ -90,10 +99,10 @@ const AddFolderBtn = ({ folder }: AddFolderBtn) => {
|
|||
</button>;
|
||||
};
|
||||
|
||||
const AddSequenceBtn = ({ folderId }: AddSequenceProps) => {
|
||||
const AddSequenceBtn = ({ folderId, close }: AddSequenceProps) => {
|
||||
return <button
|
||||
className="fb-button green"
|
||||
onClick={() => addNewSequenceToFolder(folderId)}>
|
||||
onClick={() => { close?.(); addNewSequenceToFolder(folderId); }}>
|
||||
<div className="fa-stack fa-2x">
|
||||
<i className="fa fa-server fa-stack-2x" />
|
||||
<i className="fa fa-plus fa-stack-1x" />
|
||||
|
@ -101,58 +110,62 @@ const AddSequenceBtn = ({ folderId }: AddSequenceProps) => {
|
|||
</button>;
|
||||
};
|
||||
|
||||
export const FolderButtonCluster = ({ node }: FolderNodeProps) => {
|
||||
return <div className="folder-button-cluster">
|
||||
export const FolderButtonCluster =
|
||||
({ node, close }: FolderButtonClusterProps) => {
|
||||
return <div className="folder-button-cluster">
|
||||
<button
|
||||
className="fb-button red"
|
||||
onClick={() => deleteFolder(node.id)}>
|
||||
<i className="fa fa-trash" />
|
||||
</button>
|
||||
<button
|
||||
className="fb-button gray"
|
||||
onClick={() => { close(); toggleFolderEditState(node.id); }}>
|
||||
<i className="fa fa-pencil" />
|
||||
</button>
|
||||
{node.kind !== "terminal" &&
|
||||
<AddFolderBtn folder={{ parent_id: node.id }} close={close} />}
|
||||
<AddSequenceBtn folderId={node.id} close={close} />
|
||||
</div>;
|
||||
};
|
||||
|
||||
const FolderNameInput = ({ node }: FolderNameInputProps) =>
|
||||
<div className="folder-name-input">
|
||||
<BlurableInput value={node.name} onCommit={e =>
|
||||
setFolderName(node.id, e.currentTarget.value)} />
|
||||
<button
|
||||
className="fb-button red"
|
||||
onClick={() => deleteFolder(node.id)}>
|
||||
<i className="fa fa-trash" />
|
||||
</button>
|
||||
<button
|
||||
className="fb-button gray"
|
||||
className="fb-button green"
|
||||
onClick={() => toggleFolderEditState(node.id)}>
|
||||
<i className="fa fa-pencil" />
|
||||
<i className="fa fa-check" />
|
||||
</button>
|
||||
{node.kind !== "terminal" &&
|
||||
<AddFolderBtn folder={{ parent_id: node.id }} />}
|
||||
<AddSequenceBtn folderId={node.id} />
|
||||
</div>;
|
||||
};
|
||||
|
||||
export class FolderNameEditor
|
||||
extends React.Component<FolderNodeProps, FolderNodeState> {
|
||||
state: FolderNodeState = { settingsOpen: false };
|
||||
render() {
|
||||
const { node } = this.props;
|
||||
const moveModeTarget = this.props.movedSequenceUuid ? "move-target" : "";
|
||||
const settingsOpenClass = this.state.settingsOpen ? "open" : "";
|
||||
const nodeName = moveModeTarget ? t("CLICK TO MOVE HERE") : node.name;
|
||||
const onClick = () =>
|
||||
moveModeTarget ? this.props.onMoveEnd(node.id) : undefined;
|
||||
const toggle = () =>
|
||||
moveModeTarget ? undefined : toggleFolderOpenState(node.id);
|
||||
return <div className={`folder-list-item ${moveModeTarget}`}
|
||||
onClick={onClick}>
|
||||
return <div className={"folder-list-item"}>
|
||||
<i className={`fa fa-chevron-${node.open ? "down" : "right"}`}
|
||||
title={"Open/Close Folder"}
|
||||
onClick={toggle} />
|
||||
onClick={() => toggleFolderOpenState(node.id)} />
|
||||
<ColorPicker
|
||||
saucerIcon={"fa-folder"}
|
||||
current={node.color}
|
||||
onChange={color => setFolderColor(node.id, color)} />
|
||||
<div className="folder-name" onClick={toggle}>
|
||||
<div className="folder-name">
|
||||
{node.editing
|
||||
? <BlurableInput value={nodeName} onCommit={e =>
|
||||
setFolderName(node.id, e.currentTarget.value)
|
||||
.then(() => toggleFolderEditState(node.id))} />
|
||||
: <p>{nodeName}</p>}
|
||||
? <FolderNameInput node={node} />
|
||||
: <p>{node.name}</p>}
|
||||
</div>
|
||||
<Popover className="folder-settings-icon" usePortal={false}
|
||||
isOpen={this.state.settingsOpen}>
|
||||
<i className={`fa fa-ellipsis-v ${settingsOpenClass}`}
|
||||
onClick={() =>
|
||||
this.setState({ settingsOpen: !this.state.settingsOpen })} />
|
||||
<FolderButtonCluster {...this.props} />
|
||||
<FolderButtonCluster {...this.props}
|
||||
close={() => this.setState({ settingsOpen: false })} />
|
||||
</Popover>
|
||||
</div>;
|
||||
}
|
||||
|
@ -168,8 +181,9 @@ const FolderNode = (props: FolderNodeProps) => {
|
|||
dispatch={props.dispatch}
|
||||
variableData={props.sequenceMetas[seqUuid]}
|
||||
inUse={!!props.resourceUsage[seqUuid]}
|
||||
onClick={props.onMoveStart}
|
||||
isMoveTarget={props.movedSequenceUuid === seqUuid} />);
|
||||
toggleSequenceMove={props.toggleSequenceMove}
|
||||
startSequenceMove={props.startSequenceMove}
|
||||
movedSequenceUuid={props.movedSequenceUuid} />);
|
||||
|
||||
const childFolders: FolderUnion[] = node.children || [];
|
||||
const folderNodes = childFolders.map(folder =>
|
||||
|
@ -181,16 +195,48 @@ const FolderNode = (props: FolderNodeProps) => {
|
|||
sequenceMetas={props.sequenceMetas}
|
||||
resourceUsage={props.resourceUsage}
|
||||
movedSequenceUuid={props.movedSequenceUuid}
|
||||
onMoveStart={props.onMoveStart}
|
||||
toggleSequenceMove={props.toggleSequenceMove}
|
||||
startSequenceMove={props.startSequenceMove}
|
||||
onMoveEnd={props.onMoveEnd} />);
|
||||
|
||||
return <div className="folder">
|
||||
<FolderNameEditor {...props} />
|
||||
{!!node.open && <ul className="in-folder-sequences">{sequenceItems}</ul>}
|
||||
<SequenceDropArea
|
||||
dropAreaVisible={!!props.movedSequenceUuid}
|
||||
onMoveEnd={props.onMoveEnd}
|
||||
toggleSequenceMove={props.toggleSequenceMove}
|
||||
folderId={node.id}
|
||||
folderName={node.name} />
|
||||
{!!node.open && folderNodes}
|
||||
</div>;
|
||||
};
|
||||
|
||||
export class SequenceDropArea
|
||||
extends React.Component<SequenceDropAreaProps, SequenceDropAreaState> {
|
||||
state: SequenceDropAreaState = { hovered: false };
|
||||
render() {
|
||||
const { dropAreaVisible, folderId, onMoveEnd, folderName } = this.props;
|
||||
const visible = dropAreaVisible ? "visible" : "";
|
||||
const hovered = this.state.hovered ? "hovered" : "";
|
||||
return <div
|
||||
className={`folder-drop-area ${visible} ${hovered}`}
|
||||
onClick={() => onMoveEnd(folderId)}
|
||||
onDrop={e => {
|
||||
this.setState({ hovered: false });
|
||||
dropSequence(folderId)(e);
|
||||
this.props.toggleSequenceMove();
|
||||
}}
|
||||
onDragOver={e => e.preventDefault()}
|
||||
onDragEnter={() => this.setState({ hovered: true })}
|
||||
onDragLeave={() => this.setState({ hovered: false })}>
|
||||
{folderId
|
||||
? `${t("Move into")} ${folderName}`
|
||||
: t("Move out of folders")}
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
|
||||
export class Folders extends React.Component<FolderProps, FolderState> {
|
||||
state: FolderState = { toggleDirection: false };
|
||||
|
||||
|
@ -204,7 +250,8 @@ export class Folders extends React.Component<FolderProps, FolderState> {
|
|||
sequenceMetas={this.props.sequenceMetas}
|
||||
resourceUsage={this.props.resourceUsage}
|
||||
movedSequenceUuid={this.state.movedSequenceUuid}
|
||||
onMoveStart={this.startSequenceMove}
|
||||
toggleSequenceMove={this.toggleSequenceMove}
|
||||
startSequenceMove={this.startSequenceMove}
|
||||
onMoveEnd={this.endSequenceMove}
|
||||
sequences={this.props.sequences} />;
|
||||
})}
|
||||
|
@ -216,9 +263,14 @@ export class Folders extends React.Component<FolderProps, FolderState> {
|
|||
this.setState({ toggleDirection: !this.state.toggleDirection });
|
||||
}
|
||||
|
||||
startSequenceMove = (seqUuid: string) => {
|
||||
this.setState({ movedSequenceUuid: seqUuid });
|
||||
}
|
||||
startSequenceMove = (seqUuid: UUID) => this.setState({
|
||||
movedSequenceUuid: seqUuid,
|
||||
stashedUuid: this.state.movedSequenceUuid,
|
||||
})
|
||||
|
||||
toggleSequenceMove = (seqUuid?: UUID) => this.setState({
|
||||
movedSequenceUuid: this.state.stashedUuid ? undefined : seqUuid,
|
||||
})
|
||||
|
||||
endSequenceMove = (folderId: number) => {
|
||||
moveSequence(this.state.movedSequenceUuid || "", folderId);
|
||||
|
@ -232,8 +284,9 @@ export class Folders extends React.Component<FolderProps, FolderState> {
|
|||
variableData={this.props.sequenceMetas[seqUuid]}
|
||||
inUse={!!this.props.resourceUsage[seqUuid]}
|
||||
sequence={this.props.sequences[seqUuid]}
|
||||
onClick={this.startSequenceMove}
|
||||
isMoveTarget={this.state.movedSequenceUuid === seqUuid} />);
|
||||
toggleSequenceMove={this.toggleSequenceMove}
|
||||
startSequenceMove={this.startSequenceMove}
|
||||
movedSequenceUuid={this.state.movedSequenceUuid} />);
|
||||
|
||||
render() {
|
||||
return <div className="folders-panel">
|
||||
|
@ -250,6 +303,12 @@ export class Folders extends React.Component<FolderProps, FolderState> {
|
|||
<ul className="sequences-not-in-folders">
|
||||
{this.rootSequences()}
|
||||
</ul>
|
||||
<SequenceDropArea
|
||||
dropAreaVisible={!!this.state.movedSequenceUuid}
|
||||
onMoveEnd={this.endSequenceMove}
|
||||
toggleSequenceMove={this.toggleSequenceMove}
|
||||
folderId={0}
|
||||
folderName={"none"} />
|
||||
<this.Graph />
|
||||
</EmptyStateWrapper>
|
||||
</div>;
|
||||
|
|
|
@ -73,6 +73,7 @@ export interface FolderNodeState {
|
|||
export interface FolderState {
|
||||
toggleDirection: boolean;
|
||||
movedSequenceUuid?: string;
|
||||
stashedUuid?: string;
|
||||
}
|
||||
|
||||
export interface FolderPanelTopProps {
|
||||
|
@ -85,28 +86,52 @@ export interface FolderNodeProps {
|
|||
node: FolderUnion;
|
||||
sequences: Record<string, TaggedSequence>;
|
||||
movedSequenceUuid: string | undefined;
|
||||
onMoveStart(sequenceUuid: string): void;
|
||||
startSequenceMove(sequenceUuid: UUID): void;
|
||||
toggleSequenceMove(sequenceUuid?: UUID): void;
|
||||
onMoveEnd(folderId: number): void;
|
||||
dispatch: Function;
|
||||
resourceUsage: Record<UUID, boolean | undefined>;
|
||||
sequenceMetas: Record<UUID, VariableNameSet | undefined>;
|
||||
}
|
||||
|
||||
export interface FolderButtonClusterProps extends FolderNodeProps {
|
||||
close(): void;
|
||||
}
|
||||
|
||||
export interface FolderNameInputProps {
|
||||
node: FolderUnion;
|
||||
}
|
||||
|
||||
export interface FolderItemProps {
|
||||
onClick(sequenceUuid: string): void;
|
||||
startSequenceMove(sequenceUuid: UUID): void;
|
||||
toggleSequenceMove(sequenceUuid?: UUID): void;
|
||||
sequence: TaggedSequence;
|
||||
isMoveTarget: boolean;
|
||||
movedSequenceUuid: UUID | undefined;
|
||||
dispatch: Function;
|
||||
variableData: VariableNameSet | undefined;
|
||||
inUse: boolean;
|
||||
}
|
||||
|
||||
export interface SequenceDropAreaProps {
|
||||
dropAreaVisible: boolean;
|
||||
onMoveEnd(id: number): void;
|
||||
toggleSequenceMove(sequenceUuid?: UUID): void;
|
||||
folderId: number;
|
||||
folderName: string;
|
||||
}
|
||||
|
||||
export interface SequenceDropAreaState {
|
||||
hovered: boolean;
|
||||
}
|
||||
|
||||
export interface AddFolderBtn {
|
||||
folder?: DeepPartial<Folder>;
|
||||
close?(): void;
|
||||
}
|
||||
|
||||
export interface AddSequenceProps {
|
||||
folderId?: number;
|
||||
close?(): void;
|
||||
}
|
||||
|
||||
export interface ToggleFolderBtnProps {
|
||||
|
|
|
@ -165,7 +165,7 @@ export const resourceReducer =
|
|||
.add<{ id: number }>(Actions.FOLDER_TOGGLE, (s, { payload }) => {
|
||||
const { localMetaAttributes } = s.index.sequenceFolders;
|
||||
const record = localMetaAttributes[parseInt("" + payload.id)];
|
||||
record.open = !record.open;
|
||||
record.open = !(record.open ?? true);
|
||||
reindexFolders(s.index);
|
||||
return s;
|
||||
})
|
||||
|
|
|
@ -43,8 +43,7 @@ export class RawSequences extends React.Component<Props, {}> {
|
|||
<Row>
|
||||
<LeftPanel
|
||||
className={`sequence-list-panel ${activeClasses}`}
|
||||
title={t("Sequences")}
|
||||
helpText={t(ToolTips.SEQUENCE_LIST)}>
|
||||
title={t("Sequences")}>
|
||||
<Folders {...this.props.folderData} dispatch={this.props.dispatch} />
|
||||
</LeftPanel>
|
||||
<CenterPanel
|
||||
|
|
|
@ -22,15 +22,11 @@ interface ColorPickerItemProps extends ColorPickerClusterProps {
|
|||
}
|
||||
|
||||
const ColorPickerItem = (props: ColorPickerItemProps) => {
|
||||
const isActive = props.color === props.current;
|
||||
return <div className="color-picker-item-wrapper"
|
||||
onClick={() => props.onChange(props.color)}>
|
||||
{props.saucerIcon
|
||||
? <div className={`color-picker-item ${isActive ? "active" : ""}`}>
|
||||
<i className={`icon-saucer active-border fa ${props.saucerIcon}`} />
|
||||
<i className={`icon-saucer fa ${props.saucerIcon} ${props.color}`} />
|
||||
</div>
|
||||
: <Saucer color={props.color} active={isActive} />}
|
||||
? <i className={`icon-saucer fa ${props.saucerIcon} ${props.color}`} />
|
||||
: <Saucer color={props.color} active={false} />}
|
||||
</div>;
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue