@hb-hellotech/hb-ui 2.6.0 → 2.7.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.
Files changed (37) hide show
  1. package/dist/App.d.ts.map +1 -1
  2. package/dist/components/business_comp/list_page_comp/hb_list_page_container/src/HbListPageContainer.d.ts.map +1 -1
  3. package/dist/components/func_comp/hb_func_comp_drag/src/index.d.ts.map +1 -1
  4. package/dist/components/func_comp/hb_func_comp_tree/src/tree.d.ts +24 -51
  5. package/dist/components/func_comp/hb_func_comp_tree/src/tree.d.ts.map +1 -1
  6. package/dist/components/func_comp/hb_func_icon/index.d.ts +6 -0
  7. package/dist/components/func_comp/hb_func_icon/index.d.ts.map +1 -0
  8. package/dist/components/func_comp/hb_func_icon/src/icon.d.ts +15 -0
  9. package/dist/components/func_comp/hb_func_icon/src/icon.d.ts.map +1 -0
  10. package/dist/components/func_comp/hb_func_icon_list/index.d.ts +3 -3
  11. package/dist/components/func_comp/hb_func_icon_list/index.d.ts.map +1 -1
  12. package/dist/components/func_comp/hb_func_icon_list/src/icon_list.d.ts +6 -1
  13. package/dist/components/func_comp/hb_func_icon_list/src/icon_list.d.ts.map +1 -1
  14. package/dist/components/func_comp/index.d.ts +2 -1
  15. package/dist/components/func_comp/index.d.ts.map +1 -1
  16. package/dist/components/hooks/useIconfont.d.ts +25 -18
  17. package/dist/components/hooks/useIconfont.d.ts.map +1 -1
  18. package/dist/components/utils/directives/debounce_click/index.d.ts.map +1 -1
  19. package/dist/config/index.d.ts +4 -0
  20. package/dist/config/index.d.ts.map +1 -1
  21. package/dist/demo/HbLibIcon.d.ts.map +1 -1
  22. package/dist/demo/{HbLibDetailBox.d.ts → HbLibIconDemo.d.ts} +1 -1
  23. package/dist/demo/HbLibIconDemo.d.ts.map +1 -0
  24. package/dist/demo/IconfontInjectSandbox.d.ts +23 -0
  25. package/dist/demo/IconfontInjectSandbox.d.ts.map +1 -0
  26. package/dist/demo/hb_file_preview.d.ts.map +1 -1
  27. package/dist/hb_component_lib.css +1 -1
  28. package/dist/hb_component_lib.js +1953 -1784
  29. package/dist/hb_component_lib.umd.cjs +102 -102
  30. package/dist/main.d.ts.map +1 -1
  31. package/hooks/useIconfont.ts +147 -36
  32. package/package.json +2 -2
  33. package/dist/components/func_comp/hb_func_icon_list/constants.d.ts +0 -11
  34. package/dist/components/func_comp/hb_func_icon_list/constants.d.ts.map +0 -1
  35. package/dist/demo/HbLibDetailBox.d.ts.map +0 -1
  36. package/dist/demo/HbUnDetailBox.d.ts +0 -6
  37. package/dist/demo/HbUnDetailBox.d.ts.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"AAGA,OAAO,8BAA8B,CAAC;AAatC,wBAAgB,QAAQ,CAAC,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0BAczD"}
1
+ {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"AAGA,OAAO,8BAA8B,CAAC;AAiBtC,wBAAgB,QAAQ,CAAC,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0BAczD"}
@@ -1,18 +1,45 @@
1
1
  import {
2
+ inject,
2
3
  ref,
3
4
  toValue,
4
5
  watch,
5
6
  type MaybeRefOrGetter,
6
7
  type Ref,
7
8
  } from 'vue';
