Updates to DND (#287)

New state colour/text when cannot drop
New colour when okay to drop
New bar in timeline grid when okay to drop
Tracks that are okay to drop glow

---------

Co-authored-by: Christopher Oliphant <RALifeCoach@users.noreply.github.com>
This commit is contained in:
FromTheLOLAshesandChristopher Oliphant authored and GitHub committed 2024-05-01 15:57:30 -04:00
1 parent 0b0dab8125
commit 3aa2379477
16 files changed
+415 -308

No files matched your search

+3 -11
View File
@@ -1,10 +1,6 @@
import React from "react";
import { useMouseEventsTimeline } from "~/pages/PageEnigma/comps/Timeline/utils/useMouseEventsTimeline";
import {
dndTimelineHeight,
overTimeline,
timelineHeight,
} from "~/pages/PageEnigma/store";
import { dndTimelineHeight, timelineHeight } from "~/pages/PageEnigma/store";
import useTimelineClick from "~/pages/PageEnigma/comps/Timeline/utils/useTimelineClick";
import { Pages } from "~/pages/PageEnigma/constants/page";
@@ -25,17 +21,13 @@ export const LowerPanel = ({ children }: LowerPanelPropsI) => {
return (
<>
<div
className="absolute w-full cursor-ns-resize bg-ui-panel-border h-1 z-10"
style={{bottom: displayHeight }}
className="absolute z-10 h-1 w-full cursor-ns-resize bg-ui-panel-border"
style={{ bottom: displayHeight }}
onPointerDown={onPointerDown}
/>
<div
className={["absolute bottom-0", "w-screen", "bg-ui-panel"].join(" ")}
style={{ height: displayHeight }}
onPointerOver={() => {
overTimeline.value = true;
}}
onPointerLeave={() => (overTimeline.value = false)}
onClick={onTimelineClick}>
{children}
</div>
@@ -0,0 +1,139 @@
import React from "react";
import { AssetType, MediaItem } from "~/pages/PageEnigma/models";
import {
addCharacter,
addCharacterAnimation,
addCharacterAudio,
addGlobalAudio,
addObject,
canDrop,
currPosition,
dragItem,
overTimeline,
timelineHeight,
} from "~/pages/PageEnigma/store";
import { pageHeight } from "~/store";
import { addShape } from "~/pages/PageEnigma/store/shape";
class DndAsset {
public dropId: string = "";
public overElement: DOMRect | null = null;
public dropOffset = 0;
public initX = 0;
public initY = 0;
public notDropText = "";
constructor() {
this.onPointerMove = this.onPointerMove.bind(this);
this.onPointerUp = this.onPointerUp.bind(this);
}
onPointerDown(event: React.PointerEvent<HTMLDivElement>, item: MediaItem) {
if (event.button === 0) {
dragItem.value = item;
currPosition.value = {
currX: event.pageX,
currY: event.pageY,
};
this.initX = event.pageX;
this.initY = event.pageY;
canDrop.value = false;
this.notDropText = "";
window.addEventListener("pointerup", this.onPointerUp);
window.addEventListener("pointermove", this.onPointerMove);
}
}
endDrag() {
if (dragItem.value) {
dragItem.value = null;
canDrop.value = false;
this.overElement = null;
overTimeline.value = false;
this.notDropText = "";
}
}
onPointerUp() {
window.removeEventListener("pointerup", this.onPointerUp);
window.removeEventListener("pointermove", this.onPointerMove);
if (!canDrop.value) {
this.endDrag();
return;
}
if (dragItem.value) {
const mediaItem = dragItem.value;
if (mediaItem.type === AssetType.CHARACTER) {
addCharacter(dragItem.value);
}
// if (dragItem.value.type === AssetType.CAMERA) {
// console.log("Dragged In Camera Type")
// }
if (dragItem.value.type === AssetType.OBJECT) {
addObject(dragItem.value);
}
if (dragItem.value.type === AssetType.SHAPE) {
addShape(dragItem.value);
}
}
if (canDrop.value && dragItem.value) {
if (dragItem.value.type === AssetType.ANIMATION) {
addCharacterAnimation({
dragItem: dragItem.value,
characterId: this.dropId,
offset: this.dropOffset,
});
}
if (dragItem.value.type === AssetType.AUDIO) {
addCharacterAudio({
dragItem: dragItem.value,
characterId: this.dropId,
offset: this.dropOffset,
});
addGlobalAudio({
dragItem: dragItem.value,
audioId: this.dropId,
offset: this.dropOffset,
});
}
}
this.endDrag();
}
onPointerMove(event: MouseEvent) {
if (dragItem.value) {
event.stopPropagation();
event.preventDefault();
const deltaX = event.pageX - this.initX;
const deltaY = event.pageY - this.initY;
currPosition.value = {
currX: this.initX + deltaX,
currY: this.initY + deltaY,
};
overTimeline.value =
event.pageY > pageHeight.value - timelineHeight.value;
if (this.overElement) {
const pos = this.overElement;
const eventY = event.pageY;
const inHeight = eventY >= pos.top && eventY <= pos.top + pos.height;
const eventX = event.pageX;
const inWidth = eventX >= pos.left && eventX <= pos.left + pos.width;
if (inHeight && inWidth) {
return;
}
canDrop.value = false;
this.dropId = "";
this.overElement = null;
this.notDropText = "";
}
}
}
}
const dragAndDrop = new DndAsset();
export default dragAndDrop;
+18 -9
View File
@@ -18,15 +18,19 @@ import {
sidePanelVisible,
dndSidePanelWidth,
dndTimelineHeight,
dragItem,
canDrop,
dragItem,
} from "~/pages/PageEnigma/store";
import { useSignals } from "@preact/signals-react/runtime";
import { AppUiContext } from "~/contexts/AppUiContext";
import { pageHeight, pageWidth } from "~/store";
import { TopBar } from "~/modules/TopBar";
import { AssetType } from "./models/assets";
import { editorState, EditorStates, previewSrc } from "~/pages/PageEnigma/store/engine";
import {
editorState,
EditorStates,
previewSrc,
} from "~/pages/PageEnigma/store/engine";
export const PageEditor = () => {
useSignals();
@@ -105,13 +109,18 @@ export const PageEditor = () => {
}}
/>
{editorState.value === EditorStates.PREVIEW && (
<img className="absolute inset-0" src={previewSrc.value} id="video-scene" style={{
width:
pageWidth.value -
(sidePanelVisible.value ? sidePanelWidth.value : 0) -
84,
height: pageHeight.value - timelineHeight.value - 68,
}} />
<img
className="absolute inset-0"
src={previewSrc.value}
id="video-scene"
style={{
width:
pageWidth.value -
(sidePanelVisible.value ? sidePanelWidth.value : 0) -
84,
height: pageHeight.value - timelineHeight.value - 68,
}}
/>
)}
</div>
@@ -4,66 +4,105 @@ import {
dragItem,
overTimeline,
scale,
timelineHeight,
} from "~/pages/PageEnigma/store";
import { useSignals } from "@preact/signals-react/runtime";
import "./DragComponent.scss";
import DndAsset from "~/pages/PageEnigma/DragAndDrop/DndAsset";
export const DragComponent = () => {
useSignals();
const { currX, currY } = currPosition.value;
if (!dragItem.value) {
return null;
}
const { currX, currY } = currPosition.value;
const thumbnail = dragItem.value.thumbnail
? dragItem.value.thumbnail
: `/resources/images/default-covers/${dragItem.value.imageIndex}.webp`;
return (
<>
{overTimeline.value ? (
<div
id={`ani-dnd-${dragItem.value.media_id}`}
className={[
"absolute p-2",
"rounded-lg",
!canDrop.value ? "bg-brand-primary" : "bg-brand-secondary-700",
"block",
].join(" ")}
style={{
top: currY - 16,
left: currX + 1,
zIndex: 10000,
width: (dragItem.value.length ?? 0) * 4 * scale.value,
height: 32,
}}
/>
) : (
<div
className="dragging-item-container absolute rounded-lg"
style={{
width: 91,
height: 114,
top: currY - 57,
left: currX + 1,
zIndex: 10000,
}}>
<img
{...{
crossOrigin: "anonymous",
src: thumbnail,
if (overTimeline.value) {
if (canDrop.value && DndAsset.overElement) {
return (
<>
<div
id={`ani-dnd-${dragItem.value.media_id}`}
className={[
"absolute p-2",
"rounded-lg",
!canDrop.value
? "bg-brand-primary"
: "bg-dnd-canDrop border-dnd-canDropBorder border border-dashed",
"block",
].join(" ")}
style={{
top: DndAsset.overElement.top,
left: currX + 1,
zIndex: 10000,
width: (dragItem.value.length ?? 0) * 4 * scale.value,
height: 32,
}}
alt={dragItem.value.name}
className="rounded-t-lg"
/>
<div
className="text-overflow-ellipsis w-full rounded-b-lg px-2 py-1.5 text-center text-sm"
style={{ backgroundColor: "#39394D" }}>
{dragItem.value.name || dragItem.value.media_id}
</div>
</div>
)}
</>
id={`ani-dnd-${dragItem.value.media_id}`}
className={[
"absolute p-2",
"rounded opacity-60",
"bg-dnd-timeGrid border-2-dnd-timeGridBorder border",
].join(" ")}
style={{
bottom: timelineHeight.value - 60,
left: currX + 1,
zIndex: 10000,
width: (dragItem.value.length ?? 0) * 4 * scale.value,
height: 16,
}}
/>
</>
);
}
return (
<div
id={`ani-dnd-${dragItem.value.media_id}`}
className={[
"absolute p-1",
"rounded-lg text-xs",
"bg-dnd-cannotDrop",
"block text-nowrap",
].join(" ")}
style={{
top: currY - 16,
left: currX + 1,
zIndex: 10000,
}}>
{DndAsset.notDropText || "Cannot drop here"}
</div>
);
}
return (
<div
className="dragging-item-container absolute rounded-lg"
style={{
width: 91,
height: 114,
top: currY - 57,
left: currX + 1,
zIndex: 10000,
}}>
<img
{...{
crossOrigin: "anonymous",
src: thumbnail,
}}
alt={dragItem.value.name}
className="rounded-t-lg"
/>
<div
className="text-overflow-ellipsis w-full rounded-b-lg px-2 py-1.5 text-center text-sm"
style={{ backgroundColor: "#39394D" }}>
{dragItem.value.name || dragItem.value.media_id}
</div>
</div>
);
};
@@ -13,8 +13,6 @@ export function GenerateModals() {
const [mediaFile, setMediaFile] = useState<MediaFile | null>(null);
const { addToast } = useContext(ToasterContext);
console.log(1, mediaFile);
useEffect(() => {
if (!mediaFile) {
setMovieId("");
@@ -1,26 +1,18 @@
import { useCallback, useContext, useEffect } from "react";
import { useSignals } from "@preact/signals-react/runtime";
import { WaveformPlayer } from "~/components";
import { WaveformPlayer, H5, H6 } from "~/components";
import { AudioMediaItem } from "~/pages/PageEnigma/models";
import { TrackContext } from "~/pages/PageEnigma/contexts/TrackContext/TrackContext";
import {
canDrop,
currPosition,
dragItem,
initPosition,
} from "~/pages/PageEnigma/store";
import { H5, H6 } from "~/components";
import { AudioTypePill } from "./audioTypePills"
import { AudioTypePill } from "./audioTypePills";
import {
cancelNewFromAudioItem,
updateAudioItemLength,
updateDemoAudioItemLength,
} from "~/pages/PageEnigma/store";
import DndAsset from "~/pages/PageEnigma/DragAndDrop/DndAsset";
function getGcsUrl(bucketRelativePath: string | undefined | null): string {
let bucket = "vocodes-public";
const bucket = "vocodes-public";
let path = bucketRelativePath;
if (path !== undefined && path !== null && !path.startsWith("/")) {
path = "/" + path;
@@ -34,61 +26,16 @@ interface Props {
export const AudioItemElement = ({ item }: Props) => {
useSignals();
const { startDrag, endDrag } = useContext(TrackContext);
const { initX, initY } = initPosition.value;
useEffect(() => {
const onPointerUp = () => {
if (dragItem.value) {
endDrag();
}
};
const onMouseMove = (event: MouseEvent) => {
if (dragItem.value) {
event.stopPropagation();
event.preventDefault();
const deltaX = event.pageX - initX;
const deltaY = event.pageY - initY;
currPosition.value = { currX: initX + deltaX, currY: initY + deltaY };
}
};
window.addEventListener("pointerup", onPointerUp);
window.addEventListener("pointermove", onMouseMove);
return () => {
window.removeEventListener("pointerup", onPointerUp);
window.removeEventListener("pointermove", onMouseMove);
};
}, [startDrag, endDrag, initX, initY]);
const onPointerDown = useCallback(
(event: React.PointerEvent<HTMLDivElement>) => {
if(item.isNew) {
cancelNewFromAudioItem(item.media_id);
}
if (event.button === 0) {
startDrag(item);
currPosition.value = {
currX: event.pageX,
currY: event.pageY,
};
initPosition.value = {
initX: event.pageX,
initY: event.pageY,
};
canDrop.value = false;
}
},
[item, startDrag],
);
return (
<div
className="relative w-full cursor-pointer rounded-lg transition-all duration-200"
onPointerDown={onPointerDown}>
onPointerDown={(event) => {
if (item.isNew) {
cancelNewFromAudioItem(item.media_id);
}
DndAsset.onPointerDown(event, item);
}}>
<div className="flex w-full flex-col gap-0.5 rounded-lg bg-assets-background p-2.5">
<div className="flex justify-between">
<AudioTypePill category={item.category} />
@@ -100,18 +47,18 @@ export const AudioItemElement = ({ item }: Props) => {
<WaveformPlayer
hasPlayButton
audio={getGcsUrl(item.publicBucketPath)}
onLoad={item.length ?
undefined :
({duration})=>{
// only do this for items that doesn't have a length
if(item.category === "demo"){
updateDemoAudioItemLength(item.media_id, duration * 60);
}
else{
updateAudioItemLength(item.media_id, duration * 60);
}
}
}
onLoad={
item.length
? undefined
: ({ duration }) => {
// only do this for items that doesn't have a length
if (item.category === "demo") {
updateDemoAudioItemLength(item.media_id, duration * 60);
} else {
updateAudioItemLength(item.media_id, duration * 60);
}
}
}
/>
)}
@@ -1,4 +1,3 @@
import { useSignals } from "@preact/signals-react/runtime";
import { isRetreivingAudioItems } from "~/pages/PageEnigma/store";
import { AudioItemElement } from "./audioItemElement";
@@ -13,26 +12,21 @@ interface Props {
items: AudioMediaItem[];
}
export const AudioItemElements = ({
currentPage,
pageSize,
items,
}: Props) => {
export const AudioItemElements = ({ currentPage, pageSize, items }: Props) => {
useSignals();
return (
<div className="grid grid-cols-1 gap-2.5">
{isRetreivingAudioItems.value &&
<div className="w-full flex gap-3 justify-center items-center">
<FontAwesomeIcon icon={faSpinnerThird} spin/>
{isRetreivingAudioItems.value && (
<div className="flex w-full items-center justify-center gap-3">
<FontAwesomeIcon icon={faSpinnerThird} spin />
<H6>Retreiving New Audio Items</H6>
</div>
}
)}
{items
.slice(currentPage*pageSize, (currentPage+1)*pageSize)
.slice(currentPage * pageSize, (currentPage + 1) * pageSize)
.map((item) => (
<AudioItemElement key={item.media_id} item={item} />
))
}
))}
</div>
);
};
@@ -1,13 +1,6 @@
import { useCallback, useContext, useEffect } from "react";
import { MediaItem } from "~/pages/PageEnigma/models";
import { TrackContext } from "~/pages/PageEnigma/contexts/TrackContext/TrackContext";
import {
canDrop,
currPosition,
dragItem,
initPosition,
} from "~/pages/PageEnigma/store";
import { useSignals } from "@preact/signals-react/runtime";
import DndAsset from "~/pages/PageEnigma/DragAndDrop/DndAsset";
interface Props {
debug?: string;
@@ -16,60 +9,14 @@ interface Props {
export const ItemElement = ({ item }: Props) => {
useSignals();
const { startDrag, endDrag } = useContext(TrackContext);
const { initX, initY } = initPosition.value;
const thumbnail = item.thumbnail
? item.thumbnail
: `/resources/images/default-covers/${item.imageIndex}.webp`;
useEffect(() => {
const onPointerUp = () => {
if (dragItem.value) {
endDrag();
}
};
const onMouseMove = (event: MouseEvent) => {
if (dragItem.value) {
event.stopPropagation();
event.preventDefault();
const deltaX = event.pageX - initX;
const deltaY = event.pageY - initY;
currPosition.value = { currX: initX + deltaX, currY: initY + deltaY };
}
};
window.addEventListener("pointerup", onPointerUp);
window.addEventListener("pointermove", onMouseMove);
return () => {
window.removeEventListener("pointerup", onPointerUp);
window.removeEventListener("pointermove", onMouseMove);
};
}, [startDrag, endDrag, initX, initY]);
const onPointerDown = useCallback(
(event: React.PointerEvent<HTMLDivElement>) => {
if (event.button === 0) {
startDrag(item);
currPosition.value = {
currX: event.pageX,
currY: event.pageY,
};
initPosition.value = {
initX: event.pageX,
initY: event.pageY,
};
canDrop.value = false;
}
},
[item, startDrag],
);
return (
<div
className="sidebar-object-item relative w-full cursor-pointer rounded-lg transition-all duration-200"
onPointerDown={onPointerDown}>
onPointerDown={(event) => DndAsset.onPointerDown(event, item)}>
<img
{...{
crossOrigin: "anonymous",
@@ -62,6 +62,7 @@ export const Character = ({ character }: Props) => {
if (minimized) {
return (
<div
id={`character-${character.object_uuid}`}
className="relative flex h-[35px] items-center justify-end rounded-r-lg bg-character-groupBg pr-4"
style={{ width: fullWidth.value + 16 }}>
<button
@@ -1,7 +1,13 @@
import { useMouseEventsClip } from "./utils/useMouseEventsClip";
import { useState } from "react";
import { scale, selectedItem } from "~/pages/PageEnigma/store";
import {
canDrop,
dragItem,
scale,
selectedItem,
} from "~/pages/PageEnigma/store";
import { Clip, ClipGroup } from "~/pages/PageEnigma/models";
import DndAsset from "~/pages/PageEnigma/DragAndDrop/DndAsset";
interface Props {
min: number;
@@ -50,6 +56,12 @@ export const TrackClip = ({ clip, min, max, group, updateClip }: Props) => {
left: offset * 4 * scale.value,
}}
onPointerDown={(event) => onPointerDown(event, "left")}
onPointerMove={() => {
if (dragItem.value) {
canDrop.value = false;
DndAsset.notDropText = "Cannot drop onto another clip";
}
}}
onClick={(event) => {
event.stopPropagation();
event.preventDefault();
@@ -73,6 +85,12 @@ export const TrackClip = ({ clip, min, max, group, updateClip }: Props) => {
cursor: "move",
}}
onPointerDown={(event) => onPointerDown(event, "drag")}
onPointerMove={() => {
if (dragItem.value) {
canDrop.value = false;
DndAsset.notDropText = "Cannot drop onto another clip";
}
}}
onClick={(event) => {
event.stopPropagation();
event.preventDefault();
@@ -97,6 +115,12 @@ export const TrackClip = ({ clip, min, max, group, updateClip }: Props) => {
left: offset * 4 * scale.value + length * 4 * scale.value - 15,
}}
onPointerDown={(event) => onPointerDown(event, "right")}
onPointerMove={() => {
if (dragItem.value) {
canDrop.value = false;
DndAsset.notDropText = "Cannot drop onto another clip";
}
}}
onClick={(event) => {
event.stopPropagation();
event.preventDefault();
@@ -6,16 +6,10 @@ import {
ClipType,
} from "~/pages/PageEnigma/models";
import { PointerEvent } from "react";
import {
canDrop,
dragItem,
dropId,
dropOffset,
filmLength,
scale,
} from "~/pages/PageEnigma/store";
import { canDrop, dragItem, filmLength, scale } from "~/pages/PageEnigma/store";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faArrowUp } from "@fortawesome/pro-solid-svg-icons";
import DndAsset from "~/pages/PageEnigma/DragAndDrop/DndAsset";
interface Props {
id: string;
@@ -25,7 +19,7 @@ interface Props {
updateClip: (options: { id: string; length: number; offset: number }) => void;
}
function getCanBuild({
function getCanDrop({
dragType,
type,
group,
@@ -38,6 +32,18 @@ function getCanBuild({
if (type === ClipType.ANIMATION) {
return true;
}
if (type === ClipType.AUDIO) {
DndAsset.notDropText = "Cannot drag animation onto audio track";
}
if (type === ClipType.EMOTION) {
DndAsset.notDropText = "Cannot drag animation onto expression track";
}
if (group === ClipGroup.GLOBAL_AUDIO) {
DndAsset.notDropText = "Cannot drag animation onto global audio track";
}
if (group === ClipGroup.CAMERA) {
DndAsset.notDropText = "Cannot drag animation onto camera track";
}
}
if (dragType === AssetType.AUDIO) {
if (group === ClipGroup.CHARACTER && type === ClipType.AUDIO) {
@@ -46,21 +52,39 @@ function getCanBuild({
if (group === ClipGroup.GLOBAL_AUDIO) {
return true;
}
if (group === ClipGroup.CAMERA) {
DndAsset.notDropText = "Cannot drag audio onto camera track";
}
if (type === ClipType.ANIMATION) {
DndAsset.notDropText = "Cannot drag audio onto animation track";
}
if (type === ClipType.EMOTION) {
DndAsset.notDropText = "Cannot drag audio onto expression track";
}
}
return false;
}
export const TrackClips = ({ id, clips, updateClip, group, type }: Props) => {
const trackType = (type ?? group) as ClipType;
const canDropAsset = getCanDrop({
dragType: dragItem.value?.type,
type,
group,
});
function onPointerOver() {
if (getCanBuild({ dragType: dragItem.value?.type, type, group })) {
dropId.value = id;
if (getCanDrop({ dragType: dragItem.value?.type, type, group })) {
DndAsset.dropId = id;
}
}
function onPointerMove(event: PointerEvent<HTMLDivElement>) {
if (!getCanBuild({ dragType: dragItem.value?.type, type, group })) {
if (!DndAsset.overElement) {
const element = document.getElementById(`track-${trackType}-${id}`);
DndAsset.overElement = element!.getBoundingClientRect();
}
if (!getCanDrop({ dragType: dragItem.value?.type, type, group })) {
return;
}
@@ -94,13 +118,12 @@ export const TrackClips = ({ id, clips, updateClip, group, type }: Props) => {
});
canDrop.value = !overlap;
dropOffset.value = clipOffset;
}
function onPointerLeave() {
if (canDrop.value) {
return;
if (!overlap) {
DndAsset.dropOffset = clipOffset;
}
if (overlap) {
DndAsset.notDropText = "Not enough space to drop here";
}
canDrop.value = false;
}
return (
@@ -109,11 +132,18 @@ export const TrackClips = ({ id, clips, updateClip, group, type }: Props) => {
className={[
"relative block h-9 w-full rounded-lg",
`bg-${group}-unselected`,
clips.length === 0 ? "border-2 border-dashed border-white/15" : "",
].join(" ")}
onPointerOver={onPointerOver}
onPointerLeave={onPointerLeave}
onPointerMove={onPointerMove}>
clips.length === 0 ? "border-2 border-dashed border-white/30" : "",
].join(" ")}>
<div
className={[
"absolute inset-0",
canDropAsset
? "animate-pulse bg-white/30 duration-[1500ms]"
: "opacity-0",
].join(" ")}
onPointerOver={onPointerOver}
onPointerMove={onPointerMove}
/>
{clips.map((clip, index) => (
<TrackClip
key={clip.clip_uuid}
@@ -1,6 +1,14 @@
import { ClipGroup, Keyframe } from "~/pages/PageEnigma/models";
import {
AssetType,
ClipGroup,
ClipType,
Keyframe,
} from "~/pages/PageEnigma/models";
import { TrackKeyFrame } from "~/pages/PageEnigma/comps/Timeline/TrackKeyFrame";
import { AddToast } from "~/contexts/ToasterContext";
import { PointerEvent } from "react";
import DndAsset from "~/pages/PageEnigma/DragAndDrop/DndAsset";
import { dragItem } from "~/pages/PageEnigma/store";
interface Props {
id: string;
@@ -17,6 +25,37 @@ interface Props {
}) => void;
}
function setNotDropText({
dragType,
group,
}: {
dragType?: AssetType;
group: ClipGroup;
}) {
if (dragType === AssetType.ANIMATION) {
if (group === ClipGroup.CHARACTER) {
DndAsset.notDropText = "Cannot drag animation onto movement track";
}
if (group === ClipGroup.CAMERA) {
DndAsset.notDropText = "Cannot drag animation onto camera track";
}
if (group === ClipGroup.OBJECT) {
DndAsset.notDropText = "Cannot drag animation onto object track";
}
}
if (dragType === AssetType.AUDIO) {
if (group === ClipGroup.CHARACTER) {
DndAsset.notDropText = "Cannot drag audio onto movement track";
}
if (group === ClipGroup.CAMERA) {
DndAsset.notDropText = "Cannot drag audio onto camera track";
}
if (group === ClipGroup.OBJECT) {
DndAsset.notDropText = "Cannot drag audio onto object track";
}
}
}
export const TrackKeyFrames = ({
id,
keyframes,
@@ -24,10 +63,18 @@ export const TrackKeyFrames = ({
title,
group,
}: Props) => {
function onPointerMove() {
if (!DndAsset.overElement) {
const element = document.getElementById(`track-${group}-${id}`);
DndAsset.overElement = element!.getBoundingClientRect();
}
setNotDropText({ dragType: dragItem.value?.type, group });
}
return (
<div
id={`track-${group}-${id}`}
className={`relative block h-9 w-full rounded-lg bg-${group}-unselected`}>
className={`relative block h-9 w-full rounded-lg bg-${group}-unselected`}
onPointerMove={onPointerMove}>
{keyframes.map((keyframe) => (
<TrackKeyFrame
key={keyframe.keyframe_uuid}
@@ -1,17 +1,8 @@
import { TrackContext } from "~/pages/PageEnigma/contexts/TrackContext/TrackContext";
import { ReactNode, useCallback, useMemo } from "react";
import { AssetType, MediaItem } from "~/pages/PageEnigma/models";
import { MediaItem } from "~/pages/PageEnigma/models";
import useUpdateDragDrop from "~/pages/PageEnigma/contexts/TrackContext/utils/useUpdateDragDrop";
import {
addCharacterAnimation,
addCharacterAudio,
addGlobalAudio,
addCharacter,
canDrop,
dragItem,
dropId,
dropOffset,
addObject,
characterGroup,
cameraGroup,
audioGroup,
@@ -20,7 +11,6 @@ import {
} from "~/pages/PageEnigma/store";
import useUpdateKeyframe from "~/pages/PageEnigma/contexts/TrackContext/utils/useUpdateKeyframe";
import { deleteObject } from "~/pages/PageEnigma/store/objectGroup/deleteObject";
import { addShape } from "../../store/shape";
interface Props {
children: ReactNode;
@@ -33,50 +23,6 @@ export const TrackProvider = ({ children }: Props) => {
// cross group functions
const dropClip = useCallback(() => {
if (!canDrop.value) {
endDrag();
return;
}
if (dragItem.value) {
const mediaItem = dragItem.value;
if (mediaItem.type === AssetType.CHARACTER) {
addCharacter(dragItem.value);
}
// if (dragItem.value.type === AssetType.CAMERA) {
// console.log("Dragged In Camera Type")
// }
if (dragItem.value.type === AssetType.OBJECT) {
addObject(dragItem.value);
}
if (dragItem.value.type === AssetType.SHAPE) {
addShape(dragItem.value);
}
}
if (canDrop.value && dragItem.value) {
if (dragItem.value.type === AssetType.ANIMATION) {
addCharacterAnimation({
dragItem: dragItem.value,
characterId: dropId.value,
offset: dropOffset.value,
});
}
if (dragItem.value.type === AssetType.AUDIO) {
console.log("add audio", dropId.value);
addCharacterAudio({
dragItem: dragItem.value,
characterId: dropId.value,
offset: dropOffset.value,
});
addGlobalAudio({
dragItem: dragItem.value,
audioId: dropId.value,
offset: dropOffset.value,
});
}
}
endDrag();
}, [endDrag]);
@@ -1,16 +1,9 @@
import { useCallback } from "react";
import { canDrop, dragItem } from "~/pages/PageEnigma/store";
import { MediaItem } from "~/pages/PageEnigma/models";
export default function useUpdateDragDrop() {
const startDrag = useCallback((item: MediaItem) => {
dragItem.value = item;
}, []);
const startDrag = useCallback(() => {}, []);
const endDrag = useCallback(() => {
dragItem.value = null;
canDrop.value = false;
}, []);
const endDrag = useCallback(() => {}, []);
return {
startDrag,
+3 -9
View File
@@ -1,19 +1,13 @@
import { signal } from "@preact/signals-core";
import { MediaItem } from "~/pages/PageEnigma/models";
export const dragItem = signal<MediaItem | null>(null);
export const canDrop = signal(false);
export const overTimeline = signal(false);
export const dropId = signal("");
export const dropOffset = signal(0);
export const dragItem = signal<MediaItem | null>(null);
export const dndTimelineHeight = signal(-1);
export const initPosition = signal<{ initX: number; initY: number }>({
initX: 0,
initY: 0,
});
export const currPosition = signal<{ currX: number; currY: number }>({
currX: 0,
currY: 0,
});
export const dndTimelineHeight = signal(-1);
+7
View File
@@ -146,6 +146,13 @@ export default {
background: "#39394D",
selectedTab: "#55556B",
},
dnd: {
canDrop: "#46936E",
canDropBorder: "#66E4A9",
timeGrid: "#6375B5",
timeGridBorder: "#7E92DA",
cannotDrop: "#904948",
},
ui: {
background: "#1a1a27",