Skip to content

Commit

Permalink
reorder sequence in client through drag and drop
Browse files Browse the repository at this point in the history
  • Loading branch information
z1glr committed Mar 2, 2024
1 parent 24a8b82 commit e646a10
Show file tree
Hide file tree
Showing 16 changed files with 209 additions and 55 deletions.
8 changes: 8 additions & 0 deletions client/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,14 @@ div.button, div.button > * {
margin-top: 0.125rem;
}

.sequence_item_container.dragged_placeholder {
opacity: 0;
}

.sequence_item_container.dragged {
opacity: 1 !important;
}

.item_color_indicator {
height: auto;
width: 1.5rem;
Expand Down
17 changes: 15 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 5 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,15 @@
"node-osc": "^9.1.0",
"pdfjs-dist": "^4.0.379",
"sharp": "^0.33.2",
"sortablejs": "^1.15.2",
"tmp": "^0.2.1",
"ws": "^8.16.0"
},
"devDependencies": {
"@types/mime-types": "^2.1.4",
"@types/node": "^20.11.16",
"@types/node-osc": "^6.0.3",
"@types/sortablejs": "^1.15.8",
"@types/tar": "^6.1.11",
"@types/tmp": "^0.2.6",
"@types/ws": "^8.5.10",
Expand All @@ -34,11 +36,12 @@
},
"scripts": {
"lint": "eslint src/**/*.ts",
"build-release": "esbuild build/scripts/build.ts --outfile=build/scripts/build.js --tsconfig=build/scripts/tsconfig.json --platform=node --bundle && node build/scripts/build.js",
"build-release": "esbuild build/scripts/build.ts --outfile=build/scripts/build.js --tsconfig=build/scripts/tsconfig.json --platform=node --bundle && node build/scripts/build.js",
"build-server": "esbuild src/server/main.ts --outfile=dist/build/main.js --tsconfig=src/server/tsconfig.json --platform=node --minify --bundle --external:pdfjs-dist --external:canvas",
"build-client": "esbuild src/client/main.ts --outfile=client/main.js --tsconfig=src/client/tsconfig.json --bundle --minify",
"watch-client": "esbuild src/client/main.ts --outfile=client/main.js --tsconfig=src/client/tsconfig.json --bundle --sourcemap --watch",
"build-templates": "esbuild src/templates/*.ts --outdir=casparcg-templates/JohnCG/ --tsconfig=src/templates/tsconfig.json --target=chrome117 --minify",
"watch-templates": "esbuild src/templates/*.ts --outdir=casparcg-templates/JohnCG/ --tsconfig=src/templates/tsconfig.json --target=chrome117 --sourcemap --watch"
"watch-templates": "esbuild src/templates/*.ts --outdir=casparcg-templates/JohnCG/ --tsconfig=src/templates/tsconfig.json --target=chrome117 --sourcemap --watch",
"watch-prototype": "esbuild prototyping/main.ts --outfile=prototyping/main.js --tsconfig=prototyping/tsconfig.json --platform=node --watch --bundle"
}
}
94 changes: 76 additions & 18 deletions src/client/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { ItemPartClient } from "../server/SequenceItems/SongFile";
import * as JGCPSend from "../server/JGCPSendMessages";
import * as JGCPRecv from "../server/JGCPReceiveMessages.js";
import { ActiveItemSlide } from "../server/Sequence.js";
import Sortable from "sortablejs";

