labull-framework 1.1.0 → 1.1.1
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/dist/magic-block.js +2762 -2249
- package/dist/style.css +1 -1
- package/dist/types/magic-block-bridge.d.ts +10 -1
- package/dist/types/magic-block-manifest.d.ts +7 -5
- package/dist/types/magic-block-real.d.ts +2 -1
- package/dist/types/magic-block-render.d.ts +1 -1
- package/dist/types/magic-block-steps.d.ts +44 -0
- package/dist/types/magic-block-theme.d.ts +53 -6
- package/package.json +1 -1
|
@@ -13,7 +13,16 @@ export declare const BLOCK_NO_SCROLL_CSS = "html, body { overflow: hidden !impor
|
|
|
13
13
|
/** 注入到块 iframe 里的 JS 源码(块没有构建步骤,它必须自己就是能跑的 JS)。
|
|
14
14
|
* 🔴 这是模板字符串:段内的注释出现反引号会提前截断它(实现也一律用字符串拼接)。 */
|
|
15
15
|
export declare const CLIENT_SOURCE = "\n(function () {\n var seq = 0, waiting = {}, queue = [], ready = false, last = -1;\n function call(method, args) {\n return new Promise(function (resolve, reject) {\n function run() {\n var id = ++seq;\n waiting[id] = { resolve: resolve, reject: reject };\n parent.postMessage({ '__mb': 'call', id: id, method: method, args: args }, '*');\n }\n if (ready) run(); else queue.push(run);\n });\n }\n window.addEventListener('message', function (event) {\n var data = event.data, pending;\n if (!data || data['__mb'] === undefined) return;\n if (data['__mb'] === 'host-ready') { ready = true; queue.splice(0).forEach(function (run) { run(); }); return; }\n if (data['__mb'] !== 'reply') return;\n if (!(pending = waiting[data.id])) return;\n delete waiting[data.id];\n if (data.ok) pending.resolve(data.value); else pending.reject(new Error(data.error || '\u8C03\u7528\u5931\u8D25'));\n });\n window.magicBlock = {\n getMe: function () { return call('getMe', []); },\n navigateTab: function (path) { return call('navigateTab', [path]); },\n context: function () { return call('context', []); },\n setTabTitle: function (title, icon) { return call('setTabTitle', [title, icon]); },\n /* \u5F39\u7A97\uFF1A**\u7531\u5BBF\u4E3B\u9875\u9762\u753B**\uFF08\u5757\u81EA\u5DF1\u753B\u7684\u6D6E\u5C42\u57FA\u51C6\u662F\"\u6574\u5757\u9AD8\u5EA6\"\uFF0C\u89C1 MagicBlockHost \u9876\u4E0A\u90A3\u4E00\u6BB5\uFF09\u3002 */\n dialog: function (options) { return call('dialog', [options]); },\n /* \u26A0\uFE0F \u53EA\u5728 magicBlock.dialog({ html }) \u6253\u5F00\u7684**\u5F39\u7A97\u6587\u6863**\u91CC\u6709\u610F\u4E49\uFF08\u5757\u9875\u91CC\u8C03\u4F1A\u62FF\u5230\u4E00\u53E5\u660E\u786E\u62A5\u9519\uFF09\u3002\n \u8FD9\u6BB5\u662F\u6A21\u677F\u5B57\u7B26\u4E32\uFF1A\u91CC\u9762\u7684\u6CE8\u91CA**\u4E0D\u8BB8\u51FA\u73B0\u53CD\u5F15\u53F7**\uFF08\u4F1A\u63D0\u524D\u622A\u65AD\uFF0C\u89C1\u6587\u4EF6\u5934\u90A3\u4E00\u6761\uFF09\u3002 */\n done: function (payload) { return call('done', [payload]); }\n };\n function make(prefix) {\n return new Proxy(function () {}, {\n get: function (_, name) { return make(prefix ? prefix + '.' + name : name); },\n apply: function (_, __, args) {\n var params = args[0] === undefined || args[0] === null ? {} : args[0];\n if (typeof params !== 'object' || Array.isArray(params)) return Promise.reject(new Error('magicBackendApi.' + prefix + '(params) \u7684\u53C2\u6570\u8981\u4E48\u662F\u4E00\u4E2A\u5BF9\u8C61\uFF08\u62FF\u4E0D\u51C6\u5C31\u4F20 {}\uFF09\uFF0C\u8981\u4E48\u662F\u4E00\u4E2A\u6587\u4EF6\uFF08File / Blob\uFF09'));\n return call('backend', [prefix, params]);\n }\n });\n }\n window.magicBackendApi = make('');\n if (typeof document === 'undefined') return;\n function report() {\n var body = document.body;\n if (!body) return;\n var max = -1, style = window.getComputedStyle(body), bodyTop = body.getBoundingClientRect().top;\n /* \uD83D\uDD34 gap \u91CC\u4E3A\u4EC0\u4E48\u6709 paddingBottom\uFF1Amax - bodyTop \u91CF\u7684\u5C31\u662F\"body \u4E0A\u8FB9\u6846 \u2192 \u6700\u540E\u4E00\u4E2A\u5B69\u5B50\u7684\u5E95\u8FB9\"\uFF0C\n * \u5B83**\u5DF2\u7ECF\u542B\u7740 padding-top \u4E86**\uFF0C\u6240\u4EE5\u53EA\u5DEE\u5E95\u90E8\u90A3\u4E00\u622A\u3002\u5C11\u4E86\u5B83\uFF0C\u5BBF\u4E3B\u6309\u8FD9\u4E2A\u9AD8\u5EA6\u7ED9 iframe \u5B9A\u9AD8\u65F6\n * \u4F1A\u628A body \u7684 padding-bottom \u5403\u6389\uFF08\u5757\u81EA\u5DF1\u7684 body { padding: 12px } \u5C31\u662F\u8FD9\u4E48\u88AB\u54AC\u6389\u4E00\u622A\u7684\uFF09\u3002\n * \u26A0\uFE0F \u8FD9\u4E00\u6BB5\u5728\u6A21\u677F\u5B57\u7B26\u4E32\u91CC\uFF1A\u6CE8\u91CA\u91CC**\u4E0D\u8BB8\u51FA\u73B0\u53CD\u5F15\u53F7**\uFF08\u4F1A\u63D0\u524D\u622A\u65AD\uFF0C\u89C1\u6587\u4EF6\u5934\u90A3\u4E00\u6761\uFF09\u3002 */\n var gap = (parseFloat(style.marginTop) || 0) + (parseFloat(style.marginBottom) || 0) + (parseFloat(style.paddingBottom) || 0);\n try {\n for (var i = 0; i < body.children.length; i += 1) {\n var el = body.children[i], cs = window.getComputedStyle(el), rect = el.getBoundingClientRect();\n if (cs.display === 'none' || cs.visibility === 'hidden' || cs.position === 'fixed' || rect.height <= 0) continue;\n if (rect.bottom > max) max = rect.bottom;\n }\n } catch (error) { return; }\n var height = max < 0 ? 0 : Math.ceil(max - bodyTop + gap); /* \u4E00\u6761\u6B63\u5E38\u6D41\u5185\u5BB9\u90FD\u6CA1\u6709 \u21D2 \u62A5 0\uFF08\u5BBF\u4E3B\u6309\u8FD8\u6CA1\u91CF\u5230\u5904\u7406\uFF09 */\n /* \uD83D\uDD34 +1 \u662F\"\u53D6\u6574\u7684\u4F59\u91CF\"\uFF1A\u51E0\u4F55\u4E0A\u7B97\u51FA\u6765\u7684\u5185\u5BB9\u9AD8\u5EA6\u5E38\u5E38\u5DEE\u90A3\u4E48\u96F6\u70B9\u51E0\u50CF\u7D20\uFF08\u5B50\u50CF\u7D20\u6392\u7248\uFF09\uFF0C\n * \u5BBF\u4E3B\u7167\u7740\u5B83\u8BBE\u9AD8\u5EA6\u65F6\u5C31\u4F1A**\u5DEE\u4E00\u50CF\u7D20**\u2014\u2014\u90A3\u4E00\u50CF\u7D20\u6B63\u662F\u4E00\u4E2A\u7AD6\u6EDA\u52A8\u6761\u7684\u5F00\u5173\u3002 */\n if (height > 0) height += 1;\n if (height === last) return; /* \u4E00\u6837\u9AD8\u5C31\u4E0D\u518D\u53D1\uFF1A\u5426\u5219\u300C\u5BBF\u4E3B\u6539\u9AD8\u5EA6 \u2192 \u89E6\u53D1\u89C2\u5BDF \u2192 \u53C8\u53D1\u4E00\u6B21\u300D\u4F1A\u8F6C\u4E2A\u4E0D\u505C */\n last = height;\n parent.postMessage({ '__mb': 'height', height: height }, '*');\n }\n function start() {\n report();\n if (window.ResizeObserver && document.body) new ResizeObserver(report).observe(document.body);\n }\n if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', start); else start();\n setInterval(report, 500);\n})();\n";
|
|
16
|
-
export declare const DIALOG_BOOTSTRAP
|
|
16
|
+
export declare const DIALOG_BOOTSTRAP: string;
|
|
17
|
+
/**
|
|
18
|
+
* 注入块 iframe 的那一段**完整**引导脚本:桥 + `MagicSteps`。
|
|
19
|
+
*
|
|
20
|
+
* 🔴 为什么 `MagicSteps` 也要注进去:块**没有模块系统**(不能 import / export),所以"平台提供"
|
|
21
|
+
* 只能靠这一段随预览一起注入(与 `magicBlock` / `magicBackendApi` 同一座桥)。
|
|
22
|
+
* 🔴 为什么它不在 `CLIENT_SOURCE` 里直接写:`steps.ts` 才是唯一实现(它可以被 node 跑用例),
|
|
23
|
+
* 这里只是把它的 `toString()` 拼进来 —— 抄第二份的代价是两边行为慢慢分开。
|
|
24
|
+
*/
|
|
25
|
+
export declare const CLIENT_BOOTSTRAP: string;
|
|
17
26
|
export interface BridgeDeps {
|
|
18
27
|
uuid: string;
|
|
19
28
|
onHeight?: (height: number) => void;
|
|
@@ -5,14 +5,16 @@ export declare const BLOCK_MANIFEST = "magic-block.open.yaml";
|
|
|
5
5
|
/** 块包内那两份"作者改的"文件(`.open.`)。与后端 `sdk.OPEN_FILES` 逐字一致。 */
|
|
6
6
|
export declare const README_OPEN = "README.open.md";
|
|
7
7
|
export declare const REQUIRES_OPEN = "requires.open.yaml";
|
|
8
|
-
/**
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
/** 清单认得的键(顺序就是文档里的顺序)。`type` 与 `requires` 是**可选**的两个键。
|
|
9
|
+
* 🔴 键序与后端 `MANIFEST_KEYS` 逐字一致:`type`(块类型)在四个必填键之后、`requires` 之前。 */
|
|
10
|
+
export declare const MANIFEST_KEYS: readonly ["id", "module", "name", "brief", "type", "requires"];
|
|
11
|
+
/** 必须写全的那四个键(= `MANIFEST_KEYS` 去掉**两个可选键** `type` 与 `requires`)。
|
|
11
12
|
*
|
|
12
|
-
* 🔴
|
|
13
|
+
* 🔴 为什么这两个**不进必填这一档**:存量块的清单里一个都没有 —— 判"缺少它们"
|
|
13
14
|
* 等于让库里每一个块在保存 / 体检时集体判不合格(这是**加一个字段**,不是**换一份契约**)。
|
|
15
|
+
* `type` 缺省就是 `plain`(老块零迁移)。
|
|
14
16
|
* 🔴 认得的键与必填的键**必须分开两处**:合成一处的话,报文会把可选键说成必填。
|
|
15
|
-
* 与后端 `
|
|
17
|
+
* 与后端 `manifest.MANIFEST_REQUIRED` 逐字对齐。 */
|
|
16
18
|
export declare const MANIFEST_REQUIRED: readonly ["id", "module", "name", "brief"];
|
|
17
19
|
/** **搬家走了**的那个老键:`detail` 的正文现在住在块包内的 `README.open.md`。
|
|
18
20
|
*
|
|
@@ -4,7 +4,8 @@ export { default as MagicBlockConsole } from './magic-block-MagicBlockConsole.vu
|
|
|
4
4
|
export { default as MagicBlockDialogs } from './magic-block-MagicBlockDialogs.vue';
|
|
5
5
|
export { loadBlockRender, onBlockRenderRefresh, refreshBlockRender, type BlockRender } from './magic-block-render';
|
|
6
6
|
export { BLOCK_BACKEND_FILE, BLOCK_ENTRY, buildDialogPreview, buildPreview } from './magic-block-preview';
|
|
7
|
-
export { CLIENT_SOURCE, DIALOG_BOOTSTRAP, installBridge, type BridgeDeps } from './magic-block-bridge';
|
|
7
|
+
export { CLIENT_BOOTSTRAP, CLIENT_SOURCE, DIALOG_BOOTSTRAP, installBridge, type BridgeDeps } from './magic-block-bridge';
|
|
8
|
+
export { STEPS_SOURCE, installSteps, type StepInfo, type StepPayload, type StepRenderer, type StepsApi, } from './magic-block-steps';
|
|
8
9
|
export { MAX_DIALOGS, cancelDialogsOf, dismissDialog, doneToResult, isOpenDialog, normalizeDialogOptions, openHostDialog, reportFromDialogDoc, respondDialog, setDialogHostReady, useHostDialogs, validCssLength, type DialogButton, type DialogField, type DialogOptions, type DialogResult, type DialogValue, type HostDialog, type HostDialogContext, } from './magic-block-dialogs';
|
|
9
10
|
export { BLOCK_MANIFEST, MANIFEST_KEYS, MANIFEST_REQUIRED, MANIFEST_MOVED, NAME_MAX, README_OPEN, REQUIRES_KEYS, REQUIRES_OPEN, checkSelection, parseManifest, validFileName, validName, validateBlock, type BlockValidation, type Manifest, type ManifestKey, type Requires, type SelectionCheck, } from './magic-block-manifest';
|
|
10
11
|
export { API_BASE, setApiBase } from './auth-real';
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/** 报文里 `steps[]` 的一项(步骤条按它的**声明顺序**画)。 */
|
|
2
|
+
export interface StepInfo {
|
|
3
|
+
code: string;
|
|
4
|
+
label: string;
|
|
5
|
+
terminal?: boolean;
|
|
6
|
+
}
|
|
7
|
+
/** `magicBlock` 报文里与节点内容有关的那两段(`step` / `steps`)。 */
|
|
8
|
+
export interface StepPayload {
|
|
9
|
+
step: {
|
|
10
|
+
code: string;
|
|
11
|
+
label: string;
|
|
12
|
+
ui: string;
|
|
13
|
+
editable: string[];
|
|
14
|
+
hasForm: boolean;
|
|
15
|
+
terminal?: boolean;
|
|
16
|
+
};
|
|
17
|
+
steps?: StepInfo[];
|
|
18
|
+
}
|
|
19
|
+
/** 一个内容渲染器:`(容器元素, 整份报文) => void`。 */
|
|
20
|
+
export type StepRenderer = (slot: Element, payload: StepPayload) => void;
|
|
21
|
+
/** `MagicSteps` 的四个方法(块里拿到的就是它)。 */
|
|
22
|
+
export interface StepsApi {
|
|
23
|
+
/** 登记一个内容 key(`flow.js` 里写:`MagicSteps.register('bill', fn)`)。 */
|
|
24
|
+
register: (ui: string, render: StepRenderer) => void;
|
|
25
|
+
/** 已登记的 key(登记顺序)—— 排查"为什么白屏"时先看它。 */
|
|
26
|
+
registered: () => string[];
|
|
27
|
+
/** 按 `payload.step.ui` 渲染内容;回用到的那个 key(没登记就抛中文原话)。 */
|
|
28
|
+
mount: (slot: Element, payload: StepPayload) => string;
|
|
29
|
+
/** 画步骤条(声明顺序 → 当前之前 `is-done`、相等 `is-current`)。 */
|
|
30
|
+
renderBar: (bar: Element, payload: StepPayload) => void;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* 把 `MagicSteps` 装到 `win` 上(块 iframe 里是 `window`;用例里可以塞一个假 window)。
|
|
34
|
+
*
|
|
35
|
+
* 🔴 **自包含**:这个函数体里不许出现对本模块其它名字的引用(它会被 `toString()` 注入 iframe)。
|
|
36
|
+
*/
|
|
37
|
+
export declare function installSteps(win: any): StepsApi;
|
|
38
|
+
/**
|
|
39
|
+
* 注入块 iframe 的那一段源码:`(installSteps)(window)`。
|
|
40
|
+
*
|
|
41
|
+
* 🔴 它是 `toString()` 现场拼的(见文件头"为什么还是只有一份实现")—— 上面那条"只用局部变量"
|
|
42
|
+
* 的规矩就是为了它。类型注解在运行期已经被剥掉,所以拼出来的是一段干净的 JS。
|
|
43
|
+
*/
|
|
44
|
+
export declare const STEPS_SOURCE: string;
|
|
@@ -15,10 +15,16 @@ export interface ThemeBundle {
|
|
|
15
15
|
/**
|
|
16
16
|
* 这条规则算不算"主题 / 控件"(其余一律丢掉:应用自己的业务样式不该混进开发包)。
|
|
17
17
|
*
|
|
18
|
-
* 留:`@font-face` / `@keyframes`;选择器里有 `.p-`(PrimeVue 控件)/ `.pi`(图标)/ `.labull
|
|
19
|
-
*
|
|
20
|
-
* 留的零特异度兜底 —— 它的声明里常常一个 token
|
|
21
|
-
*
|
|
18
|
+
* 留:`@font-face` / `@keyframes`;选择器里有 `.p-`(PrimeVue 控件)/ `.pi`(图标)/ `.labull-`(结构类
|
|
19
|
+
* + 我们新写的工具类)/ `:root` / `html` / `body` / `*` / `[hidden]`;**`:where(...)` 开头的那一层**
|
|
20
|
+
* (主题给原生控件与 `<dialog>` 留的零特异度兜底 —— 它的声明里常常一个 token 都没有,
|
|
21
|
+
* 例如 `::backdrop` 的纯色遮罩,所以不能只靠"有没有 token"判它);或者这条规则里在定义
|
|
22
|
+
* `--sp-*` / `--surface-*` 这类 token。
|
|
23
|
+
* 🔴 `[hidden]` 在名单里是**必须的**(`frontend/theme/labull/base.css` 那一层):它是"别显示"这个
|
|
24
|
+
* 语义的唯一出口,而作者样式永远盖过浏览器默认的那一条 —— 开发包里 33 个块有 21 个各自抄了
|
|
25
|
+
* 一遍 `[hidden] { display: none !important }`,就是因为这里没收它。
|
|
26
|
+
* 🔴 **前缀决定生死**:`.labull-*` 一律留下;作者自己起的类名(`.ad-page` / `.mb-head`)**永远不收**
|
|
27
|
+
* —— 那是块自己的样式,注入别人的块里只会打架。所以块作者自己写的类名,只有它自己的样式表认得。
|
|
22
28
|
* 🔴 纯函数:只看文本,不看 DOM —— 所以能直接用 node 跑用例。
|
|
23
29
|
*/
|
|
24
30
|
export declare function keepCssRule(selector: string, cssText: string): boolean;
|
|
@@ -53,10 +59,51 @@ export declare function carryIntoBody(doc: string, carry: string): string;
|
|
|
53
59
|
export declare function asDialogDocument(html: string): string;
|
|
54
60
|
/** 收一份自足的 CSS(规则从**当前页面已加载的样式表**里来)。 */
|
|
55
61
|
export declare function collectThemeCss(): ThemeBundle;
|
|
62
|
+
/** 一个类名,以及它在这个应用的主题里出现过多少条规则(多的通常更"核心")。 */
|
|
63
|
+
export interface ThemeClass {
|
|
64
|
+
name: string;
|
|
65
|
+
count: number;
|
|
66
|
+
}
|
|
67
|
+
/** 类名的一个"家族":`.labull-card` / `.labull-card__head` / `.labull-card--tight` 算一家。 */
|
|
68
|
+
export interface ThemeFamily {
|
|
69
|
+
family: string;
|
|
70
|
+
members: ThemeClass[];
|
|
71
|
+
}
|
|
72
|
+
/** 从一份主题 CSS 里算出来的索引(`controlsPage()` 用它画 token 表与类名总表)。 */
|
|
73
|
+
export interface ThemeIndex {
|
|
74
|
+
/** `--sp-*` 的名字与值(按首次出现的顺序 = token 定义那一份的顺序)。 */
|
|
75
|
+
tokens: [string, string][];
|
|
76
|
+
/** `.labull-*`(我们自己的词汇),按家族分组、大的在前。 */
|
|
77
|
+
families: ThemeFamily[];
|
|
78
|
+
/** `.p-*`(PrimeVue 控件类),按家族分组、大的在前。 */
|
|
79
|
+
prime: ThemeFamily[];
|
|
80
|
+
/** 图标名(`pi-*`),排好序。 */
|
|
81
|
+
icons: string[];
|
|
82
|
+
/** 前缀不认识的那一批(`.el-*` / `.ql-*` / `.ag-*` / `.labull_bill_*` …):**到不了块里**。 */
|
|
83
|
+
others: number;
|
|
84
|
+
/** 类名总数(含图标)。 */
|
|
85
|
+
total: number;
|
|
86
|
+
/** 主题里的规则条数(`labull.css` 头上那个数)。 */
|
|
87
|
+
rules: number;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* 从一份主题 CSS 里算索引(纯函数,不碰 DOM)。
|
|
91
|
+
*
|
|
92
|
+
* 认三类前缀:`.labull-`(我们的)、`.p-`(PrimeVue 控件)、`.pi`(图标);
|
|
93
|
+
* 其余一律记进 `others` —— 那些是交付件里的旧货(`.el-table` / `.ql-*` / `.ag-*` / `.labull_bill_*`),
|
|
94
|
+
* 收录判据(`keepCssRule`)**不收它们**,所以块里用不到,索引里也不该出现。
|
|
95
|
+
*/
|
|
96
|
+
export declare function themeIndex(css: string): ThemeIndex;
|
|
56
97
|
/**
|
|
57
|
-
* `CONTROLS.lock.html
|
|
98
|
+
* `CONTROLS.lock.html`:**控件 / 类名 / token 的索引页**(一页能直接打开、顶上带搜索)。
|
|
58
99
|
*
|
|
100
|
+
* 三件事,顺序就是"你会怎么找":
|
|
101
|
+
* ① **照抄的配方** —— 每一条都是能直接粘进块里的 markup(只列光有 CSS 就能看的东西);
|
|
102
|
+
* ② **token 表** + **常见的写错的名字** —— 从收下来的 CSS 里算,不是手抄;
|
|
103
|
+
* ③ **全部类名**(`.labull-*` 全列、`.p-*` 按家族列、`pi-*` 图标全列 + 图标长什么样)。
|
|
104
|
+
* 🔴 `themeCss` 是可选参数:给了就按**它**画 token 表与类名总表(导出时给的就是这一份应用在用的);
|
|
105
|
+
* 没给(例如只跑用例)就只出配方那一段,并在页面上**明说**索引没算 —— 不假装有。
|
|
59
106
|
* 🔴 只列**光有 CSS 就能看**的东西:PrimeVue 那些需要 JS 的组件(下拉、日期、弹窗)不在这里,
|
|
60
107
|
* 免得作者抄了 markup 却发现没有行为(那比不给他更糟)。
|
|
61
108
|
*/
|
|
62
|
-
export declare function controlsPage(): string;
|
|
109
|
+
export declare function controlsPage(themeCss?: string): string;
|