implement removing
This commit is contained in:
parent
ea5663c838
commit
f2928ab266
6 changed files with 106 additions and 7 deletions
|
@ -17,6 +17,7 @@
|
|||
"qs": "^6.9.1",
|
||||
"react": "^0.0.0-experimental-38dd17ab9",
|
||||
"react-beautiful-dnd": "^12.1.1",
|
||||
"react-contextmenu": "^2.13.0",
|
||||
"react-dnd": "^9.4.0",
|
||||
"react-dnd-html5-backend": "^9.4.0",
|
||||
"react-dom": "^0.0.0-experimental-38dd17ab9",
|
||||
|
|
15
src/App.css
15
src/App.css
|
@ -62,3 +62,18 @@ html, body, #root {
|
|||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.react-contextmenu--visible {
|
||||
background-color: white;
|
||||
padding: 0.7em;
|
||||
border: 1.5px solid grey;
|
||||
box-shadow: 3px 3px 6px grey;
|
||||
}
|
||||
|
||||
.react-contextmenu-item {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.react-contextmenu-item--disabled {
|
||||
cursor: not-allowed;
|
||||
}
|
|
@ -166,6 +166,12 @@ export type UpdateOp =
|
|||
channel: number;
|
||||
weight: number;
|
||||
id: string;
|
||||
}
|
||||
| {
|
||||
op: "RemoveItem";
|
||||
timeslotitemid: string;
|
||||
channel: number;
|
||||
weight: number;
|
||||
};
|
||||
|
||||
interface OpResult {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import React, { useState, useReducer, useRef, useEffect } from "react";
|
||||
import { DndProvider, useDrag, useDrop } from "react-dnd";
|
||||
import HTML5Backend from "react-dnd-html5-backend";
|
||||
import { ContextMenu, ContextMenuTrigger, MenuItem } from "react-contextmenu";
|
||||
|
||||
import {
|
||||
showPlanResource,
|
||||
|
@ -26,15 +27,20 @@ import {
|
|||
getShowplan,
|
||||
itemId,
|
||||
moveItem,
|
||||
addItem
|
||||
addItem,
|
||||
removeItem
|
||||
} from "./state";
|
||||
|
||||
const CML_CACHE: { [recordid_trackid: string]: Track } = {};
|
||||
|
||||
const TS_ITEM_MENU_ID = "SongMenu";
|
||||
|
||||
function Item({ item: x, index }: { item: PlanItem | Track; index: number }) {
|
||||
const id = itemId(x);
|
||||
const isReal = "timeslotitemid" in x;
|
||||
const isGhost = "ghostid" in x;
|
||||
return (
|
||||
<Draggable draggableId={id} index={index} isDragDisabled={"ghostid" in x}>
|
||||
<Draggable draggableId={id} index={index} isDragDisabled={isGhost}>
|
||||
{(provided, snapshot) => (
|
||||
<div
|
||||
ref={provided.innerRef}
|
||||
|
@ -43,11 +49,13 @@ function Item({ item: x, index }: { item: PlanItem | Track; index: number }) {
|
|||
{...provided.draggableProps}
|
||||
{...provided.dragHandleProps}
|
||||
>
|
||||
<ContextMenuTrigger id={isReal ? TS_ITEM_MENU_ID : ""} collect={() => ({ id })}>
|
||||
{x.title}
|
||||
{"artist" in x && " - " + x.artist}
|
||||
<code>
|
||||
{itemId(x)} {"channel" in x && x.channel + "/" + x.weight}
|
||||
</code>
|
||||
</ContextMenuTrigger>
|
||||
</div>
|
||||
)}
|
||||
</Draggable>
|
||||
|
@ -178,6 +186,11 @@ const Showplanner: React.FC<{ timeslotId: number }> = function({ timeslotId }) {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
async function onCtxRemoveClick(e: any, data: { id: string }) {
|
||||
dispatch(removeItem(timeslotId, data.id));
|
||||
}
|
||||
|
||||
if (showplan === null) {
|
||||
return (
|
||||
<div className="sp-container">
|
||||
|
@ -215,6 +228,10 @@ const Showplanner: React.FC<{ timeslotId: number }> = function({ timeslotId }) {
|
|||
<LibraryColumn />
|
||||
</DragDropContext>
|
||||
</div>
|
||||
|
||||
<ContextMenu id={TS_ITEM_MENU_ID}>
|
||||
<MenuItem onClick={onCtxRemoveClick}>Remove</MenuItem>
|
||||
</ContextMenu>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -83,6 +83,13 @@ const showplan = createSlice({
|
|||
case "AddItem":
|
||||
// no-op
|
||||
break;
|
||||
case "RemoveItem":
|
||||
const idx = state.plan!.findIndex(x => itemId(x) === op.timeslotitemid);
|
||||
if (idx < 0) {
|
||||
throw new Error();
|
||||
}
|
||||
state.plan!.splice(idx, 1);
|
||||
break;
|
||||
default:
|
||||
throw new Error();
|
||||
}
|
||||
|
@ -303,6 +310,46 @@ export const addItem = (
|
|||
);
|
||||
};
|
||||
|
||||
export const removeItem = (
|
||||
timeslotId: number,
|
||||
itemid: string
|
||||
): AppThunk => async (dispatch, getState) => {
|
||||
// This is a simplified version of the second case of moveItem
|
||||
const plan = cloneDeep(getState().showplan.plan!);
|
||||
const item = plan.find(x => itemId(x) === itemid)!;
|
||||
const planColumn = plan
|
||||
.filter(x => x.channel === item.channel)
|
||||
.sort((a, b) => a.weight - b.weight);
|
||||
|
||||
const ops: api.UpdateOp[] = [];
|
||||
ops.push({
|
||||
op: "RemoveItem",
|
||||
timeslotitemid: itemid,
|
||||
channel: item.channel,
|
||||
weight: item.weight
|
||||
});
|
||||
planColumn.splice(planColumn.indexOf(item), 1);
|
||||
for (let i = item.weight; i < planColumn.length; i++) {
|
||||
const movingItem = planColumn[i];
|
||||
ops.push({
|
||||
op: "MoveItem",
|
||||
timeslotitemid: itemId(item),
|
||||
oldchannel: item.channel,
|
||||
oldweight: item.weight,
|
||||
channel: item.channel,
|
||||
weight: item.weight - 1
|
||||
});
|
||||
movingItem.weight -= 1;
|
||||
}
|
||||
|
||||
const result = await api.updateShowplan(timeslotId, ops);
|
||||
if (!result.every(x => x.status)) {
|
||||
dispatch(showplan.actions.planSaveError("Server says no!"));
|
||||
return;
|
||||
}
|
||||
dispatch(showplan.actions.applyOps(ops));
|
||||
};
|
||||
|
||||
export const getShowplan = (timeslotId: number): AppThunk => async dispatch => {
|
||||
dispatch(showplan.actions.getShowplanStarting());
|
||||
try {
|
||||
|
|
13
yarn.lock
13
yarn.lock
|
@ -2545,6 +2545,11 @@ class-utils@^0.3.5:
|
|||
isobject "^3.0.0"
|
||||
static-extend "^0.1.1"
|
||||
|
||||
classnames@^2.2.5:
|
||||
version "2.2.6"
|
||||
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.6.tgz#43935bffdd291f326dad0a205309b38d00f650ce"
|
||||
integrity sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q==
|
||||
|
||||
clean-css@4.2.x:
|
||||
version "4.2.1"
|
||||
resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.1.tgz#2d411ef76b8569b6d0c84068dabe85b0aa5e5c17"
|
||||
|
@ -8153,6 +8158,14 @@ react-beautiful-dnd@^12.1.1:
|
|||
redux "^4.0.4"
|
||||
use-memo-one "^1.1.1"
|
||||
|
||||
react-contextmenu@^2.13.0:
|
||||
version "2.13.0"
|
||||
resolved "https://registry.yarnpkg.com/react-contextmenu/-/react-contextmenu-2.13.0.tgz#dabaea63124e30c85f1b4245c095b7045d013459"
|
||||
integrity sha512-hhFuJX4di0zGV7H7pXPn42U70OZbGpQD+PxcdmKStNT5mebSjI+inhOuFESDmDbqVsN/f99hI5/nw95oXTVRXQ==
|
||||
dependencies:
|
||||
classnames "^2.2.5"
|
||||
object-assign "^4.1.0"
|
||||
|
||||
react-dev-utils@^9.1.0:
|
||||
version "9.1.0"
|
||||
resolved "https://registry.yarnpkg.com/react-dev-utils/-/react-dev-utils-9.1.0.tgz#3ad2bb8848a32319d760d0a84c56c14bdaae5e81"
|
||||
|
|
Loading…
Reference in a new issue