one-design-next 0.0.67 → 0.0.68

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.
@@ -1,10 +1,10 @@
1
1
  import _toConsumableArray from "@babel/runtime/helpers/esm/toConsumableArray";
2
2
  import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
3
- import { useState, useRef, useEffect, useLayoutEffect, useMemo } from 'react';
4
- import HoverFill from "../hover-fill";
5
- import Icon from "../icon";
3
+ import { useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
6
4
  import Dialog from "../dialog";
7
5
  import Dropdown from "../dropdown";
6
+ import HoverFill from "../hover-fill";
7
+ import Icon from "../icon";
8
8
  import { formatRelativeTime } from "./relative-time";
9
9
  import "./style";
10
10
  var STATUS_ARIA_LABEL = {
@@ -15,9 +15,15 @@ var STATUS_ARIA_LABEL = {
15
15
  };
16
16
  var ELLIPSIS = '…';
17
17
 
18
+ /** 可用宽度变化小于该值时跳过重算(与二分容差同量级) */
19
+ var FIT_WIDTH_EPS = 0.5;
20
+
18
21
  /**
19
22
  * 有 trailing(客服 icon / status / 时间)时,用 DOM 实测把标题裁到可用宽度。
20
23
  * 解决 CSS text-overflow 按字符边界截断后,省略号右侧留白把 trailing 顶开的问题。
24
+ *
25
+ * 性能:只监听整行(btn)宽度;RO 回调经 rAF 合并(每行每帧最多 fit 一次);
26
+ * 缓存 maxWidth / trailingWidth / 裁切结果,宽度几乎不变或结果不变则跳过更新。
21
27
  */
22
28
  function useFittedTitle(title, enabled, labelRef, titleRef, /** 右对齐区(状态 / 时间),量宽预留 */
23
29
  trailingRef, /** 紧跟标题的类型 icon,量宽预留 */
@@ -27,7 +33,7 @@ typeIconRef) {
27
33
  fitted = _useState2[0],
28
34
  setFitted = _useState2[1];
29
35
  useLayoutEffect(function () {
30
- var _document$fonts;
36
+ var _document$fonts, _label$closest;
31
37
  if (!enabled) {
32
38
  setFitted(title);
33
39
  return;
@@ -36,6 +42,10 @@ typeIconRef) {
36
42
  var titleEl = titleRef.current;
37
43
  if (!label || !titleEl) return;
38
44
  var cancelled = false;
45
+ var rafId = 0;
46
+ var lastMaxWidth = Number.NaN;
47
+ var lastTrailingWidth = Number.NaN;
48
+ var lastFitted = '';
39
49
  var probe = document.createElement('span');
40
50
  probe.setAttribute('aria-hidden', 'true');
41
51
  probe.style.cssText = 'position:absolute;visibility:hidden;pointer-events:none;white-space:nowrap;left:-9999px;top:0;';
@@ -67,39 +77,53 @@ typeIconRef) {
67
77
  // 标题两侧可能各有一段 gap:title↔type、type/title↔trailing
68
78
  var gapCount = (typeWidth > 0 ? 1 : 0) + (trailingWidth > 0 ? 1 : 0);
69
79
  var maxWidth = Math.max(0, label.getBoundingClientRect().width - trailingWidth - typeWidth - gap * gapCount);
70
- if (measure(title) <= maxWidth + 0.5) {
71
- setFitted(title);
80
+ if (Number.isFinite(lastMaxWidth) && Math.abs(maxWidth - lastMaxWidth) < FIT_WIDTH_EPS && Math.abs(trailingWidth - lastTrailingWidth) < FIT_WIDTH_EPS) {
72
81
  return;
73
82
  }
74
- var lo = 0;
75
- var hi = title.length;
76
- var best = ELLIPSIS;
77
- while (lo <= hi) {
78
- var mid = lo + hi >> 1;
79
- var candidate = "".concat(title.slice(0, mid)).concat(ELLIPSIS);
80
- if (measure(candidate) <= maxWidth + 0.5) {
81
- best = candidate;
82
- lo = mid + 1;
83
- } else {
84
- hi = mid - 1;
83
+ lastMaxWidth = maxWidth;
84
+ lastTrailingWidth = trailingWidth;
85
+ var next = title;
86
+ if (measure(title) > maxWidth + FIT_WIDTH_EPS) {
87
+ var lo = 0;
88
+ var hi = title.length;
89
+ next = ELLIPSIS;
90
+ while (lo <= hi) {
91
+ var mid = lo + hi >> 1;
92
+ var candidate = "".concat(title.slice(0, mid)).concat(ELLIPSIS);
93
+ if (measure(candidate) <= maxWidth + FIT_WIDTH_EPS) {
94
+ next = candidate;
95
+ lo = mid + 1;
96
+ } else {
97
+ hi = mid - 1;
98
+ }
85
99
  }
86
100
  }
87
- setFitted(best);
101
+ if (next === lastFitted) return;
102
+ lastFitted = next;
103
+ setFitted(next);
104
+ };
105
+ var scheduleFit = function scheduleFit() {
106
+ if (cancelled || rafId) return;
107
+ rafId = window.requestAnimationFrame(function () {
108
+ rafId = 0;
109
+ fit();
110
+ });
88
111
  };
112
+
113
+ // 首屏同步裁切,避免首帧闪全标题;后续宽度变化走 rAF 合并
89
114
  fit();
90
115
  void ((_document$fonts = document.fonts) === null || _document$fonts === void 0 ? void 0 : _document$fonts.ready.then(function () {
91
- if (!cancelled) fit();
116
+ if (cancelled) return;
117
+ // 字体就绪后字形宽度可能变,需绕过 maxWidth 缓存强制重测
118
+ lastMaxWidth = Number.NaN;
119
+ scheduleFit();
92
120
  }));
93
- var ro = new ResizeObserver(fit);
94
- ro.observe(label);
95
- var trailing = trailingRef.current;
96
- if (trailing) ro.observe(trailing);
97
- var typeIcon = typeIconRef.current;
98
- if (typeIcon) ro.observe(typeIcon);
99
- var btn = label.closest('[data-odn-chat-item-btn]');
100
- if (btn) ro.observe(btn);
121
+ var row = (_label$closest = label.closest('[data-odn-chat-item-btn]')) !== null && _label$closest !== void 0 ? _label$closest : label;
122
+ var ro = new ResizeObserver(scheduleFit);
123
+ ro.observe(row);
101
124
  return function () {
102
125
  cancelled = true;
126
+ if (rafId) window.cancelAnimationFrame(rafId);
103
127
  ro.disconnect();
104
128
  probe.remove();
105
129
  };
@@ -219,7 +243,7 @@ export function ChatItem(_ref2) {
219
243
  var showStatus = Boolean(status && (!active || status === 'generating' || status === 'waiting'));
220
244
  var showTypeIcon = type === 'human';
221
245
  var timeLabel = useMemo(function () {
222
- return time == null ? '' : formatRelativeTime(time, now);
246
+ return time === null || time === undefined ? '' : formatRelativeTime(time, now);
223
247
  }, [time, now]);
224
248
  var showTime = Boolean(timeLabel);
225
249
  var showTrailing = showStatus || showTime;
@@ -240,7 +264,7 @@ export function ChatItem(_ref2) {
240
264
 
241
265
  // 相对时间每分钟刷新一次(有 time 时)
242
266
  useEffect(function () {
243
- if (time == null) return;
267
+ if (time === null || time === undefined) return;
244
268
  var id = window.setInterval(function () {
245
269
  return setNow(Date.now());
246
270
  }, 60000);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "one-design-next",
3
- "version": "0.0.67",
3
+ "version": "0.0.68",
4
4
  "description": "One Design Next from TAD@tencent.com",
5
5
  "license": "MIT",
6
6
  "type": "module",