@lark-apaas/coding-template-nestjs-react-fullstack 0.1.64 → 0.1.65-beta.0
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/package.json
CHANGED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 飞书 h5-js-sdk 全局样式降层。
|
|
3
|
+
*
|
|
4
|
+
* 问题:SDK 加载时用 style-loader 往 <head> 插入约 200 段 CSS,其中混着几条
|
|
5
|
+
* 不带类名的全局重置,例如 `body{color;background-color;font-family}`、
|
|
6
|
+
* `input,button,select,optgroup,textarea{color:inherit}`。这些规则不在任何
|
|
7
|
+
* cascade layer 里,按层叠规则会压过应用放在 `@layer base` / `@layer utilities`
|
|
8
|
+
* 里的全部样式(与选择器权重无关)。表现为:打开过一次名片后,
|
|
9
|
+
* `bg-primary text-primary-foreground` 的按钮变成蓝底黑字,body 的字体、
|
|
10
|
+
* 颜色、背景也被改掉,直到刷新页面都不会恢复。
|
|
11
|
+
*
|
|
12
|
+
* 做法:只把「所有选择器都不含类名 / id」的规则挪进 `@layer base.lark-sdk`,
|
|
13
|
+
* 让应用自己的 base 层和工具类都能覆盖它们;带类名的名片内部样式原样保留,
|
|
14
|
+
* 名片外观不受影响。
|
|
15
|
+
*
|
|
16
|
+
* ⚠️ 依据 h5-js-sdk 1.2.21 的实现(加载时同步注入)。升级 SDK 后需要重新确认
|
|
17
|
+
* 新版是否还有其他全局规则、注入时机是否变化。
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
const SDK_STYLE_LAYER = 'base.lark-sdk';
|
|
21
|
+
|
|
22
|
+
/** 选择器列表里没有任何一项带类名或 id,即作用于整页的元素级规则 */
|
|
23
|
+
function isElementLevelSelector(selectorText: string): boolean {
|
|
24
|
+
return selectorText.split(',').every((selector) => !/[.#]/.test(selector));
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** 把一个 <style> 里的元素级全局规则挪进低优先级的 cascade layer */
|
|
28
|
+
export function demoteGlobalStyleRules(style: HTMLStyleElement): void {
|
|
29
|
+
// emotion 运行时样式都是哈希类名,Vite 开发态样式属于应用自身,都不处理
|
|
30
|
+
if (
|
|
31
|
+
style.hasAttribute('data-emotion') ||
|
|
32
|
+
style.hasAttribute('data-vite-dev-id')
|
|
33
|
+
) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const sheet = style.sheet;
|
|
38
|
+
if (!sheet) return;
|
|
39
|
+
|
|
40
|
+
const moved: string[] = [];
|
|
41
|
+
for (let i = sheet.cssRules.length - 1; i >= 0; i -= 1) {
|
|
42
|
+
const rule = sheet.cssRules[i];
|
|
43
|
+
if (
|
|
44
|
+
rule instanceof CSSStyleRule &&
|
|
45
|
+
isElementLevelSelector(rule.selectorText)
|
|
46
|
+
) {
|
|
47
|
+
moved.unshift(rule.cssText);
|
|
48
|
+
sheet.deleteRule(i);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (moved.length > 0) {
|
|
53
|
+
sheet.insertRule(`@layer ${SDK_STYLE_LAYER} {\n${moved.join('\n')}\n}`, 0);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -7,6 +7,30 @@ export interface UseExternalScriptOptions {
|
|
|
7
7
|
attributes?: Record<string, string>;
|
|
8
8
|
nonce?: string;
|
|
9
9
|
onloadCallback?: () => void;
|
|
10
|
+
/**
|
|
11
|
+
* 脚本加载期间(插入 → onload / onerror)每个新插入 <head> 的 <style>
|
|
12
|
+
* 都会回调一次,用于处理脚本自带的全局样式。
|
|
13
|
+
*/
|
|
14
|
+
onStyleInjected?: (style: HTMLStyleElement) => void;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** 监听 <head> 新增的 <style>,返回的函数会先处理完积压的记录再停止监听 */
|
|
18
|
+
function watchHeadStyles(
|
|
19
|
+
onStyle: (style: HTMLStyleElement) => void,
|
|
20
|
+
): () => void {
|
|
21
|
+
const handle = (records: MutationRecord[]) => {
|
|
22
|
+
records.forEach((record) => {
|
|
23
|
+
record.addedNodes.forEach((node) => {
|
|
24
|
+
if (node instanceof HTMLStyleElement) onStyle(node);
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
};
|
|
28
|
+
const observer = new MutationObserver(handle);
|
|
29
|
+
observer.observe(document.head, { childList: true });
|
|
30
|
+
return () => {
|
|
31
|
+
handle(observer.takeRecords());
|
|
32
|
+
observer.disconnect();
|
|
33
|
+
};
|
|
10
34
|
}
|
|
11
35
|
|
|
12
36
|
/**
|
|
@@ -49,7 +73,13 @@ export function useExternalScript(
|
|
|
49
73
|
script.setAttribute(k, v);
|
|
50
74
|
});
|
|
51
75
|
|
|
76
|
+
// 组件提前卸载时脚本仍会继续加载并注入样式,所以只在 onload / onerror 停止监听
|
|
77
|
+
const stopWatchingStyles = options.onStyleInjected
|
|
78
|
+
? watchHeadStyles(options.onStyleInjected)
|
|
79
|
+
: undefined;
|
|
80
|
+
|
|
52
81
|
script.onload = () => {
|
|
82
|
+
stopWatchingStyles?.();
|
|
53
83
|
if (!cancelled) setStatus('ready');
|
|
54
84
|
if (options.onloadCallback) {
|
|
55
85
|
options.onloadCallback();
|
|
@@ -57,6 +87,7 @@ export function useExternalScript(
|
|
|
57
87
|
};
|
|
58
88
|
|
|
59
89
|
script.onerror = () => {
|
|
90
|
+
stopWatchingStyles?.();
|
|
60
91
|
if (!cancelled) setStatus('error');
|
|
61
92
|
};
|
|
62
93
|
|
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
takeSpare,
|
|
27
27
|
waitForCardReady,
|
|
28
28
|
} from '@client/src/components/business-ui/user-profile/lark-card-pool';
|
|
29
|
+
import { demoteGlobalStyleRules } from '@client/src/components/business-ui/user-profile/lark-sdk-styles';
|
|
29
30
|
import { getEnv } from '@lark-apaas/client-toolkit/utils/getEnv';
|
|
30
31
|
import { useExternalScript } from '@client/src/components/business-ui/user-profile/user-external-script';
|
|
31
32
|
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
|
|
@@ -369,7 +370,10 @@ function LarkProfileCard(props: { readonly larkCardParam: LarkCardParam }) {
|
|
|
369
370
|
const startedRef = useRef(-1);
|
|
370
371
|
|
|
371
372
|
const scriptOptions = useMemo(
|
|
372
|
-
() => ({
|
|
373
|
+
() => ({
|
|
374
|
+
onloadCallback: registerLarkAuthHandler,
|
|
375
|
+
onStyleInjected: demoteGlobalStyleRules,
|
|
376
|
+
}),
|
|
373
377
|
[],
|
|
374
378
|
);
|
|
375
379
|
const scriptStatus = useExternalScript(LARK_SDK_URL, scriptOptions);
|