const config = {
websocket: {
Expand Down Expand Up @@ -63,13 +64,17 @@ document.querySelector("#show_error_log")?.addEventListener("click", () => msg_l
function display_items(data: JGCPSend.Sequence) {
const div_sequence_items = document.querySelector("#sequence_items");

// store the new selected-item-number
const new_selected_item_number = data.new_item_order[Number(document.querySelector<HTMLDivElement>("div.sequence_item_container.selected")?.dataset.item_number)];

// initialize
init();
init(false);

data.sequence_items.forEach((item) => {
data.sequence_items.forEach((item, index) => {
const div_sequence_item_container = document.createElement("div");
div_sequence_item_container.classList.add("sequence_item_container");
div_sequence_item_container.dataset.item_number = item.item.toString();
// div_sequence_item_container.dataset.item_number = item.item.toString();
div_sequence_item_container.dataset.item_number = index.toString();

// if the item is selectable, give it the class and add the onclick-event
if (item.selectable) {
Expand Down Expand Up @@ -108,6 +113,20 @@ function display_items(data: JGCPSend.Sequence) {
div_sequence_items?.append(div_sequence_item_container);
});

// update the sortable-instance
create_sortable_sequence_items();

// if an item was previously selected, select it again
if (new_selected_item_number) {
select_item(new_selected_item_number);

// send a "null"-clientid, to prevent jumping, eventough we send the request
set_active_item_slide(data.active_item_slide, null);
} else {
// request the slides for the active-item
request_item_slides(data.active_item_slide.item);
}

// display the visibility state
display_visibility_state(data.metadata.visibility);
}
Expand All @@ -128,7 +147,7 @@ function request_item_slides(item: number) {
const command: JGCPRecv.RequestItemSlides = {
command: "request_item_slides",
item: item,
client_id: client_id
client_id
};

ws.send(JSON.stringify(command));
Expand Down Expand Up @@ -405,7 +424,7 @@ function request_item_slide_select(item: number, slide: number) {
command: "select_item_slide",
item: item,
slide: slide,
client_id: client_id
client_id
};

ws.send(JSON.stringify(message));
Expand Down Expand Up @@ -447,11 +466,10 @@ function set_active_slide(scroll: boolean = false) {
}

function set_active_item_slide(data: ActiveItemSlide, message_client_id: string) {
// store the data
active_item_slide = data;

// decide wether to jump there
// decide wether to jump there (we send the request, or there is no client_id)
const jump = message_client_id === client_id || message_client_id === undefined;

active_item_slide = data;

// remove the "active" class from the previous sequence-item and add it to the new one
const prev_selected_item = document.querySelector(".sequence_item_container.active");
Expand Down Expand Up @@ -505,7 +523,7 @@ function blank_screen(state: boolean) {
}
}

function init() {
function init(reset_slides_view: boolean = true) {
selected_item_number = null;

// remove all sequence_items
Expand All @@ -514,10 +532,12 @@ function init() {
sequence_items.innerHTML = "";
}

// remove all item-slides
const slides_view_container = document.querySelector("#slides_view_container");
if (slides_view_container !== null) {
slides_view_container.innerHTML = "";
if (reset_slides_view) {
// remove all item-slides
const slides_view_container = document.querySelector("#slides_view_container");
if (slides_view_container !== null) {
slides_view_container.innerHTML = "";
}
}

// remove the visibility-selection
Expand All @@ -541,6 +561,8 @@ function ws_connect() {

ws.addEventListener("message", (event: MessageEvent) => {
const data: JGCPSend.Message = JSON.parse(event.data as string) as JGCPSend.Message;

console.debug(data);

const command_parser_map = {
sequence_items: display_items,
Expand Down Expand Up @@ -587,7 +609,10 @@ function ws_connect() {
let ws: WebSocket;
ws_connect();

let active_item_slide = {
let active_item_slide: {
item: number;
slide: number;
} = {
item: 0,
slide: 0
};
Expand All @@ -614,10 +639,43 @@ document.addEventListener("keydown", (event) => {
case "ArrowDown":
navigate("item", 1);
break;
default:
console.debug(event.code);
break;
}
}
});

function create_sortable_sequence_items() {
sortabel_sequence_items?.destroy();

sortabel_sequence_items = Sortable.create(document.querySelector("#sequence_items"), {
group: "sequence_items",
animation: 150, // ms, animation speed moving items when sorting, `0` — without animation
easing: "cubic-bezier(1, 0, 0, 1)", // Easing for animation. Defaults to null. See https://easings.net/ for examples.

dataIdAttr: 'data-item_number', // HTML attribute that is used by the `toArray()` method

ghostClass: "dragged_placeholder", // Class name for the drop placeholder
chosenClass: "dragged2", // Class name for the chosen item

forceFallback: true, // ignore the HTML5 DnD behaviour and force the fallback to kick in

fallbackClass: "dragged", // Class name for the cloned DOM Element when using forceFallback

// Element dragging ended
onEnd: function (/**Event*/evt) {
// send only, if the index has changed
if (evt.oldIndex !== evt.newIndex) {
const message: JGCPRecv.MoveSequenceItem = {
command: "move_sequence_item",
from: evt.oldIndex,
to: evt.newIndex,
client_id
};

ws.send(JSON.stringify(message));
}
},
});
}

let sortabel_sequence_items: Sortable;
create_sortable_sequence_items();
6 changes: 6 additions & 0 deletions src/server/JGCPReceiveMessages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@ export interface SelectItemSlide extends Base {
slide: number;
}

export interface MoveSequenceItem extends Base {
command: "move_sequence_item";
from: number;
to: number;
}

/**
* Uniun of the different JGCP-messages
*/
Expand Down
2 changes: 2 additions & 0 deletions src/server/JGCPSendMessages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export interface Response {
*/
export interface Sequence extends Base, SequenceClass.ClientSequenceItems {
command: "sequence_items";
new_item_order: number[];
}

/**
Expand All @@ -40,6 +41,7 @@ export interface State extends Base {

interface ItemSlidesBase extends Base{
client_id: string;
item: number;
command: "item_slides";
resolution: SequenceClass.CasparCGResolution;
}
Expand Down
33 changes: 29 additions & 4 deletions src/server/Sequence.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ import PDF, { PDFProps } from "./SequenceItems/PDF";

interface ClientSequenceItems {
sequence_items: ItemProps[];
active_item_slide: ActiveItemSlide;
metadata: {
item: number;
visibility: boolean;
}
}
Expand Down Expand Up @@ -148,7 +148,7 @@ class Sequence {
Caption: "",
Color: "",
slide_count: 0,
item: this.sequence_items.length,
// item: this.sequence_items.length,
selectable: true
/* eslint-enable @typescript-eslint/naming-convention */
};
Expand Down Expand Up @@ -223,8 +223,8 @@ class Sequence {
create_client_object_sequence(): ClientSequenceItems {
const return_sequence: ClientSequenceItems = {
sequence_items: this.sequence_items.map((item) => item.props),
active_item_slide: this.active_item_slide,
metadata: {
item: this.active_item_number,
visibility: this.visibility
}
};
Expand Down Expand Up @@ -315,6 +315,32 @@ class Sequence {
return item_steps !== 0;
}

move_sequence_item(from: number, to: number) {
from = this.validate_item_number(from);
to = this.validate_item_number(to);

if (this.active_item === from) {
this.active_item_number = to;

// if one of the move-positions lays before and the other after the active-item, adjust the active-item-number
} else if (this.active_item < from !== this.active_item < to) {
// if the moved item is the active one, set it accordingly
if (this.active_item < from) {
// if the item gets moved from before the active-item to after, increase the active-item-number
this.active_item_number++;
} else {
// else decrease it
this.active_item_number--;
}
}

const new_item_order: number[] = Array.from(Array(this.sequence_items.length).keys());
new_item_order.splice(from, 0, new_item_order.splice(to, 1)[0]);
this.sequence_items.splice(to, 0, this.sequence_items.splice(from, 1)[0]);

return new_item_order;
}

private validate_item_number(item: number): number {
const item_count = this.sequence_items.length;

Expand Down Expand Up @@ -787,6 +813,5 @@ function convert_color_to_hex(color: string): string | undefined {
}
}

// export { NavigateType, isItemNavigateType, ClientSequenceItems, ClientItemSlides, ActiveItemSlide };
export { ClientSequenceItems, ActiveItemSlide, convert_color_to_hex };
export default Sequence;
1 change: 0 additions & 1 deletion src/server/SequenceItems/CommandComment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ export default class CommandComment extends SequenceItemBase {
return Promise.resolve({
type: "CommandComment",
title,
item: this.props.item,
slides: [],
template
});
Expand Down
1 change: 0 additions & 1 deletion src/server/SequenceItems/Comment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ export default class Comment extends SequenceItemBase {
return Promise.resolve({
type: "Comment",
title: this.props.Caption,
item: this.props.item,
slides: []
});
}
Expand Down
Loading

0 comments on commit e646a10

Please sign in to comment.