b8849b8c01
- 顶栏去掉两侧 flex-wrap、工具条改 overflow: hidden,空间不足按 data-layout 三档降级而不是换行 - 新增 workbenchToolbarModel(档位顺序与 1px 判据)与 useWorkbenchToolbarLayout(ResizeObserver 管宽度、MutationObserver 管内容,实测内容宽度写档位) - 版本入口 margin-left: auto 钉在动作区最右,标识改「版本 N(原因 · 时间)」,窄档只留「版本 N」(序号复用资源画布版本卡编号口径) - 新增 WorkbenchMoreActionsMenu:「打开项目目录 / 资源面板 / 整理画布」在 collapsed-actions 档收进「更多」下拉 - 删除独立「播放」按钮,运行页签前加 ▶ 图标,点页签即切视图并载入游戏资源 - 运行画面右下角新增「刷新运行画面」,LocalGamePreviewFrame 新增 reloadNonce 换 iframe 元素身份重新载入页面 - 用例:新增 workbenchToolbarLayout / runPreviewRefresh;改写 appSurface 顶栏与播放/刷新断言、版本标识断言与顶栏样式断言
88 lines
3.3 KiB
TypeScript
88 lines
3.3 KiB
TypeScript
import { type RefObject, useLayoutEffect } from 'react';
|
||
|
||
import {
|
||
resolveWorkbenchToolbarLayout,
|
||
type WorkbenchToolbarLayout,
|
||
} from './workbenchToolbarModel';
|
||
|
||
/**
|
||
* 顶栏当前档位挂在 DOM 属性上(`data-layout`),并**只由这个 hook 写**。
|
||
*
|
||
* 不让 React 渲染这个属性:档位是量出来的,不是 state 推出来的。这条链路上每次量宽都要先把
|
||
* 候选档位真的写到 DOM(否则量到的还是当前档位的宽度),React 受控属性会被这类「量一次写一次」
|
||
* 的中间值污染,且每次窗口拖动都要触发一轮工作台整树重渲染——顶栏只有这一处样式差异,不值得。
|
||
* React 不声明它就不会覆盖它,见 `styles.css` 里的 `[data-layout=...]` 选择器。
|
||
*/
|
||
export const WORKBENCH_TOOLBAR_LAYOUT_ATTRIBUTE = 'data-layout';
|
||
|
||
/**
|
||
* 把候选档位写到 DOM 上量一次内容宽度。
|
||
*
|
||
* 顶栏是 `flex-wrap: nowrap` + `overflow: hidden`:放不下时溢出的那部分仍计入 `scrollWidth`,
|
||
* 所以「量得的内容宽度 > clientWidth」就是这一档放不下。
|
||
*/
|
||
function measureToolbarContentWidth(
|
||
element: HTMLElement,
|
||
candidate: WorkbenchToolbarLayout,
|
||
): number {
|
||
element.dataset.layout = candidate;
|
||
return element.scrollWidth;
|
||
}
|
||
|
||
/** 重新爬一次档位阶梯并落定;返回落定后的档位(测试与排障用)。 */
|
||
export function applyWorkbenchToolbarLayout(
|
||
element: HTMLElement,
|
||
): WorkbenchToolbarLayout {
|
||
const layout = resolveWorkbenchToolbarLayout(
|
||
element.clientWidth,
|
||
(candidate) => measureToolbarContentWidth(element, candidate),
|
||
);
|
||
element.dataset.layout = layout;
|
||
return layout;
|
||
}
|
||
|
||
/**
|
||
* 让顶栏按实际可用宽度自己选档位(`workbenchToolbarModel` 给了顺序与判据)。
|
||
*
|
||
* 两个信号都要:
|
||
* - `ResizeObserver`:可用宽度变了(窗口拖动、对话列收放);
|
||
* - `MutationObserver`:宽度不变但**内容**变了(切运行页、出现「恢复草稿」、面板开合)。
|
||
* 这种变化不改顶栏宽度,只看尺寸会停在旧档位上——动作变少了却还收在「更多」里。
|
||
*
|
||
* 观测本身不引起递归:写的是被观测节点自己的属性,而属性不在观测范围内(`childList` /
|
||
* `characterData` / `subtree` 都不含根节点自身属性),且档位变化不改顶栏尺寸。
|
||
* 没有 `ResizeObserver` 的环境(jsdom)只按内容变化重算,量到 0 宽就停在默认的 `full`——
|
||
* 与关闭脚本前的默认形态一致。
|
||
*/
|
||
export function useWorkbenchToolbarLayout(
|
||
toolbarRef: RefObject<HTMLElement | null>,
|
||
): void {
|
||
useLayoutEffect(() => {
|
||
const element = toolbarRef.current;
|
||
if (!element) {
|
||
return undefined;
|
||
}
|
||
applyWorkbenchToolbarLayout(element);
|
||
|
||
const resizeObserver =
|
||
typeof ResizeObserver === 'function'
|
||
? new ResizeObserver(() => applyWorkbenchToolbarLayout(element))
|
||
: null;
|
||
resizeObserver?.observe(element);
|
||
const mutationObserver =
|
||
typeof MutationObserver === 'function'
|
||
? new MutationObserver(() => applyWorkbenchToolbarLayout(element))
|
||
: null;
|
||
mutationObserver?.observe(element, {
|
||
childList: true,
|
||
characterData: true,
|
||
subtree: true,
|
||
});
|
||
|
||
return () => {
|
||
resizeObserver?.disconnect();
|
||
mutationObserver?.disconnect();
|
||
};
|
||
}, [toolbarRef]);
|
||
}
|