9
+ import type { InjectionKey } from 'vue';
10
+
11
+ /** 图标库的默认 iconfont CSS 地址 */
12
+ export const HB_ICON_DEFAULT_CSS_URL =
13
+ '//at.alicdn.com/t/c/font_5174416_x116sg1gb1q.css';
14
+
15
+ export const HB_ICON_DEFAULT_SYMBOL_CSS_URL =
16
+ '//at.alicdn.com/t/c/font_5174416_x116sg1gb1q.js';
17
+
18
+ /** 图标渲染模式:字体 class 或多色 SVG Symbol */
19
+ export type HbLibIconMode = 'font' | 'symbol';
20
+
21
+ /** 通过 app.use(HbLibIconList, options) 注入的全局配置 */
22
+ export interface HbLibIconGlobalOptions {
23
+ /** iconfont 资源地址,支持 `.css`(字体图标)或 `.js`(Symbol / 彩色 SVG) */
24
+ cssUrl?: string;
25
+ }
26
+
27
+ /** provide/inject 的 key */
28
+ export const HB_ICON_GLOBAL_KEY: InjectionKey<HbLibIconGlobalOptions> =
29
+ Symbol('HB_ICON_GLOBAL_KEY');
30
+
31
+ // app.use(...) 注入的全局默认配置
32
+ const globalOptions = inject<HbLibIconGlobalOptions>(HB_ICON_GLOBAL_KEY, {});
8
33
 
9
34
  export interface UseIconfontResult {
10
- /** iconfont @font-face 中声明的 font-family,通常也是渲染图标时需要挂载的基础 class */
35
+ /** 字体模式下为 @font-face font-family;Symbol 模式下为空字符串 */
11
36
  iconBaseClass: Ref<string>;
12
- /** 所有图标 class 的公共前缀,例如 full class `strong-user` 时,公共前缀可能是 `strong-` */
37
+ /** 字体模式下为图标 class 公共前缀;Symbol 模式下为 symbol id 的公共前缀(常见为 `icon-`) */
13
38
  iconPrefix: Ref<string>;
14
- /** 去掉公共前缀后的图标名称列表,可直接用于图标选择器或组件的 name 属性 */
39
+ /** 去掉公共前缀后的名称列表,可作为 `HbLibIcon` name */
15
40
  iconClasses: Ref<string[]>;
41
+ /** `font`:class + 字体;`symbol`:`<use href="#前缀+name">`,保留路径中的多色填充 */
42
+ iconMode: Ref<HbLibIconMode>;
16
43
  }
17
44
 
