@ant-design/agentic-ui 2.29.37 → 2.29.39

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 (29) hide show
  1. package/dist/Hooks/useLanguage.d.ts +21 -0
  2. package/dist/I18n/locales.d.ts +22 -3
  3. package/dist/I18n/locales.js +51 -5
  4. package/dist/MarkdownEditor/editor/elements/index.js +15 -1
  5. package/dist/MarkdownEditor/editor/parser/constants.d.ts +5 -0
  6. package/dist/MarkdownEditor/editor/parser/constants.js +4 -0
  7. package/dist/MarkdownEditor/editor/parser/parserSlateNodeToMarkdown.js +5 -4
  8. package/dist/MarkdownEditor/editor/parser/remarkParse.d.ts +5 -0
  9. package/dist/MarkdownEditor/editor/parser/remarkParse.js +16 -0
  10. package/dist/MarkdownEditor/editor/plugins/useHighlight.js +53 -10
  11. package/dist/MarkdownEditor/editor/plugins/useKeyboard.js +2 -2
  12. package/dist/MarkdownEditor/editor/style.js +1 -1
  13. package/dist/MarkdownEditor/editor/tools/JinjaTemplatePanel/index.js +35 -23
  14. package/dist/MarkdownEditor/editor/tools/JinjaTemplatePanel/style.js +47 -37
  15. package/dist/MarkdownEditor/editor/tools/JinjaTemplatePanel/templates.d.ts +13 -1
  16. package/dist/MarkdownEditor/editor/tools/JinjaTemplatePanel/templates.js +30 -11
  17. package/dist/MarkdownEditor/editor/utils/markdownToHtml.js +5 -3
  18. package/dist/MarkdownInputField/RefinePromptButton/index.js +4 -2
  19. package/dist/Plugins/chart/ChartRender.js +4 -2
  20. package/dist/Plugins/chart/DonutChart/Legend.js +132 -12
  21. package/dist/Plugins/chart/DonutChart/index.js +58 -6
  22. package/dist/Plugins/chart/DonutChart/plugins.d.ts +5 -0
  23. package/dist/Plugins/chart/DonutChart/plugins.js +48 -0
  24. package/dist/Plugins/chart/DonutChart/style.js +17 -6
  25. package/dist/Plugins/chart/DonutChart/types.d.ts +2 -0
  26. package/dist/Plugins/chart/components/ChartContainer/ChartErrorBoundary.d.ts +44 -2
  27. package/dist/Plugins/code/CodeUI/Katex/katex.min.css +1 -3
  28. package/dist/Plugins/katex/katex.min.css +1 -3
  29. package/package.json +3 -1
@@ -1,5 +1,17 @@
1
+ import type { LocalKeys } from '../../../../I18n';
1
2
  import type { JinjaTemplateItem } from '../../../types';
2
- /** 内置 Jinja 模板数据,与 agent-ui-pc JinjaTemplateData 文案一致 */
3
+ declare const JINJA_TEMPLATE_IDS: readonly ["variableInterpolation", "condition", "loop", "filter", "setVariable"];
4
+ /** 内置 Jinja 模板基础数据(不含 i18n 文案) */
5
+ export declare const JINJA_TEMPLATE_BASE: {
6
+ id: (typeof JINJA_TEMPLATE_IDS)[number];
7
+ template: string;
8
+ }[];
9
+ /**
10
+ * 根据 locale 生成带国际化文案的 Jinja 模板列表
11
+ */
12
+ export declare function getJinjaTemplateData(locale: LocalKeys): JinjaTemplateItem[];
13
+ /** 内置 Jinja 模板数据(默认中文),与 agent-ui-pc JinjaTemplateData 文案一致 */
3
14
  export declare const JINJA_TEMPLATE_DATA: JinjaTemplateItem[];
4
15
  /** 默认使用说明链接,未配置 jinja.docLink 时使用 */
5
16
  export declare const JINJA_DOC_LINK = "https://jinja.palletsprojects.com/";
