@holyer-lib/ui 0.0.8 → 0.0.10

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/ui.cjs.js CHANGED
@@ -14,6 +14,318 @@ function __$styleInject(css) {
14
14
  return css;
15
15
  }
16
16
 
17
+ function __$styleInject$1(css) {
18
+ if (!css) return;
19
+
20
+ if (typeof window == 'undefined') return;
21
+ var style = document.createElement('style');
22
+ style.setAttribute('media', 'screen');
23
+
24
+ style.innerHTML = css;
25
+ document.head.appendChild(style);
26
+ return css;
27
+ }
28
+
29
+ __$styleInject$1(".hi-expand-text {\n display: flex;\n width: 100%;\n line-height: 1.5;\n}\n.hi-expand-text--content {\n display: -webkit-box;\n -webkit-box-orient: vertical;\n overflow: hidden;\n text-overflow: ellipsis;\n word-break: break-word;\n -webkit-line-clamp: var(--hi-expand-text-line-clamp);\n line-clamp: var(--hi-expand-text-line-clamp);\n}\n.hi-expand-text--content.hi-expand-text--content__show-toggle::before {\n content: '';\n float: right;\n height: 100%;\n margin-bottom: calc(-1em * 1.5);\n}\n.hi-expand-text--content__expanded {\n display: block;\n overflow: visible;\n -webkit-line-clamp: unset;\n line-clamp: unset;\n}\n.hi-expand-text--toggle {\n color: var(--td-brand-color);\n float: right;\n clear: both;\n cursor: pointer;\n}\n");
30
+
31
+ //
32
+
33
+ var script$1 = {
34
+ name: 'HiExpandText',
35
+ props: {
36
+ content: {
37
+ type: String,
38
+ default: ''
39
+ },
40
+
41
+ /**
42
+ * [0]: 展开时显示的文本,[1]: 收起时显示的文本
43
+ */
44
+ label: {
45
+ type: Array,
46
+ default: () => ['展开', '收起'],
47
+ validator: arr => {
48
+ return arr.length === 2 && arr.every(s => typeof s === 'string');
49
+ }
50
+ },
51
+
52
+ lineClamp: {
53
+ type: Number,
54
+ default: 2
55
+ }
56
+ },
57
+ data() {
58
+ return {
59
+ showToggle: false,
60
+ isExpanded: false,
61
+ resizeTimer: null,
62
+ resizeObserver: null
63
+ };
64
+ },
65
+ computed: {
66
+ expandText() {
67
+ return this.isExpanded ? this.label[1] : this.label[0];
68
+ },
69
+
70
+ textClass() {
71
+ return {
72
+ 'hi-expand-text--content': true,
73
+ 'hi-expand-text--content__show-toggle': this.showToggle,
74
+ 'hi-expand-text--content__expanded': this.isExpanded
75
+ };
76
+ }
77
+ },
78
+
79
+ watch: {
80
+ content() {
81
+ this.$nextTick(this.checkEllipsis);
82
+ }
83
+ },
84
+ mounted() {
85
+ this.checkEllipsis();
86
+ window.addEventListener('resize', this.handleResize);
87
+ if (window.ResizeObserver && this.$el) {
88
+ this.resizeObserver = new ResizeObserver(() => {
89
+ this.handleResize();
90
+ });
91
+ this.resizeObserver.observe(this.$el);
92
+ }
93
+ },
94
+ beforeDestroy() {
95
+ window.removeEventListener('resize', this.handleResize);
96
+ if (this.resizeObserver) this.resizeObserver.disconnect();
97
+ if (this.resizeTimer) clearTimeout(this.resizeTimer);
98
+ },
99
+
100
+ methods: {
101
+ handleResize() {
102
+ if (this.resizeTimer) clearTimeout(this.resizeTimer);
103
+ this.resizeTimer = setTimeout(() => {
104
+ this.checkEllipsis();
105
+ }, 100);
106
+ },
107
+
108
+ /**
109
+ * @Description 检查是否存在溢出情况,兼容展开和收起两种状态
110
+ * @Author holyer
111
+ * @Date 2026/02/08 17:01:51
112
+ */
113
+ checkEllipsis() {
114
+ const textEl = this.$refs.textRef;
115
+ if (!textEl || !textEl.offsetParent) {
116
+ this.showToggle = false;
117
+ return;
118
+ }
119
+
120
+ if (this.isExpanded) {
121
+ // 展开状态下:模拟收起状态,检测是否需要 toggle
122
+ this.showToggle = this.wouldOverflowIfCollapsed(textEl);
123
+ } else {
124
+ // 收起状态下:直接检测是否溢出
125
+ this.showToggle = textEl.scrollHeight - textEl.clientHeight > 2;
126
+ }
127
+ },
128
+
129
+ /**
130
+ * 模拟收起状态,检测内容是否会溢出
131
+ */
132
+ wouldOverflowIfCollapsed(el) {
133
+ // 1. 保存原始状态
134
+ const originalDisplay = el.style.display;
135
+ const originalWebkitLineClamp = el.style.webkitLineClamp;
136
+ const originalClassList = el.className;
137
+
138
+ try {
139
+ // 2. 临时应用“收起”样式,移除 --expanded 和 --show-toggle
140
+ el.className = 'hi-expand-text--content';
141
+ el.style.display = '-webkit-box';
142
+ el.style.webkitBoxOrient = 'vertical';
143
+ el.style.overflow = 'hidden';
144
+ el.style.webkitLineClamp = this.lineClamp;
145
+
146
+ // 3. 强制 reflow(触发 layout)
147
+ const { scrollHeight, clientHeight } = el;
148
+
149
+ // 4. 判断是否溢出
150
+ return scrollHeight - clientHeight > 2;
151
+ } finally {
152
+ // 5. 恢复原始状态(确保无副作用)
153
+ el.className = originalClassList;
154
+ el.style.display = originalDisplay;
155
+ el.style.webkitLineClamp = originalWebkitLineClamp;
156
+ }
157
+ },
158
+
159
+ handleToggle() {
160
+ this.isExpanded = !this.isExpanded;
161
+ this.$emit('toggle', this.isExpanded);
162
+ },
163
+
164
+ // 供外部手动更新
165
+ update() {
166
+ this.$nextTick(this.checkEllipsis);
167
+ }
168
+ }
169
+ };
170
+
171
+ function normalizeComponent$1(template, style, script, scopeId, isFunctionalTemplate, moduleIdentifier /* server only */, shadowMode, createInjector, createInjectorSSR, createInjectorShadow) {
172
+ if (typeof shadowMode !== 'boolean') {
173
+ createInjectorSSR = createInjector;
174
+ createInjector = shadowMode;
175
+ shadowMode = false;
176
+ }
177
+ // Vue.extend constructor export interop.
178
+ const options = typeof script === 'function' ? script.options : script;
179
+ // render functions
180
+ if (template && template.render) {
181
+ options.render = template.render;
182
+ options.staticRenderFns = template.staticRenderFns;
183
+ options._compiled = true;
184
+ // functional template
185
+ if (isFunctionalTemplate) {
186
+ options.functional = true;
187
+ }
188
+ }
189
+ // scopedId
190
+ if (scopeId) {
191
+ options._scopeId = scopeId;
192
+ }
193
+ let hook;
194
+ if (moduleIdentifier) {
195
+ // server build
196
+ hook = function (context) {
197
+ // 2.3 injection
198
+ context =
199
+ context || // cached call
200
+ (this.$vnode && this.$vnode.ssrContext) || // stateful
201
+ (this.parent && this.parent.$vnode && this.parent.$vnode.ssrContext); // functional
202
+ // 2.2 with runInNewContext: true
203
+ if (!context && typeof __VUE_SSR_CONTEXT__ !== 'undefined') {
204
+ context = __VUE_SSR_CONTEXT__;
205
+ }
206
+ // inject component styles
207
+ if (style) {
208
+ style.call(this, createInjectorSSR(context));
209
+ }
210
+ // register component module identifier for async chunk inference
211
+ if (context && context._registeredComponents) {
212
+ context._registeredComponents.add(moduleIdentifier);
213
+ }
214
+ };
215
+ // used by ssr in case component is cached and beforeCreate
216
+ // never gets called
217
+ options._ssrRegister = hook;
218
+ }
219
+ else if (style) {
220
+ hook = shadowMode
221
+ ? function (context) {
222
+ style.call(this, createInjectorShadow(context, this.$root.$options.shadowRoot));
223
+ }
224
+ : function (context) {
225
+ style.call(this, createInjector(context));
226
+ };
227
+ }
228
+ if (hook) {
229
+ if (options.functional) {
230
+ // register for functional component in vue file
231
+ const originalRender = options.render;
232
+ options.render = function renderWithStyleInjection(h, context) {
233
+ hook.call(context);
234
+ return originalRender(h, context);
235
+ };
236
+ }
237
+ else {
238
+ // inject component registration as beforeCreate hook
239
+ const existing = options.beforeCreate;
240
+ options.beforeCreate = existing ? [].concat(existing, hook) : [hook];
241
+ }
242
+ }
243
+ return script;
244
+ }
245
+
246
+ /* script */
247
+ const __vue_script__$1 = script$1;
248
+
249
+ /* template */
250
+ var __vue_render__$1 = function () {
251
+ var _vm = this;
252
+ var _h = _vm.$createElement;
253
+ var _c = _vm._self._c || _h;
254
+ return _c(
255
+ "div",
256
+ {
257
+ staticClass: "hi-expand-text",
258
+ style: { "--hi-expand-text-line-clamp": _vm.lineClamp },
259
+ },
260
+ [
261
+ _c(
262
+ "div",
263
+ { ref: "textRef", class: _vm.textClass },
264
+ [
265
+ _vm.showToggle
266
+ ? _c(
267
+ "div",
268
+ {
269
+ staticClass: "hi-expand-text--toggle",
270
+ on: { click: _vm.handleToggle },
271
+ },
272
+ [
273
+ _vm._t("toggleText", function () {
274
+ return [_vm._v(_vm._s(_vm.expandText))]
275
+ }),
276
+ ],
277
+ 2
278
+ )
279
+ : _vm._e(),
280
+ _vm._v(" "),
281
+ _vm._t("default", function () {
282
+ return [_vm._v(_vm._s(_vm.content))]
283
+ }),
284
+ ],
285
+ 2
286
+ ),
287
+ ]
288
+ )
289
+ };
290
+ var __vue_staticRenderFns__$1 = [];
291
+ __vue_render__$1._withStripped = true;
292
+
293
+ /* style */
294
+ const __vue_inject_styles__$1 = undefined;
295
+ /* scoped */
296
+ const __vue_scope_id__$1 = undefined;
297
+ /* module identifier */
298
+ const __vue_module_identifier__$1 = undefined;
299
+ /* functional template */
300
+ const __vue_is_functional_template__$1 = false;
301
+ /* style inject */
302
+
303
+ /* style inject SSR */
304
+
305
+ /* style inject shadow dom */
306
+
307
+
308
+
309
+ const __vue_component__$1 = /*#__PURE__*/normalizeComponent$1(
310
+ { render: __vue_render__$1, staticRenderFns: __vue_staticRenderFns__$1 },
311
+ __vue_inject_styles__$1,
312
+ __vue_script__$1,
313
+ __vue_scope_id__$1,
314
+ __vue_is_functional_template__$1,
315
+ __vue_module_identifier__$1,
316
+ false,
317
+ undefined,
318
+ undefined,
319
+ undefined
320
+ );
321
+
322
+ __vue_component__$1.name = 'HiExpandText';
323
+
324
+ // 添加 install
325
+ __vue_component__$1.install = Vue => {
326
+ Vue.component(__vue_component__$1.name, __vue_component__$1);
327
+ };
328
+
17
329
  function __$styleInject(css) {
18
330
  if (!css) return;
19
331
 
@@ -26,19 +338,90 @@ function __$styleInject(css) {
26
338
  return css;
27
339
  }
28
340
 
29
- __$styleInject(".hi-title {\n font-size: 24px;\n font-weight: bold;\n margin: 16px 0;\n border-left: 4px solid var(--td-brand-color);\n padding-left: 12px;\n}\n");
341
+ __$styleInject(".hi-title {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n.hi-title--small {\n gap: 2px;\n}\n.hi-title--large {\n gap: 6px;\n}\n.hi-title__header {\n display: flex;\n align-items: center;\n gap: 12px;\n}\n.hi-title__prefix {\n flex-shrink: 0;\n display: flex;\n align-items: center;\n}\n.hi-title__bar {\n width: 4px;\n background-color: var(--td-brand-color);\n flex-shrink: 0;\n}\n.hi-title__icon {\n flex-shrink: 0;\n line-height: 1;\n}\n.hi-title__text {\n margin: 0;\n font-weight: 600;\n color: var(--td-text-color-primary);\n}\n.hi-title__description {\n margin: 0;\n font-size: 12px;\n color: var(--td-text-color-secondary);\n}\n");
30
342
 
31
343
  //
344
+ const TITILE_SIZE_MAP = {
345
+ small: '14px',
346
+ medium: '16px',
347
+ large: '18px'
348
+ };
32
349
  var script = {
33
350
  name: 'HiTitle',
351
+ inheritAttrs: false,
34
352
  props: {
353
+ // 自定义图标类名
354
+ iconClass: {
355
+ type: String,
356
+ default: ''
357
+ },
358
+ // 主标题文本(优先级低于 default slot)
35
359
  content: {
36
360
  type: String,
37
- default: '标题'
361
+ default: ''
362
+ },
363
+ // 描述文本(优先级低于 description slot)
364
+ description: {
365
+ type: String,
366
+ default: ''
367
+ },
368
+ // 尺寸:控制整体大小(影响文字、图标、bar 高度)
369
+ size: {
370
+ type: String,
371
+ default: 'medium',
372
+ validator: val => ['small', 'medium', 'large'].includes(val)
373
+ },
374
+ // 主标题颜色(支持自定义)
375
+ color: {
376
+ type: String,
377
+ default: ''
378
+ },
379
+ // 自定义前缀图标组件
380
+ prefixIcon: {
381
+ type: [Object, Function],
382
+ default: null
383
+ },
384
+ // 自定义 bar 类名(用于覆盖样式)
385
+ barClass: {
386
+ type: String,
387
+ default: ''
388
+ },
389
+ // 自定义标题文字类名
390
+ textClass: {
391
+ type: String,
392
+ default: ''
393
+ },
394
+ // 自定义描述文字类名
395
+ descClass: {
396
+ type: String,
397
+ default: ''
38
398
  }
39
399
  },
40
- data() {
41
- return {};
400
+ computed: {
401
+ titleClass() {
402
+ return ['hi-title', `hi-title--${this.size}`, { 'hi-title--has-desc': this.hasDescription }];
403
+ },
404
+ // 根据 size 计算文字大小
405
+ textSize() {
406
+ return TITILE_SIZE_MAP[this.size] || TITILE_SIZE_MAP.medium;
407
+ },
408
+ // 图标大小 = 文字大小(保持视觉一致)
409
+ iconSize() {
410
+ return this.textSize;
411
+ },
412
+ // 装饰条高度 = 文字行高 ≈ 文字大小 * 1.2~1.5
413
+ barHeight() {
414
+ const base = parseFloat(TITILE_SIZE_MAP[this.size] || '16');
415
+ return `${base * 1.2}px`;
416
+ },
417
+ // 主标题颜色(props 优先,否则继承)
418
+ textColor() {
419
+ return this.color || 'inherit';
420
+ },
421
+ // 是否存在描述内容(用于控制布局)
422
+ hasDescription() {
423
+ return this.description || this.$slots.description;
424
+ }
42
425
  }
43
426
  };
44
427
 
@@ -127,13 +510,64 @@ var __vue_render__ = function () {
127
510
  var _c = _vm._self._c || _h;
128
511
  return _c(
129
512
  "div",
130
- { staticClass: "hi-title" },
513
+ _vm._b({ class: _vm.titleClass }, "div", _vm.$attrs, false),
131
514
  [
132
- _vm._t("default", function () {
133
- return [_vm._v(_vm._s(_vm.content))]
134
- }),
135
- ],
136
- 2
515
+ _c("div", { staticClass: "hi-title__header" }, [
516
+ _c(
517
+ "div",
518
+ { staticClass: "hi-title__prefix" },
519
+ [
520
+ _vm._t("prefix", function () {
521
+ return [
522
+ _vm.prefixIcon
523
+ ? _c(_vm.prefixIcon, {
524
+ tag: "component",
525
+ class: ["hi-title__icon", _vm.iconClass],
526
+ style: { fontSize: _vm.iconSize },
527
+ })
528
+ : _c("div", {
529
+ class: ["hi-title__bar", _vm.barClass],
530
+ style: { height: _vm.barHeight },
531
+ }),
532
+ ]
533
+ }),
534
+ ],
535
+ 2
536
+ ),
537
+ _vm._v(" "),
538
+ _vm.content || _vm.$slots.default
539
+ ? _c(
540
+ "div",
541
+ {
542
+ class: ["hi-title__text", _vm.textClass],
543
+ style: {
544
+ color: _vm.textColor,
545
+ fontSize: _vm.textSize,
546
+ },
547
+ },
548
+ [
549
+ _vm._t("default", function () {
550
+ return [_vm._v(_vm._s(_vm.content))]
551
+ }),
552
+ ],
553
+ 2
554
+ )
555
+ : _vm._e(),
556
+ ]),
557
+ _vm._v(" "),
558
+ _vm.hasDescription
559
+ ? _c(
560
+ "p",
561
+ { class: ["hi-title__description", _vm.descClass] },
562
+ [
563
+ _vm._t("description", function () {
564
+ return [_vm._v(_vm._s(_vm.description))]
565
+ }),
566
+ ],
567
+ 2
568
+ )
569
+ : _vm._e(),
570
+ ]
137
571
  )
138
572
  };
139
573
  var __vue_staticRenderFns__ = [];
@@ -172,11 +606,15 @@ __vue_render__._withStripped = true;
172
606
  __vue_component__.name = 'HiTitle';
173
607
 
174
608
  // 添加 install
175
- __vue_component__.install = (Vue) => {
609
+ __vue_component__.install = Vue => {
176
610
  Vue.component(__vue_component__.name, __vue_component__);
177
611
  };
178
612
 
179
- const components = [__vue_component__];
613
+ // eslint-disable-next-line prettier/prettier
614
+ const components = [
615
+ __vue_component__$1,
616
+ __vue_component__
617
+ ];
180
618
 
181
619
  const install = function (Vue) {
182
620
  components.forEach(component => {
@@ -184,6 +622,10 @@ const install = function (Vue) {
184
622
  });
185
623
  };
186
624
 
187
- var index = { install, HiTitle: __vue_component__ };
625
+ var index = {
626
+ install,
627
+ HiExpandText: __vue_component__$1,
628
+ HiTitle: __vue_component__
629
+ };
188
630
 
189
631
  module.exports = index;