18
45
  interface IconfontParseResult {
@@ -21,6 +48,21 @@ interface IconfontParseResult {
21
48
  names: string[];
22
49
  }
23
50
 
51
+ /** 是否为 iconfont「Symbol(彩色 SVG)」类型的 `.js` 地址 */
52
+ export function isIconfontSymbolJsUrl(url: string): boolean {
53
+ const path = url.trim().split(/[?#]/)[0].toLowerCase();
54
+ return path.endsWith('.js');
55
+ }
56
+
57
+ function stableIdPart(url: string): string {
58
+ let h = 2166136261;
59
+ for (let i = 0; i < url.length; i++) {
60
+ h ^= url.charCodeAt(i);
61
+ h = Math.imul(h, 16777619);
62
+ }
63
+ return (h >>> 0).toString(36);
64
+ }
65
+
24
66
  /**
25
67
  * 解析 iconfont 的 CSS 文本,提取组件渲染和图标选择所需的信息。
26
68
  *
@@ -29,11 +71,6 @@ interface IconfontParseResult {
29
71
  * 2. 读取所有 `.xxx:before { content: "\eXXX" }` 规则中的 xxx,作为完整图标 class。
30
72
  * 3. 计算完整图标 class 的最长公共前缀,例如 "strong-"。
31
73
  * 4. 将公共前缀从完整 class 中移除,得到更适合使用者传入的图标 name。
32
- *
33
- * 示例:
34
- * - CSS 中存在 `.strong-user:before`、`.strong-home:before`
35
- * - 返回 prefix 为 `strong-`
36
- * - 返回 names 为 `['user', 'home']`
37
74
  */
38
75
  function parseIconfontCss(css: string): IconfontParseResult {
39
76
  const fontFaceMatch = css.match(
@@ -57,11 +94,28 @@ function parseIconfontCss(css: string): IconfontParseResult {
57
94
  return { baseClass, prefix, names };
58
95
  }
59
96
 
97
+ /**
98
+ * 从 iconfont 生成的 `.js` 文本中提取 `<symbol id="...">`,规则与 CSS 模式一致,
99
+ * 用最长公共前缀拆出简短的 `iconClasses` name。
100
+ */
101
+ function parseIconfontJs(
102
+ js: string
103
+ ): Pick<IconfontParseResult, 'prefix' | 'names'> {
104
+ const fullIds: string[] = [];
105
+ const symbolIdRe = /<symbol\b[^>]*?\bid\s*=\s*["']([^"']+)["']/gi;
106
+ let m: RegExpExecArray | null;
107
+ while ((m = symbolIdRe.exec(js)) !== null) {
108
+ fullIds.push(m[1]);
109
+ }
110
+ const prefix = findCommonPrefix(fullIds);
111
+ const names = prefix
112
+ ? fullIds.map((id) => id.slice(prefix.length))
113
+ : fullIds.slice();
114
+ return { prefix, names };
115
+ }
116
+
60
117
  /**
61
118
  * 计算字符串数组的最长公共前缀。
62
- *
63
- * iconfont 生成的 class 通常会共享同一个业务前缀,例如 `strong-user`、`strong-home`。
64
- * 将该前缀单独提取出来后,外部使用时只需要关心 `user`、`home` 这类图标名。
65
119
  */
66
120
  function findCommonPrefix(list: string[]): string {
67
121
  if (list.length === 0) return '';
@@ -76,10 +130,7 @@ function findCommonPrefix(list: string[]): string {
76
130
  }
77
131
 
78
132
  /**
79
- * 将 iconfont CSS 注入到页面,确保 @font-face 字体和 class 规则可以被浏览器识别。
80
- *
81
- * 同一个 key 只会注入一次,避免响应式 url 重复触发时创建多份相同 style。
82
- * SSR 环境没有 document,因此会直接跳过注入。
133
+ * 将 iconfont CSS 注入到页面。
83
134
  */
84
135
  function injectCss(cssText: string, key: string) {
85
136
  if (typeof document === 'undefined' || document.getElementById(key)) return;
@@ -91,59 +142,119 @@ function injectCss(cssText: string, key: string) {
91
142
  }
92
143
 
93
144
  /**
94
- * 加载并解析 iconfont CSS 的组合式函数。
95
- *
96
- * @param cssUrl iconfont 的 CSS 地址,支持普通字符串、ref 或 getter。
97
- * @returns iconfont 的基础 class、公共前缀,以及去掉公共前缀后的图标名称列表。
98
- *
99
- * 使用方式:
100
- * ```ts
101
- * const { iconBaseClass, iconPrefix, iconClasses } = useIconfont(cssUrl);
102
- * ```
145
+ * 执行 iconfont `.js`(内部为 Symbol 精灵图脚本)。同一 resolvedUrl 只注入一次;
146
+ * 使用单次 fetch 得到的文本 + Blob URL 执行,避免重复请求。
147
+ */
148
+ function injectIconfontJs(jsText: string, resolvedUrl: string): Promise<void> {
149
+ if (typeof document === 'undefined') return Promise.resolve();
150
+
151
+ const id = `hb-iconfont-js-${stableIdPart(resolvedUrl)}`;
152
+ if (document.getElementById(id)) return Promise.resolve();
153
+
154
+ return new Promise((resolve, reject) => {
155
+ const blob = new Blob([jsText], { type: 'text/javascript' });
156
+ const blobUrl = URL.createObjectURL(blob);
157
+ const script = document.createElement('script');
158
+ script.id = id;
159
+ script.async = true;
160
+ script.onload = () => {
161
+ URL.revokeObjectURL(blobUrl);
162
+ resolve();
163
+ };
164
+ script.onerror = () => {
165
+ URL.revokeObjectURL(blobUrl);
166
+ reject(
167
+ new Error(
168
+ `[HbLibIcon] iconfont JS 脚本执行失败:${resolvedUrl}`
169
+ )
170
+ );
171
+ };
172
+ script.src = blobUrl;
173
+ document.head.appendChild(script);
174
+ });
175
+ }
176
+
177
+ /**
178
+ * 加载并解析 iconfont 资源(CSS 字体或 Symbol JS)的组合式函数。
103
179
  *
104
- * 典型用途:
105
- * - 将 `iconBaseClass` 和 `iconPrefix + name` 组合成真实图标 class。
106
- * - 使用 `iconClasses` 渲染图标选择器的可选项。
107
- * - 当 `cssUrl` 是响应式数据时,地址变化会自动重新加载并解析。
180
+ * @param cssUrl 资源地址:`.css` 为字体图标,`.js` 为 Symbol 彩色 SVG。
108
181
  */
109
- export function useIconfont(cssUrl: MaybeRefOrGetter<string>): UseIconfontResult {
182
+ export function useIconfont(
183
+ cssUrl?: MaybeRefOrGetter<string>
184
+ ): UseIconfontResult {
185
+ if (!cssUrl) {
186
+ cssUrl = globalOptions?.cssUrl || HB_ICON_DEFAULT_CSS_URL;
187
+ }
188
+
110
189
  const iconBaseClass = ref('');
111
190
  const iconPrefix = ref('');
112
191
  const iconClasses = ref<string[]>([]);
192
+ const iconMode = ref<HbLibIconMode>('font');
113
193
  let requestId = 0;
114
194
 
115
195
  const loadIconfont = async (url: string) => {
116
196
  const currentRequestId = ++requestId;
117
- if (!url) {
197
+ const resolvedUrl =
198
+ url || globalOptions?.cssUrl || HB_ICON_DEFAULT_CSS_URL;
199
+
200
+ if (!resolvedUrl) {
118
201
  iconBaseClass.value = '';
119
202
  iconPrefix.value = '';
120
203
  iconClasses.value = [];
204
+ iconMode.value = 'font';
205
+ return;
206
+ }
207
+
208
+ if (isIconfontSymbolJsUrl(resolvedUrl)) {
209
+ try {
210
+ const res = await fetch(resolvedUrl);
211
+ const jsText = await res.text();
212
+ if (currentRequestId !== requestId) return;
213
+
214
+ const { prefix, names } = parseIconfontJs(jsText);
215
+ await injectIconfontJs(jsText, resolvedUrl);
216
+ if (currentRequestId !== requestId) return;
217
+
218
+ iconMode.value = 'symbol';
219
+ iconBaseClass.value = '';
220
+ iconPrefix.value = prefix;
221
+ iconClasses.value = names;
222
+ } catch (err) {
223
+ console.error(
224
+ '[HbLibIcon] 加载 iconfont Symbol 失败:',
225
+ resolvedUrl,
226
+ err
227
+ );
228
+ }
121
229
  return;
122
230
  }
123
231
 
124
232
  try {
125
- const res = await fetch(url);
233
+ const res = await fetch(resolvedUrl);
126
234
  const cssText = await res.text();
127
- injectCss(cssText, 'hb-iconfont-' + url);
235
+ injectCss(cssText, 'hb-iconfont-' + resolvedUrl);
128
236
 
129
237
  const { baseClass, prefix, names } = parseIconfontCss(cssText);
130
- // 避免较早的慢请求覆盖较新的 cssUrl 解析结果。
131
238
  if (currentRequestId !== requestId) return;
132
239
 
240
+ iconMode.value = 'font';
133
241
  iconBaseClass.value = baseClass;
134
242
  iconPrefix.value = prefix;
135
243
  iconClasses.value = names;
136
244
  } catch (err) {
137
- console.error('[HbLibIcon] 加载 iconfont 失败:', url, err);
245
+ console.error(
246
+ '[HbLibIcon] 加载 iconfont CSS 失败:',
247
+ resolvedUrl,
248
+ err
249
+ );
138
250
  }
139
251
  };
140
252
 
141
253
  watch(() => toValue(cssUrl), loadIconfont, { immediate: true });
142
-
143
254
  return {
144
255
  iconBaseClass,
145
256
  iconPrefix,
146
257
  iconClasses,
258
+ iconMode,
147
259
  };
148
260
  }
149
-
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hb-hellotech/hb-ui",
3
- "version": "2.6.0",
3
+ "version": "2.7.1",
4
4
  "type": "module",
5
5
  "main": "dist/hb_component_lib.umd.cjs",
6
6
  "module": "dist/hb_component_lib.js",
@@ -28,7 +28,7 @@
28
28
  ],
29
29
  "scripts": {
30
30
  "dev": "vite --port 3003",
31
- "pub": "npm publish --tag latest --access public --registry=https://registry.npmjs.org && node scripts/notify-dingtalk-changelog.mjs",
31
+ "pub": "npm publish --tag latest --access public --registry=https://registry.npmjs.org ",
32
32
  "pub:notify": "node scripts/notify-dingtalk-changelog.mjs",
33
33
  "build": "vite build",
34
34
  "lint": "eslint ./src/**/*.{ts,vue}",
@@ -1,11 +0,0 @@
1
- import { InjectionKey } from 'vue';
2
- /** 图标库的默认 iconfont CSS 地址 */
3
- export declare const HB_ICON_DEFAULT_CSS_URL = "//at.alicdn.com/t/c/font_2862164_1wnr7wxlgxf.css";
4
- /** 通过 app.use(HbLibIcon, options) 注入的全局配置 */
5
- export interface HbLibIconGlobalOptions {
6
- /** iconfont css 地址 */
7
- cssUrl?: string;
8
- }
9
- /** provide/inject 的 key */
10
- export declare const HB_ICON_GLOBAL_KEY: InjectionKey<HbLibIconGlobalOptions>;
11
- //# sourceMappingURL=constants.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../../src/components/func_comp/hb_func_icon_list/constants.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,KAAK,CAAC;AAExC,6BAA6B;AAC7B,eAAO,MAAM,uBAAuB,qDACkB,CAAC;AAEvD,6CAA6C;AAC7C,MAAM,WAAW,sBAAsB;IACnC,sBAAsB;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,2BAA2B;AAC3B,eAAO,MAAM,kBAAkB,EAAE,YAAY,CAAC,sBAAsB,CAEnE,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"HbLibDetailBox.vue.d.ts","sourceRoot":"","sources":["../../src/demo/HbLibDetailBox.vue"],"names":[],"mappings":";;AA2NA,wBAMG"}
@@ -1,6 +0,0 @@
1
- import { DefineComponent, ComponentOptionsMixin, PublicProps, ComponentProvideOptions } from 'vue';
2
- declare const _default: DefineComponent<{}, {}, {}, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, {}, string, PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, ComponentProvideOptions, true, {
3
- hbEditPageContainer: unknown;
4
- }, HTMLDivElement>;
5
- export default _default;
6
- //# sourceMappingURL=HbUnDetailBox.vue.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"HbUnDetailBox.vue.d.ts","sourceRoot":"","sources":["../../src/demo/HbUnDetailBox.vue"],"names":[],"mappings":";;;;AA8GA,wBAOG"}