Files
video-shader-toys/ui/src/components/LayerStack.jsx
Aiden bc536bd751
All checks were successful
CI / React UI Build (push) Successful in 11s
CI / Native Windows Build And Tests (push) Successful in 1m54s
CI / Windows Release Package (push) Successful in 2m9s
Control ui adjsutments
2026-05-08 13:54:02 +10:00

134 lines
3.8 KiB
JavaScript

import { postJson } from "../api/controlApi";
import { LayerCard } from "./LayerCard";
import { ShaderPicker } from "./ShaderPicker";
function moveItem(array, fromIndex, toIndex) {
if (fromIndex < 0 || fromIndex >= array.length || toIndex < 0 || toIndex >= array.length) {
return array;
}
const copy = [...array];
const [item] = copy.splice(fromIndex, 1);
copy.splice(toIndex, 0, item);
return copy;
}
export function LayerStack({
dragLayerId,
dropTargetLayerId,
expandedLayerIds,
layers,
setAppState,
setDragLayerId,
setDropTargetLayerId,
setExpandedLayerIds,
shaders,
}) {
const expandedSet = new Set(expandedLayerIds);
function updateLayerParameterOptimistically(layerId, parameterId, value) {
return postJson("/api/layers/update-parameter", {
layerId,
parameterId,
value,
});
}
function toggleExpanded(layerId) {
setExpandedLayerIds((current) =>
current.includes(layerId) ? current.filter((id) => id !== layerId) : [...current, layerId],
);
}
function removeLayer(layerId) {
setExpandedLayerIds((current) => current.filter((id) => id !== layerId));
postJson("/api/layers/remove", { layerId });
}
function handleDrop(event, targetLayerId, targetIndex) {
const sourceLayerId = event.dataTransfer.getData("text/plain") || dragLayerId;
if (!sourceLayerId || sourceLayerId === targetLayerId) {
setDragLayerId(null);
setDropTargetLayerId(null);
return;
}
setAppState((current) => {
if (!current?.layers) {
return current;
}
const sourceIndex = current.layers.findIndex((layer) => layer.id === sourceLayerId);
const destinationIndex = current.layers.findIndex((layer) => layer.id === targetLayerId);
if (sourceIndex < 0 || destinationIndex < 0 || sourceIndex === destinationIndex) {
return current;
}
return {
...current,
layers: moveItem(current.layers, sourceIndex, destinationIndex),
};
});
postJson("/api/layers/reorder", {
layerId: sourceLayerId,
targetIndex,
});
setDragLayerId(null);
setDropTargetLayerId(null);
}
return (
<section className="panel">
<div className="panel__header">
<div>
<h3>Layers</h3>
<p className="muted">Drag layers to reorder them. Each layer processes the output of the one above it.</p>
</div>
</div>
<div className="layer-stack">
{layers.map((layer, index) => (
<LayerCard
key={layer.id}
layer={layer}
index={index}
shaders={shaders}
expanded={expandedSet.has(layer.id)}
isDragging={dragLayerId === layer.id}
isDropTarget={dropTargetLayerId === layer.id}
onToggleExpanded={toggleExpanded}
onDragStart={setDragLayerId}
onDragEnd={() => {
setDragLayerId(null);
setDropTargetLayerId(null);
}}
onDragOver={setDropTargetLayerId}
onDrop={handleDrop}
onRemove={removeLayer}
onLayerParameterChange={updateLayerParameterOptimistically}
/>
))}
<div className="layer-card layer-card--add">
<div className="layer-card__header">
<div className="layer-card__meta">
<span className="layer-card__index">+</span>
<div className="layer-card__title layer-card__title--static">Add Layer</div>
</div>
</div>
<div className="layer-card__body">
<div className="layer-card__field">
<ShaderPicker
id="add-layer"
shaders={shaders}
onAdd={(shaderId) => postJson("/api/layers/add", { shaderId })}
/>
</div>
</div>
</div>
</div>
</section>
);
}