@agile-team/wl-skills-ui 1.9.11 → 1.9.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,38 @@ All notable changes to **@agile-team/wl-skills-ui** will be documented in this f
4
4
 
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
6
6
 
7
+ ## [1.9.13] - 2026-08-07
8
+
9
+ ### Added
10
+
11
+ - 新增 `runtime/split-grid-resize` 分屏表格尺寸守护:只观察 `jh-drag-row` 的直接 pane,支持动态路由晚挂载,连续 Resize 按动画帧合并,并向 grid host 派发局部 `wl-ui:split-grid-resize` 事件。
12
+ - 复合结构清单新增 Element Plus input-group 新旧 DOM 与 common-core drag-row/AG Grid 契约,并补充 Node 行为测试和真实浏览器回归。
13
+
14
+ ### Fixed
15
+
16
+ - 修复带 prefix/append 的输入框主体与右侧图标高度、圆角、边框不一致;组合根统一 26px、6px 和五态描边,左右图标统一 14px,纯图标附加段统一 32px,同时兼容旧版 input 直挂和新版 wrapper DOM。
17
+ - 修复上下分屏拖动后 AG Grid 宿主未沿 pane 收缩、上表只被外层 `overflow:hidden` 裁切且缺少内部纵向滚动的问题;只补齐已识别 AG Grid 的 flex 高度链,让 AG Grid 自身 ResizeObserver 完成内部布局。
18
+
19
+ ### Changed
20
+
21
+ - 分屏守护不增加外层滚动条、不广播全局 `window.resize`、不访问 Vue 私有实例或猜测 gridApi;普通分屏、普通表格、非分屏 AG Grid、登录页和显式豁免区域不接管,卸载时清理包内结构标记。
22
+
23
+ ## [1.9.12] - 2026-08-06
24
+
25
+ ### Added
26
+
27
+ - 新增 `standards/component-structures.json` 复合控件结构契约,首批登记 common-core 多标签、人员/部门/树选择、多选、混合数字输入与 BaseToolbar 分裂按钮的边框所有者、高度策略、状态、内部无描边层和 Teleport 出口。
28
+ - 新增 R040 未知复合结构审查规则与真实 DOM fixtures;已登记结构正常通过,疑似新结构要求人工核对后再增加精准适配,不执行机械修复。
29
+ - 新增 Chromium 浏览器视觉回归及发布门禁,覆盖主题防反覆盖、按钮/圆角、textarea focus、数字输入单描边、复合输入自然增高、长文本提示、表格行状态和定制区域豁免。
30
+
31
+ ### Changed
32
+
33
+ - `prepublishOnly` 升级为完整 `release:check`,npm 发布前必须同时通过代码、文档、构建、包内容及真实浏览器视觉回归;测试页和截图不进入 npm 包。
34
+
35
+ ### Fixed
36
+
37
+ - 真实浏览器回归发现并修复复合多标签内部 editor 仍继承 Element Plus 默认 inset 的问题;仅在已登记的 `.com-input-multi-tag-wrap` 结构中清除子 wrapper 描边,普通输入与其他 picker 不受影响。
38
+
7
39
  ## [1.9.11] - 2026-08-06
8
40
 
9
41
  ### Fixed
package/README.md CHANGED
@@ -104,8 +104,9 @@ wl-skills-ui/
104
104
  │ │ ├── common.ts # 通用业务预设(enable/audit/verify + 起步包)
105
105
  │ │ └── index.ts
106
106
  │ ├── auto.ts # Skin/老项目包级保护自动入口
107
- │ ├── guards.ts # 主题锁 + 长文本溢出保护聚合
107
+ │ ├── guards.ts # 主题锁 + 长文本 + 分屏表格尺寸保护聚合
108
108
  │ ├── overflow-tooltip.ts # 普通表格真实溢出 Tooltip 兜底
109
+ │ ├── split-grid-resize.ts # jh-drag-row 内 AG Grid Resize/高度链守护
109
110
  │ └── index.ts # 公共 API 入口
110
111
  │
111
112
  ├── scanner/ # 自动化扫描 / 修复
@@ -115,6 +116,7 @@ wl-skills-ui/
115
116
  │ │ ├── table.mjs # R001 R002 R003 R014 R039
116
117
  │ │ ├── form.mjs # R006 R007 R008
117
118
  │ │ ├── button.mjs # R004 R005 R015 R038
119
+ │ │ ├── componentStructure.mjs # R040 复合结构登记审查
118
120
  │ │ ├── tag.mjs # R009 R010 R012
119
121
  │ │ ├── dialog.mjs # R011
120
122
  │ │ ├── color.mjs # R016 R017 R018
@@ -166,9 +168,12 @@ wl-skills-ui/
166
168
  │ └── ag-grid-page/TPL-AG-GRID.md
167
169
  │
168
170
  ├── standards/ # 团队规范文档
171
+ │ ├── component-structures.json # 复合控件结构契约单一事实源
169
172
  │ ├── ui/ # UI 规范(01-table / 02-button / ...)
170
173
  │ └── engineering/ # 工程规范(import 顺序 / 命名 / SCSS 结构)
171
174
  │
