@jzg223/design-runtime 0.0.1 → 0.1.0-rc.2

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.
Files changed (54) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +52 -0
  3. package/dist/board/api.js +57 -0
  4. package/dist/board/board-chart.vue.js +7 -0
  5. package/dist/board/board-chart2.vue.js +93 -0
  6. package/dist/board/context.js +15 -0
  7. package/dist/board/datav-map.js +66 -0
  8. package/dist/board/design-board.vue.js +172 -0
  9. package/dist/board/design-board2.vue.js +331 -0
  10. package/dist/board/echarts.js +40 -0
  11. package/dist/board/part-node.vue.js +63 -0
  12. package/dist/board/part-node2.vue.js +125 -0
  13. package/dist/board/part-renderer.vue.js +561 -0
  14. package/dist/board/part-renderer2.vue.js +884 -0
  15. package/dist/board/theme-host.js +129 -0
  16. package/dist/board-engine/asset.js +32 -0
  17. package/dist/board-engine/chart-option.js +377 -0
  18. package/dist/board-engine/data-hub.js +67 -0
  19. package/dist/board-engine/datav-meta.js +51 -0
  20. package/dist/board-engine/i18n.js +130 -0
  21. package/dist/board-engine/param-catalog.js +137 -0
  22. package/dist/board-engine/params.js +61 -0
  23. package/dist/board-engine/render-model.js +65 -0
  24. package/dist/board-engine/schema.js +856 -0
  25. package/dist/board-engine/theme-manifest.js +19 -0
  26. package/dist/board-engine/version.js +4 -0
  27. package/dist/host.js +6 -1
  28. package/dist/index.js +172 -24
  29. package/dist/style.css +1067 -0
  30. package/dist/types/board/api.d.ts +45 -0
  31. package/dist/types/board/board-chart.vue.d.ts +15 -0
  32. package/dist/types/board/context.d.ts +29 -0
  33. package/dist/types/board/datav-map.d.ts +24 -0
  34. package/dist/types/board/design-board.vue.d.ts +1409 -0
  35. package/dist/types/board/echarts.d.ts +25 -0
  36. package/dist/types/board/part-node.vue.d.ts +1237 -0
  37. package/dist/types/board/part-renderer.vue.d.ts +1205 -0
  38. package/dist/types/board/theme-host.d.ts +41 -0
  39. package/dist/types/board-engine/asset.d.ts +37 -0
  40. package/dist/types/board-engine/chart-option.d.ts +737 -0
  41. package/dist/types/board-engine/data-hub.d.ts +48 -0
  42. package/dist/types/board-engine/datav-meta.d.ts +37 -0
  43. package/dist/types/board-engine/i18n.d.ts +43 -0
  44. package/dist/types/board-engine/index.d.ts +25 -0
  45. package/dist/types/board-engine/param-catalog.d.ts +82 -0
  46. package/dist/types/board-engine/params.d.ts +37 -0
  47. package/dist/types/board-engine/render-model.d.ts +87 -0
  48. package/dist/types/board-engine/schema.d.ts +513 -0
  49. package/dist/types/board-engine/theme-manifest.d.ts +23 -0
  50. package/dist/types/board-engine/types.d.ts +147 -0
  51. package/dist/types/board-engine/version.d.ts +10 -0
  52. package/dist/types/host.d.ts +21 -0
  53. package/dist/types/index.d.ts +21 -2
  54. package/package.json +21 -4
