@alifd/chat 0.3.38 → 0.3.40-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.
@@ -98,7 +98,7 @@ const HTMLRenderer = memo(function HTMLRenderer({ className, children, imagePrev
98
98
  })));
99
99
  };
100
100
  // 已替换
101
- const parserOptions = {
101
+ const parserOptionsOld = {
102
102
  // @ts-ignore
103
103
  replace: domNode => {
104
104
  var _a, _b, _c, _d, _e, _f, _g;
@@ -192,6 +192,99 @@ const HTMLRenderer = memo(function HTMLRenderer({ className, children, imagePrev
192
192
  };
193
193
  const element = useMemo(() => {
194
194
  indexRef.current = 0;
195
+ // 已替换
196
+ const parserOptions = {
197
+ // @ts-ignore
198
+ replace: domNode => {
199
+ var _a, _b, _c, _d, _e, _f, _g;
200
+ // 处理文本节点
201
+ if ((domNode.type === 'text' || domNode.nodeType === 3) && typewriterEffect) {
202
+ return processTextNode(domNode);
203
+ }
204
+ if (domNode instanceof Element && domNode.attribs) {
205
+ const { name } = domNode;
206
+ if (name === 'link-reference') {
207
+ const element = (domToReact([domNode]));
208
+ // link-reference 的子元素一定是 a 标签
209
+ const aElement = (_a = element.props) === null || _a === void 0 ? void 0 : _a.children;
210
+ // a 标签的子元素一定是 span 节点
211
+ const spanElement = (_b = aElement === null || aElement === void 0 ? void 0 : aElement.props) === null || _b === void 0 ? void 0 : _b.children;
212
+ const handleUrlClick = () => {
213
+ var _a;
214
+ if (!((_a = aElement === null || aElement === void 0 ? void 0 : aElement.props) === null || _a === void 0 ? void 0 : _a.href)) {
215
+ return;
216
+ }
217
+ if (handleOpenLink) {
218
+ handleOpenLink === null || handleOpenLink === void 0 ? void 0 : handleOpenLink(aElement.props.href);
219
+ }
220
+ else {
221
+ defaultOpenLink(aElement.props.href);
222
+ }
223
+ };
224
+ return (React.createElement(Balloon, { v2: true, align: "b", className: "link-reference-balloon", closable: false, offset: [0, -8], triggerType: ['hover'], trigger: React.createElement("span", { className: "link-reference-index", onClick: handleUrlClick }, (_c = spanElement === null || spanElement === void 0 ? void 0 : spanElement.props) === null || _c === void 0 ? void 0 : _c.children) },
225
+ React.createElement("div", { className: "link-reference-content" },
226
+ React.createElement("img", { className: "link-reference-source-icon", src: ((_d = element.props) === null || _d === void 0 ? void 0 : _d['data-source-icon']) || '' }),
227
+ React.createElement("a", { className: "link-reference-title", onClick: handleUrlClick, title: ((_e = element.props) === null || _e === void 0 ? void 0 : _e['data-title']) || '' }, ((_f = element.props) === null || _f === void 0 ? void 0 : _f['data-title']) || ''))));
228
+ }
229
+ if (name === 'a') {
230
+ const element = (domToReact([domNode]));
231
+ const { props } = element;
232
+ if (props.href && props.href.startsWith('message://')) {
233
+ const msgValue = decodeURIComponent(props.href.slice(10));
234
+ return React.cloneElement(element, {
235
+ onClick: () => {
236
+ if (sendTextMessage) {
237
+ sendTextMessage(msgValue);
238
+ return;
239
+ }
240
+ defaultSendMessage();
241
+ },
242
+ href: undefined
243
+ });
244
+ }
245
+ if (props.href && props.href.startsWith('copy://')) {
246
+ const copyValue = decodeURIComponent(props.href.slice(7));
247
+ return React.cloneElement(element, {
248
+ onClick: () => {
249
+ if (copyText) {
250
+ copyText(copyValue);
251
+ return;
252
+ }
253
+ defaultCopyText(copyValue);
254
+ },
255
+ href: undefined
256
+ });
257
+ }
258
+ // H5场景和钉协议场景都需要代理Click事件
259
+ if (props.href && (/^dtmd:\/\/dingtalkclient|^dingtalk:\/\/dingtalkclient\/action\/jumprobot|^(https?:)?\/\/qr.dingtalk.com\/action\/jumprobot|^https:\/\/applink\.dingtalk\.com\/page\/link/.test(props.href) || isMobile)) {
260
+ return React.cloneElement(element, {
261
+ onClick: () => {
262
+ if (handleOpenLink) {
263
+ handleOpenLink === null || handleOpenLink === void 0 ? void 0 : handleOpenLink(props.href);
264
+ return;
265
+ }
266
+ defaultOpenLink(props.href);
267
+ },
268
+ href: undefined
269
+ });
270
+ }
271
+ return React.cloneElement(element, {
272
+ target: '_blank',
273
+ });
274
+ }
275
+ if (name === 'img') {
276
+ const element = (domToReact([domNode]));
277
+ if (renderImage) {
278
+ return renderImage(element.props);
279
+ }
280
+ // 换成统一的图片渲染
281
+ return React.createElement(Img, Object.assign({}, element.props, { imageClassName: (_g = element.props) === null || _g === void 0 ? void 0 : _g.className, enablePreview: imagePreview, onImageClick: () => {
282
+ handleImageClick === null || handleImageClick === void 0 ? void 0 : handleImageClick(element.props.src);
283
+ } }));
284
+ }
285
+ }
286
+ }
287
+ };
195
288
  return parse(sanitizeHtml(children || '', sanitizeHtmlOptions), parserOptions);
196
289
  }, [children, typewriterEffect]);
197
290
  return (React.createElement("div", { className: classnames(`markdown-body ${PREFIX_DEFAULT}html`, className, {
@@ -211,16 +211,15 @@
211
211
  }
212
212
 
213
213
  li>p {
214
- margin: 0.3rem 0;
214
+ line-height: 2.2;
215
215
  }
216
216
 
217
217
  p {
218
- margin: 0.3rem 0;
219
- line-height: 1.8;
218
+ line-height: 2.2;
220
219
  }
221
220
 
222
221
  span {
223
- line-height: 2;
222
+ line-height: 2.2;
224
223
  }
225
224
 
226
225
  blockquote {
package/es/index.js CHANGED
@@ -32,4 +32,4 @@ export { default as CheckboxGroup } from './checkbox-group';
32
32
  export { default as Select } from './select';
33
33
  export { default as Flip } from './flip';
34
34
  export { default as ToolStatus } from './tool-status';
35
- export const version = '0.3.38';
35
+ export const version = '0.3.40-beta.0';
package/es/text/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import React, { useRef, useLayoutEffect, useCallback, useEffect, useState, useMemo, } from 'react';
1
+ import React, { useRef, useLayoutEffect, useCallback, useEffect, useState, } from 'react';
2
2
  import { ConfigProvider } from '@alifd/next';
3
3
  import cs from 'classnames';
4
4
  import { PREFIX_DEFAULT, useControlable, useDebounce } from '../utils';
@@ -24,13 +24,19 @@ function useCollapseText({ enable, boxRef, textRef, maxLine, placeholder = '...'
24
24
  if (lineHeight === 'normal') {
25
25
  // 向容器里添加一段文本来测试行高
26
26
  const span = document.createElement('span');
27
- span.style.whiteSpace = 'nowrap';
28
- span.innerText = 'test';
29
- span.style.visibility = 'hidden';
30
- dom.appendChild(span);
31
- const height = span.offsetHeight;
32
- dom.removeChild(span);
33
- return height;
27
+ try {
28
+ span.style.whiteSpace = 'nowrap';
29
+ span.innerText = 'test';
30
+ span.style.visibility = 'hidden';
31
+ dom.appendChild(span);
32
+ return span.offsetHeight;
33
+ }
34
+ finally {
35
+ // 确保无论是否发生错误,都会尝试移除子节点
36
+ if (span.parentNode === dom) {
37
+ dom.removeChild(span);
38
+ }
39
+ }
34
40
  }
35
41
  return parseFloat(lineHeight);
36
42
  }, []);
@@ -183,16 +189,22 @@ function useEnsureBoxFullLine({ enableRef, boxRef, modifierRef, before, after, }
183
189
  ensureTriggerEnd(box, modifier);
184
190
  });
185
191
  }
192
+ // 修改 useResize Hook
186
193
  function useResize(domRef, onResize, shouldTriggerRef) {
187
194
  const sizeRef = useRef();
188
195
  const debounceOnResize = useDebounce(onResize, 50);
189
- const observer = useMemo(() => {
190
- return new ResizeObserver(() => {
191
- const dom = domRef.current;
192
- if (!dom || !shouldTriggerRef.current) {
196
+ // observer 不再使用 useMemo,而是在 useEffect 中创建和销毁
197
+ useEffect(() => {
198
+ const dom = domRef.current;
199
+ if (!dom) {
200
+ return;
201
+ }
202
+ const observer = new ResizeObserver(() => {
203
+ // 回调函数逻辑不变
204
+ if (!domRef.current || !shouldTriggerRef.current) {
193
205
  return;
194
206
  }
195
- const { offsetWidth, offsetHeight } = dom;
207
+ const { offsetWidth, offsetHeight } = domRef.current;
196
208
  const lastSize = sizeRef.current;
197
209
  const newSize = { width: offsetWidth, height: offsetHeight };
198
210
  if (!lastSize ||
@@ -202,18 +214,51 @@ function useResize(domRef, onResize, shouldTriggerRef) {
202
214
  }
203
215
  sizeRef.current = newSize;
204
216
  });
205
- }, []);
206
- useEffect(() => {
207
- const dom = domRef.current;
208
- if (!dom) {
209
- return;
210
- }
211
217
  observer.observe(dom);
218
+ // 清理函数现在负责销毁 observer 实例
212
219
  return () => {
213
- observer.unobserve(dom);
220
+ // unobserve 不是必须的,因为 disconnect 会停止所有观察
221
+ observer.disconnect();
214
222
  };
215
- }, [domRef.current, observer]);
223
+ }, [domRef, debounceOnResize, shouldTriggerRef]); // 依赖项也需要更新
216
224
  }
225
+ // function useResizeOld(
226
+ // domRef: RefObject<HTMLElement>,
227
+ // onResize: () => void,
228
+ // shouldTriggerRef: MutableRefObject<boolean>
229
+ // ) {
230
+ // const sizeRef = useRef<{ width: number; height: number }>();
231
+ // const debounceOnResize = useDebounce(onResize, 50);
232
+ // const observer = useMemo(() => {
233
+ // return new ResizeObserver(() => {
234
+ // const dom = domRef.current;
235
+ // if (!dom || !shouldTriggerRef.current) {
236
+ // return;
237
+ // }
238
+ // const { offsetWidth, offsetHeight } = dom;
239
+ // const lastSize = sizeRef.current;
240
+ // const newSize = { width: offsetWidth, height: offsetHeight };
241
+ // if (
242
+ // !lastSize ||
243
+ // newSize.width !== lastSize.width ||
244
+ // newSize.height !== lastSize.height
245
+ // ) {
246
+ // debounceOnResize();
247
+ // }
248
+ // sizeRef.current = newSize;
249
+ // });
250
+ // }, []);
251
+ // useEffect(() => {
252
+ // const dom = domRef.current;
253
+ // if (!dom) {
254
+ // return;
255
+ // }
256
+ // observer?.observe(dom);
257
+ // return () => {
258
+ // observer?.unobserve(dom);
259
+ // };
260
+ // }, [domRef.current, observer]);
261
+ // }
217
262
  function TextComp(props) {
218
263
  const { className, maxLine, renderTrigger, triggerType = 'click', locale, showTrigger = true, children, } = props;
219
264
  const collapsable = typeof maxLine === 'number' && maxLine > 0;
@@ -101,7 +101,7 @@ const HTMLRenderer = (0, react_1.memo)(function HTMLRenderer({ className, childr
101
101
  })));
102
102
  };
103
103
  // 已替换
104
- const parserOptions = {
104
+ const parserOptionsOld = {
105
105
  // @ts-ignore
106
106
  replace: domNode => {
107
107
  var _a, _b, _c, _d, _e, _f, _g;
@@ -195,6 +195,99 @@ const HTMLRenderer = (0, react_1.memo)(function HTMLRenderer({ className, childr
195
195
  };
196
196
  const element = (0, react_1.useMemo)(() => {
197
197
  indexRef.current = 0;
198
+ // 已替换
199
+ const parserOptions = {
200
+ // @ts-ignore
201
+ replace: domNode => {
202
+ var _a, _b, _c, _d, _e, _f, _g;
203
+ // 处理文本节点
204
+ if ((domNode.type === 'text' || domNode.nodeType === 3) && typewriterEffect) {
205
+ return processTextNode(domNode);
206
+ }
207
+ if (domNode instanceof html_react_parser_1.Element && domNode.attribs) {
208
+ const { name } = domNode;
209
+ if (name === 'link-reference') {
210
+ const element = ((0, html_react_parser_1.domToReact)([domNode]));
211
+ // link-reference 的子元素一定是 a 标签
212
+ const aElement = (_a = element.props) === null || _a === void 0 ? void 0 : _a.children;
213
+ // a 标签的子元素一定是 span 节点
214
+ const spanElement = (_b = aElement === null || aElement === void 0 ? void 0 : aElement.props) === null || _b === void 0 ? void 0 : _b.children;
215
+ const handleUrlClick = () => {
216
+ var _a;
217
+ if (!((_a = aElement === null || aElement === void 0 ? void 0 : aElement.props) === null || _a === void 0 ? void 0 : _a.href)) {
218
+ return;
219
+ }
220
+ if (handleOpenLink) {
221
+ handleOpenLink === null || handleOpenLink === void 0 ? void 0 : handleOpenLink(aElement.props.href);
222
+ }
223
+ else {
224
+ defaultOpenLink(aElement.props.href);
225
+ }
226
+ };
227
+ return (react_1.default.createElement(balloon_1.default, { v2: true, align: "b", className: "link-reference-balloon", closable: false, offset: [0, -8], triggerType: ['hover'], trigger: react_1.default.createElement("span", { className: "link-reference-index", onClick: handleUrlClick }, (_c = spanElement === null || spanElement === void 0 ? void 0 : spanElement.props) === null || _c === void 0 ? void 0 : _c.children) },
228
+ react_1.default.createElement("div", { className: "link-reference-content" },
229
+ react_1.default.createElement("img", { className: "link-reference-source-icon", src: ((_d = element.props) === null || _d === void 0 ? void 0 : _d['data-source-icon']) || '' }),
230
+ react_1.default.createElement("a", { className: "link-reference-title", onClick: handleUrlClick, title: ((_e = element.props) === null || _e === void 0 ? void 0 : _e['data-title']) || '' }, ((_f = element.props) === null || _f === void 0 ? void 0 : _f['data-title']) || ''))));
231
+ }
232
+ if (name === 'a') {
233
+ const element = ((0, html_react_parser_1.domToReact)([domNode]));
234
+ const { props } = element;
235
+ if (props.href && props.href.startsWith('message://')) {
236
+ const msgValue = decodeURIComponent(props.href.slice(10));
237
+ return react_1.default.cloneElement(element, {
238
+ onClick: () => {
239
+ if (sendTextMessage) {
240
+ sendTextMessage(msgValue);
241
+ return;
242
+ }
243
+ defaultSendMessage();
244
+ },
245
+ href: undefined
246
+ });
247
+ }
248
+ if (props.href && props.href.startsWith('copy://')) {
249
+ const copyValue = decodeURIComponent(props.href.slice(7));
250
+ return react_1.default.cloneElement(element, {
251
+ onClick: () => {
252
+ if (copyText) {
253
+ copyText(copyValue);
254
+ return;
255
+ }
256
+ defaultCopyText(copyValue);
257
+ },
258
+ href: undefined
259
+ });
260
+ }
261
+ // H5场景和钉协议场景都需要代理Click事件
262
+ if (props.href && (/^dtmd:\/\/dingtalkclient|^dingtalk:\/\/dingtalkclient\/action\/jumprobot|^(https?:)?\/\/qr.dingtalk.com\/action\/jumprobot|^https:\/\/applink\.dingtalk\.com\/page\/link/.test(props.href) || isMobile)) {
263
+ return react_1.default.cloneElement(element, {
264
+ onClick: () => {
265
+ if (handleOpenLink) {
266
+ handleOpenLink === null || handleOpenLink === void 0 ? void 0 : handleOpenLink(props.href);
267
+ return;
268
+ }
269
+ defaultOpenLink(props.href);
270
+ },
271
+ href: undefined
272
+ });
273
+ }
274
+ return react_1.default.cloneElement(element, {
275
+ target: '_blank',
276
+ });
277
+ }
278
+ if (name === 'img') {
279
+ const element = ((0, html_react_parser_1.domToReact)([domNode]));
280
+ if (renderImage) {
281
+ return renderImage(element.props);
282
+ }
283
+ // 换成统一的图片渲染
284
+ return react_1.default.createElement(img_1.default, Object.assign({}, element.props, { imageClassName: (_g = element.props) === null || _g === void 0 ? void 0 : _g.className, enablePreview: imagePreview, onImageClick: () => {
285
+ handleImageClick === null || handleImageClick === void 0 ? void 0 : handleImageClick(element.props.src);
286
+ } }));
287
+ }
288
+ }
289
+ }
290
+ };
198
291
  return (0, html_react_parser_1.default)((0, sanitize_html_1.default)(children || '', sanitizeHtmlOptions), parserOptions);
199
292
  }, [children, typewriterEffect]);
200
293
  return (react_1.default.createElement("div", { className: (0, classnames_1.default)(`markdown-body ${utils_1.PREFIX_DEFAULT}html`, className, {
@@ -211,16 +211,15 @@
211
211
  }
212
212
 
213
213
  li>p {
214
- margin: 0.3rem 0;
214
+ line-height: 2.2;
215
215
  }
216
216
 
217
217
  p {
218
- margin: 0.3rem 0;
219
- line-height: 1.8;
218
+ line-height: 2.2;
220
219
  }
221
220
 
222
221
  span {
223
- line-height: 2;
222
+ line-height: 2.2;
224
223
  }
225
224
 
226
225
  blockquote {
package/lib/index.js CHANGED
@@ -70,4 +70,4 @@ var flip_1 = require("./flip");
70
70
  Object.defineProperty(exports, "Flip", { enumerable: true, get: function () { return tslib_1.__importDefault(flip_1).default; } });
71
71
  var tool_status_1 = require("./tool-status");
72
72
  Object.defineProperty(exports, "ToolStatus", { enumerable: true, get: function () { return tslib_1.__importDefault(tool_status_1).default; } });
73
- exports.version = '0.3.38';
73
+ exports.version = '0.3.40-beta.0';
package/lib/text/index.js CHANGED
@@ -27,13 +27,19 @@ function useCollapseText({ enable, boxRef, textRef, maxLine, placeholder = '...'
27
27
  if (lineHeight === 'normal') {
28
28
  // 向容器里添加一段文本来测试行高
29
29
  const span = document.createElement('span');
30
- span.style.whiteSpace = 'nowrap';
31
- span.innerText = 'test';
32
- span.style.visibility = 'hidden';
33
- dom.appendChild(span);
34
- const height = span.offsetHeight;
35
- dom.removeChild(span);
36
- return height;
30
+ try {
31
+ span.style.whiteSpace = 'nowrap';
32
+ span.innerText = 'test';
33
+ span.style.visibility = 'hidden';
34
+ dom.appendChild(span);
35
+ return span.offsetHeight;
36
+ }
37
+ finally {
38
+ // 确保无论是否发生错误,都会尝试移除子节点
39
+ if (span.parentNode === dom) {
40
+ dom.removeChild(span);
41
+ }
42
+ }
37
43
  }
38
44
  return parseFloat(lineHeight);
39
45
  }, []);
@@ -186,16 +192,22 @@ function useEnsureBoxFullLine({ enableRef, boxRef, modifierRef, before, after, }
186
192
  ensureTriggerEnd(box, modifier);
187
193
  });
188
194
  }
195
+ // 修改 useResize Hook
189
196
  function useResize(domRef, onResize, shouldTriggerRef) {
190
197
  const sizeRef = (0, react_1.useRef)();
191
198
  const debounceOnResize = (0, utils_1.useDebounce)(onResize, 50);
192
- const observer = (0, react_1.useMemo)(() => {
193
- return new ResizeObserver(() => {
194
- const dom = domRef.current;
195
- if (!dom || !shouldTriggerRef.current) {
199
+ // observer 不再使用 useMemo,而是在 useEffect 中创建和销毁
200
+ (0, react_1.useEffect)(() => {
201
+ const dom = domRef.current;
202
+ if (!dom) {
203
+ return;
204
+ }
205
+ const observer = new ResizeObserver(() => {
206
+ // 回调函数逻辑不变
207
+ if (!domRef.current || !shouldTriggerRef.current) {
196
208
  return;
197
209
  }
198
- const { offsetWidth, offsetHeight } = dom;
210
+ const { offsetWidth, offsetHeight } = domRef.current;
199
211
  const lastSize = sizeRef.current;
200
212
  const newSize = { width: offsetWidth, height: offsetHeight };
201
213
  if (!lastSize ||
@@ -205,18 +217,51 @@ function useResize(domRef, onResize, shouldTriggerRef) {
205
217
  }
206
218
  sizeRef.current = newSize;
207
219
  });
208
- }, []);
209
- (0, react_1.useEffect)(() => {
210
- const dom = domRef.current;
211
- if (!dom) {
212
- return;
213
- }
214
220
  observer.observe(dom);
221
+ // 清理函数现在负责销毁 observer 实例
215
222
  return () => {
216
- observer.unobserve(dom);
223
+ // unobserve 不是必须的,因为 disconnect 会停止所有观察
224
+ observer.disconnect();
217
225
  };
218
- }, [domRef.current, observer]);
226
+ }, [domRef, debounceOnResize, shouldTriggerRef]); // 依赖项也需要更新
219
227
  }
228
+ // function useResizeOld(
229
+ // domRef: RefObject<HTMLElement>,
230
+ // onResize: () => void,
231
+ // shouldTriggerRef: MutableRefObject<boolean>
232
+ // ) {
233
+ // const sizeRef = useRef<{ width: number; height: number }>();
234
+ // const debounceOnResize = useDebounce(onResize, 50);
235
+ // const observer = useMemo(() => {
236
+ // return new ResizeObserver(() => {
237
+ // const dom = domRef.current;
238
+ // if (!dom || !shouldTriggerRef.current) {
239
+ // return;
240
+ // }
241
+ // const { offsetWidth, offsetHeight } = dom;
242
+ // const lastSize = sizeRef.current;
243
+ // const newSize = { width: offsetWidth, height: offsetHeight };
244
+ // if (
245
+ // !lastSize ||
246
+ // newSize.width !== lastSize.width ||
247
+ // newSize.height !== lastSize.height
248
+ // ) {
249
+ // debounceOnResize();
250
+ // }
251
+ // sizeRef.current = newSize;
252
+ // });
253
+ // }, []);
254
+ // useEffect(() => {
255
+ // const dom = domRef.current;
256
+ // if (!dom) {
257
+ // return;
258
+ // }
259
+ // observer?.observe(dom);
260
+ // return () => {
261
+ // observer?.unobserve(dom);
262
+ // };
263
+ // }, [domRef.current, observer]);
264
+ // }
220
265
  function TextComp(props) {
221
266
  const { className, maxLine, renderTrigger, triggerType = 'click', locale, showTrigger = true, children, } = props;
222
267
  const collapsable = typeof maxLine === 'number' && maxLine > 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alifd/chat",
3
- "version": "0.3.38",
3
+ "version": "0.3.40-beta.0",
4
4
  "description": "A configurable component library for chat built on React.",
5
5
  "main": "lib/index.js",
6
6
  "module": "es/index.js",