17
+ export {};
@@ -1,28 +1,47 @@
1
- /** 内置 Jinja 模板数据,与 agent-ui-pc JinjaTemplateData 文案一致 */ export var JINJA_TEMPLATE_DATA = [
1
+ import { cnLabels } from "../../../../I18n";
2
+ var JINJA_TEMPLATE_IDS = [
3
+ 'variableInterpolation',
4
+ 'condition',
5
+ 'loop',
6
+ 'filter',
7
+ 'setVariable'
8
+ ];
9
+ /** 内置 Jinja 模板基础数据(不含 i18n 文案) */ export var JINJA_TEMPLATE_BASE = [
2
10
  {
3
- title: '变量插值',
4
- description: '{{ variable }}',
11
+ id: 'variableInterpolation',
5
12
  template: '{{ }}'
6
13
  },
7
14
  {
8
- title: '条件语句',
9
- description: '{% if condition %}...{% endif %}',
15
+ id: 'condition',
10
16
  template: '{% if %}\n \n{% endif %}'
11
17
  },
12
18
  {
13
- title: '循环遍历',
14
- description: '{% for item in list %}...{% endfor %}',
19
+ id: 'loop',
15
20
  template: '{% for in %}\n \n{% endfor %}'
16
21
  },
17
22
  {
18
- title: '过滤器',
19
- description: '{{ value | filter }}',
23
+ id: 'filter',
20
24
  template: '{{ | }}'
21
25
  },
22
26
  {
23
- title: '设置变量',
24
- description: '{% set name = value %}',
27
+ id: 'setVariable',
25
28
  template: '{% set = %}'
26
29
  }
27
30
  ];
31
+ /**
32
+ * 根据 locale 生成带国际化文案的 Jinja 模板列表
33
+ */ export function getJinjaTemplateData(locale) {
34
+ return JINJA_TEMPLATE_BASE.map(function(param) {
35
+ var id = param.id, template = param.template;
36
+ var _locale_titleKey, _locale_descKey;
37
+ var titleKey = "jinja.template.".concat(id, ".title");
38
+ var descKey = "jinja.template.".concat(id, ".description");
39
+ return {
40
+ title: (_locale_titleKey = locale[titleKey]) !== null && _locale_titleKey !== void 0 ? _locale_titleKey : id,
41
+ description: (_locale_descKey = locale[descKey]) !== null && _locale_descKey !== void 0 ? _locale_descKey : undefined,
42
+ template: template
43
+ };
44
+ });
45
+ }
46
+ /** 内置 Jinja 模板数据(默认中文),与 agent-ui-pc JinjaTemplateData 文案一致 */ export var JINJA_TEMPLATE_DATA = getJinjaTemplateData(cnLabels);
28
47
  /** 默认使用说明链接,未配置 jinja.docLink 时使用 */ export var JINJA_DOC_LINK = 'https://jinja.palletsprojects.com/';
@@ -170,7 +170,8 @@ import remarkParse from "remark-parse";
170
170
  import remarkRehype from "remark-rehype";
171
171
  import { unified } from "unified";
172
172
  import { visit } from "unist-util-visit";
173
- import { convertParagraphToImage, fixStrongWithSpecialChars } from "../parser/remarkParse";
173
+ import { JINJA_DOLLAR_PLACEHOLDER } from "../parser/constants";
174
+ import { convertParagraphToImage, fixStrongWithSpecialChars, protectJinjaDollarInText } from "../parser/remarkParse";
174
175
  // HTML 转义相关的正则表达式和工具
175
176
  var ESCAPE_TEST_NO_ENCODE = /[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/;
176
177
  var ESCAPE_TEST = /[&<>"']/;
@@ -305,6 +306,7 @@ export var DEFAULT_MARKDOWN_REMARK_PLUGINS = [
305
306
  ],
306
307
  fixStrongWithSpecialChars,
307
308
  convertParagraphToImage,
309
+ protectJinjaDollarInText,
308
310
  [
309
311
  remarkMath,
310
312
  INLINE_MATH_WITH_SINGLE_DOLLAR
@@ -414,7 +416,7 @@ var createMarkdownProcessor = function createMarkdownProcessor(plugins, config)
414
416
  htmlContent = _state.sent();
415
417
  return [
416
418
  2,
417
- String(htmlContent)
419
+ String(htmlContent).split(JINJA_DOLLAR_PLACEHOLDER).join('$')
418
420
  ];
419
421
  case 2:
420
422
  error = _state.sent();
@@ -463,7 +465,7 @@ var createMarkdownProcessor = function createMarkdownProcessor(plugins, config)
463
465
  */ export var markdownToHtmlSync = function markdownToHtmlSync(markdown, plugins, config) {
464
466
  try {
465
467
  var file = createMarkdownProcessor(plugins, config).processSync(markdown);
466
- return String(file);
468
+ return String(file).split(JINJA_DOLLAR_PLACEHOLDER).join('$');
467
469
  } catch (error) {
468
470
  console.error('Error converting markdown to HTML:', error);
469
471
  return '';
@@ -2,6 +2,7 @@ import { LoadingOutlined } from "@ant-design/icons";
2
2
  import { TextOptimize } from "@sofa-design/icons";
3
3
  import { ConfigProvider, Tooltip } from "antd";
4
4
  import React, { useContext } from "react";
5
+ import { useLocale } from "../../I18n";
5
6
  import { ErrorBoundary } from "react-error-boundary";
6
7
  import { ActionIconBox } from "../../Components/ActionIconBox";
7
8
  import { isBrowserEnv } from "./env";
@@ -9,6 +10,7 @@ import { useStyle } from "./style";
9
10
  export var RefinePromptButton = function RefinePromptButton(props) {
10
11
  var disabled = props.disabled, status = props.status, onRefine = props.onRefine;
11
12
  var getPrefixCls = useContext(ConfigProvider.ConfigContext).getPrefixCls;
13
+ var locale = useLocale();
12
14
  var baseCls = getPrefixCls('agentic-md-input-field-refine-button');
13
15
  var wrapSSR = useStyle(baseCls).wrapSSR;
14
16
  var handleClick = function handleClick() {
@@ -24,9 +26,9 @@ export var RefinePromptButton = function RefinePromptButton(props) {
24
26
  return null;
25
27
  }
26
28
  return wrapSSR(/*#__PURE__*/ React.createElement(Tooltip, {
27
- title: status === 'loading' ? '优化中' : '一键优化提示词'
29
+ title: status === 'loading' ? locale['refine.loading'] : locale['refine.oneClickOptimize']
28
30
  }, /*#__PURE__*/ React.createElement(ActionIconBox, {
29
- title: '优化提示词',
31
+ title: locale['refine.optimizePrompt'],
30
32
  onClick: handleClick,
31
33
  "data-testid": "refine-prompt-button"
32
34
  }, /*#__PURE__*/ React.createElement(ErrorBoundary, {
@@ -345,6 +345,7 @@ import { debounce, getDataHash, isConfigEqual, isNotEmpty, toNumber } from "./ut
345
345
  * 负责使用已加载的 runtime 渲染图表
346
346
  */ var ChartRuntimeRendererImpl = function ChartRuntimeRendererImpl(param) {
347
347
  var chartType = param.chartType, runtime = param.runtime, convertDonutData = param.convertDonutData, convertFlatData = param.convertFlatData, config = param.config, title = param.title, dataTime = param.dataTime, toolBar = param.toolBar, filterBy = param.filterBy, groupBy = param.groupBy, colorLegend = param.colorLegend, chartData = param.chartData, getFieldValue = param.getFieldValue, _param_loading = param.loading, loading = _param_loading === void 0 ? false : _param_loading;
348
+ var i18n = useContext(I18nContext);
348
349
  var DonutChart = runtime.DonutChart, FunnelChart = runtime.FunnelChart, AreaChart = runtime.AreaChart, BarChart = runtime.BarChart, LineChart = runtime.LineChart, RadarChart = runtime.RadarChart, ScatterChart = runtime.ScatterChart;
349
350
  if (chartType === 'pie') {
350
351
  return /*#__PURE__*/ React.createElement(DonutChart, {
@@ -505,6 +506,7 @@ import { debounce, getDataHash, isConfigEqual, isNotEmpty, toNumber } from "./ut
505
506
  });
506
507
  }
507
508
  if (chartType === 'funnel') {
509
+ var _i18n_locale, _i18n_locale1;
508
510
  var funnelData = (chartData || []).map(function(row, i) {
509
511
  var filterLabel = getFieldValue(row, filterBy);
510
512
  var category = getFieldValue(row, groupBy);
@@ -532,8 +534,8 @@ import { debounce, getDataHash, isConfigEqual, isNotEmpty, toNumber } from "./ut
532
534
  title: title || '',
533
535
  dataTime: dataTime,
534
536
  typeNames: {
535
- rate: '转化率',
536
- name: colorLegend || '转化'
537
+ rate: (i18n === null || i18n === void 0 ? void 0 : (_i18n_locale = i18n.locale) === null || _i18n_locale === void 0 ? void 0 : _i18n_locale['common.conversionRate']) || '转化率',
538
+ name: colorLegend || (i18n === null || i18n === void 0 ? void 0 : (_i18n_locale1 = i18n.locale) === null || _i18n_locale1 === void 0 ? void 0 : _i18n_locale1['common.conversion']) || '转化'
537
539
  },
538
540
  toolbarExtra: toolBar,
539
541
  loading: loading
@@ -1,3 +1,11 @@
1
+ function _array_like_to_array(arr, len) {
2
+ if (len == null || len > arr.length) len = arr.length;
3
+ for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
4
+ return arr2;
5
+ }
6
+ function _array_with_holes(arr) {
7
+ if (Array.isArray(arr)) return arr;
8
+ }
1
9
  function _define_property(obj, key, value) {
2
10
  if (key in obj) {
3
11
  Object.defineProperty(obj, key, {
@@ -11,6 +19,33 @@ function _define_property(obj, key, value) {
11
19
  }
12
20
  return obj;
13
21
  }
22
+ function _iterable_to_array_limit(arr, i) {
23
+ var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];
24
+ if (_i == null) return;
25
+ var _arr = [];
26
+ var _n = true;
27
+ var _d = false;
28
+ var _s, _e;
29
+ try {
30
+ for(_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true){
31
+ _arr.push(_s.value);
32
+ if (i && _arr.length === i) break;
33
+ }
34
+ } catch (err) {
35
+ _d = true;
36
+ _e = err;
37
+ } finally{
38
+ try {
39
+ if (!_n && _i["return"] != null) _i["return"]();
40
+ } finally{
41
+ if (_d) throw _e;
42
+ }
43
+ }
44
+ return _arr;
45
+ }
46
+ function _non_iterable_rest() {
47
+ throw new TypeError("Invalid attempt to destructure non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
48
+ }
14
49
  function _object_spread(target) {
15
50
  for(var i = 1; i < arguments.length; i++){
16
51
  var source = arguments[i] != null ? arguments[i] : {};
@@ -26,32 +61,76 @@ function _object_spread(target) {
26
61
  }
27
62
  return target;
28
63
  }
29
- import React from "react";
64
+ function _sliced_to_array(arr, i) {
65
+ return _array_with_holes(arr) || _iterable_to_array_limit(arr, i) || _unsupported_iterable_to_array(arr, i) || _non_iterable_rest();
66
+ }
67
+ function _unsupported_iterable_to_array(o, minLen) {
68
+ if (!o) return;
69
+ if (typeof o === "string") return _array_like_to_array(o, minLen);
70
+ var n = Object.prototype.toString.call(o).slice(8, -1);
71
+ if (n === "Object" && o.constructor) n = o.constructor.name;
72
+ if (n === "Map" || n === "Set") return Array.from(n);
73
+ if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array(o, minLen);
74
+ }
75
+ import React, { useEffect, useState } from "react";
76
+ import { useLocale } from "../../../I18n";
77
+ /** 图例每页显示条数,超过则显示分页 */ var LEGEND_PAGE_SIZE = 12;
30
78
  var Legend = function Legend(param) {
31
79
  var chartData = param.chartData, backgroundColors = param.backgroundColors, hiddenDataIndicesByChart = param.hiddenDataIndicesByChart, chartIndex = param.chartIndex, onLegendItemClick = param.onLegendItemClick, total = param.total, baseClassName = param.baseClassName, hashId = param.hashId, isMobile = param.isMobile;
80
+ var locale = useLocale();
32
81
  var hiddenDataIndices = React.useMemo(function() {
33
82
  return hiddenDataIndicesByChart[chartIndex] || new Set();
34
83
  }, [
35
84
  hiddenDataIndicesByChart,
36
85
  chartIndex
37
86
  ]);
87
+ var totalPages = Math.max(1, Math.ceil(chartData.length / LEGEND_PAGE_SIZE));
88
+ var _useState = _sliced_to_array(useState(0), 2), currentPage = _useState[0], setCurrentPage = _useState[1];
89
+ useEffect(function() {
90
+ setCurrentPage(0);
91
+ }, [
92
+ chartData.length,
93
+ chartIndex
94
+ ]);
95
+ var startIndex = currentPage * LEGEND_PAGE_SIZE;
96
+ var displayedItems = chartData.slice(startIndex, startIndex + LEGEND_PAGE_SIZE);
97
+ var handlePrevPage = function handlePrevPage() {
98
+ setCurrentPage(function(p) {
99
+ return Math.max(0, p - 1);
100
+ });
101
+ };
102
+ var handleNextPage = function handleNextPage() {
103
+ setCurrentPage(function(p) {
104
+ return Math.min(totalPages - 1, p + 1);
105
+ });
106
+ };
38
107
  return /*#__PURE__*/ React.createElement("div", {
39
108
  className: [
40
109
  "".concat(baseClassName, "-legend"),
41
110
  hashId
42
111
  ].filter(Boolean).join(' '),
43
112
  style: _object_spread({
44
- marginLeft: isMobile ? 0 : 12,
45
- maxHeight: isMobile ? '120px' : 'none',
46
- overflowY: isMobile ? 'auto' : 'visible'
113
+ marginLeft: isMobile ? 0 : 12
47
114
  }, isMobile ? {
115
+ display: 'flex',
116
+ flexDirection: 'column',
117
+ maxHeight: '200px',
118
+ minHeight: 0,
48
119
  alignSelf: 'center'
49
120
  } : {})
50
- }, chartData.map(function(d, i) {
51
- var isHidden = hiddenDataIndices.has(i);
121
+ }, /*#__PURE__*/ React.createElement("div", {
122
+ style: _object_spread({}, isMobile ? {
123
+ flex: 1,
124
+ minHeight: 0,
125
+ overflowY: 'auto',
126
+ overflowX: 'hidden'
127
+ } : {})
128
+ }, displayedItems.map(function(d, i) {
129
+ var dataIndex = startIndex + i;
130
+ var isHidden = hiddenDataIndices.has(dataIndex);
52
131
  var _obj;
53
132
  return /*#__PURE__*/ React.createElement("div", {
54
- key: i,
133
+ key: dataIndex,
55
134
  className: [
56
135
  "".concat(baseClassName, "-legend-item"),
57
136
  hashId
@@ -64,23 +143,23 @@ var Legend = function Legend(param) {
64
143
  textDecoration: isHidden ? 'line-through' : 'none'
65
144
  },
66
145
  onClick: function onClick() {
67
- return onLegendItemClick(i);
146
+ return onLegendItemClick(dataIndex);
68
147
  },
69
148
  onKeyDown: function onKeyDown(e) {
70
149
  if (e.key === 'Enter' || e.key === ' ') {
71
150
  e.preventDefault();
72
- onLegendItemClick(i);
151
+ onLegendItemClick(dataIndex);
73
152
  }
74
153
  },
75
154
  tabIndex: 0,
76
155
  role: "button",
77
- "aria-label": "".concat(isHidden ? '显示' : '隐藏', " ").concat(d.label)
156
+ "aria-label": "".concat(isHidden ? locale['chart.legend.show'] : locale['chart.legend.hide'], " ").concat(d.label)
78
157
  }, /*#__PURE__*/ React.createElement("span", {
79
158
  className: [
80
159
  "".concat(baseClassName, "-legend-color"),
81
160
  hashId
82
161
  ].filter(Boolean).join(' '),
83
- style: (_obj = {}, _define_property(_obj, '--donut-legend-color', backgroundColors[i] || '#ccc'), _define_property(_obj, "width", isMobile ? 10 : 12), _define_property(_obj, "height", isMobile ? 10 : 12), _define_property(_obj, "borderRadius", 4), _define_property(_obj, "marginRight", isMobile ? 4 : 6), _obj)
162
+ style: (_obj = {}, _define_property(_obj, '--donut-legend-color', backgroundColors[dataIndex] || '#ccc'), _define_property(_obj, "width", isMobile ? 10 : 12), _define_property(_obj, "height", isMobile ? 10 : 12), _define_property(_obj, "borderRadius", 4), _define_property(_obj, "marginRight", isMobile ? 4 : 6), _obj)
84
163
  }), /*#__PURE__*/ React.createElement("span", {
85
164
  className: [
86
165
  "".concat(baseClassName, "-legend-label"),
@@ -118,6 +197,47 @@ var Legend = function Legend(param) {
118
197
  var v = typeof d.value === 'number' ? d.value : Number(d.value);
119
198
  return total > 0 && Number.isFinite(v) ? (v / total * 100).toFixed(0) : '0';
120
199
  }(), "%")));
121
- }));
200
+ })), totalPages > 1 && /*#__PURE__*/ React.createElement("div", {
201
+ className: [
202
+ "".concat(baseClassName, "-legend-pagination"),
203
+ hashId
204
+ ].filter(Boolean).join(' '),
205
+ style: isMobile ? {
206
+ flexShrink: 0
207
+ } : undefined
208
+ }, /*#__PURE__*/ React.createElement("button", {
209
+ type: "button",
210
+ "aria-label": locale['chart.legend.prevPage'],
211
+ disabled: currentPage <= 0,
212
+ onClick: handlePrevPage,
213
+ style: {
214
+ padding: '2px 6px',
215
+ fontSize: 12,
216
+ cursor: currentPage <= 0 ? 'not-allowed' : 'pointer',
217
+ opacity: currentPage <= 0 ? 0.5 : 1,
218
+ border: '1px solid rgba(0,0,0,0.15)',
219
+ borderRadius: 4,
220
+ background: '#fff'
221
+ }
222
+ }, "<"), /*#__PURE__*/ React.createElement("span", {
223
+ style: {
224
+ fontSize: 12,
225
+ color: '#767E8B'
226
+ }
227
+ }, currentPage + 1, "/", totalPages), /*#__PURE__*/ React.createElement("button", {
228
+ type: "button",
229
+ "aria-label": locale['chart.legend.nextPage'],
230
+ disabled: currentPage >= totalPages - 1,
231
+ onClick: handleNextPage,
232
+ style: {
233
+ padding: '2px 6px',
234
+ fontSize: 12,
235
+ cursor: currentPage >= totalPages - 1 ? 'not-allowed' : 'pointer',
236
+ opacity: currentPage >= totalPages - 1 ? 0.5 : 1,
237
+ border: '1px solid rgba(0,0,0,0.15)',
238
+ borderRadius: 4,
239
+ background: '#fff'
240
+ }
241
+ }, ">")));
122
242
  };
123
243
  export default Legend;
@@ -146,8 +146,10 @@ function _unsupported_iterable_to_array(o, minLen) {
146
146
  }
147
147
  import { ConfigProvider } from "antd";
148
148
  import { ArcElement, Chart as ChartJS, Legend, Tooltip } from "chart.js";
149
+ import ChartDataLabels from "chartjs-plugin-datalabels";
149
150
  import classNames from "clsx";
150
151
  import React, { useContext, useMemo, useRef, useState } from "react";
152
+ import { useLocale } from "../../../I18n";
151
153
  import { Doughnut } from "react-chartjs-2";
152
154
  import { ChartContainer, ChartFilter, ChartStatistic, ChartToolBar, downloadChart } from "../components";
153
155
  import { isWindowDefined } from "../env";
@@ -156,7 +158,7 @@ import { resolveCssVariable } from "../utils";
156
158
  import { SINGLE_MODE_DESKTOP_CUTOUT, SINGLE_MODE_MOBILE_CUTOUT } from "./constants";
157
159
  import { useAutoCategory, useFilterLabels, useMobile, useResponsiveDimensions } from "./hooks";
158
160
  import LegendView from "./Legend";
159
- import { createBackgroundArcPlugin, createCenterTextPlugin } from "./plugins";
161
+ import { createBackgroundArcPlugin, createCenterTextPlugin, createDataLabelsLeaderLinePlugin } from "./plugins";
160
162
  import { useStyle } from "./style";
161
163
  /**
162
164
  * @fileoverview 环形图组件文件
@@ -225,11 +227,12 @@ import { useStyle } from "./style";
225
227
  if (!isWindowDefined()) {
226
228
  return undefined;
227
229
  }
228
- ChartJS.register(ArcElement, Tooltip, Legend);
230
+ ChartJS.register(ArcElement, Tooltip, Legend, ChartDataLabels);
229
231
  donutChartComponentsRegistered = true;
230
232
  return undefined;
231
233
  }, []);
232
234
  var _useMobile = useMobile(), isMobile = _useMobile.isMobile, windowWidth = _useMobile.windowWidth;
235
+ var locale = useLocale();
233
236
  // 默认配置:当 configs 不传时,使用默认配置,showLegend 默认为 true
234
237
  var defaultConfigs = [
235
238
  {
@@ -454,7 +457,7 @@ import { useStyle } from "./style";
454
457
  chartData = [
455
458
  currentDataItem,
456
459
  {
457
- label: '剩余',
460
+ label: locale['chart.legend.remaining'],
458
461
  value: remainingValue,
459
462
  category: currentDataItem.category
460
463
  }
@@ -546,6 +549,9 @@ import { useStyle } from "./style";
546
549
  legend: {
547
550
  display: false
548
551
  },
552
+ /** 默认不展示扇区上的数值与占比,仅通过 configs[].showDataLabels === true 开启 */ datalabels: {
553
+ display: false
554
+ },
549
555
  tooltip: {
550
556
  enabled: cfg.showTooltip !== false,
551
557
  filter: function filter(tooltipItem) {
@@ -568,7 +574,10 @@ import { useStyle } from "./style";
568
574
  var label = param.label, raw = param.raw;
569
575
  var val = typeof raw === 'number' ? raw : Number(raw);
570
576
  var pct = total > 0 && Number.isFinite(val) ? (val / total * 100).toFixed(0) : '0';
571
- return "".concat(label, ": ").concat(Number.isFinite(val) ? val : raw, " (").concat(pct, "%)");
577
+ if (cfg.showDataLabels) {
578
+ return "".concat(label, ": ").concat(Number.isFinite(val) ? val : raw, " (").concat(pct, "%)");
579
+ }
580
+ return "".concat(label, " (").concat(pct, "%)");
572
581
  }
573
582
  }
574
583
  }
@@ -584,6 +593,43 @@ import { useStyle } from "./style";
584
593
  padding: isMobile ? 4 : 6
585
594
  }
586
595
  };
596
+ var dataLabelOffset = isMobile ? 22 : 36;
597
+ var optionsWithDataLabels = _object_spread_props(_object_spread({}, options, cfg.showDataLabels === true && !isSingleValueMode && {
598
+ layout: _object_spread_props(_object_spread({}, options.layout), {
599
+ padding: isMobile ? 36 : 52
600
+ })
601
+ }), {
602
+ plugins: _object_spread({}, options.plugins, cfg.showDataLabels === true && !isSingleValueMode && {
603
+ datalabels: {
604
+ display: function display(context) {
605
+ var _context_dataset_data, _context_dataset;
606
+ var value = (_context_dataset = context.dataset) === null || _context_dataset === void 0 ? void 0 : (_context_dataset_data = _context_dataset.data) === null || _context_dataset_data === void 0 ? void 0 : _context_dataset_data[context.dataIndex];
607
+ var numVal = typeof value === 'number' ? value : Number(value);
608
+ if (!Number.isFinite(numVal) || total <= 0) return false;
609
+ var pct = numVal / total * 100;
610
+ if (pct < 2) return false;
611
+ return true;
612
+ },
613
+ formatter: function formatter(value, context) {
614
+ var _context_chart_data_labels;
615
+ var pct = total > 0 ? (value / total * 100).toFixed(2) : '0';
616
+ var label = (_context_chart_data_labels = context.chart.data.labels) === null || _context_chart_data_labels === void 0 ? void 0 : _context_chart_data_labels[context.dataIndex];
617
+ var labelStr = label !== undefined && label !== null ? String(label) : '';
618
+ return labelStr ? "".concat(labelStr, ": ").concat(pct, "%") : "".concat(value, " (").concat(pct, "%)");
619
+ },
620
+ color: isDarkTheme ? '#fff' : '#343A45',
621
+ font: {
622
+ size: isMobile ? 10 : 11,
623
+ weight: 'normal'
624
+ },
625
+ anchor: 'end',
626
+ align: 'end',
627
+ offset: dataLabelOffset,
628
+ clamp: false,
629
+ clip: false
630
+ }
631
+ })
632
+ });
587
633
  var _obj, _obj1;
588
634
  return /*#__PURE__*/ React.createElement(ChartContainer, {
589
635
  key: idx,
@@ -622,10 +668,16 @@ import { useStyle } from "./style";
622
668
  chartRefs.current[idx] = instance;
623
669
  },
624
670
  data: chartJsData,
625
- options: options
671
+ options: optionsWithDataLabels,
672
+ plugins: cfg.showDataLabels === true && !isSingleValueMode ? [
673
+ createDataLabelsLeaderLinePlugin(dataLabelOffset),
674
+ ChartDataLabels
675
+ ] : []
626
676
  })), cfg.showLegend && /*#__PURE__*/ React.createElement(LegendView, {
627
677
  chartData: chartData,
628
- backgroundColors: backgroundColors,
678
+ backgroundColors: chartData.map(function(_, i) {
679
+ return backgroundColors[i % backgroundColors.length];
680
+ }),
629
681
  hiddenDataIndicesByChart: hiddenDataIndicesByChart,
630
682
  chartIndex: idx,
631
683
  onLegendItemClick: function onLegendItemClick(dataIndex) {
@@ -1,3 +1,8 @@
1
1
  import { Plugin } from 'chart.js';
2
+ /**
3
+ * 数据标签指示线插件:仅在会展示数据标签的扇区上绘制线(与 datalabels display 逻辑一致,避免无线无文案)。
4
+ * 传入的 lineLength 应与 options.plugins.datalabels.offset 一致。
5
+ */
6
+ export declare const createDataLabelsLeaderLinePlugin: (lineLength?: number) => Plugin<'doughnut'>;
2
7
  export declare const createCenterTextPlugin: (value: number, label: string, isMobile?: boolean) => Plugin<'doughnut'>;
3
8
  export declare const createBackgroundArcPlugin: (bgColor?: string, padding?: number) => Plugin<'doughnut'>;
@@ -1,3 +1,51 @@
1
+ /** 指示线默认长度(像素),需与 datalabels 的 offset 一致,使线头与文案衔接 */ var DEFAULT_LEADER_LINE_PX = 4;
2
+ /** 曲线控制点偏移系数,使指示线呈柔和弧线 */ var CURVE_OFFSET = 12;
3
+ /** 与 datalabels 的 display 阈值一致:占比不超过此不展示标签,也不画指示线 */ var MIN_PCT_FOR_LABEL = 2;
4
+ /**
5
+ * 数据标签指示线插件:仅在会展示数据标签的扇区上绘制线(与 datalabels display 逻辑一致,避免无线无文案)。
6
+ * 传入的 lineLength 应与 options.plugins.datalabels.offset 一致。
7
+ */ export var createDataLabelsLeaderLinePlugin = function createDataLabelsLeaderLinePlugin() {
8
+ var lineLength = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : DEFAULT_LEADER_LINE_PX;
9
+ return {
10
+ id: 'datalabelsLeaderLine',
11
+ afterDatasetDraw: function afterDatasetDraw(chart, args) {
12
+ var _chart_data_datasets, _meta_data;
13
+ var ctx = chart.ctx;
14
+ var meta = chart.getDatasetMeta(args.index);
15
+ var dataset = (_chart_data_datasets = chart.data.datasets) === null || _chart_data_datasets === void 0 ? void 0 : _chart_data_datasets[args.index];
16
+ var values = dataset === null || dataset === void 0 ? void 0 : dataset.data;
17
+ if (!(meta === null || meta === void 0 ? void 0 : (_meta_data = meta.data) === null || _meta_data === void 0 ? void 0 : _meta_data.length) || !(values === null || values === void 0 ? void 0 : values.length)) return;
18
+ var total = values.reduce(function(s, v) {
19
+ return s + (Number.isFinite(Number(v)) ? Number(v) : 0);
20
+ }, 0);
21
+ if (total <= 0) return;
22
+ ctx.save();
23
+ ctx.strokeStyle = 'rgba(0, 25, 61, 0.16)';
24
+ ctx.lineWidth = 1;
25
+ meta.data.forEach(function(element, i) {
26
+ var val = Number(values[i]);
27
+ if (!Number.isFinite(val) || val / total * 100 < MIN_PCT_FOR_LABEL) return;
28
+ var arc = element;
29
+ var angle = (arc.startAngle + arc.endAngle) / 2;
30
+ var cos = Math.cos(angle);
31
+ var sin = Math.sin(angle);
32
+ var x1 = arc.x + arc.outerRadius * cos;
33
+ var y1 = arc.y + arc.outerRadius * sin;
34
+ var x2 = arc.x + (arc.outerRadius + lineLength) * cos;
35
+ var y2 = arc.y + (arc.outerRadius + lineLength) * sin;
36
+ var midX = (x1 + x2) / 2;
37
+ var midY = (y1 + y2) / 2;
38
+ var ctrlX = midX + CURVE_OFFSET * -sin;
39
+ var ctrlY = midY + CURVE_OFFSET * cos;
40
+ ctx.beginPath();
41
+ ctx.moveTo(x1, y1);
42
+ ctx.quadraticCurveTo(ctrlX, ctrlY, x2, y2);
43
+ ctx.stroke();
44
+ });
45
+ ctx.restore();
46
+ }
47
+ };
48
+ };
1
49
  export var createCenterTextPlugin = function createCenterTextPlugin(value, label) {
2
50
  var isMobile = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : false;
3
51
  return {
@@ -77,20 +77,24 @@ var genStyle = function genStyle(token) {
77
77
  gap: 8,
78
78
  marginBottom: 6
79
79
  }),
80
- '&-chart-wrapper': _define_property({}, "@media (max-width: 768px)", {
80
+ '&-chart-wrapper': _define_property({
81
+ overflow: 'visible'
82
+ }, "@media (max-width: 768px)", {
81
83
  marginBottom: 12
82
84
  }),
83
85
  '&-row': _define_property({
84
86
  display: 'flex',
85
87
  alignItems: 'center',
86
- flexDirection: 'row'
88
+ flexDirection: 'row',
89
+ overflow: 'visible'
87
90
  }, "@media (max-width: 768px)", {
88
91
  flexDirection: 'column',
89
92
  alignItems: 'stretch'
90
93
  }),
91
94
  '&-chart': _define_property({
92
95
  width: 'var(--donut-chart-width, 200px)',
93
- height: 'var(--donut-chart-height, 200px)'
96
+ height: 'var(--donut-chart-height, 200px)',
97
+ overflow: 'visible'
94
98
  }, "@media (max-width: 768px)", {
95
99
  alignSelf: 'center',
96
100
  marginBottom: 8
@@ -105,9 +109,7 @@ var genStyle = function genStyle(token) {
105
109
  marginLeft: 12
106
110
  }, "@media (max-width: 768px)", {
107
111
  marginLeft: 0,
108
- marginTop: 8,
109
- maxHeight: '120px',
110
- overflowY: 'auto'
112
+ marginTop: 8
111
113
  }),
112
114
  '&-legend-item': _define_property({
113
115
  display: 'flex',
@@ -168,6 +170,15 @@ var genStyle = function genStyle(token) {
168
170
  fontSize: 10,
169
171
  marginTop: 1
170
172
  }),
173
+ '&-legend-pagination': {
174
+ display: 'flex',
175
+ alignItems: 'center',
176
+ justifyContent: 'center',
177
+ gap: 8,
178
+ marginTop: 8,
179
+ paddingTop: 8,
180
+ borderTop: '1px solid rgba(0,0,0,0.06)'
181
+ },
171
182
  '&-statistic-container': {
172
183
  display: 'flex',
173
184
  gap: '16px',
@@ -19,6 +19,8 @@ export interface DonutChartConfig {
19
19
  borderColor?: string;
20
20
  /** 图表样式:'donut' 为环形图(默认),'pie' 为饼图 */
21
21
  chartStyle?: 'donut' | 'pie';
22
+ /** 是否展示数据标签与指示线:开启后扇区外显示数值/占比及连接线,tooltip 中也会展示原始数值,默认 false */
23
+ showDataLabels?: boolean;
22
24
  }
23
25
  export interface DonutChartProps extends ChartContainerProps {
24
26
  data: DonutChartData[];