mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-03 00:47:53 +00:00
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:
1 parent
0b0dab8125
commit
3aa2379477
16 files changed
+415
-308
No files matched your search
@@ -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,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,
|
||||
|
||||
@@ -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);
|
||||
@@ -146,6 +146,13 @@ export default {
|
||||
background: "#39394D",
|
||||
selectedTab: "#55556B",
|
||||
},
|
||||
dnd: {
|
||||
canDrop: "#46936E",
|
||||
canDropBorder: "#66E4A9",
|
||||
timeGrid: "#6375B5",
|
||||
timeGridBorder: "#7E92DA",
|
||||
cannotDrop: "#904948",
|
||||
},
|
||||
|
||||
ui: {
|
||||
background: "#1a1a27",
|
||||
|
||||
Reference in new issue
Block a user