175
+ ├── tests/visual/ # Chromium 真实浏览器视觉回归与基准图
176
+ │
172
177
  ├── bin/ # CLI
173
178
  │ └── wl-ui.js # 统一入口(init/update/diff/clean/doctor/prompts/scan/fix/add-preset)
174
179
  │
@@ -208,7 +213,7 @@ yarn add @agile-team/wl-skills-ui
208
213
  |---|---|---|
209
214
  | `element-plus` | **`2.2.6-prod.3`** | 集团 jh- 定制版;EP 2.3.0 起引入 `.el-input__wrapper`,与 jh-ui 3.x 不兼容 |
210
215
  | `@jhlc/jh-ui` | **`3.1.0`** | SCSS 皮肤包,`.com-text` label 包裹 + `.has-colon` 冒号注入(wl-skills-ui 已统一屏蔽表单冒号) |
211
- | `@agile-team/wl-skills-ui` | `^1.9.11` | 已对齐上述组合的 DOM 假设、客户主题锁、圆角契约、复合输入、表格长文本兜底与定制页样式边界 |
216
+ | `@agile-team/wl-skills-ui` | `^1.9.13` | 已对齐上述组合的 DOM 假设、客户主题锁、圆角契约、input-group 附加段、分屏 AG Grid 重布局、表格长文本兜底与定制页边界 |
212
217
 
213
218
  三种识别方式,任选其一:
214
219
 
@@ -222,7 +227,20 @@ yarn add @agile-team/wl-skills-ui
222
227
 
223
228
  ## 版本亮点
224
229
 
225
- 当前 v1.9.11:
230
+ 当前 v1.9.13:
231
+
232
+ - **输入附加段不再割裂**:`el-input-group` 由组合根统一 26px 高度、6px 外圆角和 default/hover/focus/error/disabled 描边;左侧 prefix、右侧 append 图标统一 14px,纯图标附加段 32px,文字/单位/按钮仍按内容宽度。
233
+ - **新旧 DOM 同时覆盖**:兼容 jh-ui 配套旧版 input 直挂结构和新版 `.el-input__wrapper` 结构,内部输入不再重复描边;普通 input、普通按钮、登录页和显式豁免区保持原行为。
234
+ - **分屏表格真正重布局**:`runtime/auto` 只观察 `jh-drag-row` 内真实 AG Grid pane,补齐可收缩高度链并按帧派发局部重布局事件;AG Grid 自身 ResizeObserver 生成内部滚动条,不给外层加滚动条、不广播全局 resize、不访问 Vue 私有实例。
235
+ - **回归边界闭环**:结构清单新增 input-group 新旧 DOM 与 drag-row/AG Grid 契约;Node 行为测试覆盖动态挂载、批量 Resize、豁免区,真实浏览器验证附加段计算样式及 220px→120px 收缩后的内部滚动。
236
+
237
+ 上一版 v1.9.12:
238
+
239
+ - **复合结构有清单**:`standards/component-structures.json` 登记边框所有者、内部无描边层、高度策略、状态与 Teleport 出口;首批覆盖 common-core 多标签、人员/部门/树选择、多选、混合数字框及 BaseToolbar 分裂按钮。
240
+ - **未知结构先评审**:新增 R040,对疑似复合 Element wrapper 只报告、不自动修复,要求先核对真实 DOM 并登记契约,禁止继续放大普通 Element 选择器。
241
+ - **发版前真实浏览器门禁**:新增 Chromium 视觉回归,验证客户主题防反覆盖、按钮与圆角、textarea focus、数字框单描边、复合输入自然增高、长文本省略/提示、表格行状态和定制区域豁免;截图基准不进入 npm 产物。
242
+
243
+ 上一版 v1.9.11:
226
244
 
227
245
  - **复合输入不再误伤**:common-core 多标签/人员选择器不再被普通 input 的固定高度压缩;无标签时保持 26px 紧凑基线,标签换行时容器自然增高。
228
246
  - **视觉契约保持统一**:复合输入外层继续使用统一 6px 圆角及默认、hover、品牌 focus、error、disabled 边框,内部编辑 input 不重复描边;普通 input/select/date/input-number 原有规则不变。
@@ -393,7 +411,7 @@ npx wl-ui init --mode native
393
411
  // 4. src/main.ts
394
412
  import { installCommonPreset } from "@agile-team/wl-skills-ui/runtime/common-preset";
395
413
  installCommonPreset();
396
- // installCommonPreset 已包含主题锁和普通表格长文本包级保护,无需重复引入 runtime/auto。
414
+ // 已包含主题锁、普通表格长文本和分屏 AG Grid 尺寸保护,无需重复引入 runtime/auto。
397
415
  ```
398
416
 
399
417
  ```vue
