feat: add layer and mask thumbnails
This commit is contained in:
@@ -12,6 +12,8 @@ import { resolveLayerDrop } from "@input/index";
|
||||
import type { DocumentActions } from "@app/document-actions";
|
||||
import { addArtboard, addEmptyLayer, addGroupLayer, addLayerMask, deleteSelection, groupLayers, moveLayer } from "@operations/document/layerActions";
|
||||
import { MaskOperationButtons, MaskStatus } from "./layers/MaskControls";
|
||||
import { LayerThumbnail } from "./layers/LayerThumbnail";
|
||||
import { createLayerThumbnailIndex, type LayerThumbnailModel } from "./layers/thumbnailModel";
|
||||
|
||||
export type LayersSheetProps = {
|
||||
document: ImageDocument;
|
||||
@@ -72,6 +74,7 @@ function LayersSheetBody({
|
||||
const canGroup = Boolean(selection.artboardId && selection.layerIds.length > 0);
|
||||
const canUngroup = selectedLayer?.layer.type === "group";
|
||||
const maskLayerIds = documentIndex.maskLayerIds;
|
||||
const thumbnailByLayerId = useMemo(() => createLayerThumbnailIndex(document, documentIndex.assetById, maskLayerIds), [document, documentIndex, maskLayerIds]);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -182,6 +185,7 @@ function LayersSheetBody({
|
||||
setEditingTitle={setEditingTitle}
|
||||
maskLayerIds={maskLayerIds}
|
||||
maskEdit={maskEdit}
|
||||
thumbnailByLayerId={thumbnailByLayerId}
|
||||
dispatch={dispatch}
|
||||
/>
|
||||
))
|
||||
@@ -207,6 +211,7 @@ function LayerRow({
|
||||
setEditingTitle,
|
||||
maskLayerIds,
|
||||
maskEdit,
|
||||
thumbnailByLayerId,
|
||||
dispatch,
|
||||
}: {
|
||||
document: ImageDocument;
|
||||
@@ -220,6 +225,7 @@ function LayerRow({
|
||||
setEditingTitle: (editingTitle: EditingTitle | undefined) => void;
|
||||
maskLayerIds: ReadonlySet<string>;
|
||||
maskEdit?: MaskEditState;
|
||||
thumbnailByLayerId: ReadonlyMap<string, LayerThumbnailModel>;
|
||||
dispatch: AppStore["dispatch"];
|
||||
}) {
|
||||
if (maskLayerIds.has(layer.id)) return null;
|
||||
@@ -231,6 +237,8 @@ function LayerRow({
|
||||
const maskAsset = maskLayer && maskLayer.type !== "group" ? documentIndex.assetById.get(maskLayer.assetId) : undefined;
|
||||
const canAddMask = Boolean(layerInfo && layer.type !== "group" && !layerMask);
|
||||
const editingMask = Boolean(maskEdit && layerMask && maskEdit.targetLayerId === layer.id && maskEdit.maskLayerId === layerMask.maskLayerId);
|
||||
const thumbnail = thumbnailByLayerId.get(layer.id) ?? { kind: "empty" };
|
||||
const maskThumbnail = maskLayer ? thumbnailByLayerId.get(maskLayer.id) : undefined;
|
||||
const rowPadding = 12 + depth * 16;
|
||||
|
||||
return (
|
||||
@@ -260,6 +268,14 @@ function LayerRow({
|
||||
<button type="button" className={editingMask || selected ? "text-black/55" : "text-white/45 transition hover:text-white"} aria-label={layer.visible ? "Hide layer" : "Show layer"} onClick={() => dispatch(commandIds.documentSetLayerVisible, { layerId: layer.id, visible: !layer.visible })}>
|
||||
{layer.visible ? <Eye size={24} weight="regular" /> : <EyeSlash size={24} weight="regular" />}
|
||||
</button>
|
||||
<span className={`relative flex shrink-0 items-center ${layer.visible ? "" : "opacity-35 grayscale"}`}>
|
||||
<LayerThumbnail model={thumbnail} label={`${layer.name} thumbnail`} />
|
||||
{maskThumbnail ? (
|
||||
<span className={`-ml-2 rounded-md ${editingMask ? "ring-2 ring-sky-500 ring-offset-2 ring-offset-sky-300" : "ring-1 ring-sky-300/70"}`}>
|
||||
<LayerThumbnail model={maskThumbnail} label={`${layer.name} mask thumbnail${editingMask ? ", currently editing" : ""}`} compact />
|
||||
</span>
|
||||
) : null}
|
||||
</span>
|
||||
{editingTitle?.type === "layer" && editingTitle.id === layer.id ? (
|
||||
<RenameInput
|
||||
value={editingTitle.draft}
|
||||
@@ -298,9 +314,10 @@ function LayerRow({
|
||||
</button>
|
||||
</div>
|
||||
{layerMask ? (
|
||||
<div className="mt-2 flex min-h-14 flex-wrap items-center gap-2 rounded-[1.5rem] py-2 pl-4 pr-2 text-sm text-sky-100/70 hover:bg-white/[0.04]">
|
||||
<Stack size={24} weight="fill" className="shrink-0" />
|
||||
<span className="min-w-28 flex-1 truncate">{maskLayer ? "Layer mask" : "Layer mask missing"}</span>
|
||||
<div role="group" aria-label={`Mask attached to ${layer.name}${editingMask ? ", currently editing" : ""}`} className={`relative mt-2 ml-10 flex min-h-14 flex-wrap items-center gap-2 rounded-[1.5rem] border-l-2 py-2 pl-4 pr-2 text-sm transition ${editingMask ? "border-sky-300 bg-sky-300/15 text-sky-50 ring-1 ring-inset ring-sky-300/30" : "border-sky-300/30 text-sky-100/70 hover:bg-white/[0.04]"}`}>
|
||||
<span aria-hidden="true" className="absolute -left-2 top-1/2 h-px w-2 bg-sky-300/40" />
|
||||
{maskThumbnail ? <LayerThumbnail model={maskThumbnail} label={`${layer.name} mask preview`} /> : <Stack size={24} weight="fill" className="shrink-0" />}
|
||||
<span className="min-w-28 flex-1 truncate font-medium">{maskLayer ? editingMask ? "Editing layer mask" : "Layer mask" : "Layer mask missing"}</span>
|
||||
{maskAsset ? <MaskStatus asset={maskAsset} /> : null}
|
||||
{maskLayer ? (
|
||||
<div className="flex flex-wrap items-center gap-1">
|
||||
@@ -366,6 +383,7 @@ function LayerRow({
|
||||
setEditingTitle={setEditingTitle}
|
||||
maskLayerIds={maskLayerIds}
|
||||
maskEdit={maskEdit}
|
||||
thumbnailByLayerId={thumbnailByLayerId}
|
||||
dispatch={dispatch}
|
||||
/>
|
||||
))
|
||||
|
||||
Reference in New Issue
Block a user