更新 UI 编辑器布局与控件行为

优化侧栏组件结构
调整 TransformEditor 渲染位置,
改进预览模式切换交互。
This commit is contained in:
2026-08-19 17:20:17 +08:00
parent 10d5bcf713
commit 1338089b07
3 changed files with 65 additions and 91 deletions
@@ -328,6 +328,34 @@ function NodeInspector({
return (
<div className="mt-4 space-y-4">
<div
className="flex items-center rounded-lg border border-(--platform-subpanel-border) bg-white/70 p-0.5 text-[10px]"
role="group"
aria-label="子节点布局行为"
>
<button
type="button"
aria-pressed={!keepChildrenUnchanged}
disabled={transformReadOnly || isReadOnly}
onClick={() => {
if (!isReadOnly) onKeepChildrenUnchangedChange(false);
}}
className={`flex-1 rounded-md px-2 py-1.5 transition ${!keepChildrenUnchanged ? 'bg-blue-600 text-white' : 'text-(--platform-text-soft) hover:bg-black/5'} disabled:cursor-not-allowed disabled:opacity-50`}
>
</button>
<button
type="button"
aria-pressed={keepChildrenUnchanged}
disabled={transformReadOnly || isReadOnly}
onClick={() => {
if (!isReadOnly) onKeepChildrenUnchangedChange(true);
}}
className={`flex-1 rounded-md px-2 py-1.5 transition ${keepChildrenUnchanged ? 'bg-orange-500 text-white' : 'text-(--platform-text-soft) hover:bg-black/5'} disabled:cursor-not-allowed disabled:opacity-50`}
>
</button>
</div>
<label className="flex items-center gap-2 rounded-lg border border-(--platform-subpanel-border) bg-white/35 p-2 text-xs">
<input
type="checkbox"
@@ -359,34 +387,6 @@ function NodeInspector({
</select>
</label>
) : null}
<div
className="flex items-center rounded-lg border border-(--platform-subpanel-border) bg-white/70 p-0.5 text-[10px]"
role="group"
aria-label="子节点布局行为"
>
<button
type="button"
aria-pressed={!keepChildrenUnchanged}
disabled={transformReadOnly || isReadOnly}
onClick={() => {
if (!isReadOnly) onKeepChildrenUnchangedChange(false);
}}
className={`flex-1 rounded-md px-2 py-1.5 transition ${!keepChildrenUnchanged ? 'bg-blue-600 text-white' : 'text-(--platform-text-soft) hover:bg-black/5'} disabled:cursor-not-allowed disabled:opacity-50`}
>
</button>
<button
type="button"
aria-pressed={keepChildrenUnchanged}
disabled={transformReadOnly || isReadOnly}
onClick={() => {
if (!isReadOnly) onKeepChildrenUnchangedChange(true);
}}
className={`flex-1 rounded-md px-2 py-1.5 transition ${keepChildrenUnchanged ? 'bg-orange-500 text-white' : 'text-(--platform-text-soft) hover:bg-black/5'} disabled:cursor-not-allowed disabled:opacity-50`}
>
</button>
</div>
<InspectorInput
label="名称"
value={node.metadata.name}
@@ -470,17 +470,17 @@ function NodeInspector({
LLM
</label>
</div>
{parentIsContainer ? (
<p className="m-0 rounded-lg bg-amber-50 p-2 text-xs text-amber-800">
Container Transform Preview
</p>
) : null}
<TransformEditor
transform={node.layout.transform}
parentSize={parentSize}
readOnly={transformReadOnly || isReadOnly}
onChange={onTransformChange}
/>
{parentIsContainer ? (
<p className="m-0 rounded-lg bg-amber-50 p-2 text-xs text-amber-800">
Container Transform Preview
</p>
) : null}
<LayoutEditor
node={node}
readOnly={isReadOnly}
@@ -589,7 +589,6 @@ function LayoutEditor({
return (
<section className="space-y-2 rounded-lg border border-(--platform-subpanel-border) bg-white/35 p-3">
<h3 className="m-0 text-xs font-semibold">Control Container</h3>
<div className="grid grid-cols-2 gap-2">
<InspectorInput
label="最小宽度"
@@ -5,7 +5,6 @@ import {
Crosshair,
Info,
Move,
SlidersHorizontal,
} from 'lucide-react';
import { useEffect, useMemo, useRef, useState } from 'react';
@@ -375,25 +374,8 @@ export function TransformEditor({
return (
<section
aria-readonly={readOnly}
className={`w-full min-w-0 max-w-full overflow-visible rounded-2xl border text-(--platform-text-strong) shadow-[0_16px_40px_rgb(91_62_45/10%)] ${readOnly ? 'border-slate-300 bg-slate-100/90' : 'border-(--platform-subpanel-border) bg-(--platform-subpanel-fill)'}`}
className={`w-full min-w-0 max-w-full overflow-visible rounded-lg border text-(--platform-text-strong) shadow-[0_16px_40px_rgb(91_62_45/10%)] ${readOnly ? 'border-slate-300 bg-slate-100/90' : 'border-(--platform-subpanel-border) bg-(--platform-subpanel-fill)'}`}
>
<header className="flex min-w-0 flex-wrap items-center justify-between gap-2 border-b border-(--platform-subpanel-border) px-4 py-3">
<div className="flex min-w-0 items-center gap-2">
<span className="grid size-8 place-items-center rounded-xl bg-orange-100 text-orange-600">
<SlidersHorizontal size={15} aria-hidden="true" />
</span>
<div>
<h2 className="text-sm font-semibold"></h2>
<p
className="text-[10px] text-(--platform-text-soft)"
title="通过锚点比例和像素偏移控制元素在父容器中的位置与尺寸。"
>
</p>
</div>
</div>
</header>
<div className="grid gap-4 p-4">
<div ref={presetRef} className="relative">
<button
@@ -308,48 +308,41 @@ export function PreviewWorkspace({
return (
<section className="flex h-full min-h-0 min-w-0 flex-col overflow-hidden bg-(--platform-body-fill)">
<header className="flex min-w-0 shrink-0 flex-wrap items-center justify-between gap-2 border-b border-(--platform-subpanel-border) px-4 py-2">
<h1 className="m-0 text-sm font-semibold">UI布局编辑器</h1>
<div className="flex min-w-0 flex-wrap items-center justify-end gap-2">
<div className="flex items-center rounded-lg border border-(--platform-subpanel-border) bg-white/70 p-0.5 text-[10px]">
<button
type="button"
className={`rounded-md px-2 py-1 ${renderMode === 'editor-overlay' ? 'bg-blue-600 text-white' : 'text-(--platform-text-soft)'}`}
aria-pressed={renderMode === 'editor-overlay'}
onClick={() => setRenderMode('editor-overlay')}
>
</button>
<button
type="button"
className={`rounded-md px-2 py-1 ${renderMode === 'final-preview' ? 'bg-orange-500 text-white' : 'text-(--platform-text-soft)'}`}
aria-pressed={renderMode === 'final-preview'}
onClick={() => setRenderMode('final-preview')}
>
</button>
</div>
{renderMode === 'final-preview' ? (
<label className="inline-flex cursor-pointer select-none items-center gap-1.5 text-[10px] text-(--platform-text-soft)">
<input
type="checkbox"
checked={showFrame}
onChange={(event) => setShowFrame(event.target.checked)}
className="size-3 accent-orange-500"
/>
<span>线</span>
</label>
) : null}
<span className="text-[10px] text-(--platform-text-soft)">
{Object.keys(canvas.images).length} ·{' '}
{Object.keys(canvas.sprites).length}
</span>
</div>
</header>
{activeImage &&
logicalSize &&
(renderMode === 'final-preview' || previewUrls[activeImageId ?? '']) ? (
<div className="relative min-h-0 flex-1">
<div className="pointer-events-auto absolute right-3 top-3 z-40 flex max-w-[calc(100%-1.5rem)] flex-wrap items-center justify-end gap-2 bg-transparent text-[10px]">
{renderMode === 'final-preview' ? (
<label className="inline-flex cursor-pointer select-none items-center gap-1.5 rounded-lg bg-white/70 px-2 py-1 text-(--platform-text-soft) shadow-sm backdrop-blur">
<input
type="checkbox"
checked={showFrame}
onChange={(event) => setShowFrame(event.target.checked)}
className="size-3 accent-orange-500"
/>
<span>线</span>
</label>
) : null}
<div className="flex items-center rounded-lg border border-(--platform-subpanel-border) bg-white/70 p-0.5 shadow-sm backdrop-blur">
<button
type="button"
className={`rounded-md px-2 py-1 ${renderMode === 'editor-overlay' ? 'bg-blue-600 text-white' : 'text-(--platform-text-soft)'}`}
aria-pressed={renderMode === 'editor-overlay'}
onClick={() => setRenderMode('editor-overlay')}
>
</button>
<button
type="button"
className={`rounded-md px-2 py-1 ${renderMode === 'final-preview' ? 'bg-orange-500 text-white' : 'text-(--platform-text-soft)'}`}
aria-pressed={renderMode === 'final-preview'}
onClick={() => setRenderMode('final-preview')}
>
</button>
</div>
</div>
{renderMode === 'final-preview' && canvas.selectedNode ? (
<ExclusiveChildrenTabs
parent={canvas.selectedNode}