/**
* 视图区容器 —— **净化渲染,无脚本执行权**(编码规范 §9.2;语义出处 AM 07)。
*
* 「视图区容器渲染活动推送的 HTML:沙箱 iframe(srcdoc + sandbox)或净化后注入,
* 二选一由 `ui-kit` 的 `ViewContainer` 统一实现,**feature 不得自行 dangerouslySetInnerHTML`。」
*
* 这里选沙箱 iframe:结构上就没有脚本执行权,不依赖净化算法的完备性。
*
* **高度随内容长**:HTML 报告既是独立文件,也是交互视图的组成部分 ——
* 它得像页面里的一段正文那样整篇铺开、跟着外层一起滚,而不是缩在一个定高小窗里自己滚。
* 定高会把长报告拦腰截断,人得在两层滚动条之间来回找。
*
* ── 就地改:默认锁着,按「编辑」才开(2026-08-01 用户定)────────────────
* 待处理那一屏摆的**只有这份件**,而件本身要能改 —— 于是编辑态长在件里面:
* 每个可改的格子在渲染时就配了一个 `` 孪生件
* (见 `view-render.ts` 的 `twinInputs`),平时收着,开了闸才现身。
*
* **看与改是两件事**:进这一屏整份件就是编辑态的话,人还没决定要改什么,
* 屏上已经是一张待填的表(A1 那份是 54 个输入框一起亮着)。所以每张可改的表
* 各带一道闸(`[data-edit-for]`):锁着 → 「编辑」;开了 → 「取消 / 保存」。
*
* 外层怎么开、怎么收:`allow-same-origin` 让父文档**读得到也改得动** iframe 的 DOM——
* · 挂事件、加 class、读 input,全在父文档这一侧;
* · **iframe 里始终一行脚本都没有**:`allow-scripts` 永远不给,两者同时给才是破沙箱。
*/
import { useCallback, useEffect, useRef, useState } from 'react';
/** 一张可改的表收上来的样子:`{ 台账地址: [{ 列名: 值 }] }` */
export type ViewEdits = Readonly>>>>;
/** 按一次保存交出去的东西:现在这张表长什么样(`tables`)+ 人删掉了哪几行(`drops`) */
export type ViewSave = { readonly tables: ViewEdits; readonly drops: ViewEdits };
export type ViewContainerProps = {
readonly html: string;
readonly title?: string;
/** 这一屏的件**可以改** —— 闸出现(默认仍是锁着的,要按「编辑」才开) */
readonly editable?: boolean;
/** 人按下「保存」时把那张表收上来;不传就是纯呈现 */
readonly onEdits?: (save: ViewSave) => void;
/**
* **件上的资料名被点了** —— 收到 `data-file="域.对象/主键值"` 那一下。
*
* 容器只报「点的是哪一份」,取文件、渲文件都不在这儿:这份组件的本分是
* 把沙箱里发生的事如实交出去,至于跳到哪一屏是工作区的事。
*/
readonly onOpenFile?: (ref: { address: string; key: string }) => void;
/**
* **多页件**(2026-08-02 用户:「一个执行单可以派多个 HTML 待执行报告,
* 合理利用右上角的翻页」):件里带 `data-page` 的可见段各算一页,
* 外层用这两个口翻 —— `page` 显示第几页(0 起),`onPages` 回报共几页。
* 不传 `page` 或只有一页时全部照常显示。
*/
readonly page?: number;
readonly onPages?: (total: number) => void;
};
/** 量不到高度时的兜底,避免塌成 0 高 */
const FALLBACK_HEIGHT = 420;
/**
* **就地换内容的第二层:只换真变了的节点**(2026-08-03 用户:「保存的时候 HTML 会闪一下」)。
*
* 2026-08-02 那一刀把 srcDoc 重载换成了 `body.innerHTML = html` —— 文档不再重载,
* 可整棵 DOM 仍是**推倒重建**:47KB 样式表在 body 里再解析一遍(head 那份还在,从此一文档
* 两份同样的表)、二十来个 SVG 全部重建、CSS 动画一律回到第 0 帧重新起跳 —— 人眼里就是
* 保存那一下整张件「闪一下」。同一个症状,病根在下一层。
*
* 治法:新旧两棵树**逐节点对齐**,一样的一根手指都不碰 —— 样式表不重解析、精灵不重建、
* 没变的段落连重绘都没有;保存的涟漪只落在真改了的那几个格子上。
*
* 细则:
* · 顶层 `