@manohub/app-kit 0.2.5 → 0.2.7
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/CONTRACT.md +17 -3
- package/dist/atoms/app-tooltip.d.ts +8 -3
- package/dist/atoms/app-tooltip.js +14 -9
- package/dist/components/app-dialog.js +6 -1
- package/dist/components/app-drawer.d.ts +6 -2
- package/dist/components/app-drawer.js +12 -4
- package/dist/services/app-container.d.ts +14 -0
- package/dist/services/app-container.js +16 -1
- package/package.json +2 -2
package/CONTRACT.md
CHANGED
|
@@ -783,9 +783,23 @@ pnpm --filter @manohub/app-kit test:unit # vitest(契约单测)+ 护栏
|
|
|
783
783
|
而令牌(`--ui-*`)与主题桥接(`--f-theme-*`)都锚在 `.app-container` 上、微前端下应用侧 CSS 还被
|
|
784
784
|
scopecss 限定在容器内 —— 落到 body 会让弹窗**内容里所有组件与应用样式一起失效**。
|
|
785
785
|
`AppDialog` / `modalService` 内部已改投 `.app-container`,消费方无需也不应干预渲染宿主。
|
|
786
|
-
11.
|
|
787
|
-
|
|
788
|
-
|
|
786
|
+
11. **抽屉的宿主只接受「选择器字符串」**:`AppDrawer`(§4.18)同样把内容投到 `.app-container`(否则样式全丢),
|
|
787
|
+
但**不能像 `AppDialog` 那样传 Element**:上游 `FDrawer` 算 `f-drawer-inline` 时写的是
|
|
788
|
+
`document.querySelector(host)`,传 Element 会拿 `"[object HTMLDivElement]"` 当选择器解析并抛
|
|
789
|
+
`SyntaxError: not a valid selector`(整页渲染中断,已在 mcp 实测)。本件走容器唯一 id 的
|
|
790
|
+
`#id` 字符串(`resolveAppContainerSelector()`)。
|
|
791
|
+
随之而来的定位问题:上游在「宿主不是 `body`」时会给抽屉根节点加 `f-drawer-inline`
|
|
792
|
+
(把 `position: fixed` 改成 `absolute`)—— 微前端里绝对定位的包含块不可控
|
|
793
|
+
(`.app-container` 自身未定位、宿主层级随门户而变),抽屉与遮罩会错位。
|
|
789
794
|
包内桥接层因此把 `.f-drawer.f-drawer-inline` 的定位压回 `fixed`:**覆盖范围与改造前 `host: "body"` 完全一致**。
|
|
790
795
|
另:上游 `showFooter` 默认 `true` 且高度取 `footerHeight`(默认 60px),不给内容也留一条空白 ——
|
|
791
796
|
`AppDrawer` 在无页脚时直接关掉该区块,应用侧不必再写「隐藏空 footer」的覆盖样式。
|
|
797
|
+
12. **控制台告警的归属(接入方排查时先看这里,别改自己的代码)**:
|
|
798
|
+
|
|
799
|
+
| 告警 | 归属 | 现状 |
|
|
800
|
+
|---|---|---|
|
|
801
|
+
| `Invalid prop: … prop "content". Expected Object, got String`(来自 `FTooltip`) | 上游 `tooltip/index.esm.js` 把 `content` 声明成 `Object`(默认 `""`) | `AppTooltip` 已改走 `contentTemplate` 插槽,**不再出现**;同时文本从 `innerHTML` 变文本节点 |
|
|
802
|
+
| `Invalid prop: … prop "width"/"height". Expected String … got Number 300`(来自 `FDrawer`) | 上游 `drawer/index.esm.js` 声明 `String` 而默认值是 **Number 300**(默认值自身就过不了校验) | `AppDrawer` 传带单位字符串(`"640px"` / `"100%"`),**不再出现** |
|
|
803
|
+
| `Invalid prop: … prop "popupOffsetX"/"popupOffsetY". Expected Object, got Number 0`(来自 `FButtonEdit`,链路里能看到 `FComboList`) | 上游 `button/index.esm.js` 写的是 `{ type: Object, default: 0 }` | **无法在应用侧消除**(`FComboList` 不转发这两个 prop)。每渲染一个 `AppSelect` 会刷 2 条,属上游缺陷,忽略即可 |
|
|
804
|
+
| `inject() can only be used inside setup()` | farris 内部与 `@gsp-sys/utils` 在组件上下文外调用 `inject`(app-kit 自己的 `inject` 只在 `AppForm.Item` 的 `setup` 内,`modalService` 走 `app.runWithContext`) | 平台/上游行为,与本包无关 |
|
|
805
|
+
| `Extraneous non-props attributes (customClass) …`(来自 `FModal`) | 早期 `AppDialog` 曾把 class 当 `customClass` 转发,而 `FModal` 运行时没有该 prop、根节点又是 Teleport ⇒ 类名被丢弃还刷告警 | 已移除转发,**不再出现**;消费方要钩子请包在内容元素上 |
|
|
@@ -5,8 +5,11 @@ export type AppTooltipTrigger = 'hover' | 'click';
|
|
|
5
5
|
* 文字提示(封装 farris FTooltip)。
|
|
6
6
|
*
|
|
7
7
|
* 吃掉两个 farris 坑:
|
|
8
|
-
* 1. `content`
|
|
9
|
-
*
|
|
8
|
+
* 1. `content` 的**运行时**声明是 `Object`(默认 `""`)—— 类型面要把字符串断言进去,
|
|
9
|
+
* 而运行时的 prop 类型检查照样会刷「Invalid prop: … Expected Object, got String」告警。
|
|
10
|
+
* 本件因此**不走 `content` prop**,改走 `contentTemplate` 插槽(上游 `Z()` 的第一条分支):
|
|
11
|
+
* 既没有告警,又把文本当**文本节点**渲染(`content` prop 那条路是 `innerHTML`,
|
|
12
|
+
* 业务值里的 `<` `&` 会被当标签解析)。
|
|
10
13
|
* 2. 浮层定位属性会被 farris 自身规则覆盖导致微前端下偏移,修复规则收敛在
|
|
11
14
|
* app-kit 的 `farris-bridge.css`(`.app-container .fv-tooltip`),应用侧不再各写补丁。
|
|
12
15
|
*
|
|
@@ -37,7 +40,9 @@ export declare const AppTooltip: import("vue").DefineComponent<import("vue").Ext
|
|
|
37
40
|
type: BooleanConstructor;
|
|
38
41
|
default: boolean;
|
|
39
42
|
};
|
|
40
|
-
}>, () => import("vue
|
|
43
|
+
}>, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
|
|
44
|
+
[key: string]: any;
|
|
45
|
+
}>, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
|
41
46
|
content: {
|
|
42
47
|
type: StringConstructor;
|
|
43
48
|
default: string;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { defineComponent, createVNode } from "vue";
|
|
2
1
|
import { FTooltip } from "@farris/ui-vue";
|
|
2
|
+
import { defineComponent, h } from "vue";
|
|
3
|
+
const FarrisTooltip = FTooltip;
|
|
3
4
|
const AppTooltip = /* @__PURE__ */ defineComponent({
|
|
4
5
|
name: "AppTooltip",
|
|
5
6
|
inheritAttrs: false,
|
|
@@ -31,15 +32,19 @@ const AppTooltip = /* @__PURE__ */ defineComponent({
|
|
|
31
32
|
slots,
|
|
32
33
|
attrs
|
|
33
34
|
}) {
|
|
34
|
-
return () =>
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
"
|
|
40
|
-
"customClass": ["ak-tooltip", attrs.class].filter(Boolean).join(" ")
|
|
35
|
+
return () => h(FarrisTooltip, {
|
|
36
|
+
placement: props.placement,
|
|
37
|
+
trigger: props.trigger,
|
|
38
|
+
width: props.width,
|
|
39
|
+
enable: !props.disabled,
|
|
40
|
+
customClass: ["ak-tooltip", attrs.class].filter(Boolean).join(" ")
|
|
41
41
|
}, {
|
|
42
|
-
default: () =>
|
|
42
|
+
default: () => slots.default?.(),
|
|
43
|
+
/**
|
|
44
|
+
* 文本走 `contentTemplate` 插槽(上游 `Z()` 里 `slots.contentTemplate` 优先于 `content`):
|
|
45
|
+
* 既绕开 `content` 的 Object 类型检查,又让文本以**文本节点**渲染(`content` 那条路是 innerHTML)。
|
|
46
|
+
*/
|
|
47
|
+
contentTemplate: () => props.content
|
|
43
48
|
});
|
|
44
49
|
}
|
|
45
50
|
});
|
|
@@ -91,7 +91,12 @@ const AppDialog = /* @__PURE__ */ defineComponent({
|
|
|
91
91
|
* `Object`,传字符串会多刷一条 prop 类型告警。
|
|
92
92
|
*/
|
|
93
93
|
host: resolveAppContainer(),
|
|
94
|
-
|
|
94
|
+
/**
|
|
95
|
+
* ⚠️ **不要给 FModal 传 `customClass`**:它的 d.ts 是索引签名(看着什么都能传),
|
|
96
|
+
* 但运行时 props 里**没有**这个键 ⇒ 会落进 attrs;而 FModal 渲染的是 Teleport 根节点,
|
|
97
|
+
* Vue 无法自动继承 ⇒ 控制台刷「Extraneous non-props attributes (customClass)…」且类名最终被丢弃。
|
|
98
|
+
* 消费方要钩子请包在内容元素上(弹窗根节点由上游 Teleport 渲染,挂不上应用侧类名)。
|
|
99
|
+
*/
|
|
95
100
|
"onUpdate:modelValue": handleUpdate
|
|
96
101
|
}, {
|
|
97
102
|
default: () => slots.default?.(),
|
|
@@ -9,8 +9,12 @@ import { type PropType, type SlotsType, type VNodeChild } from 'vue';
|
|
|
9
9
|
* 吃掉的上游坑:
|
|
10
10
|
* 1. **被当 DOM 容器的宽度**:上游 `width` 声明成 `String`、默认 `"300"`,值会原样落进行内 style
|
|
11
11
|
* ⇒ `width:300` 不是合法 CSS,抽屉宽度会退化成「内容宽度」。本件默认 `width: 480`(数值 ⇒ `480px`)。
|
|
12
|
-
* 2. **渲染宿主固定为 `.app-container
|
|
13
|
-
*
|
|
12
|
+
* 2. **渲染宿主固定为 `.app-container`,且必须传「选择器字符串」**:上游 `host` 默认 `"body"`,
|
|
13
|
+
* 抽屉落容器外会同时丢令牌(`--ui-*`)与应用侧样式(微前端下被 scopecss 限定在容器内)——
|
|
14
|
+
* 与 AppDialog 同一件事。但**传法不同**:`FModal` 的 `host` 是 `Object`(可传 Element),
|
|
15
|
+
* `FDrawer` 算 `f-drawer-inline` 时写的是 `document.querySelector(host)` —— 传 Element 会拿
|
|
16
|
+
* `"[object HTMLDivElement]"` 当选择器解析并抛 `SyntaxError`(非法选择器,整页渲染中断)。
|
|
17
|
+
* 故这里走 `resolveAppContainerSelector()`(给容器补唯一 id 再返回 `#id`)。
|
|
14
18
|
* 随之而来的第二个坑:上游在 host ≠ body 时给根节点加 `f-drawer-inline`(`position: absolute`),
|
|
15
19
|
* 而绝对定位的包含块在微前端里不可控,包内桥接层已把它改回 `fixed`
|
|
16
20
|
* (覆盖范围与改造前 `host: "body"` 一致)。
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { FDrawer } from "@farris/ui-vue";
|
|
2
2
|
import { defineComponent, computed, h } from "vue";
|
|
3
|
-
import {
|
|
3
|
+
import { resolveAppContainerSelector } from "../services/app-container.js";
|
|
4
4
|
const FarrisDrawer = FDrawer;
|
|
5
5
|
let drawerSeq = 0;
|
|
6
6
|
const AppDrawer = /* @__PURE__ */ defineComponent({
|
|
@@ -72,7 +72,15 @@ const AppDrawer = /* @__PURE__ */ defineComponent({
|
|
|
72
72
|
id: drawerId.value,
|
|
73
73
|
modelValue: props.open,
|
|
74
74
|
title: props.title,
|
|
75
|
-
|
|
75
|
+
/**
|
|
76
|
+
* 上游两个尺寸 prop 都声明成 `String` 而默认值是 **Number 300** ⇒ 默认值自身就过不了
|
|
77
|
+
* 类型检查(Vue 会为「Expected String with value "300", got Number 300」告警)。
|
|
78
|
+
* 传字符串既消掉告警、又保证落进行内 style 的是合法 CSS(如 `640px`)。
|
|
79
|
+
* `height` 上游本来就用 `.f-drawer-wrapper-right/left{height:100%!important}` 兜住,
|
|
80
|
+
* 这里显式给 `100%` 只是把那条告警一并按下去(视觉不变)。
|
|
81
|
+
*/
|
|
82
|
+
width: `${props.width}px`,
|
|
83
|
+
height: "100%",
|
|
76
84
|
position: props.position,
|
|
77
85
|
showHeader: props.showHeader,
|
|
78
86
|
beforeClose: props.beforeClose,
|
|
@@ -80,8 +88,8 @@ const AppDrawer = /* @__PURE__ */ defineComponent({
|
|
|
80
88
|
* 无内容时关掉上游页脚区块 —— 否则恒定渲染一条 `footerHeight`(默认 60px)的空白。
|
|
81
89
|
*/
|
|
82
90
|
showFooter: hasFooter,
|
|
83
|
-
/** 渲染宿主:见头注释第 2 条(farris 默认 `"body"`
|
|
84
|
-
host:
|
|
91
|
+
/** 渲染宿主:见头注释第 2 条(farris 默认 `"body"` 会让抽屉样式全丢;且上游只吃字符串选择器) */
|
|
92
|
+
host: resolveAppContainerSelector(),
|
|
85
93
|
customClass: ["ak-drawer", attrs.class].filter(Boolean).join(" "),
|
|
86
94
|
customStyle: attrs.style,
|
|
87
95
|
"onUpdate:modelValue": handleUpdate
|
|
@@ -13,3 +13,17 @@
|
|
|
13
13
|
* → `document.body`(兜底,行为与改写前一致,不会更差)。
|
|
14
14
|
*/
|
|
15
15
|
export declare function resolveAppContainer(): Element;
|
|
16
|
+
/**
|
|
17
|
+
* 同 `resolveAppContainer`,但返回**选择器字符串** —— 给「上游只吃字符串宿主」的件用。
|
|
18
|
+
*
|
|
19
|
+
* 为什么不能直接给 Element:farris `FDrawer` 算 `f-drawer-inline` 时写的是
|
|
20
|
+
* `typeof host === 'string' ? host !== 'body' : document.querySelector(host)` ——
|
|
21
|
+
* 传 Element 会落到后半段,`document.querySelector(元素)` 拿到的是 `"[object HTMLDivElement]"`
|
|
22
|
+
* 这个**非法选择器**,当场抛 `SyntaxError: ... is not a valid selector`(实测,整页渲染因此中断)。
|
|
23
|
+
* (对照:`FModal` 的 `host` 声明为 `Object`,传 Element 正常 —— 两个件不是一套规则。)
|
|
24
|
+
*
|
|
25
|
+
* 给字符串又要避免「门户里多应用并存时串到第一个 `.app-container`」,故给容器补一个
|
|
26
|
+
* **进程内唯一 id** 再用 `#id` 定位;容器已有 id 时直接用它的。
|
|
27
|
+
* 兜底到 `document.body` 时返回 `"body"`(上游对该值有专门分支,不查 DOM)。
|
|
28
|
+
*/
|
|
29
|
+
export declare function resolveAppContainerSelector(): string;
|
|
@@ -2,6 +2,21 @@ import { getCurrentAppContainer } from "./app-context.js";
|
|
|
2
2
|
function resolveAppContainer() {
|
|
3
3
|
return getCurrentAppContainer() ?? document.querySelector(".app-container") ?? document.body;
|
|
4
4
|
}
|
|
5
|
+
const containerIds = /* @__PURE__ */ new WeakMap();
|
|
6
|
+
let containerSeq = 0;
|
|
7
|
+
function resolveAppContainerSelector() {
|
|
8
|
+
const container = resolveAppContainer();
|
|
9
|
+
if (container === document.body) return "body";
|
|
10
|
+
if (container.id) return `#${container.id}`;
|
|
11
|
+
let id = containerIds.get(container);
|
|
12
|
+
if (!id) {
|
|
13
|
+
id = `ak-app-container-${containerSeq += 1}`;
|
|
14
|
+
containerIds.set(container, id);
|
|
15
|
+
container.id = id;
|
|
16
|
+
}
|
|
17
|
+
return `#${id}`;
|
|
18
|
+
}
|
|
5
19
|
export {
|
|
6
|
-
resolveAppContainer
|
|
20
|
+
resolveAppContainer,
|
|
21
|
+
resolveAppContainerSelector
|
|
7
22
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@manohub/app-kit",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.7",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "子应用统一骨架层:入口编排(createSubApp)、布局契约(AppShell)、页面组件、原子件、服务、样式底座。farris 被收敛在本包内部,对外只暴露标准 API。",
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
},
|
|
64
64
|
"dependencies": {
|
|
65
65
|
"@farris/ui-vue": "^1.8.4",
|
|
66
|
-
"@manohub/icon": "^0.2.
|
|
66
|
+
"@manohub/icon": "^0.2.7"
|
|
67
67
|
},
|
|
68
68
|
"devDependencies": {
|
|
69
69
|
"@tanstack/vue-query": "catalog:",
|