@@ -717,7 +735,7 @@ export const myRules = [
717
735
 
718
736
  ## 规范清单
719
737
 
720
- ### UI 规则(R001-R039,按 layer 自动分组)
738
+ ### UI 规则(R001-R040,按 layer 自动分组)
721
739
 
722
740
  | Rule | Layer | Vendor | 说明 |
723
741
  | ---- | ----- | --------- | ------------------------------------ |
@@ -750,6 +768,30 @@ export const myRules = [
750
768
  | R037 | L1 | element | 空/异常反馈建议统一操作入口 |
751
769
  | R038 | L1 | element | 创建类主按钮缺 primary 填充主题色 |
752
770
  | R039 | L1 | element | 普通数据列缺省略与悬停完整提示 |
771
+ | R040 | L2 | common-core | 未登记复合控件结构需人工评审 |
772
+
773
+ ### 维护者防回归
774
+
775
+ 复合控件必须先登记结构契约,再增加精准样式;包发版必须经过 Chromium 基准图对比:
776
+
777
+ ```bash
778
+ # Windows 默认使用已安装的 Edge Chromium;非 Windows 首次安装一次
779
+ pnpm exec playwright install chromium
780
+
781
+ # 日常验证
782
+ pnpm test
783
+ pnpm test:visual
784
+
785
+ # 仅在设计变更已经人工确认时更新基准图
786
+ pnpm test:visual:update
787
+
788
+ # 完整发版门禁(含 lint、契约、SCSS、文档、构建、包校验、视觉回归)
789
+ pnpm release:check
790
+ ```
791
+
792
+ 机制与准入细则见 [复合控件结构契约](docs/composite-component-contracts.md) 和
793
+ [浏览器视觉回归](docs/visual-regression.md)。视觉基准依赖字体、浏览器与操作系统渲染,
794
+ 必须在相同环境中比较;当前项目基准由 Windows Edge Chromium 生成。
753
795
 
754
796
  ### 工程规范
755
797
 
@@ -11,7 +11,7 @@
11
11
  | `element-plus` | **`2.2.6-prod.3`** | 集团 jh- 定制版,搭配 `@jhlc/jh-ui` 使用,DOM 仍为 `.el-input > .el-input__inner` 直挂结构(**EP 2.3.0 起才引入 `.el-input__wrapper`**) |
12
12
  | `@jhlc/jh-ui` | **`3.1.0`** | SCSS 皮肤包,`.com-text` label 包裹、`.has-colon ::after` 冒号注入、`.el-form-item--default { margin-bottom: 24px }` 等强约束 |
13
13
  | `@jhlc/common-core` | `3.1.0` 或 `3.1.0-prod.x` | 基础 util/types,`@jhlc/jh-ui` 间接依赖 |
14
- | `@agile-team/wl-skills-ui` | `^1.9.11` | 已对齐 jh-ui 3.1.0 + EP 2.2.6-prod.3 的 DOM 假设、客户主题锁、6px 基础圆角、紧凑表单、复合多标签输入、表格行状态、分裂按钮、包级长文本提示、定制页样式边界与 Vite 4 开发转换 |
14
+ | `@agile-team/wl-skills-ui` | `^1.9.13` | 已对齐 jh-ui 3.1.0 + EP 2.2.6-prod.3 的 DOM 假设、客户主题锁、6px 基础圆角、紧凑表单、input-group 附加段、分屏 AG Grid 重布局、复合结构契约、表格长文本提示与定制页边界 |
15
15
 
16
16
  ## 项目集群当前实测
17
17
 
@@ -35,7 +35,7 @@
35
35
 
36
36
  ## 适配建议
37
37
 
38
- - 集团内项目:**统一锚定 `element-plus@2.2.6-prod.3` + `@jhlc/jh-ui@3.1.0`**,与 wl-skills-ui v1.9.11 三方对齐。
38
+ - 集团内项目:**统一锚定 `element-plus@2.2.6-prod.3` + `@jhlc/jh-ui@3.1.0`**,与 wl-skills-ui v1.9.13 三方对齐。
39
39
  - Skin 项目除全局样式外,在 `main.ts` 引入一次 `@agile-team/wl-skills-ui/runtime/auto`;Native 项目的 `installCommonPreset()` 已包含相同包级保护,不要重复安装。
40
40
  - 升级 `element-plus` 到 2.3+ 前,必须同步升级 `@jhlc/jh-ui` 到对应支持版本,否则 `.com-text` / `.el-input__inner` DOM 与 EP `__wrapper` 体系不兼容。
41
41
  - 新项目接入:见 README 「快速接入」章节,默认会按本表声明 peerDependency 范围。
@@ -0,0 +1,51 @@
1
+ # 复合控件结构契约
2
+
3
+ ## 目标
4
+
5
+ 复合控件经常同时包含 Element Plus 外壳、内部输入编辑器、标签容器以及 Teleport
6
+ 弹层。普通 `.el-input` / `.el-input__wrapper` 选择器无法判断谁负责边框和高度,直接扩大
7
+ 覆盖会导致固定高度裁切、边框消失或双描边。
8
+
9
+ `standards/component-structures.json` 是这类结构的单一事实源。它不修改 common-core,
10
+ 只记录 wl-skills-ui 已核对和允许治理的 DOM 契约。
11
+
12
+ ## 每项必须声明
13
+
14
+ - `rootSelector`:组件结构边界。
15
+ - `ownerSelector`:唯一负责默认、hover、focus、error、disabled 边框的节点。
16
+ - `innerBorderlessSelectors`:必须保持无描边的内部编辑器。
17
+ - `heightPolicy`:固定单行高度、最小高度自然增长或内容驱动。
18
+ - `states`:必须回归的交互状态。
19
+ - `portalSelectors`:下拉、Tooltip、Dialog 等 Teleport 出口;没有则可省略。
20
+ - `fixture`:从真实 DOM 最小化得到的结构样本。
21
+ - `knownRootClasses`:供 R040 识别已登记结构。
22
+
23
+ ## 新复合控件准入流程
24
+
25
+ 1. 在浏览器中复制真实 DOM,确认 Element Plus / jh-ui / common-core 版本。
26
+ 2. 提取最小 fixture,保留会影响选择器匹配的 class、父子层级和 Teleport 出口。
27
+ 3. 在 `standards/component-structures.json` 登记边框所有者、高度策略与状态。
28
+ 4. 运行 `pnpm test`;结构测试会校验 selector 唯一命中和 fixture 完整性。
29
+ 5. 只为已登记结构增加精准 SCSS,并在 `tests/visual` 补充状态断言或截图。
30
+ 6. 运行 `pnpm release:check` 后方可发布。
31
+
32
+ R040 只做审查提示,不自动改业务代码。已登记根类不会重复报告;发现疑似未知复合
33
+ wrapper 时,应先按上述流程确认,不能直接扩大全局 Element 选择器。
34
+
35
+ ## 当前首批结构
36
+
37
+ - common-core 多标签输入与人员选择多选。
38
+ - 部门选择、树选择和 Element 多选封装。
39
+ - 同节点 wrapper + 子 wrapper 的混合数字输入。
40
+ - BaseToolbar 分裂按钮及其动作菜单 Teleport 出口。
41
+ - Element Plus 新旧两种 input-group prepend/append DOM:组合根负责唯一外轮廓,
42
+ 输入主体与附加段统一 26px,纯图标附加段 32px、图标 14px。
43
+ - common-core `jh-drag-row + BaseTable/AG Grid` 高度链:只在真实分屏 AG Grid 中
44
+ 补齐可收缩结构并通知表格内部重布局。
45
+
46
+ 组件结构变化时,测试会先在 fixture/selector 契约处失败,从而把风险阻断在发版前,
47
+ 不会等十余个业务项目升级后再发现。
48
+
49
+ input-group 附加段保留文字、单位、按钮的内容驱动宽度,只有“直接子节点为唯一
50
+ `.el-icon`”时才使用 32px 紧凑宽度;普通 input、普通按钮和非 input-group 不命中。
51
+ 分屏重布局的运行机制与接入方式见 `docs/split-grid-resize.md`。
@@ -0,0 +1,68 @@
1
+ # 上下分屏 AG Grid 尺寸重布局
2
+
3
+ ## 问题与边界
4
+
5
+ `jh-drag-row` 的 `.drager_top/.drager_bottom` 使用 `overflow:hidden` 隔离上下区域。
6
+ 拖动分隔条后,如果 BaseTable/AG Grid 宿主没有沿 flex 高度链一起收缩,外层只会裁切
7
+ 旧尺寸表格,上表内部不会得到新的 viewport 高度,也就看不到应有的纵向滚动条。
8
+
9
+ 本方案只治理以下同时成立的结构:
10
+
11
+ - 根节点是 `.drager_row`;
12
+ - 直接 pane 是 `.drager_top` 或 `.drager_bottom`;
13
+ - pane 内真实存在 `.ag-grid-table` 或 `.ag-root-wrapper`;
14
+ - 不在 `.lp-root`、`.session-login`、`.wl-ui-skin-exempt`、
15
+ `[data-wl-ui-skin="off"]` 中。
16
+
17
+ 普通分屏内容、Element Table、非分屏 AG Grid 和定制区域均不接管。
18
+
19
+ ## 实现机制
20
+
21
+ 1. `runtime/auto` 安装 `ResizeObserver`,只观察已识别分屏的两个直接 pane。
22
+ 2. 动态路由晚挂载的新分屏通过仅监听 `childList` 的 `MutationObserver` 注册;不监听
23
+ 全站 style/class 变化,也不扫描每一帧。
24
+ 3. 发现 AG Grid 后,为 pane、必要祖先和 grid host 增加包内结构标记;SCSS 只对这些
25
+ 标记补 `min-height:0`。当 grid 的直接父级本来就是纵向 flex 时,grid 使用
26
+ `flex:1 1 0 + height:0` 占据剩余空间。
27
+ 4. grid host 真实尺寸改变后,AG Grid 自身的 `ResizeObserver` 完成布局计算并让内部
28
+ `.ag-body-viewport` 生成滚动条。
29
+ 5. 同一动画帧内的连续 Resize 会合并,并在 grid host 上派发局部
30
+ `wl-ui:split-grid-resize` 事件,供特殊平台封装按需调用公开 grid API。
31
+
32
+ 本实现不会给 `.drager_top/.drager_bottom` 增加 `overflow:auto`,不会访问 Vue 私有实例,
33
+ 不会遍历或猜测 `gridApi`,也不会广播全局 `window.resize`。卸载守护时,本包增加的结构
34
+ 标记会一并清理。
35
+
36
+ ## 接入
37
+
38
+ Skin 项目必须同时接入样式与一次运行时保护:
39
+
40
+ ```scss
41
+ @use "@agile-team/wl-skills-ui/styles/presets/skin" as *;
42
+ ```
43
+
44
+ ```ts
45
+ import "@agile-team/wl-skills-ui/runtime/auto";
46
+ ```
47
+
48
+ Native 项目调用 `installCommonPreset()` 即可,它已经包含相同守护,不要重复引入
49
+ `runtime/auto`。
50
+
51
+ 平台封装若确实还需要主动调用 `api.doLayout()`,可选监听局部事件:
52
+
53
+ ```ts
54
+ gridHost.addEventListener("wl-ui:split-grid-resize", () => {
55
+ gridApi.doLayout?.();
56
+ });
57
+ ```
58
+
59
+ 常规 AG Grid 无需业务代码监听;宿主尺寸变化已经会触发它自己的 ResizeObserver。
60
+
61
+ ## 验收
62
+
63
+ - 拖动前后 `.drager_top/.drager_bottom` 仍为 `overflow:hidden`。
64
+ - `.ag-grid-table` 的实际高度随 pane 收缩,并带
65
+ `data-wl-ui-split-grid-host`。
66
+ - `.ag-body-viewport` 的 `scrollHeight > clientHeight` 时出现内部纵向滚动条。
67
+ - 分屏外的 AG Grid、普通表格、登录页和显式豁免区无新增结构标记。
68
+ - `npm run test:visual` 中“上下分屏收缩后由 AG Grid 内部 viewport 滚动”通过。
@@ -0,0 +1,51 @@
1
+ # 浏览器视觉回归
2
+
3
+ ## 覆盖目标
4
+
5
+ `tests/visual` 使用真实 Vue、Element Plus、Skin SCSS 和 `runtime/auto`,在 Chromium 中
6
+ 验证最终计算样式与关键区域截图。它补充现有 Node DOM/SCSS 契约测试,专门发现
7
+ “选择器存在但浏览器层叠后没生效”以及“修一处却破坏另一处”的问题。
8
+
9
+ 当前覆盖:
10
+
11
+ - 客户主色被平台 JS 改写后由主题锁恢复。
12
+ - 主按钮、禁用态、功能色和统一圆角。
13
+ - textarea focus 品牌边框与轻焦点环。
14
+ - 数字输入只有一个边框所有者。
15
+ - common-core 复合多标签外壳自然增高、内层无双描边。
16
+ - input-group 输入主体、前后附加段、左右图标的统一高度/尺寸/圆角与 focus 描边。
17
+ - jh-drag-row 收缩后 AG Grid host 跟随变高,纵向滚动只发生在内部 viewport。
18
+ - Element Table 长文本真实溢出、省略号与按需完整提示。
19
+ - 表格选中行柔和状态色、BaseToolbar 分裂按钮与定制区域豁免。
20
+
21
+ ## 使用
22
+
23
+ ```bash
24
+ # Windows 默认复用已安装的 Edge Chromium;非 Windows 首次安装一次
25
+ pnpm exec playwright install chromium
26
+
27
+ # 对比现有基准
28
+ pnpm test:visual
29
+
30
+ # 调试
31
+ pnpm test:visual:ui
32
+
33
+ # 设计变更经评审确认后才更新基准
34
+ pnpm test:visual:update
35
+
36
+ # 发版完整门禁
37
+ pnpm release:check
38
+ ```
39
+
40
+ 截图基准按 Playwright 的浏览器/平台规则保存,不随 npm 包发布。Windows 默认使用企业
41
+ 环境已有的 Edge Chromium,也可通过 `WL_UI_BROWSER_CHANNEL` 显式指定 Playwright 通道。
42
+ 视觉像素受操作系统、浏览器版本和字体渲染影响,生成与比较必须使用相同环境;当前
43
+ 基准为 Windows Edge Chromium。
44
+ 升级 Playwright、Chromium 或字体后,应单独提交基准变化并人工审图,不能把更新截图当成
45
+ 消除失败的常规手段。
46
+
47
+ ## 增加场景
48
+
49
+ 优先向现有固定尺寸区域加入最小状态,不复制整套业务页面。每个缺陷至少提供一个
50
+ 计算样式断言;只有颜色、边框、布局关系需要肉眼整体判断时再补截图。动态时间、随机数、
51
+ 动画和远程资源不得进入基准页。
package/es/auto.js CHANGED
@@ -1,4 +1,4 @@
1
- import { installUiRuntimeGuards } from './chunk-Z2OTWMW6.js';
1
+ import { installUiRuntimeGuards } from './chunk-QLPJLXY6.js';
2
2
  import './chunk-Q3N5QNRZ.js';
3
3
 
4
4
  // runtime/auto.ts
@@ -232,10 +232,187 @@ function uninstallOverflowTooltipGuard() {
232
232
  installedOptions = DEFAULT_OPTIONS;
233
233
  }
234
234
 
235
+ // runtime/split-grid-resize.ts
236
+ var SPLIT_ROOT_SELECTOR = ".drager_row";
237
+ var SPLIT_PANE_SELECTOR = ".drager_top, .drager_bottom";
238
+ var GRID_SELECTOR = ".ag-grid-table, .ag-root-wrapper";
239
+ var EXEMPT_SELECTOR = ".lp-root, .session-login, .wl-ui-skin-exempt, [data-wl-ui-skin='off']";
240
+ var SPLIT_GRID_RESIZE_EVENT = "wl-ui:split-grid-resize";
241
+ var installed = false;
242
+ var resizeObserver = null;
243
+ var mutationObserver = null;
244
+ var observedPanes = /* @__PURE__ */ new WeakSet();
245
+ var pendingPanes = /* @__PURE__ */ new Set();
246
+ var addedMarkers = /* @__PURE__ */ new Map();
247
+ var frameId = null;
248
+ function addMarker(element, name) {
249
+ if (element.hasAttribute(name)) return;
250
+ element.setAttribute(name, "");
251
+ const names = addedMarkers.get(element) ?? /* @__PURE__ */ new Set();
252
+ names.add(name);
253
+ addedMarkers.set(element, names);
254
+ }
255
+ function removeOwnedMarker(element, name) {
256
+ const names = addedMarkers.get(element);
257
+ if (!names?.has(name)) return;
258
+ element.removeAttribute(name);
259
+ names.delete(name);
260
+ if (names.size === 0) addedMarkers.delete(element);
261
+ }
262
+ function isManagedSplitRoot(element) {
263
+ return element instanceof window.HTMLElement && element.matches(SPLIT_ROOT_SELECTOR) && !element.closest(EXEMPT_SELECTOR);
264
+ }
265
+ function getGridHosts(pane) {
266
+ const hosts = /* @__PURE__ */ new Set();
267
+ for (const element of Array.from(
268
+ pane.querySelectorAll(GRID_SELECTOR)
269
+ )) {
270
+ const agGridTable = element.closest(".ag-grid-table");
271
+ if (agGridTable && pane.contains(agGridTable)) {
272
+ hosts.add(agGridTable);
273
+ continue;
274
+ }
275
+ if (element.matches(".ag-root-wrapper")) {
276
+ hosts.add(element);
277
+ }
278
+ }
279
+ return [...hosts];
280
+ }
281
+ function markHeightChain(pane, gridHost) {
282
+ addMarker(pane, "data-wl-ui-split-pane");
283
+ addMarker(gridHost, "data-wl-ui-split-grid-host");
284
+ let parent = gridHost.parentElement;
285
+ while (parent && parent !== pane) {
286
+ addMarker(parent, "data-wl-ui-split-grid-chain");
287
+ parent = parent.parentElement;
288
+ }
289
+ const directParent = gridHost.parentElement;
290
+ if (!directParent) return;
291
+ const style = window.getComputedStyle(directParent);
292
+ if ((style.display === "flex" || style.display === "inline-flex") && style.flexDirection === "column") {
293
+ addMarker(directParent, "data-wl-ui-split-grid-flex-parent");
294
+ } else {
295
+ removeOwnedMarker(directParent, "data-wl-ui-split-grid-flex-parent");
296
+ }
297
+ }
298
+ function notifyPane(pane) {
299
+ const hosts = getGridHosts(pane);
300
+ if (hosts.length === 0) return;
301
+ for (const host of hosts) {
302
+ markHeightChain(pane, host);
303
+ const { height, width } = host.getBoundingClientRect();
304
+ host.dispatchEvent(
305
+ new window.CustomEvent(SPLIT_GRID_RESIZE_EVENT, {
306
+ bubbles: true,
307
+ detail: { height, pane, width }
308
+ })
309
+ );
310
+ }
311
+ }
312
+ function flushPendingPanes() {
313
+ frameId = null;
314
+ const panes = [...pendingPanes];
315
+ pendingPanes.clear();
316
+ for (const pane of panes) notifyPane(pane);
317
+ }
318
+ function schedulePane(pane) {
319
+ if (!pane.isConnected || !pane.querySelector(GRID_SELECTOR)) return;
320
+ pendingPanes.add(pane);
321
+ if (frameId !== null) return;
322
+ frameId = window.requestAnimationFrame(flushPendingPanes);
323
+ }
324
+ function registerSplitRoot(root) {
325
+ for (const pane of Array.from(
326
+ root.querySelectorAll(SPLIT_PANE_SELECTOR)
327
+ )) {
328
+ if (pane.parentElement !== root) continue;
329
+ if (!observedPanes.has(pane)) {
330
+ observedPanes.add(pane);
331
+ resizeObserver?.observe(pane);
332
+ }
333
+ schedulePane(pane);
334
+ }
335
+ }
336
+ function scanNode(node) {
337
+ if (!(node instanceof window.Element)) return;
338
+ if (isManagedSplitRoot(node)) registerSplitRoot(node);
339
+ for (const root of Array.from(
340
+ node.querySelectorAll(SPLIT_ROOT_SELECTOR)
341
+ )) {
342
+ if (isManagedSplitRoot(root)) registerSplitRoot(root);
343
+ }
344
+ const containingRoot = node.closest(SPLIT_ROOT_SELECTOR);
345
+ if (containingRoot && isManagedSplitRoot(containingRoot)) {
346
+ registerSplitRoot(containingRoot);
347
+ }
348
+ }
349
+ function scanDocument() {
350
+ for (const root of Array.from(
351
+ document.querySelectorAll(SPLIT_ROOT_SELECTOR)
352
+ )) {
353
+ if (isManagedSplitRoot(root)) registerSplitRoot(root);
354
+ }
355
+ }
356
+ function handleDragEnd(event) {
357
+ const { target } = event;
358
+ if (!(target instanceof window.Element)) return;
359
+ const root = target.closest(SPLIT_ROOT_SELECTOR);
360
+ if (!root || !isManagedSplitRoot(root)) return;
361
+ registerSplitRoot(root);
362
+ }
363
+ function installSplitGridResizeGuard() {
364
+ if (installed || typeof window === "undefined" || typeof document === "undefined") {
365
+ return;
366
+ }
367
+ installed = true;
368
+ if (typeof window.ResizeObserver !== "undefined") {
369
+ resizeObserver = new window.ResizeObserver((entries) => {
370
+ for (const entry of entries) {
371
+ if (entry.target instanceof window.HTMLElement) schedulePane(entry.target);
372
+ }
373
+ });
374
+ }
375
+ if (document.body && typeof window.MutationObserver !== "undefined") {
376
+ mutationObserver = new window.MutationObserver((records) => {
377
+ for (const record of records) {
378
+ for (const node of Array.from(record.addedNodes)) scanNode(node);
379
+ }
380
+ });
381
+ mutationObserver.observe(document.body, { childList: true, subtree: true });
382
+ }
383
+ document.addEventListener("mouseup", handleDragEnd, true);
384
+ document.addEventListener("touchend", handleDragEnd, true);
385
+ if (document.readyState === "loading") {
386
+ document.addEventListener("DOMContentLoaded", scanDocument, { once: true });
387
+ } else {
388
+ scanDocument();
389
+ }
390
+ }
391
+ function uninstallSplitGridResizeGuard() {
392
+ if (!installed) return;
393
+ installed = false;
394
+ resizeObserver?.disconnect();
395
+ mutationObserver?.disconnect();
396
+ resizeObserver = null;
397
+ mutationObserver = null;
398
+ observedPanes = /* @__PURE__ */ new WeakSet();
399
+ pendingPanes.clear();
400
+ for (const [element, names] of addedMarkers) {
401
+ for (const name of names) element.removeAttribute(name);
402
+ }
403
+ addedMarkers = /* @__PURE__ */ new Map();
404
+ if (frameId !== null) window.cancelAnimationFrame(frameId);
405
+ frameId = null;
406
+ document.removeEventListener("mouseup", handleDragEnd, true);
407
+ document.removeEventListener("touchend", handleDragEnd, true);
408
+ document.removeEventListener("DOMContentLoaded", scanDocument);
409
+ }
410
+
235
411
  // runtime/guards.ts
236
412
  function installUiRuntimeGuards() {
237
413
  installBrandThemeLock();
238
414
  installOverflowTooltipGuard();
415
+ installSplitGridResizeGuard();
239
416
  }
240
417
 
241
- export { installOverflowTooltipGuard, installUiRuntimeGuards, uninstallOverflowTooltipGuard };
418
+ export { SPLIT_GRID_RESIZE_EVENT, installOverflowTooltipGuard, installSplitGridResizeGuard, installUiRuntimeGuards, uninstallOverflowTooltipGuard, uninstallSplitGridResizeGuard };
@@ -1,4 +1,4 @@
1
- import { installUiRuntimeGuards } from './chunk-Z2OTWMW6.js';
1
+ import { installUiRuntimeGuards } from './chunk-QLPJLXY6.js';
2
2
  import { renderTagNode, renderClassifyTag, registerColumnAutoMaps, renderRatingLevel, renderBadge } from './chunk-4IOYRBW4.js';
3
3
  export { registerDictColorMap, registerDictColorMaps, renderDictClassifyTag, setDictResolver } from './chunk-4IOYRBW4.js';
4
4
  import './chunk-Q3N5QNRZ.js';
package/es/index.d.ts CHANGED
@@ -180,10 +180,20 @@ declare function installOverflowTooltipGuard(options?: OverflowTooltipGuardOptio
180
180
  /** 卸载兜底监听,主要供微前端销毁和回归测试使用。 */
181
181
  declare function uninstallOverflowTooltipGuard(): void;
182
182
 
183
+ declare const SPLIT_GRID_RESIZE_EVENT = "wl-ui:split-grid-resize";
184
+ interface SplitGridResizeDetail {
185
+ height: number;
186
+ pane: HTMLElement;
187
+ width: number;
188
+ }
189
+ declare function installSplitGridResizeGuard(): void;
190
+ declare function uninstallSplitGridResizeGuard(): void;
191
+
183
192
  /**
184
193
  * 安装不依赖业务页面写法的包级运行时保护。
185
- * 包含客户主题锁与普通表格长文本溢出提示兜底;重复调用安全。
194
+ * 包含客户主题锁、普通表格长文本溢出提示兜底与分屏 AG Grid 尺寸守护;
195
+ * 重复调用安全。
186
196
  */
187
197
  declare function installUiRuntimeGuards(): void;
188
198
 
189
- export { BRAND_THEME_TOKENS, ColumnLike, type OverflowTooltipGuardOptions, type PresetConfig, clearColumnAutoMap, createPreset, defineColumns, getColumnAutoMap, getInstalledPresets, installBrandThemeLock, installOverflowTooltipGuard, installPreset, installUiRuntimeGuards, registerColumnAutoMap, registerColumnAutoMaps, uninstallOverflowTooltipGuard };
199
+ export { BRAND_THEME_TOKENS, ColumnLike, type OverflowTooltipGuardOptions, type PresetConfig, SPLIT_GRID_RESIZE_EVENT, type SplitGridResizeDetail, clearColumnAutoMap, createPreset, defineColumns, getColumnAutoMap, getInstalledPresets, installBrandThemeLock, installOverflowTooltipGuard, installPreset, installSplitGridResizeGuard, installUiRuntimeGuards, registerColumnAutoMap, registerColumnAutoMaps, uninstallOverflowTooltipGuard, uninstallSplitGridResizeGuard };
package/es/index.js CHANGED
@@ -1,4 +1,4 @@
1
- export { installOverflowTooltipGuard, installUiRuntimeGuards, uninstallOverflowTooltipGuard } from './chunk-Z2OTWMW6.js';
1
+ export { SPLIT_GRID_RESIZE_EVENT, installOverflowTooltipGuard, installSplitGridResizeGuard, installUiRuntimeGuards, uninstallOverflowTooltipGuard, uninstallSplitGridResizeGuard } from './chunk-QLPJLXY6.js';
2
2
  import { registerColumnAutoMaps } from './chunk-4IOYRBW4.js';
3
3
  export { AUDIT_STATUS_MAP, ENABLE_STATUS_MAP, RATING_LEVEL_COLORS, VERIFY_STATUS_MAP, clearColumnAutoMap, defineColumns, getColumnAutoMap, registerColumnAutoMap, registerColumnAutoMaps, registerDictColorMap, registerDictColorMaps, renderAuditStatus, renderBadge, renderClassifyTag, renderCountBadge, renderDangerText, renderDictClassifyTag, renderEnableStatus, renderOps, renderRatingLevel, renderTagNode, renderTagSlot, renderVerifyStatus, setDictResolver } from './chunk-4IOYRBW4.js';
4
4
  export { BRAND_THEME_TOKENS, installBrandThemeLock } from './chunk-Q3N5QNRZ.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agile-team/wl-skills-ui",
3
- "version": "1.9.11",
3
+ "version": "1.9.13",
4
4
  "description": "企业级 UI 风格对齐框架 — Vue + Element Plus 项目通用化妆/原生双模式(tokens / element / vendors / layouts / runtime / scanner / fixer / skills)",
5
5
  "type": "module",
6
6
  "main": "./es/index.js",
@@ -48,6 +48,9 @@
48
48
  "prepare": "npm run build",
49
49
  "sync:tokens": "node -e \"import('fs').then(fs=>fs.copyFileSync('design/tokens/base.css','dist/tokens.css'))\"",
50
50
  "test": "node --test scanner/__tests__/*.test.mjs",
51
+ "test:visual": "playwright test --config tests/visual/playwright.config.ts",
52
+ "test:visual:ui": "playwright test --config tests/visual/playwright.config.ts --ui",
53
+ "test:visual:update": "playwright test --config tests/visual/playwright.config.ts --update-snapshots",
51
54
  "check:scss": "node scripts/check-scss.mjs",
52
55
  "docs:check": "node scripts/check-docs.mjs",
53
56
  "scan": "node scanner/index.mjs scan",
@@ -60,8 +63,8 @@
60
63
  "verify:package": "node scripts/verify-package.mjs",
61
64
  "verify": "npm run lint && npm test && npm run check:scss && npm run docs:check && npm run build && npm run verify:package",
62
65
  "pack:dry": "npm pack --dry-run --ignore-scripts",
63
- "release:check": "npm run verify && npm pack --dry-run --ignore-scripts",
64
- "prepublishOnly": "npm run verify"
66
+ "release:check": "npm run verify && npm run test:visual && npm pack --dry-run --ignore-scripts",
67
+ "prepublishOnly": "npm run release:check"
65
68
  },
66
69
  "files": [
67
70
  "dist",
@@ -92,8 +95,10 @@
92
95
  "@commitlint/cli": "^20.5.3",
93
96
  "@commitlint/config-conventional": "^20.5.3",
94
97
  "@element-plus/icons-vue": "^2.3.0",
98
+ "@playwright/test": "^1.62.1",
95
99
  "@typescript-eslint/eslint-plugin": "^8.59.2",
96
100
  "@typescript-eslint/parser": "^8.59.2",
101
+ "@vitejs/plugin-vue": "^6.0.8",
97
102
  "@vue/eslint-config-typescript": "^14.7.0",
98
103
  "commitizen": "^4.3.1",
99
104
  "cz-customizable": "^7.5.4",
@@ -106,6 +111,7 @@
106
111
  "sass": "1.77.8",
107
112
  "tsup": "^8.3.0",
108
113
  "typescript": "^5.4.0",
114
+ "vite": "^8.2.0",
109
115
  "vue": "^3.4.0"
110
116
  },
111
117
  "peerDependencies": {