Merge pull request 'ui/fix: some more fixes' (#5063) from ui-more-3 into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/5063
This commit is contained in:
@@ -37,6 +37,11 @@ export const Menu = (props: {
|
|||||||
"pointer-events": "auto",
|
"pointer-events": "auto",
|
||||||
}}
|
}}
|
||||||
class={styles.list}
|
class={styles.list}
|
||||||
|
onContextMenu={(e) => {
|
||||||
|
// Prevent default context menu
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<li
|
<li
|
||||||
class={styles.item}
|
class={styles.item}
|
||||||
|
|||||||
@@ -132,8 +132,6 @@ const ClanSceneController = (props: RouteSectionProps) => {
|
|||||||
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const [showService, setShowService] = createSignal(false);
|
|
||||||
|
|
||||||
const [currentPromise, setCurrentPromise] = createSignal<{
|
const [currentPromise, setCurrentPromise] = createSignal<{
|
||||||
resolve: ({ id }: { id: string }) => void;
|
resolve: ({ id }: { id: string }) => void;
|
||||||
reject: (err: unknown) => void;
|
reject: (err: unknown) => void;
|
||||||
@@ -219,21 +217,9 @@ const ClanSceneController = (props: RouteSectionProps) => {
|
|||||||
console.error("Error creating service instance", result.errors);
|
console.error("Error creating service instance", result.errors);
|
||||||
}
|
}
|
||||||
toast.success("Created");
|
toast.success("Created");
|
||||||
setShowService(false);
|
|
||||||
setWorldMode("select");
|
setWorldMode("select");
|
||||||
};
|
};
|
||||||
|
|
||||||
createEffect(
|
|
||||||
on(worldMode, (mode) => {
|
|
||||||
if (mode === "service") {
|
|
||||||
setShowService(true);
|
|
||||||
} else {
|
|
||||||
// TODO: request soft close instead of forced close
|
|
||||||
setShowService(false);
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Show when={loadingError()}>
|
<Show when={loadingError()}>
|
||||||
@@ -268,11 +254,10 @@ const ClanSceneController = (props: RouteSectionProps) => {
|
|||||||
isLoading={ctx.isLoading()}
|
isLoading={ctx.isLoading()}
|
||||||
cubesQuery={ctx.machinesQuery}
|
cubesQuery={ctx.machinesQuery}
|
||||||
toolbarPopup={
|
toolbarPopup={
|
||||||
<Show when={showService()}>
|
<Show when={worldMode() === "service"}>
|
||||||
<ServiceWorkflow
|
<ServiceWorkflow
|
||||||
handleSubmit={handleSubmitService}
|
handleSubmit={handleSubmitService}
|
||||||
onClose={() => {
|
onClose={() => {
|
||||||
setShowService(false);
|
|
||||||
setWorldMode("select");
|
setWorldMode("select");
|
||||||
currentPromise()?.resolve({ id: "0" });
|
currentPromise()?.resolve({ id: "0" });
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
/* <div class="absolute bottom-4 left-1/2 flex -translate-x-1/2 flex-col items-center">
|
/* <div class="absolute bottom-4 left-1/2 flex -translate-x-1/2 flex-col items-center">
|
||||||
<Show when={show()}> */
|
<Show when={show()}> */
|
||||||
.toolbar-container {
|
.toolbar-container {
|
||||||
@apply absolute bottom-10 z-10 w-full;
|
@apply absolute bottom-10 z-30 left-1/2;
|
||||||
@apply flex justify-center items-center;
|
@apply flex justify-center items-center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ function intersectMachines(
|
|||||||
camera: THREE.Camera,
|
camera: THREE.Camera,
|
||||||
machineManager: MachineManager,
|
machineManager: MachineManager,
|
||||||
raycaster: THREE.Raycaster,
|
raycaster: THREE.Raycaster,
|
||||||
): string[] {
|
) {
|
||||||
const rect = renderer.domElement.getBoundingClientRect();
|
const rect = renderer.domElement.getBoundingClientRect();
|
||||||
const mouse = new THREE.Vector2(
|
const mouse = new THREE.Vector2(
|
||||||
((event.clientX - rect.left) / rect.width) * 2 - 1,
|
((event.clientX - rect.left) / rect.width) * 2 - 1,
|
||||||
@@ -49,7 +49,10 @@ function intersectMachines(
|
|||||||
Array.from(machineManager.machines.values().map((m) => m.group)),
|
Array.from(machineManager.machines.values().map((m) => m.group)),
|
||||||
);
|
);
|
||||||
|
|
||||||
return intersects.map((i) => i.object.userData.id);
|
return {
|
||||||
|
machines: intersects.map((i) => i.object.userData.id),
|
||||||
|
intersection: intersects,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function garbageCollectGroup(group: THREE.Group) {
|
function garbageCollectGroup(group: THREE.Group) {
|
||||||
@@ -129,6 +132,8 @@ export function CubeScene(props: {
|
|||||||
let sharedCubeGeometry: THREE.BoxGeometry;
|
let sharedCubeGeometry: THREE.BoxGeometry;
|
||||||
let sharedBaseGeometry: THREE.BoxGeometry;
|
let sharedBaseGeometry: THREE.BoxGeometry;
|
||||||
|
|
||||||
|
let machineManager: MachineManager;
|
||||||
|
|
||||||
const [positionMode, setPositionMode] = createSignal<"grid" | "circle">(
|
const [positionMode, setPositionMode] = createSignal<"grid" | "circle">(
|
||||||
"grid",
|
"grid",
|
||||||
);
|
);
|
||||||
@@ -137,6 +142,7 @@ export function CubeScene(props: {
|
|||||||
|
|
||||||
const [cancelMove, setCancelMove] = createSignal<NodeJS.Timeout>();
|
const [cancelMove, setCancelMove] = createSignal<NodeJS.Timeout>();
|
||||||
|
|
||||||
|
// TODO: Unify this with actionRepr position
|
||||||
const [cursorPosition, setCursorPosition] = createSignal<[number, number]>();
|
const [cursorPosition, setCursorPosition] = createSignal<[number, number]>();
|
||||||
|
|
||||||
const [cameraInfo, setCameraInfo] = createSignal({
|
const [cameraInfo, setCameraInfo] = createSignal({
|
||||||
@@ -446,7 +452,7 @@ export function CubeScene(props: {
|
|||||||
|
|
||||||
const registry = new ObjectRegistry();
|
const registry = new ObjectRegistry();
|
||||||
|
|
||||||
const machineManager = new MachineManager(
|
machineManager = new MachineManager(
|
||||||
scene,
|
scene,
|
||||||
registry,
|
registry,
|
||||||
props.sceneStore,
|
props.sceneStore,
|
||||||
@@ -478,11 +484,10 @@ export function CubeScene(props: {
|
|||||||
.finally(() => {
|
.finally(() => {
|
||||||
if (actionBase) actionBase.visible = false;
|
if (actionBase) actionBase.visible = false;
|
||||||
|
|
||||||
setWorldMode("default");
|
setWorldMode("select");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (worldMode() === "move") {
|
if (worldMode() === "move") {
|
||||||
console.log("sanpped");
|
|
||||||
const currId = menuIntersection().at(0);
|
const currId = menuIntersection().at(0);
|
||||||
const pos = cursorPosition();
|
const pos = cursorPosition();
|
||||||
if (!currId || !pos) return;
|
if (!currId || !pos) return;
|
||||||
@@ -502,10 +507,11 @@ export function CubeScene(props: {
|
|||||||
const intersects = raycaster.intersectObjects(
|
const intersects = raycaster.intersectObjects(
|
||||||
Array.from(machineManager.machines.values().map((m) => m.group)),
|
Array.from(machineManager.machines.values().map((m) => m.group)),
|
||||||
);
|
);
|
||||||
console.log("Intersects:", intersects);
|
|
||||||
if (intersects.length > 0) {
|
if (intersects.length > 0) {
|
||||||
console.log("Clicked on cube:", intersects);
|
const id = intersects.find((i) => i.object.userData?.id)?.object
|
||||||
const id = intersects[0].object.userData.id;
|
.userData.id;
|
||||||
|
|
||||||
|
if (!id) return;
|
||||||
|
|
||||||
if (worldMode() === "select") props.onSelect(new Set<string>([id]));
|
if (worldMode() === "select") props.onSelect(new Set<string>([id]));
|
||||||
|
|
||||||
@@ -545,7 +551,7 @@ export function CubeScene(props: {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleMouseDown = (e: MouseEvent) => {
|
const handleMouseDown = (e: MouseEvent) => {
|
||||||
const intersection = intersectMachines(
|
const { machines, intersection } = intersectMachines(
|
||||||
e,
|
e,
|
||||||
renderer,
|
renderer,
|
||||||
camera,
|
camera,
|
||||||
@@ -555,7 +561,7 @@ export function CubeScene(props: {
|
|||||||
if (e.button === 0) {
|
if (e.button === 0) {
|
||||||
// Left button
|
// Left button
|
||||||
|
|
||||||
if (worldMode() === "select" && intersection.length) {
|
if (worldMode() === "select" && machines.length) {
|
||||||
// Disable controls to avoid conflict
|
// Disable controls to avoid conflict
|
||||||
controls.enabled = false;
|
controls.enabled = false;
|
||||||
|
|
||||||
@@ -563,7 +569,8 @@ export function CubeScene(props: {
|
|||||||
const cancelMove = setTimeout(() => {
|
const cancelMove = setTimeout(() => {
|
||||||
setIsDragging(true);
|
setIsDragging(true);
|
||||||
// Set machine as flying
|
// Set machine as flying
|
||||||
setHighlightGroups({ move: new Set(intersection) });
|
setHighlightGroups({ move: new Set(machines) });
|
||||||
|
|
||||||
setWorldMode("move");
|
setWorldMode("move");
|
||||||
renderLoop.requestRender();
|
renderLoop.requestRender();
|
||||||
}, 500);
|
}, 500);
|
||||||
@@ -575,25 +582,22 @@ export function CubeScene(props: {
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
if (!intersection.length) return;
|
if (!intersection.length) return;
|
||||||
setMenuIntersection(intersection);
|
setMenuIntersection(machines);
|
||||||
setMenuPos({ x: e.clientX, y: e.clientY });
|
setMenuPos({ x: e.clientX, y: e.clientY });
|
||||||
setContextOpen(true);
|
setContextOpen(true);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const handleMouseUp = (e: MouseEvent) => {
|
const handleMouseUp = (e: MouseEvent) => {
|
||||||
if (e.button === 0) {
|
if (e.button === 0) {
|
||||||
console.log("Left mouse up");
|
|
||||||
setIsDragging(false);
|
setIsDragging(false);
|
||||||
if (cancelMove()) {
|
if (cancelMove()) {
|
||||||
clearTimeout(cancelMove()!);
|
clearTimeout(cancelMove()!);
|
||||||
setCancelMove(undefined);
|
setCancelMove(undefined);
|
||||||
}
|
}
|
||||||
|
// Always re-enable controls
|
||||||
|
controls.enabled = true;
|
||||||
|
|
||||||
if (worldMode() === "move") {
|
if (worldMode() === "move") {
|
||||||
// Cancel long-press if it wasn't triggered yet
|
|
||||||
// Re-enable controls
|
|
||||||
controls.enabled = true;
|
|
||||||
|
|
||||||
// Set machine as not flying
|
// Set machine as not flying
|
||||||
props.setMachinePos(
|
props.setMachinePos(
|
||||||
highlightGroups["move"].values().next().value!,
|
highlightGroups["move"].values().next().value!,
|
||||||
@@ -652,6 +656,39 @@ export function CubeScene(props: {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const snapToGrid = (point: THREE.Vector3) => {
|
||||||
|
if (!props.sceneStore) return;
|
||||||
|
// Snap to grid
|
||||||
|
const snapped = new THREE.Vector3(
|
||||||
|
Math.round(point.x / GRID_SIZE) * GRID_SIZE,
|
||||||
|
0,
|
||||||
|
Math.round(point.z / GRID_SIZE) * GRID_SIZE,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Skip snapping if there's already a cube at this position
|
||||||
|
const positions = Object.entries(props.sceneStore());
|
||||||
|
const intersects = positions.some(
|
||||||
|
([_id, p]) => p.position[0] === snapped.x && p.position[1] === snapped.z,
|
||||||
|
);
|
||||||
|
const movingMachine = Array.from(highlightGroups["move"] || [])[0];
|
||||||
|
const startingPos = positions.find(([_id, p]) => _id === movingMachine);
|
||||||
|
if (startingPos) {
|
||||||
|
const isStartingPos =
|
||||||
|
snapped.x === startingPos[1].position[0] &&
|
||||||
|
snapped.z === startingPos[1].position[1];
|
||||||
|
// If Intersect any other machine and not the one being moved
|
||||||
|
if (!isStartingPos && intersects) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (intersects) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return snapped;
|
||||||
|
};
|
||||||
|
|
||||||
const onAddClick = (event: MouseEvent) => {
|
const onAddClick = (event: MouseEvent) => {
|
||||||
setPositionMode("grid");
|
setPositionMode("grid");
|
||||||
setWorldMode("create");
|
setWorldMode("create");
|
||||||
@@ -660,8 +697,6 @@ export function CubeScene(props: {
|
|||||||
const onMouseMove = (event: MouseEvent) => {
|
const onMouseMove = (event: MouseEvent) => {
|
||||||
if (!(worldMode() === "create" || worldMode() === "move")) return;
|
if (!(worldMode() === "create" || worldMode() === "move")) return;
|
||||||
|
|
||||||
console.log("Mouse move in create/move mode");
|
|
||||||
|
|
||||||
const actionRepr = worldMode() === "create" ? actionBase : actionMachine;
|
const actionRepr = worldMode() === "create" ? actionBase : actionMachine;
|
||||||
if (!actionRepr) return;
|
if (!actionRepr) return;
|
||||||
|
|
||||||
@@ -683,24 +718,8 @@ export function CubeScene(props: {
|
|||||||
if (intersects.length > 0) {
|
if (intersects.length > 0) {
|
||||||
const point = intersects[0].point;
|
const point = intersects[0].point;
|
||||||
|
|
||||||
// Snap to grid
|
const snapped = snapToGrid(point);
|
||||||
const snapped = new THREE.Vector3(
|
if (!snapped) return;
|
||||||
Math.round(point.x / GRID_SIZE) * GRID_SIZE,
|
|
||||||
0,
|
|
||||||
Math.round(point.z / GRID_SIZE) * GRID_SIZE,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Skip snapping if there's already a cube at this position
|
|
||||||
if (props.sceneStore()) {
|
|
||||||
const positions = Object.values(props.sceneStore());
|
|
||||||
const intersects = positions.some(
|
|
||||||
(p) => p.position[0] === snapped.x && p.position[1] === snapped.z,
|
|
||||||
);
|
|
||||||
if (intersects) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
Math.abs(actionRepr.position.x - snapped.x) > 0.01 ||
|
Math.abs(actionRepr.position.x - snapped.x) > 0.01 ||
|
||||||
Math.abs(actionRepr.position.z - snapped.z) > 0.01
|
Math.abs(actionRepr.position.z - snapped.z) > 0.01
|
||||||
@@ -715,9 +734,29 @@ export function CubeScene(props: {
|
|||||||
const handleMenuSelect = (mode: "move") => {
|
const handleMenuSelect = (mode: "move") => {
|
||||||
setWorldMode(mode);
|
setWorldMode(mode);
|
||||||
setHighlightGroups({ move: new Set(menuIntersection()) });
|
setHighlightGroups({ move: new Set(menuIntersection()) });
|
||||||
console.log("Menu selected, new World mode", worldMode());
|
|
||||||
|
// Find the position of the first selected machine
|
||||||
|
// Set the actionMachine position to that
|
||||||
|
const firstId = menuIntersection()[0];
|
||||||
|
if (firstId) {
|
||||||
|
const machine = machineManager.machines.get(firstId);
|
||||||
|
if (machine && actionMachine) {
|
||||||
|
actionMachine.position.set(
|
||||||
|
machine.group.position.x,
|
||||||
|
0,
|
||||||
|
machine.group.position.z,
|
||||||
|
);
|
||||||
|
setCursorPosition([machine.group.position.x, machine.group.position.z]);
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
createEffect(
|
||||||
|
on(worldMode, (mode) => {
|
||||||
|
console.log("World mode changed to", mode);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
const machinesQuery = useMachinesQuery(props.clanURI);
|
const machinesQuery = useMachinesQuery(props.clanURI);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
Reference in New Issue
Block a user