Refactor brush and model instance selection handling in App.tsx

This commit is contained in:
2026-04-03 01:04:33 +02:00
parent 31f170e2fa
commit bbfc17c66c

View File

@@ -4203,6 +4203,7 @@ export function App({ store, initialStatusMessage }: AppProps) {
<div className="outliner-list" data-testid="outliner-brush-list"> <div className="outliner-list" data-testid="outliner-brush-list">
{brushList.map((brush, brushIndex) => { {brushList.map((brush, brushIndex) => {
const label = getBrushLabel(brush, brushIndex); const label = getBrushLabel(brush, brushIndex);
const isSelected = selectedBrush?.id === brush.id;
return ( return (
<div <div
@@ -4210,25 +4211,43 @@ export function App({ store, initialStatusMessage }: AppProps) {
className={`outliner-item outliner-item--compact ${isBrushSelected(editorState.selection, brush.id) ? "outliner-item--selected" : ""}`} className={`outliner-item outliner-item--compact ${isBrushSelected(editorState.selection, brush.id) ? "outliner-item--selected" : ""}`}
> >
<div className="outliner-item__row"> <div className="outliner-item__row">
<button {isSelected ? (
className="outliner-item__select" <input
type="button" className="outliner-item__rename"
data-testid={`outliner-brush-${brush.id}`} data-testid="selected-brush-name"
onClick={() => type="text"
applySelection( value={brushNameDraft}
{ placeholder={`Box Brush ${brushIndex + 1}`}
kind: "brushes", onChange={(event) => setBrushNameDraft(event.currentTarget.value)}
ids: [brush.id] onBlur={applyBrushNameChange}
}, onFocus={(event) => event.currentTarget.select()}
"outliner", onKeyDown={(event) =>
{ handleInlineNameInputKeyDown(event, () => {
focusViewport: true setBrushNameDraft(selectedBrush?.name ?? "");
} })
) }
} />
> ) : (
<span className="outliner-item__title">{label}</span> <button
</button> className="outliner-item__select"
type="button"
data-testid={`outliner-brush-${brush.id}`}
onClick={() =>
applySelection(
{
kind: "brushes",
ids: [brush.id]
},
"outliner",
{
focusViewport: true
}
)
}
>
<span className="outliner-item__title">{label}</span>
</button>
)}
<button <button
className="outliner-item__delete" className="outliner-item__delete"
type="button" type="button"
@@ -4239,26 +4258,6 @@ export function App({ store, initialStatusMessage }: AppProps) {
x x
</button> </button>
</div> </div>
{selectedBrush?.id !== brush.id ? null : (
<label className="form-field outliner-item__editor">
<span className="label">Name</span>
<input
className="text-input text-input--dense"
data-testid="selected-brush-name"
type="text"
value={brushNameDraft}
placeholder={`Box Brush ${brushIndex + 1}`}
onChange={(event) => setBrushNameDraft(event.currentTarget.value)}
onBlur={applyBrushNameChange}
onKeyDown={(event) => {
if (event.key === "Enter") {
applyBrushNameChange();
}
}}
/>
</label>
)}
</div> </div>
); );
})} })}
@@ -4272,47 +4271,66 @@ export function App({ store, initialStatusMessage }: AppProps) {
<div className="outliner-empty">No model instances placed yet.</div> <div className="outliner-empty">No model instances placed yet.</div>
) : ( ) : (
<div className="outliner-list" data-testid="outliner-model-instance-list"> <div className="outliner-list" data-testid="outliner-model-instance-list">
{modelInstanceDisplayList.map(({ modelInstance, label }) => ( {modelInstanceDisplayList.map(({ modelInstance, label }) => {
<div const isSelected =
key={modelInstance.id} editorState.selection.kind === "modelInstances" && editorState.selection.ids.includes(modelInstance.id);
className={`outliner-item ${
editorState.selection.kind === "modelInstances" && editorState.selection.ids.includes(modelInstance.id) return (
? "outliner-item--selected" <div
: "" key={modelInstance.id}
} outliner-item--compact`} className={`outliner-item ${isSelected ? "outliner-item--selected" : ""} outliner-item--compact`}
> >
<div className="outliner-item__row"> <div className="outliner-item__row">
<button {isSelected ? (
data-testid={`outliner-model-instance-${modelInstance.id}`} <input
className="outliner-item__select" className="outliner-item__rename"
type="button" data-testid="selected-model-instance-name"
onClick={() => type="text"
applySelection( value={modelInstanceNameDraft}
{ placeholder={editorState.document.assets[modelInstance.assetId]?.sourceName ?? "Model Instance"}
kind: "modelInstances", onChange={(event) => setModelInstanceNameDraft(event.currentTarget.value)}
ids: [modelInstance.id] onBlur={applyModelInstanceNameChange}
}, onFocus={(event) => event.currentTarget.select()}
"outliner", onKeyDown={(event) =>
{ handleInlineNameInputKeyDown(event, () => {
focusViewport: true setModelInstanceNameDraft(selectedModelInstance?.name ?? "");
})
} }
) />
} ) : (
> <button
<span className="outliner-item__title">{label}</span> data-testid={`outliner-model-instance-${modelInstance.id}`}
</button> className="outliner-item__select"
<button type="button"
className="outliner-item__delete" onClick={() =>
type="button" applySelection(
data-testid={`outliner-delete-model-instance-${modelInstance.id}`} {
aria-label={`Delete ${label}`} kind: "modelInstances",
onClick={() => handleDeleteModelInstance(modelInstance.id)} ids: [modelInstance.id]
> },
x "outliner",
</button> {
focusViewport: true
}
)
}
>
<span className="outliner-item__title">{label}</span>
</button>
)}
<button
className="outliner-item__delete"
type="button"
data-testid={`outliner-delete-model-instance-${modelInstance.id}`}
aria-label={`Delete ${label}`}
onClick={() => handleDeleteModelInstance(modelInstance.id)}
>
x
</button>
</div>
</div> </div>
</div> );
))} })}
</div> </div>
)} )}
</div> </div>
@@ -4324,47 +4342,65 @@ export function App({ store, initialStatusMessage }: AppProps) {
{entityDisplayList.length === 0 ? null : ( {entityDisplayList.length === 0 ? null : (
<div className="outliner-list"> <div className="outliner-list">
{entityDisplayList.map(({ entity, label }) => ( {entityDisplayList.map(({ entity, label }) => {
<div const isSelected = editorState.selection.kind === "entities" && editorState.selection.ids.includes(entity.id);
key={entity.id}
className={`outliner-item ${ return (
editorState.selection.kind === "entities" && editorState.selection.ids.includes(entity.id) <div
? "outliner-item--selected" key={entity.id}
: "" className={`outliner-item ${isSelected ? "outliner-item--selected" : ""} outliner-item--compact`}
} outliner-item--compact`} >
> <div className="outliner-item__row">
<div className="outliner-item__row"> {isSelected ? (
<button <input
data-testid={`outliner-entity-${entity.id}`} className="outliner-item__rename"
className="outliner-item__select" data-testid="selected-entity-name"
type="button" type="text"
onClick={() => value={entityNameDraft}
applySelection( placeholder={getEntityKindLabel(entity.kind)}
{ onChange={(event) => setEntityNameDraft(event.currentTarget.value)}
kind: "entities", onBlur={applyEntityNameChange}
ids: [entity.id] onFocus={(event) => event.currentTarget.select()}
}, onKeyDown={(event) =>
"outliner", handleInlineNameInputKeyDown(event, () => {
{ setEntityNameDraft(selectedEntity?.name ?? "");
focusViewport: true })
} }
) />
} ) : (
> <button
<span className="outliner-item__title">{label}</span> data-testid={`outliner-entity-${entity.id}`}
</button> className="outliner-item__select"
<button type="button"
className="outliner-item__delete" onClick={() =>
type="button" applySelection(
data-testid={`outliner-delete-entity-${entity.id}`} {
aria-label={`Delete ${label}`} kind: "entities",
onClick={() => handleDeleteEntity(entity.id)} ids: [entity.id]
> },
x "outliner",
</button> {
focusViewport: true
}
)
}
>
<span className="outliner-item__title">{label}</span>
</button>
)}
<button
className="outliner-item__delete"
type="button"
data-testid={`outliner-delete-entity-${entity.id}`}
aria-label={`Delete ${label}`}
onClick={() => handleDeleteEntity(entity.id)}
>
x
</button>
</div>
</div> </div>
</div> );
))} })}
</div> </div>
)} )}
</div> </div>