Files
video-shader-toys/ui/src/components/LayerCard.jsx
Aiden ce5905373a
Some checks failed
CI / Native Windows Build And Tests (push) Has been cancelled
CI / React UI Build (push) Has been cancelled
CI / Windows Release Package (push) Has been cancelled
Added new shaders
2026-05-05 22:36:52 +10:00

145 lines
4.6 KiB
JavaScript

import { GripVertical, Trash2 } from "lucide-react";
import { postJson } from "../api/controlApi";
import { ParameterField } from "./ParameterField";
export function LayerCard({
layer,
index,
shaders,
expanded,
isDragging,
isDropTarget,
onToggleExpanded,
onDragStart,
onDragEnd,
onDragOver,
onDrop,
onRemove,
onLayerParameterChange,
}) {
const selectedShader = shaders.find((shader) => shader.id === layer.shaderId);
return (
<div
className={`layer-card${expanded ? " layer-card--expanded" : ""}${isDragging ? " layer-card--dragging" : ""}${isDropTarget ? " layer-card--drop-target" : ""}`}
onDragOver={(event) => {
event.preventDefault();
onDragOver(layer.id);
}}
onDrop={(event) => {
event.preventDefault();
onDrop(event, layer.id, index);
}}
>
<div className="layer-card__header">
<div className="layer-card__meta">
<button
type="button"
className="layer-card__drag-handle"
title="Drag to reorder"
aria-label="Drag to reorder"
draggable
onDragStart={(event) => {
event.dataTransfer.effectAllowed = "move";
event.dataTransfer.setData("text/plain", layer.id);
event.stopPropagation();
onDragStart(layer.id);
}}
onDragEnd={(event) => {
event.stopPropagation();
onDragEnd();
}}
>
<GripVertical size={16} strokeWidth={1.75} />
</button>
<span className="layer-card__index">{index + 1}</span>
<button type="button" className="layer-card__title" onClick={() => onToggleExpanded(layer.id)}>
{layer.shaderName}
</button>
</div>
<div className="layer-card__actions">
<label className="toggle toggle--compact">
<input
type="checkbox"
checked={Boolean(layer.bypass)}
onChange={(event) =>
postJson("/api/layers/set-bypass", {
layerId: layer.id,
bypass: event.target.checked,
})
}
/>
<span>Bypass</span>
</label>
<button type="button" onClick={() => onToggleExpanded(layer.id)}>
{expanded ? "Hide" : "Controls"}
</button>
<button
type="button"
className="icon-button"
title="Remove layer"
aria-label="Remove layer"
onClick={() => onRemove(layer.id)}
>
<Trash2 size={16} strokeWidth={1.75} />
</button>
</div>
</div>
{expanded ? (
<div className="layer-card__body">
{layer.temporal?.enabled ? (
<div className="layer-card__field">
<label>Temporal</label>
<div className="muted">
{layer.temporal.historySource} history, requested {layer.temporal.requestedHistoryLength} frame{layer.temporal.requestedHistoryLength === 1 ? "" : "s"}, using {layer.temporal.effectiveHistoryLength}
</div>
</div>
) : (
<div className="layer-card__field">
<label>Temporal</label>
<div className="muted">Stateless shader</div>
</div>
)}
{selectedShader?.description ? (
<div className="shader-description">
<div className="shader-description__meta">{selectedShader.category || "Shader"}</div>
<p>{selectedShader.description}</p>
</div>
) : null}
<div className="layer-card__subheader">
<h3>Parameters</h3>
<button
type="button"
disabled={layer.parameters.length === 0}
onClick={() => postJson("/api/layers/reset-parameters", { layerId: layer.id })}
>
Reset
</button>
</div>
{layer.parameters.length > 0 ? (
<div className="parameter-grid">
{layer.parameters.map((parameter) => (
<ParameterField
key={`${layer.id}:${parameter.id}`}
layer={layer}
parameter={parameter}
onParameterChange={(parameterId, value) => onLayerParameterChange(layer.id, parameterId, value)}
/>
))}
</div>
) : (
<p className="muted">This shader does not expose any user parameters.</p>
)}
</div>
) : null}
</div>
);
}