@@ -0,0 +1,513 @@
1
+ /**
2
+ * 看板配置的「schema 层」:**配置 → 部件 / 布局 / 样式值** 的全部纯计算。
3
+ *
4
+ * 来源:`design-ui/src/views/board/engine/schema.js`(1328 行)**逐字搬运**,
5
+ * 改动只有两类:文件头本注释 + 参数上的 `: any` 标注(纯打字,无行为改动)。
6
+ * 设计端与运行端因此共用同一份"配置语义"—— 这是 D2「引擎单源」的核心,
7
+ * 任何时候都**不要**在运行端再抄一份。
8
+ *
9
+ * ⚠ 类型状态(**技术债,已登记 TODO P6-48**)
10
+ * 原文件是 1300 行无类型 JS,80 个导出里大量 `(part, page)` 这种宽松参数。
11
+ * 本轮的取舍:**行为先对齐**,参数一律标 `any`(能过 strict),不手抄、不改逻辑。
12
+ * 类型的真正出口是 `render-model.ts`(新写、强类型),运行端只依赖它;
13
+ * 本文件的行为由 `scripts/test-board-engine.mjs` 的 fixture 锁住。
14
+ * 收紧顺序:先给 `parseConfig` / `partStyle` / `gridPartStyle` / `partVisible` 补真实类型,
15
+ * 其余按被调用频率逐步来,不要一次重写。
16
+ *
17
+ * ⚠ 改这个文件的规矩
18
+ * 1. 只做**定点补丁**(edit 工具 + 唯一锚点),不要整段重写;
19
+ * 2. 任何行为改动都要同步 design-ui 那份(`@board/engine/schema.js`)—— 两边不一致就是漂移;
20
+ * 3. 不许 import 任何东西(本层必须零依赖:这条现在靠"没有 import"保证)。
21
+ */
22
+ /**
23
+ * 看板配置契约(前后端共用的一份 JSON 结构)
24
+ *
25
+ * {
26
+ * version: 1,
27
+ * page: { width, height, scaleMode, minScale, fontAdaptive, minFont,
28
+ * theme, chartTheme, bgColor, bgImage, opacity,
29
+ * grid:{show,size,snap}, guides, scroll, watermark,
30
+ * layoutMode:'free'|'grid', gridCols, gridRowHeight, gridGap, gridAutoHeight },
31
+ * parts: [ {
32
+ * id, type, name, x, y, w, h, z, hidden, locked,
33
+ * // 栅格定位(顶层栅格与容器内子栅格共用这三个字段,参照系不同)
34
+ * col, colSpan, rowSpan,
35
+ * // 容器嵌套:空串 = 顶层;tabIndex 只对 tab容器的子级有意义
36
+ * parentId, tabIndex,
37
+ * style: { bg, opacity, border:{width,style,color,radius}, shadow:{enable,color,blur,spread,x,y}, rotate },
38
+ * text: { fontFamily, fontSize, bold, italic, color, align },
39
+ * data: { setId, dims:[], measures:[{field,agg,alias}], filters:[], params:{}, sort:{field,dir}, limit },
40
+ * view: { ... 按 type 不同 ... }
41
+ * } ]
42
+ * }
43
+ *
44
+ * 【为什么是"扁平数组 + parentId"而不是嵌套 children】
45
+ * - 老配置没有 parentId ⇒ 全部当顶层,行为与从前完全一致(向后兼容,迁移不怕)。
46
+ * - find/删除/复制/撤销快照/模板导入导出/多语写回 这些现有逻辑全都在扁平数组上跑,一行都不用改。
47
+ * - 跨容器搬运 = 改一个字段;嵌套 children 则要在两棵子树上 splice,容易错。
48
+ * - DOM 顺序仍与数组顺序一一对应(见下),设计器拖拽的下标映射不受影响。
49
+ *
50
+ * 【子级怎么定位】容器内部**永远**是一套 24 列子栅格(与整页 layoutMode 无关):
51
+ * 列宽跟着容器宽度走,所以容器一变宽子级就重排;行高/间距/列数沿用 page 上的那三个数
52
+ * (不再给容器单独加一套数,避免"两处数字对不上")。
53
+ * 顶层在 free 模式下用 x/y/w/h,进容器后改用 col/colSpan/rowSpan,进出容器时自动换算。
54
+ *
55
+ * 【DOM 顺序 = 数组顺序】这条不变式必须守住:一个容器在数组里紧跟它的子级(深度优先),
56
+ * 渲染出来的 .part 节点 document order 就与数组一致,拖拽/命中的下标映射才成立。
57
+ */
58
+ export declare const PART_TYPES: {
59
+ type: string;
60
+ label: string;
61
+ icon: string;
62
+ group: string;
63
+ w: number;
64
+ h: number;
65
+ }[];
66
+ export declare const CHART_TYPES: {
67
+ value: string;
68
+ label: string;
69
+ }[];
70
+ export declare const DEFAULT_THEME = "tech-blue";
71
+ /**
72
+ * 能装子部件的容器类型。
73
+ * 三个都是"一个盒子",共用同一套子栅格渲染;区别只在盒子的外观与分页行为。
74
+ */
75
+ export declare const CONTAINER_TYPES: string[];
76
+ export declare const isContainer: (type: any) => boolean;
77
+ /**
78
+ * 部件的显示条件(**看板参数驱动**)—— 导航切页的核心机制。
79
+ *
80
+ * view.visibleWhen = { param: 'nav', in: ['overview'] }
81
+ * view.visibleWhen = { param: 'nav', notIn: ['overview'] }
82
+ *
83
+ * 语义:
84
+ * - 没写 visibleWhen(或没写 param)⇒ 永远显示
85
+ * - in :参数值命中其一才显示(不填 = 不限制)
86
+ * - notIn :参数值命中其一就隐藏
87
+ * - 参数未设/为空串 ⇒ 按 '' 比较;所以 `in: ['overview']` 的部件在"什么都没选"时不显示,
88
+ * 想让它在默认态也显示,就把 '' 或默认值写进 in。
89
+ *
90
+ * ⚠ 隐藏的部件**不创建组件** ⇒ 天然不会去取数(隐藏页不会白发请求)。
91
+ * ⚠ 设计器里不过滤(否则第 2 页的部件根本选不中):PartNode 用 design 开关区分。
92
+ */
93
+ export declare function partVisible(part: any, params: any): boolean;
94
+ /**
95
+ * 部件类型徽标文案(**只在设计器画布上用**,运行时/预览页不显示)。
96
+ * 图表额外带上图型,因为"图表"和"环形图"在画布上看起来完全是两回事:
97
+ * chart·donut / chart·hbar / kpi / table / text / border / container …
98
+ * 用类型标识符而不是中文名,是为了让设计器与「部件设置」里的类型下拉、
99
+ * 以及配置 JSON 里的 `type` 字段能一一对上。
100
+ */
101
+ export declare const partTypeBadge: (part: any) => string;
102
+ /** 聚合的中文名(多度量时用来给数值列起个不歧义的表头) */
103
+ export declare const AGG_LABELS: Record<string, string>;
104
+ /**
105
+ * 度量列/系列该显示什么名字。
106
+ * - 度量自己起了别名(alias ≠ field)→ 用别名
107
+ * - 否则用"字段别名(聚合)",例如「任务名称(计数)」
108
+ * 为什么不用裸的字段别名:明细表里"任务名称"既可能是维度列又可能是 count 出来的数值列,
109
+ * 两个同名列完全分不清(多度量上线后这个问题会立刻暴露)。
110
+ */
111
+ export declare function measureLabel(measure: any, fieldAlias: any): any;
112
+ /**
113
+ * 舞台底色(画布背后那一层)。
114
+ * ⚠️ 设计器与运行时**必须用同一个值**:早先只有运行时写死 `#0d131c`、设计器用 `page.bgColor`(大屏常常是空的),
115
+ * 结果设计器里深色看板"发白"——部件的底色都是半透明的,叠在浅色页面底上全部变浅、白字几乎看不见(实测踩到)。
116
+ */
117
+ export declare const STAGE_BG = "#070c18";
118
+ /**
119
+ * 部件缺省观感的**归属划分**(2026-09 与用户确认的规范):
120
+ *
121
+ * - **布局部件 = 容器**(自由容器 / tab容器 / 静态边框)拥有"面板":底色渐变 + 圆角 + 投影
122
+ * (再加上容器自己的装饰语言:四角切角 / 上沿高光 / 内侧微光)。
123
+ * - **内容部件**(指标卡 / 图表 / 明细表 / 交叉表 / 进度 / 筛选)**不再自带底色与投影**,
124
+ * 只画自己的数据(坐标轴、扇形、表格……)⇒ 一个容器里放几块内容都只是在同一块面板上分区。
125
+ *
126
+ * 为什么反过来(之前的做法是内容部件自带卡片底):容器包住内容部件时,
127
+ * 面板底缩在内框里、容器只剩一圈空框 ⇒ 看着像"空框里浮着一块牌子",很怪异(用户截图指出)。
128
+ * 反过来之后:面板 = 容器的框,内容贴在面板上,这才是大屏里最常见的"模块"结构。
129
+ *
130
+ * 老配置里显式写了自己的 style 的,一切以配置为准(deepMerge 时配置值优先)。
131
+ */
132
+ export declare const CARD_TYPES: string[];
133
+ /** 内容部件缺省:透明(底色/投影都由容器负责)、**直角**(2026-09 用户要求:整套改成硬边/切角语言,先去掉圆角) */
134
+ export declare const CARD_STYLE: {
135
+ bg: string;
136
+ border: {
137
+ width: number;
138
+ style: string;
139
+ color: string;
140
+ radius: number;
141
+ };
142
+ shadow: {
143
+ enable: boolean;
144
+ color: string;
145
+ blur: number;
146
+ spread: number;
147
+ x: number;
148
+ y: number;
149
+ };
150
+ };
151
+ /**
152
+ * 布局部件(容器)缺省:**不画底色、不画投影**(2026-09 用户:"这种风格应该在主题里定义好,
153
+ * 不用每个部件再单独设置")⇒ 面板底与投影由**主题 CSS** 给(`--brd-panel-bg` / `--brd-panel-shadow`,
154
+ * 见 public/theme/*.css 与 PartRenderer 的 .part-body.is-panel 规则);
155
+ * 某个容器想跟主题不一样时,再在部件设置里显式配底色/投影(内联样式优先)。
156
+ * 圆角与描边同样默认 0/无,装饰由 CONTAINER_DECOR 那套负责。
157
+ */
158
+ export declare const PANEL_STYLE: {
159
+ bg: string;
160
+ border: {
161
+ width: number;
162
+ style: string;
163
+ color: string;
164
+ radius: number;
165
+ };
166
+ shadow: {
167
+ enable: boolean;
168
+ color: string;
169
+ blur: number;
170
+ spread: number;
171
+ x: number;
172
+ y: number;
173
+ };
174
+ };
175
+ /**
176
+ * 容器类部件的**装饰语言**(默认值 + 读取函数)。
177
+ *
178
+ * 规范(2026-09 与用户确认):
179
+ * - "四角切角 / 上沿高光 / 内侧微光"是**框的语言** ⇒ 只给容器(自由容器 / tab容器 / 静态边框),
180
+ * 而且做成部件级可配 —— 同一个看板里"这块要框起来强调、那块只是分区"是版式的决定。
181
+ * - 数据类部件(图表 / 交叉表 / 明细表 / 指标卡 / 进度 / 筛选)**不给这些配置、也不画这些装饰**:
182
+ * 它们本身就是"内容",外面已经有卡片底 + 描边 + 圆角 + 投影,再加框就是两层框;
183
+ * 而且一屏 10 个部件 = 40 个角标,装饰一泛滥就变成贴纸,反而抢数据的注意力。
184
+ * - 老配置没有 view.decor 字段 ⇒ 按默认值渲染(不用改配置)。
185
+ */
186
+ export declare const CONTAINER_DECOR: {
187
+ corner: boolean;
188
+ glass: boolean;
189
+ };
190
+ /**
191
+ * 容器的**边框样式预设**(借 DataV 那套"大屏边框"的视觉语言,但零依赖、跟主题变色、跟尺寸自适应)。
192
+ *
193
+ * - `corner`(默认):四角 L 形括号 —— 最轻,适合"框住一组内容"
194
+ * - `cut` :八角切角描边(四条边 + 四个 45° 斜切角)—— 最像 DataV 的 border-box,
195
+ * 科技感强、又不会把内容压住(描边压在容器内沿 6px 处)
196
+ * - `glow` :整圈细描边 + 沿上边缓慢流动的光带(CSS 动画,无 JS)
197
+ * - `none` :不画框
198
+ * 老配置没有 view.borderPreset 字段 ⇒ 用 decor.corner 的开关(true→corner / false→none),行为不变。
199
+ */
200
+ export declare const BORDER_PRESETS: {
201
+ value: string;
202
+ label: string;
203
+ }[];
204
+ /** 读取容器的边框样式(兼容老的 decor.corner 开关) */
205
+ export declare function borderPresetOf(part: any): any;
206
+ /** 读取某容器的装饰开关(缺字段用默认;数据类部件调用它也会得到默认值,但渲染时不使用) */
207
+ export declare function decorOf(part: any): {
208
+ corner: boolean;
209
+ glass: boolean;
210
+ };
211
+ /**
212
+ * 主题色系:图表调色板 + 画布底色 + 观感令牌(卡片/提示框/发光/网格底纹)。
213
+ *
214
+ * 改版口径(2026-09):从"平铺的深蓝玻璃卡片"改成**大屏科技感**——
215
+ * 底色不再是纯色而是"径向光晕 + 细网格"的舞台;卡片上沿有高光细线、四角有切角装饰、
216
+ * 描边带发光;指标数字有辉光;图表线/柱/扇形都带同色辉光。
217
+ * 所有令牌都**按主题给**,浅色主题(极光白)把发光换成轻投影,免得白底上一片糊。
218
+ */
219
+ export declare const THEMES: Record<string, any>;
220
+ /** 登记某个主题从 CSS 读到的令牌(null/undefined 的字段不覆盖内置值;只认内置主题里有的键) */
221
+ export declare function setThemeTokens(code: any, tokens: any): void;
222
+ /** 只给测试/调试用:清空覆盖 */
223
+ export declare function resetThemeOverrides(): void;
224
+ export declare function themeOf(code: any): any;
225
+ /** 新建看板的默认整页配置 */
226
+ export declare function defaultConfig(width?: number, height?: number): {
227
+ version: number;
228
+ page: {
229
+ width: number;
230
+ height: number;
231
+ layoutMode: string;
232
+ mobileMode: string;
233
+ gridCols: number;
234
+ gridRowHeight: number;
235
+ gridGap: number;
236
+ gridAutoHeight: boolean;
237
+ scaleMode: string;
238
+ minScale: number;
239
+ fontAdaptive: boolean;
240
+ minFont: number;
241
+ theme: string;
242
+ chartTheme: string;
243
+ bgColor: any;
244
+ bgImage: string;
245
+ opacity: number;
246
+ grid: {
247
+ show: boolean;
248
+ size: number;
249
+ snap: boolean;
250
+ };
251
+ guides: boolean;
252
+ scroll: boolean;
253
+ watermark: string;
254
+ };
255
+ parts: never[];
256
+ };
257
+ export declare function newPartId(): string;
258
+ /** 部件的公共缺省形状(不含 id / name,供"补齐缺省字段"复用) */
259
+ export declare function basePartShape(type: any): any;
260
+ /**
261
+ * 补齐部件的缺省字段(就地返回合并后的新对象,由调用方决定怎么用)。
262
+ *
263
+ * 为什么需要:配置可能来自**模板导入 / 手写 JSON / 旧版本产物**,里面的部件往往只有
264
+ * 业务字段,没有 `style.border`、`style.shadow` 这些"从来没人改过"的嵌套对象。
265
+ * 而设置面板直接读 `part.style.border.width`、渲染器读 `part.view.xxx`,
266
+ * 少一层就抛 TypeError —— 更糟的是**生产构建下这个渲染异常是静默的**
267
+ * (部件设置面板整块空白、控制台干净),排查成本极高(实测踩到)。
268
+ * 所以统一在 parseConfig 的清洗阶段按类型补齐,缺什么补什么,已有值一律保留。
269
+ */
270
+ export declare function fillPartDefaults(part: any): any;
271
+ /**
272
+ * 去掉与"类型缺省值"完全相同的字段(保存前调用)。
273
+ *
274
+ * 【为什么必须做这一步】`fillPartDefaults()` 会在**读取配置时**把缺省值写进内存里的部件对象;
275
+ * 而设计器的保存是 `JSON.stringify(this.config)` —— 于是"当时的缺省值"就被固化进 config_json。
276
+ * 之后再去调缺省值(比如把内容性部件的卡片描边从 1px 改成 0),对那些配置**一律不生效**
277
+ * (显式值优先),表现为"代码改了、页面上没变",非常费解(这次就踩了:用户在设计器里保存过一次)。
278
+ *
279
+ * 口径:只处理 `style / text / view / data` 四块里的字段,递归比较;与缺省值相等的键直接删掉。
280
+ * 用户改过的值一定与缺省值不同 ⇒ 不会被删。几何字段(x/y/w/h/col…)与 id/name 不处理。
281
+ */
282
+ export declare function stripPartDefaults(part: any): any;
283
+ /** 整份配置:保存前把每个部件里"等于缺省值"的字段剥掉(见 stripPartDefaults 的说明) */
284
+ export declare function stripConfigDefaults(cfg: any): any;
285
+ /** 按类型建一个部件(默认配置) */
286
+ export declare function createPart(type: any, overrides?: {}): any;
287
+ export declare function deepMerge(target: any, source: any): any;
288
+ /** 解析后端返回的 config_json(字符串或对象都能吃) */
289
+ export declare function parseConfig(raw: any, fallbackSize: any): any;
290
+ /** 按 z 排序后的部件(渲染顺序) */
291
+ export declare function sortedParts(parts: any): any[];
292
+ /** 取某个容器的子级(可再按页签过滤) */
293
+ export declare function childParts(parts: any, parentId: any, tabIndex: any): any;
294
+ /** 某部件的全部后代 id(含自己),用于级联删除与防环 */
295
+ export declare function descendantIds(parts: any, id: any): any[];
296
+ /** 这个部件能否作为容器接收子级(任意层级的容器都可以) */
297
+ export declare function canHostChildren(part: any): boolean;
298
+ /**
299
+ * 容器内子级的定位方式。
300
+ * - 'grid'(默认,兼容老配置):容器内一套 24 列子栅格,子级跟着容器宽度重排
301
+ * - 'free':子级用 x/y 相对容器左上角绝对定位(自由摆放,不随容器宽度重排)
302
+ */
303
+ export declare function childLayoutOf(container: any): any;
304
+ /**
305
+ * 某个部件当前的"定位权威":决定 x/y/w/h 与 col/colSpan/rowSpan 哪个说了算。
306
+ * 顶层看页面 layoutMode,子级看父容器的 childLayout。
307
+ */
308
+ export declare function layoutAuthority(part: any, parentPart: any, page: any): any;
309
+ /** 容器内部子栅格的几何:列数/行高/间距沿用 page,宽度取容器内框宽度 */
310
+ export declare function containerGrid(page: any, innerWidth: any): {
311
+ gridCols: any;
312
+ gridRowHeight: any;
313
+ gridGap: any;
314
+ width: any;
315
+ };
316
+ /** 容器内边距默认值(**参与几何换算**,不只是外观:见 paddingOf 注释) */
317
+ export declare const CONTAINER_PAD = 10;
318
+ /** 内边距允许范围(设置面板里也用它做 min/max) */
319
+ export declare const CONTAINER_PAD_MIN = 0;
320
+ export declare const CONTAINER_PAD_MAX = 40;
321
+ /**
322
+ * 容器的内边距(部件级可配,默认 {@link CONTAINER_PAD})。
323
+ *
324
+ * ⚠️ 它**不只是样式**:容器内框宽度 = 盒子宽 - 边框×2 - padding×2,
325
+ * 子栅格的列宽、以及"拖进容器时按落点算起始列"都依赖它。
326
+ * 所以这个值必须由这里统一提供,别在渲染/设计器里再写死 10。
327
+ */
328
+ export declare function paddingOf(part: any): number;
329
+ /** 容器标题的样式:chip=胶囊骑在框线上(默认,老看板观感不变)/ bar=框内标题条 */
330
+ export declare function titleStyleOf(part: any): "bar" | "chip";
331
+ /**
332
+ * 容器内子栅格的行高方式:
333
+ * 'fixed'(默认,老行为)= minmax(行高, auto),跨 N 行就是 N×行高+(N-1)×间距 —— 与整页同一套;
334
+ * 'fill' = 行高按容器内区**均分**(minmax(0, 1fr)),子级跨满时正好填满内框。
335
+ *
336
+ * 为什么需要 'fill':容器内区 = 容器高 - 2×内边距。用固定行高时**内边距会把子级挤出去**:
337
+ * 子级跨 R 行的高度是 R×行高+(R-1)×间距,比"内区"高出正好 2×内边距 ⇒ 容器内部出现滚动条、
338
+ * 子级底部被裁。而"1:1 把某个部件包进容器"这种场景,恰恰希望内外大小严丝合缝 —— 那就把内边距
339
+ * 当"留白"用、行高改成撑满(本模式);子级会小一圈,但完整可见。
340
+ */
341
+ export declare function childRowsOf(part: any): "fixed" | "fill";
342
+ /** 标题条占用的高度(padding 之外额外让出的空间);chip 骑在框线上时不占内容高度 */
343
+ export declare const TITLE_BAR_HEIGHT = 34;
344
+ /** 胶囊标题的近似高度(用于"标题挪进面板内"时给内容让位) */
345
+ export declare const TITLE_CHIP_HEIGHT = 20;
346
+ /** 标题上移默认值(px):0 = 与面板上沿齐平(标题在面板内,让开左上角的切角标记) */
347
+ export declare const TITLE_OFFSET_DEFAULT = 0;
348
+ export declare const TITLE_OFFSET_MIN = -20;
349
+ export declare const TITLE_OFFSET_MAX = 40;
350
+ /** 标题左缩默认值(px):相对"容器内边距"再多缩进一点(0 = 与内容左边缘完全对齐) */
351
+ export declare const TITLE_INSET_DEFAULT = 8;
352
+ export declare const TITLE_INSET_MIN = -20;
353
+ export declare const TITLE_INSET_MAX = 120;
354
+ /**
355
+ * 标题的横向位置:相对容器内边距的**额外缩进**(px)。
356
+ * 0 = 与内容左边缘对齐;默认 8 = 比内容再往右一点(贴着切角标记的外侧,看着更"入框")。
357
+ */
358
+ export declare function titleInsetOf(part: any): number;
359
+ /**
360
+ * 标题相对**面板上沿**的偏移(px):正值往上(骑在框线上),0 = 与上沿齐平,负值往下(进入面板内)。
361
+ * 默认 0:胶囊标题贴着面板上沿放在框内 —— 骑在框线上(11)会正好压住左上角的四角切角装饰。
362
+ */
363
+ export declare function titleOffsetOf(part: any): number;
364
+ /**
365
+ * 标题要占用的"面板内高度"(内容区顶部要额外让出的空间)。
366
+ * - bar 样式:整条标题栏,恒定 34px;
367
+ * - chip 样式:标题底边 = 20 - 偏移,内容从 padding 开始 ⇒ 让位 = max(0, 20 - 偏移 - padding)。
368
+ * 默认(偏移 0、padding 10)时让 10px,标题正好在内容上方不打架;
369
+ * 偏移 ≥ padding 差(老看板的 11)时让位为 0 ⇒ **老看板布局一字不变**。
370
+ */
371
+ export declare function titleReserveOf(part: any): number;
372
+ /** 兼容旧调用(bar 样式之外返回 0 的老语义已被 titleReserveOf 取代) */
373
+ export declare function titleBarHeightOf(part: any): 0 | 34;
374
+ /** 容器内框宽度 = 盒子宽 - 边框 - 内边距(与 PartRenderer 里 .part-children 的 padding 一致) */
375
+ export declare function innerWidthFromBox(boxWidth: any, part: any): number;
376
+ /**
377
+ * 容器内框宽度(按 part.w 估算)。
378
+ * ⚠️ 只对"自由布局里的容器"准确:栅格模式(或本身是别的容器的子级)时,
379
+ * 容器的实际宽度由栅格决定,part.w 是陈旧值(实测 300 vs 真实 775,算出的列宽差 240 倍)。
380
+ * 那种场景必须用 DOM 实测宽度调 innerWidthFromBox(),别用这个函数。
381
+ */
382
+ export declare function containerInnerWidth(part: any): number;
383
+ /**
384
+ * 把部件挂到某个容器下 / 提到顶层,并做防环把关。
385
+ * 多层嵌套与"容器放进容器"都是允许的(只有成环不行)。
386
+ * 返回 { ok, reason };不改数组顺序(顺序由调用方按深度优先维护)。
387
+ */
388
+ export declare function canReparent(parts: any, id: any, parentId: any): {
389
+ ok: boolean;
390
+ reason: string;
391
+ parent?: undefined;
392
+ } | {
393
+ ok: boolean;
394
+ reason?: undefined;
395
+ parent?: undefined;
396
+ } | {
397
+ ok: boolean;
398
+ parent: any;
399
+ reason?: undefined;
400
+ };
401
+ /**
402
+ * 把 parts 重排成"深度优先"顺序:容器后面紧跟它的子级。
403
+ * 老配置(全顶层)跑完顺序不变。
404
+ */
405
+ export declare function reorderDepthFirst(parts: any): any[];
406
+ /**
407
+ * 清洗配置:修好悬空 parentId、非法容器类型、多层嵌套(都提升为顶层),再按深度优先重排。
408
+ * 原则是"修"而不是"报错" —— 只有整份配置解析不了才该报错。
409
+ * 返回 { parts, fixed: [...] },fixed 里是改过什么,便于日志/提示。
410
+ */
411
+ export declare function normalizeParts(parts: any): {
412
+ parts: any[];
413
+ fixed: any[];
414
+ };
415
+ export declare const GRID_DEFAULTS: {
416
+ cols: number;
417
+ rowHeight: number;
418
+ gap: number;
419
+ };
420
+ /** 自由布局 → 栅格:按 x/w 反推列起与跨列,按 h 反推跨行 */
421
+ export declare function freeToGrid(part: any, page: any): {
422
+ col: number;
423
+ colSpan: number;
424
+ rowSpan: number;
425
+ };
426
+ /**
427
+ * 栅格 → 自由:**整页**批量换算(切回 free 时用这个,别逐件按 z/下标铺 y)。
428
+ *
429
+ * 为什么不能逐件算 y 然后按数组下标堆叠:栅格里同一"行带"的部件跨行数往往远大于
430
+ * 行距(一行 4 个 KPI 各跨 2 行、旁边图表跨 5 行),按下标 × 行距 铺开必然互相压住
431
+ * —— 实测切回自由布局后出现 5 处重叠。
432
+ *
433
+ * 正确做法是复现栅格的"行带"(band):按流式顺序扫描,列号回绕就另起一带,
434
+ * 带高取该带内最大跨行,带内部件顶对齐、各自保留真实宽高。既保住 2D 排布又不重叠。
435
+ */
436
+ export declare function gridToFreeParts(parts: any, page: any): any;
437
+ /** 栅格模式的行高取值:固定行高,或随内容自动增长(设计器与运行时共用,保证一致) */
438
+ export declare function gridAutoRows(page: any): string;
439
+ /** 容器内框的列宽(注意容器是 n-1 个 gap,与整页画布的 gap*(n+1) 不同) */
440
+ export declare function containerColWidth(page: any, innerW: any): number;
441
+ /**
442
+ * 容器子级:子栅格 → 绝对定位(按行带铺开,不重叠)。
443
+ * @param pad 容器自身的 padding(默认 CONTAINER_PAD;自由子级的坐标原点含这段内边距,见文件头说明)
444
+ */
445
+ export declare function containerGridToFree(children: any, page: any, innerW: any, pad: any): any;
446
+ /** 容器子级:绝对定位 → 子栅格(按 x/y 反推列与跨列跨行) */
447
+ export declare function containerFreeToGrid(children: any, page: any, innerW: any, pad: any): any;
448
+ /**
449
+ * 一个像素盒 → 容器子栅格的 col/colSpan/rowSpan。
450
+ * 与 freeToGrid 的区别:容器是 n-1 个列间 gap、且内框已扣掉 padding,
451
+ * 所以列宽公式是 (innerW - gap*(n-1))/n(freeToGrid 用的是整页画布的 gap*(n+1))。
452
+ */
453
+ export declare function freeToContainerGrid(box: any, page: any, innerW: any, pad: any): {
454
+ col: number;
455
+ colSpan: number;
456
+ rowSpan: number;
457
+ };
458
+ /**
459
+ * 把某一层(同一个 parentId)的部件按"先上后下、再先左后右"重排。
460
+ * 栅格的行由数组顺序决定,所以从自由定位切到子栅格前必须做这一步。
461
+ * 直接改扁平数组里这些兄弟占用的槽位,各自的子树跟着走;调用方随后跑 reorderDepthFirst。
462
+ */
463
+ export declare function sortSiblingsByVisual(parts: any, parentId: any): any;
464
+ /** 栅格的"跨列/跨行"换算成页面像素宽高(把部件从容器里拖到自由布局顶层时用) */
465
+ export declare function gridSpanToSize(part: any, page: any): {
466
+ w: number;
467
+ h: number;
468
+ };
469
+ /** 栅格模式下部件的 CSS(grid-column / grid-row + 样式) */
470
+ export declare function gridPartStyle(part: any, page: any): any;
471
+ /** 部件样式的内联 style(设计器与运行时共用,保证"所见即所得") */
472
+ /**
473
+ * 部件的**定位盒**样式(写到 `.part` 这个外层盒子上的内联 style)。
474
+ *
475
+ * ⚠️ 这里**只放几何/层级/透明度/旋转**,不放任何"外观"(底色/描边/圆角/投影)。
476
+ * 规范(2026-09 用户):"part view-part 的背景色…我看是写在 style 里的" ⇒
477
+ * 外层 `.part`(运行时的 `.part.view-part`)的内联 style 里**不应该出现 background**;
478
+ * 面板外观由内层的**面板层**(`.part-body`,见 {@link partVisualStyle})负责。
479
+ * 好处:外层只当"框",想换面板画法(内缩、加内衬、贴图、发光层)都不用动定位逻辑。
480
+ */
481
+ export declare function partStyle(part: any): any;
482
+ /**
483
+ * 部件的**面板外观**样式(底色/圆角/描边/投影),写到内层面板 `.part-body` 上。
484
+ *
485
+ * 【为什么单独一个函数】① 外层 `.part` 只负责"框在哪、多大";② 底色归属有规范:
486
+ * **只有布局部件(容器)才画底色**,内容部件一律透明 —— 所以非容器**忽略** style.bg
487
+ * (老看板配置里固化的那层卡片渐变不会再画出来;保存时也会被 stripPartDefaults 洗掉)。
488
+ */
489
+ export declare function partVisualStyle(part: any): any;
490
+ /** 部件文本样式 */
491
+ export declare function textStyle(part: any, theme: any): {
492
+ fontFamily: any;
493
+ fontSize: string;
494
+ fontWeight: string;
495
+ fontStyle: string;
496
+ color: any;
497
+ textAlign: any;
498
+ };
499
+ /** 窄屏判定阈值(与 CSS 媒体查询里那档保持一致) */
500
+ export declare const MOBILE_MAX_WIDTH = 768;
501
+ /** 页面配置里的移动端模式(缺省 auto,老配置不加字段也能受益) */
502
+ export declare function mobileModeOf(page: any): any;
503
+ /**
504
+ * 当前是否"堆叠单列"。
505
+ * @param {object} page 页面配置
506
+ * @param {number} viewportWidth 视口宽度(px)
507
+ */
508
+ export declare function isMobileStack(page: any, viewportWidth: any): boolean;
509
+ /** 窄屏堆叠时该类型的最小高度(大屏上的矮部件在手机上会挤成一团) */
510
+ export declare function stackMinHeight(type: any): number;
511
+ export declare function stackPartStyle(part: any, childCount: any): any;
512
+ /** 按视觉顺序(先上后下、再先左后右)排一份副本;堆叠显示时用,不改动配置里的数组顺序 */
513
+ export declare function visualOrder(list: any): any[];
@@ -0,0 +1,23 @@
1
+ /**
2
+ * 主题清单的**纯解析**(`{themeBaseUrl}/index.json`)。
3
+ *
4
+ * 注意分工:
5
+ * - 本文件只做「清单 → 结构化」的纯函数(可单测、可在 node 里跑);
6
+ * - **读 CSS / 插 `<link>` / 读计算样式** 属于 DOM 操作,放在运行端 `board/theme-host.ts`,
7
+ * base url 由宿主注入(引擎层不许碰 window,见 `scripts/assert-board-engine.mjs`)。
8
+ *
9
+ * 清单只服务"设计器里的主题下拉":运行端根本不需要它 —— 配置里写的就是主题 code,
10
+ * 直接按 `{themeBaseUrl}/{code}.css` 取即可。
11
+ */
12
+ export interface BoardThemeEntry {
13
+ code: string;
14
+ name: string;
15
+ /** 文件名(缺省 = `<code>.css`) */
16
+ file: string;
17
+ }
18
+ /**
19
+ * 容错口径(沿用 design-ui,勿改):非数组 → 空;没有 code 的条目丢掉;
20
+ * 重复 code 只留第一个;没写 file 就按约定用 `<code>.css`
21
+ * (这样加一个主题 = 一个 css + 一行清单)。
22
+ */
23
+ export declare function parseManifest(raw: unknown): BoardThemeEntry[];